Merge branch 'release-3.21' into release-3.22
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2ead13b..06570d9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,5 @@
 include:
-    # Metadata shared my many jobs
+    # Metadata shared by many jobs
     - local: .gitlab/rules.yml
     - local: .gitlab/artifacts.yml
 
@@ -34,9 +34,16 @@
 #   - dependency/needs jobs for required jobs
 ################################################################################
 
+# Job prefixes:
+#   - `b:` build
+#   - `l:` lint
+#   - `p:` prep
+#   - `t:` test
+#   - `u:` upload
+
 # Prep jobs
 
-prep:source-package:
+p:source-package:
     extends:
         - .linux_prep_source
         - .cmake_prep_source_linux
@@ -44,7 +51,7 @@
         - .cmake_release_artifacts
         - .run_only_for_package
 
-prep:doc-package:
+p:doc-package:
     extends:
         - .fedora34_sphinx_package
         - .cmake_prep_doc_linux
@@ -52,74 +59,74 @@
         - .cmake_doc_artifacts
         - .run_only_for_package
 
-.upload:source-package:
+.u:source-package:
     extends:
         - .rsync_upload_binary
         - .run_only_for_package
     dependencies:
-        - prep:source-package
+        - p:source-package
     needs:
-        - prep:source-package
+        - p:source-package
     variables:
         RSYNC_DESTINATION: dev
 
 # Documentation builds
 
-build:help:master:
+b:help:master:
     extends:
         - .cmake_org_help
         - .run_only_for_continuous_master
 
-.upload:help:master:
+.u:help:master:
     extends:
         - .rsync_upload_help
         - .run_only_for_continuous_master
     variables:
         RSYNC_DESTINATION: git-master
     dependencies:
-        - build:help:master
+        - b:help:master
     needs:
-        - build:help:master
+        - b:help:master
 
-build:help:stage:
+b:help:stage:
     extends:
         - .cmake_org_help
         - .run_only_for_continuous_stage
 
-.upload:help:stage:
+.u:help:stage:
     extends:
         - .rsync_upload_help
         - .run_only_for_continuous_stage
     variables:
         RSYNC_DESTINATION: git-stage
     dependencies:
-        - build:help:stage
+        - b:help:stage
     needs:
-        - build:help:stage
+        - b:help:stage
 
 # Lint builds
 
-lint:codespell:
+l:codespell:
     extends:
         - .cmake_codespell_linux
         - .linux_builder_tags
         - .run_automatically
 
-lint:debian10-iwyu:
+l:iwyu-debian10:
     extends:
         - .debian10_iwyu
         - .cmake_build_linux
         - .linux_builder_tags
         - .run_automatically
 
-lint:fedora34-tidy:
+l:tidy-fedora34:
     extends:
         - .fedora34_tidy
         - .cmake_build_linux
         - .linux_builder_tags_qt
         - .run_automatically
 
-lint:fedora34-sphinx:
+l:sphinx-fedora34:
     extends:
         - .fedora34_sphinx
         - .cmake_build_linux
@@ -129,7 +136,7 @@
         CMAKE_CI_JOB_CONTINUOUS: "true"
         CMAKE_CI_JOB_HELP: "true"
 
-lint:fedora34-clang-analyzer:
+l:clang-analyzer-fedora34:
     extends:
         - .fedora34_clang_analyzer
         - .cmake_build_linux
@@ -140,7 +147,7 @@
 
 # Linux builds
 
-build:centos6-x86_64:
+b:centos6-x86_64:
     extends:
         - .linux_release_x86_64
         - .cmake_build_linux_release
@@ -148,7 +155,7 @@
         - .linux_builder_tags
         - .run_manually
 
-build:centos7-aarch64:
+b:centos7-aarch64:
     extends:
         - .linux_release_aarch64
         - .cmake_build_linux_release
@@ -158,7 +165,7 @@
     variables:
         CMAKE_CI_NO_MR: "true"
 
-test:debian10-ninja:
+t:debian10-ninja:
     extends:
         - .debian10_ninja
         - .cmake_test_linux_release
@@ -166,8 +173,10 @@
         - .cmake_junit_artifacts
         - .run_dependent
         - .needs_centos6_x86_64
+    variables:
+        CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
 
-test:debian10-aarch64-ninja:
+t:debian10-aarch64-ninja:
     extends:
         - .debian10_aarch64_ninja
         - .cmake_test_linux_release
@@ -178,7 +187,7 @@
     variables:
         CMAKE_CI_NO_MR: "true"
 
-test:debian10-ninja-clang:
+t:debian10-ninja-clang:
     extends:
         - .debian10_ninja_clang
         - .cmake_test_linux_release
@@ -188,7 +197,7 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-test:debian10-makefiles-clang:
+t:debian10-makefiles-clang:
     extends:
         - .debian10_makefiles_clang
         - .cmake_test_linux_release
@@ -198,7 +207,7 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-test:fedora34-makefiles:
+t:fedora34-makefiles:
     extends:
         - .fedora34_makefiles
         - .cmake_test_linux_release
@@ -206,7 +215,7 @@
         - .run_dependent
         - .needs_centos6_x86_64
 
-test:fedora34-makefiles-nospace:
+t:fedora34-makefiles-nospace:
     extends:
         - .fedora34_makefiles
         - .cmake_test_linux_release
@@ -219,7 +228,17 @@
         CMAKE_CI_BUILD_NAME: fedora34_makefiles_nospace
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-test:cuda10.2-nvidia:
+t:cuda9.2-nvidia:
+    extends:
+        - .cuda9.2_nvidia
+        - .cmake_test_linux_release
+        - .linux_builder_tags_cuda
+        - .run_dependent
+        - .needs_centos6_x86_64
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
+t:cuda10.2-nvidia:
     extends:
         - .cuda10.2_nvidia
         - .cmake_test_linux_release
@@ -228,7 +247,7 @@
         - .run_dependent
         - .needs_centos6_x86_64
 
-test:cuda10.2-clang:
+t:cuda10.2-clang:
     extends:
         - .cuda10.2_clang
         - .cmake_test_linux_release
@@ -238,7 +257,17 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-build:fedora34-ninja:
+t:hip4.2-radeon:
+    extends:
+        - .hip4.2_radeon
+        - .cmake_test_linux_release
+        - .linux_builder_tags_radeon
+        - .run_dependent
+        - .needs_centos6_x86_64
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
+b:fedora34-ninja:
     extends:
         - .fedora34_ninja
         - .cmake_build_linux
@@ -246,7 +275,7 @@
         - .linux_builder_tags_qt
         - .run_manually
 
-test:fedora34-ninja:
+t:fedora34-ninja:
     extends:
         - .fedora34_ninja
         - .cmake_test_linux
@@ -254,11 +283,11 @@
         - .cmake_test_artifacts
         - .run_dependent
     dependencies:
-        - build:fedora34-ninja
+        - b:fedora34-ninja
     needs:
-        - build:fedora34-ninja
+        - b:fedora34-ninja
 
-test:fedora34-ninja-multi:
+t:fedora34-ninja-multi:
     extends:
         - .fedora34_ninja_multi
         - .cmake_test_linux_external
@@ -266,228 +295,228 @@
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
-        - test:fedora34-ninja
+        - t:fedora34-ninja
     needs:
-        - test:fedora34-ninja
+        - t:fedora34-ninja
 
-test:intel2016-makefiles:
+t:intel2016-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2016_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2016-el7
 
-test:intel2016u1-makefiles:
+t:intel2016u1-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2016u1_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2016u1-el7
 
-test:intel2016u2-makefiles:
+t:intel2016u2-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2016u2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2016u2-el7
 
-test:intel2017-makefiles:
+t:intel2017-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017-el7
 
-test:intel2017u1-makefiles:
+t:intel2017u1-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u1_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u1-el7
 
-test:intel2017u2-makefiles:
+t:intel2017u2-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u2-el7
 
-test:intel2017u3-makefiles:
+t:intel2017u3-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u3_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u3-el7
 
-test:intel2017u4-makefiles:
+t:intel2017u4-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u4_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u4-el7
 
-test:intel2017u5-makefiles:
+t:intel2017u5-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u5_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u5-el7
 
-test:intel2017u6-makefiles:
+t:intel2017u6-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u6_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u6-el7
 
-test:intel2017u7-makefiles:
+t:intel2017u7-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u7_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u7-el7
 
-test:intel2017u8-makefiles:
+t:intel2017u8-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2017u8_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2017u8-el7
 
-test:intel2018-makefiles:
+t:intel2018-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2018_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018-el7
 
-test:intel2018u1-makefiles:
+t:intel2018u1-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2018u1_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u1-el7
 
-test:intel2018u2-makefiles:
+t:intel2018u2-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2018u2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u2-el7
 
-test:intel2018u3-makefiles:
+t:intel2018u3-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2018u3_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u3-el7
 
-test:intel2018u4-makefiles:
+t:intel2018u4-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2018u4_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2018u4-el7
 
-test:intel2019-makefiles:
+t:intel2019-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2019_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019-el7
 
-test:intel2019u1-makefiles:
+t:intel2019u1-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2019u1_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u1-el7
 
-test:intel2019u2-makefiles:
+t:intel2019u2-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2019u2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u2-el7
 
-test:intel2019u3-makefiles:
+t:intel2019u3-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2019u3_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u3-el7
 
-test:intel2019u4-makefiles:
+t:intel2019u4-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2019u4_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2019u4-el7
 
-test:intel2020-makefiles:
+t:intel2020-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2020_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2020-el8
 
-test:intel2020u2-makefiles:
+t:intel2020u2-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2020u2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2020u2-el8
 
-test:intel2020u4-makefiles:
+t:intel2020u4-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2020u4_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2020u4-el8
 
-test:intel2021.1.1-makefiles:
+t:intel2021.1.1-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2021.1.1_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.1-el8
 
-test:intel2021.1.2-makefiles:
+t:intel2021.1.2-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2021.1.2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.2-el8
 
-test:intel2021.2.0-makefiles:
+t:intel2021.2.0-makefiles:
     extends:
         - .cmake_test_linux_intelclassic_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: intel2021.2.0_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.2.0-el8
 
-test:oneapi2021.1.1-makefiles:
+t:oneapi2021.1.1-makefiles:
     extends:
         - .cmake_test_linux_inteloneapi_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: oneapi2021.1.1_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.1-el8
 
-test:oneapi2021.1.2-makefiles:
+t:oneapi2021.1.2-makefiles:
     extends:
         - .cmake_test_linux_inteloneapi_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: oneapi2021.1.2_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.1.2-el8
 
-test:oneapi2021.2.0-makefiles:
+t:oneapi2021.2.0-makefiles:
     extends:
         - .cmake_test_linux_inteloneapi_makefiles
     variables:
         CMAKE_CI_BUILD_NAME: oneapi2021.2.0_makefiles
         CMAKE_CI_INTELCOMPILER_IMAGE_TAG: 2021.2.0-el8
 
-build:linux-x86_64-package:
+b:linux-x86_64-package:
     extends:
         - .linux_package
         - .linux_release_x86_64
@@ -496,22 +525,22 @@
         - .linux_builder_tags
         - .run_only_for_package
     dependencies:
-        - prep:doc-package
+        - p:doc-package
     needs:
-        - prep:doc-package
+        - p:doc-package
 
-.upload:linux-x86_64-package:
+.u:linux-x86_64-package:
     extends:
         - .rsync_upload_binary
         - .run_only_for_package
     dependencies:
-        - build:linux-x86_64-package
+        - b:linux-x86_64-package
     needs:
-        - build:linux-x86_64-package
+        - b:linux-x86_64-package
     variables:
         RSYNC_DESTINATION: dev
 
-build:linux-aarch64-package:
+b:linux-aarch64-package:
     extends:
         - .linux_package
         - .linux_release_aarch64
@@ -520,24 +549,24 @@
         - .linux_builder_tags_aarch64
         - .run_only_for_package
     dependencies:
-        - prep:doc-package
+        - p:doc-package
     needs:
-        - prep:doc-package
+        - p:doc-package
 
-.upload:linux-aarch64-package:
+.u:linux-aarch64-package:
     extends:
         - .rsync_upload_binary
         - .run_only_for_package
     dependencies:
-        - build:linux-aarch64-package
+        - b:linux-aarch64-package
     needs:
-        - build:linux-aarch64-package
+        - b:linux-aarch64-package
     variables:
         RSYNC_DESTINATION: dev
 
 ## Sanitizer builds
 
-build:fedora34-asan:
+b:fedora34-asan:
     extends:
         - .fedora34_asan
         - .cmake_build_linux
@@ -547,22 +576,22 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-test:fedora34-asan:
+t:fedora34-asan:
     extends:
         - .fedora34_asan
         - .cmake_memcheck_linux
         - .linux_builder_tags_qt
-        - .run_automatically
+        - .run_dependent
     dependencies:
-        - build:fedora34-asan
+        - b:fedora34-asan
     needs:
-        - build:fedora34-asan
+        - b:fedora34-asan
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
 # macOS builds
 
-build:macos-x86_64-ninja:
+b:macos-x86_64-ninja:
     extends:
         - .macos_x86_64_ninja
         - .cmake_build_macos
@@ -570,7 +599,7 @@
         - .macos_x86_64_builder_tags
         - .run_manually
 
-build:macos-arm64-ninja:
+b:macos-arm64-ninja:
     extends:
         - .macos_arm64_ninja
         - .cmake_build_macos
@@ -580,7 +609,7 @@
     variables:
         CMAKE_CI_NO_MR: "true"
 
-test:macos-x86_64-ninja:
+t:macos-x86_64-ninja:
     extends:
         - .macos_x86_64_ninja
         - .cmake_test_macos
@@ -588,11 +617,13 @@
         - .macos_x86_64_builder_tags
         - .run_dependent
     dependencies:
-        - build:macos-x86_64-ninja
+        - b:macos-x86_64-ninja
     needs:
-        - build:macos-x86_64-ninja
+        - b:macos-x86_64-ninja
+    variables:
+        CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
 
-test:macos-arm64-ninja:
+t:macos-arm64-ninja:
     extends:
         - .macos_arm64_ninja
         - .cmake_test_macos
@@ -600,13 +631,13 @@
         - .macos_arm64_builder_tags
         - .run_dependent
     dependencies:
-        - build:macos-arm64-ninja
+        - b:macos-arm64-ninja
     needs:
-        - build:macos-arm64-ninja
+        - b:macos-arm64-ninja
     variables:
         CMAKE_CI_NO_MR: "true"
 
-build:macos-x86_64-makefiles:
+b:macos-x86_64-makefiles:
     extends:
         - .macos_x86_64_makefiles
         - .cmake_build_macos
@@ -614,18 +645,18 @@
         - .macos_x86_64_builder_tags
         - .run_manually
 
-test:macos-x86_64-makefiles:
+t:macos-x86_64-makefiles:
     extends:
         - .macos_x86_64_makefiles
         - .cmake_test_macos
         - .macos_x86_64_builder_tags
         - .run_dependent
     dependencies:
-        - build:macos-x86_64-makefiles
+        - b:macos-x86_64-makefiles
     needs:
-        - build:macos-x86_64-makefiles
+        - b:macos-x86_64-makefiles
 
-test:macos-x86_64-xcode:
+t:macos-x86_64-xcode:
     extends:
         - .macos_x86_64_xcode
         - .cmake_test_macos_external
@@ -633,11 +664,11 @@
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
-        - test:macos-x86_64-ninja
+        - t:macos-x86_64-ninja
     needs:
-        - test:macos-x86_64-ninja
+        - t:macos-x86_64-ninja
 
-test:macos-arm64-xcode:
+t:macos-arm64-xcode:
     extends:
         - .macos_arm64_xcode
         - .cmake_test_macos_external
@@ -645,13 +676,13 @@
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
-        - test:macos-arm64-ninja
+        - t:macos-arm64-ninja
     needs:
-        - test:macos-arm64-ninja
+        - t:macos-arm64-ninja
     variables:
         CMAKE_CI_NO_MR: "true"
 
-build:macos-package:
+b:macos-package:
     extends:
         - .macos_package
         - .cmake_build_macos_package
@@ -659,22 +690,22 @@
         - .macos_x86_64_builder_tags_package
         - .run_only_for_package
     dependencies:
-        - prep:doc-package
+        - p:doc-package
     needs:
-        - prep:doc-package
+        - p:doc-package
 
-.upload:macos-package:
+.u:macos-package:
     extends:
         - .rsync_upload_binary
         - .run_only_for_package
     dependencies:
-        - build:macos-package
+        - b:macos-package
     needs:
-        - build:macos-package
+        - b:macos-package
     variables:
         RSYNC_DESTINATION: dev
 
-build:macos10.10-package:
+b:macos10.10-package:
     extends:
         - .macos10.10_package
         - .cmake_build_macos_package
@@ -682,51 +713,51 @@
         - .macos_x86_64_builder_tags_package
         - .run_only_for_package
     dependencies:
-        - prep:doc-package
+        - p:doc-package
     needs:
-        - prep:doc-package
+        - p:doc-package
 
-.upload:macos10.10-package:
+.u:macos10.10-package:
     extends:
         - .rsync_upload_binary
         - .run_only_for_package
     dependencies:
-        - build:macos10.10-package
+        - b:macos10.10-package
     needs:
-        - build:macos10.10-package
+        - b:macos10.10-package
     variables:
         RSYNC_DESTINATION: dev
 
 # Windows builds
 
-build:windows-vs2019-x64-ninja:
+b:windows-vs2022-x64-ninja:
     extends:
-        - .windows_vs2019_x64_ninja
+        - .windows_vs2022_x64_ninja
         - .cmake_build_windows
         - .cmake_build_artifacts
-        - .windows_builder_tags
+        - .windows_tags_nonconcurrent_vs2022
         - .run_manually
 
-test:windows-vs2019-x64-ninja:
+t:windows-vs2022-x64-ninja:
     extends:
-        - .windows_vs2019_x64_ninja
+        - .windows_vs2022_x64_ninja
         - .cmake_test_windows
-        - .windows_builder_tags
+        - .windows_tags_nonconcurrent_vs2022
         - .cmake_test_artifacts
         - .run_dependent
     dependencies:
-        - build:windows-vs2019-x64-ninja
+        - b:windows-vs2022-x64-ninja
     needs:
-        - build:windows-vs2019-x64-ninja
+        - b:windows-vs2022-x64-ninja
 
-test:windows-vs2019-x64:
+t:windows-vs2022-x64:
     extends:
-        - .windows_vs2019_x64
+        - .windows_vs2022_x64
         - .cmake_test_windows_external
-        - .windows_builder_ext_tags
+        - .windows_tags_concurrent_vs2022
         - .cmake_junit_artifacts
         - .run_dependent
     dependencies:
-        - test:windows-vs2019-x64-ninja
+        - t:windows-vs2022-x64-ninja
     needs:
-        - test:windows-vs2019-x64-ninja
+        - t:windows-vs2022-x64-ninja
diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml
index 45e4c5b..982f50b 100644
--- a/.gitlab/artifacts.yml
+++ b/.gitlab/artifacts.yml
@@ -56,7 +56,7 @@
             # Test directories.
             - build/Tests/CTest*
             - build/Tests/Find*
-            - build/Tests/Qt5*
+            - build/Tests/Qt*
             - build/Tests/RunCMake/
             - build/Tests/CMakeOnly/
             - build/Tests/CMakeTests/
diff --git a/.gitlab/ci/configure_cuda9.2_nvidia.cmake b/.gitlab/ci/configure_cuda9.2_nvidia.cmake
new file mode 100644
index 0000000..519699b
--- /dev/null
+++ b/.gitlab/ci/configure_cuda9.2_nvidia.cmake
@@ -0,0 +1,3 @@
+set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
index 4d8dde6..8e03eef 100644
--- a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
+++ b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
@@ -1,6 +1,5 @@
 set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
-set(CMake_TEST_FindBLAS "All" CACHE STRING "")
-set(CMake_TEST_FindBLAS_STATIC "Generic" CACHE STRING "")
+set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
 set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
 set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
 set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
@@ -16,6 +15,7 @@
 set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
 set(CMake_TEST_FindGit "ON" CACHE BOOL "")
 set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
 set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
 set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
 set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
@@ -24,8 +24,7 @@
 set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
 set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
 set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
-set(CMake_TEST_FindLAPACK "All" CACHE STRING "")
-set(CMake_TEST_FindLAPACK_STATIC "Generic" CACHE STRING "")
+set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
 set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
 set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
 set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_debian10_ninja.cmake b/.gitlab/ci/configure_debian10_ninja.cmake
index 732624a..d50ab1f 100644
--- a/.gitlab/ci/configure_debian10_ninja.cmake
+++ b/.gitlab/ci/configure_debian10_ninja.cmake
@@ -1,6 +1,5 @@
 set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
-set(CMake_TEST_FindBLAS "All" CACHE STRING "")
-set(CMake_TEST_FindBLAS_STATIC "Generic" CACHE STRING "")
+set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
 set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
 set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
 set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
@@ -16,6 +15,7 @@
 set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
 set(CMake_TEST_FindGit "ON" CACHE BOOL "")
 set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
 set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
 set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
 set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
@@ -24,8 +24,7 @@
 set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
 set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
 set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
-set(CMake_TEST_FindLAPACK "All" CACHE STRING "")
-set(CMake_TEST_FindLAPACK_STATIC "Generic" CACHE STRING "")
+set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
 set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
 set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
 set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_fedora34_makefiles.cmake b/.gitlab/ci/configure_fedora34_makefiles.cmake
index 48786e4..a482378 100644
--- a/.gitlab/ci/configure_fedora34_makefiles.cmake
+++ b/.gitlab/ci/configure_fedora34_makefiles.cmake
@@ -1,6 +1,5 @@
 set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
-set(CMake_TEST_FindBLAS "All" CACHE STRING "")
-set(CMake_TEST_FindBLAS_STATIC "Generic" CACHE STRING "")
+set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
 set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
 set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
 set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
@@ -16,6 +15,7 @@
 set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
 set(CMake_TEST_FindGit "ON" CACHE BOOL "")
 set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
 set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
 set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
 set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
@@ -24,8 +24,7 @@
 set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
 set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
 set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
-set(CMake_TEST_FindLAPACK "All" CACHE STRING "")
-set(CMake_TEST_FindLAPACK_STATIC "Generic" CACHE STRING "")
+set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
 set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
 set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
 set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_fedora34_ninja.cmake b/.gitlab/ci/configure_fedora34_ninja.cmake
index 37bc189..629f792 100644
--- a/.gitlab/ci/configure_fedora34_ninja.cmake
+++ b/.gitlab/ci/configure_fedora34_ninja.cmake
@@ -1,6 +1,10 @@
 set(CMake_TEST_ISPC "ON" CACHE STRING "")
 set(CMake_TEST_GUI "ON" CACHE BOOL "")
 
+# "Release" flags without "-DNDEBUG" so we get assertions.
+set(CMAKE_C_FLAGS_RELEASE "-O3" CACHE STRING "")
+set(CMAKE_CXX_FLAGS_RELEASE "-O3" CACHE STRING "")
+
 # Cover compilation with C++11 only and not higher standards.
 set(CMAKE_CXX_STANDARD "11" CACHE STRING "")
 
diff --git a/.gitlab/ci/configure_hip4.2_radeon.cmake b/.gitlab/ci/configure_hip4.2_radeon.cmake
new file mode 100644
index 0000000..58036b0
--- /dev/null
+++ b/.gitlab/ci/configure_hip4.2_radeon.cmake
@@ -0,0 +1,3 @@
+set(CMake_TEST_HIP "ON" CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_intelcompiler_common.cmake b/.gitlab/ci/configure_intelcompiler_common.cmake
index be175e4..bd34740 100644
--- a/.gitlab/ci/configure_intelcompiler_common.cmake
+++ b/.gitlab/ci/configure_intelcompiler_common.cmake
@@ -1,10 +1,13 @@
-set(CMake_TEST_FindBLAS "All;Intel10_64lp;Intel10_64lp.gcc" CACHE STRING "")
-set(CMake_TEST_FindBLAS_All "Intel10_64lp" CACHE STRING "")
-set(CMake_TEST_FindBLAS_STATIC "All;Intel10_64lp;Intel10_64lp.gcc" CACHE STRING "")
-set(CMake_TEST_FindBLAS_STATIC_All "Intel10_64lp" CACHE STRING "")
-set(CMake_TEST_FindLAPACK "All;Intel10_64lp;Intel10_64lp.gcc" CACHE STRING "")
-set(CMake_TEST_FindLAPACK_All "Intel10_64lp" CACHE STRING "")
-set(CMake_TEST_FindLAPACK_STATIC "All;Intel10_64lp;Intel10_64lp.gcc" CACHE STRING "")
-set(CMake_TEST_FindLAPACK_STATIC_All "Intel10_64lp" CACHE STRING "")
+set(blas_lapack_cases
+  model=lp64 all=Intel10_64lp
+    static=0 All Intel10_64lp compiler=gcc Intel10_64lp compiler=
+    static=1 All Intel10_64lp compiler=gcc Intel10_64lp compiler=
+
+  model=ilp64 all=Intel10_64ilp
+    static=0 All Intel10_64ilp compiler=gcc Intel10_64ilp compiler=
+    static=1 All Intel10_64ilp compiler=gcc Intel10_64ilp compiler=
+  )
+set(CMake_TEST_FindBLAS "${blas_lapack_cases}" CACHE STRING "")
+set(CMake_TEST_FindLAPACK "${blas_lapack_cases}" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_macos_common.cmake b/.gitlab/ci/configure_macos_common.cmake
index 37cd51c..e78c9ce 100644
--- a/.gitlab/ci/configure_macos_common.cmake
+++ b/.gitlab/ci/configure_macos_common.cmake
@@ -2,7 +2,7 @@
 # detect that Java is available and working, but a test machine then not have a
 # working Java installed. To work around this, just act as if Java is not
 # available on any CI machine.
-set(CMake_TEST_Java 0 CACHE FILEPATH "")
+set(CMake_TEST_Java OFF CACHE BOOL "")
 
 # Qt binaries get placed inside the source directory, which causes them to not
 # be included in the install-time rpath, but we still want them in the
diff --git a/.gitlab/ci/configure_windows_common.cmake b/.gitlab/ci/configure_windows_common.cmake
index 45250ac..112846a 100644
--- a/.gitlab/ci/configure_windows_common.cmake
+++ b/.gitlab/ci/configure_windows_common.cmake
@@ -1,4 +1,5 @@
 set(BUILD_QtDialog ON CACHE BOOL "")
 set(CMAKE_PREFIX_PATH "$ENV{CI_PROJECT_DIR}/.gitlab/qt" CACHE STRING "")
+set(CMake_TEST_Java OFF CACHE BOOL "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2019_x64.cmake b/.gitlab/ci/configure_windows_vs2022_x64.cmake
similarity index 74%
rename from .gitlab/ci/configure_windows_vs2019_x64.cmake
rename to .gitlab/ci/configure_windows_vs2022_x64.cmake
index f6ece57..7c024a8 100644
--- a/.gitlab/ci/configure_windows_vs2019_x64.cmake
+++ b/.gitlab/ci/configure_windows_vs2022_x64.cmake
@@ -1,3 +1,4 @@
 set(CMake_TEST_WIX_NO_VERIFY "ON" CACHE BOOL "")
+set(CMake_TEST_Java OFF CACHE BOOL "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2019_x64_ninja.cmake b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
similarity index 100%
rename from .gitlab/ci/configure_windows_vs2019_x64_ninja.cmake
rename to .gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
diff --git a/.gitlab/ci/docker/cuda9.2/Dockerfile b/.gitlab/ci/docker/cuda9.2/Dockerfile
new file mode 100644
index 0000000..7eae886
--- /dev/null
+++ b/.gitlab/ci/docker/cuda9.2/Dockerfile
@@ -0,0 +1,5 @@
+FROM nvidia/cuda:9.2-devel-ubuntu16.04
+MAINTAINER Brad King <brad.king@kitware.com>
+
+COPY install_deps.sh /root/install_deps.sh
+RUN sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/cuda9.2/install_deps.sh b/.gitlab/ci/docker/cuda9.2/install_deps.sh
new file mode 100755
index 0000000..0dcb507
--- /dev/null
+++ b/.gitlab/ci/docker/cuda9.2/install_deps.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -e
+
+apt-get update
+
+# Update base packages.
+apt-get install -y \
+    libgnutls30 \
+    libssl1.0.0 \
+    openssl
+
+# Install development tools.
+apt-get install -y \
+    g++ \
+    clang-3.8 \
+    curl \
+    git
+
+apt-get clean
diff --git a/.gitlab/ci/docker/debian10-aarch64/install_deps.sh b/.gitlab/ci/docker/debian10-aarch64/install_deps.sh
index d7f4f9c..d5c5e22 100755
--- a/.gitlab/ci/docker/debian10-aarch64/install_deps.sh
+++ b/.gitlab/ci/docker/debian10-aarch64/install_deps.sh
@@ -52,6 +52,7 @@
     libgif-dev \
     libgl1-mesa-dev \
     libglew-dev \
+    libgmock-dev \
     libgrpc++-dev libgrpc-dev \
     libgsl-dev \
     libgtest-dev \
diff --git a/.gitlab/ci/docker/debian10/install_deps.sh b/.gitlab/ci/docker/debian10/install_deps.sh
index b4d0f86..d3d6b67 100755
--- a/.gitlab/ci/docker/debian10/install_deps.sh
+++ b/.gitlab/ci/docker/debian10/install_deps.sh
@@ -52,6 +52,7 @@
     libgif-dev \
     libgl1-mesa-dev \
     libglew-dev \
+    libgmock-dev \
     libgrpc++-dev libgrpc-dev \
     libgsl-dev \
     libgtest-dev \
diff --git a/.gitlab/ci/docker/hip4.2/Dockerfile b/.gitlab/ci/docker/hip4.2/Dockerfile
new file mode 100644
index 0000000..563e94f
--- /dev/null
+++ b/.gitlab/ci/docker/hip4.2/Dockerfile
@@ -0,0 +1,7 @@
+FROM rocm/dev-ubuntu-20.04:4.2
+MAINTAINER Brad King <brad.king@kitware.com>
+
+ENV PATH="/opt/rocm/bin:$PATH"
+
+COPY install_deps.sh /root/install_deps.sh
+RUN sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/hip4.2/install_deps.sh b/.gitlab/ci/docker/hip4.2/install_deps.sh
new file mode 100755
index 0000000..2b45bc9
--- /dev/null
+++ b/.gitlab/ci/docker/hip4.2/install_deps.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e
+
+apt-get update
+
+# Install development tools.
+apt-get install -y --no-install-recommends \
+    g++ \
+    curl \
+    git
+
+apt-get clean
diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake
index 3990430..5940a28 100644
--- a/.gitlab/ci/download_qt.cmake
+++ b/.gitlab/ci/download_qt.cmake
@@ -18,7 +18,8 @@
     set(msvc_year "2015")
     set(qt_abi "win64_msvc${msvc_year}_64")
   elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2017" OR
-          "$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2019")
+          "$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2019" OR
+          "$ENV{CMAKE_CONFIGURATION}" MATCHES "vs2022")
     set(qt_platform "windows_x86")
     set(msvc_year "2019")
     set(qt_abi "win64_msvc${msvc_year}_64")
diff --git a/.gitlab/ci/env_cuda9.2_nvidia.sh b/.gitlab/ci/env_cuda9.2_nvidia.sh
new file mode 100644
index 0000000..16bfba4
--- /dev/null
+++ b/.gitlab/ci/env_cuda9.2_nvidia.sh
@@ -0,0 +1,4 @@
+export CC=/usr/bin/clang-3.8
+export CXX=/usr/bin/clang++-3.8
+export CUDAHOSTCXX=/usr/bin/g++-5
+export CUDACXX=/usr/local/cuda/bin/nvcc
diff --git a/.gitlab/ci/ninja-nightly.sh b/.gitlab/ci/ninja-nightly.sh
new file mode 100755
index 0000000..b78b64e
--- /dev/null
+++ b/.gitlab/ci/ninja-nightly.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+cd .gitlab
+
+git clone https://github.com/ninja-build/ninja.git ninja-src
+cmake -S ninja-src -B ninja-src/build -DCMAKE_BUILD_TYPE=Release
+cmake --build ninja-src/build --parallel --target ninja
+mv ninja-src/build/ninja .
+rm -rf ninja-src
diff --git a/.gitlab/ci/ninja.sh b/.gitlab/ci/ninja.sh
index 8ead670..866d1d5 100755
--- a/.gitlab/ci/ninja.sh
+++ b/.gitlab/ci/ninja.sh
@@ -2,6 +2,10 @@
 
 set -e
 
+if test "$CMAKE_CI_JOB_NIGHTLY_NINJA" = "true" -a "$CMAKE_CI_NIGHTLY" = "true"; then
+    exec .gitlab/ci/ninja-nightly.sh
+fi
+
 readonly version="1.10.2"
 baseurl="https://github.com/ninja-build/ninja/releases/download/v$version"
 
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index b814655..90c10a2 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -32,20 +32,20 @@
 
 .needs_centos6_x86_64:
     dependencies:
-        - build:centos6-x86_64
+        - b:centos6-x86_64
     needs:
-        - build:centos6-x86_64
+        - b:centos6-x86_64
 
 .needs_centos7_aarch64:
     dependencies:
-        - build:centos7-aarch64
+        - b:centos7-aarch64
     needs:
-        - build:centos7-aarch64
+        - b:centos7-aarch64
 
 ### Debian
 
 .debian10:
-    image: "kitware/cmake:ci-debian10-x86_64-2021-10-05"
+    image: "kitware/cmake:ci-debian10-x86_64-2021-11-18"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -60,7 +60,7 @@
         CMake_SKIP_INSTALL: 1
 
 .debian10_aarch64:
-    image: "kitware/cmake:ci-debian10-aarch64-2021-10-05"
+    image: "kitware/cmake:ci-debian10-aarch64-2021-11-18"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -131,6 +131,7 @@
 
     variables:
         CMAKE_CONFIGURATION: fedora34_ninja
+        CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
 .fedora34_ninja_multi:
@@ -208,6 +209,21 @@
 
 ### CUDA builds
 
+.cuda9.2:
+    image: "kitware/cmake:ci-cuda9.2-x86_64-2021-10-01"
+
+    variables:
+        GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
+        CMAKE_ARCH: x86_64
+        CTEST_LABELS: "CUDA"
+
+.cuda9.2_nvidia:
+    extends: .cuda9.2
+
+    variables:
+        CMAKE_CONFIGURATION: cuda9.2_nvidia
+        CMAKE_GENERATOR: "Ninja Multi-Config"
+
 .cuda10.2:
     image: "kitware/cmake:ci-cuda10.2-x86_64-2021-06-16"
 
@@ -230,6 +246,23 @@
         CMAKE_CONFIGURATION: cuda10.2_clang
         CTEST_NO_WARNINGS_ALLOWED: 1
 
+### HIP builds
+
+.hip4.2:
+    image: "kitware/cmake:ci-hip4.2-x86_64-2021-07-09"
+
+    variables:
+        GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
+        CMAKE_ARCH: x86_64
+        CTEST_LABELS: "HIP"
+
+.hip4.2_radeon:
+    extends: .hip4.2
+
+    variables:
+        CMAKE_CONFIGURATION: hip4.2_radeon
+        CMAKE_GENERATOR: "Ninja Multi-Config"
+
 ## Tags
 
 .linux_builder_tags:
@@ -262,6 +295,13 @@
         - docker
         - linux
 
+.linux_builder_tags_radeon:
+    tags:
+        - cmake
+        - radeon
+        - docker
+        - linux
+
 .linux_builder_tags_aarch64:
     tags:
         - cmake
@@ -282,8 +322,9 @@
 .before_script_linux: &before_script_linux
     - source .gitlab/ci/env.sh
     - .gitlab/ci/cmake.sh
+    - export PATH=$PWD/.gitlab/cmake/bin:$PATH
     - .gitlab/ci/ninja.sh
-    - export PATH=$PWD/.gitlab:$PWD/.gitlab/cmake/bin:$PATH
+    - export PATH=$PWD/.gitlab:$PATH
     - cmake --version
     - ninja --version
 
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 9eca6f8..b0844cb 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -127,8 +127,9 @@
 
 .before_script_macos: &before_script_macos
     - .gitlab/ci/cmake.sh
+    - export PATH=$PWD/.gitlab/cmake/bin:$PATH
     - .gitlab/ci/ninja.sh
-    - export PATH=$PWD/.gitlab:$PWD/.gitlab/cmake/bin:$PATH
+    - export PATH=$PWD/.gitlab:$PATH
     - cmake --version
     - ninja --version
     # Download Qt
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml
index 8037b33..806e7ca 100644
--- a/.gitlab/os-windows.yml
+++ b/.gitlab/os-windows.yml
@@ -31,45 +31,45 @@
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.windows_vs2019_x64_ninja:
+.windows_vs2022_x64_ninja:
     extends: .windows_ninja
 
     variables:
-        CMAKE_CONFIGURATION: windows_vs2019_x64_ninja
-        VCVARSALL: "${VS160COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat"
+        CMAKE_CONFIGURATION: windows_vs2022_x64_ninja
+        VCVARSALL: "${VS170COMNTOOLS}\\..\\..\\VC\\Auxiliary\\Build\\vcvarsall.bat"
         VCVARSPLATFORM: "x64"
-        VCVARSVERSION: "14.29.30133"
+        VCVARSVERSION: "14.31.31103"
 
 ### External testing
 
-.windows_vs2019_x64:
+.windows_vs2022_x64:
     extends: .windows
 
     variables:
-        CMAKE_CONFIGURATION: windows_vs2019_x64
-        CMAKE_GENERATOR: "Visual Studio 16 2019"
+        CMAKE_CONFIGURATION: windows_vs2022_x64
+        CMAKE_GENERATOR: "Visual Studio 17 2022"
         CMAKE_GENERATOR_PLATFORM: "x64"
-        CMAKE_GENERATOR_TOOLSET: "v142,version=14.29.30133"
+        CMAKE_GENERATOR_TOOLSET: "v143,version=14.31.31103"
         CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
 
 ## Tags
 
-.windows_builder_tags:
+.windows_tags_nonconcurrent_vs2022:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
         - windows
         - shell
-        - vs2019
-        - msvc-19.29-16.11
+        - vs2022
+        - msvc-19.31
         - nonconcurrent
 
-.windows_builder_ext_tags:
+.windows_tags_concurrent_vs2022:
     tags:
         - cmake # Since this is a bare runner, pin to a project.
         - windows
         - shell
-        - vs2019
-        - msvc-19.29-16.11
+        - vs2022
+        - msvc-19.31
         - concurrent
 
 ## Windows-specific scripts
diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml
index 4846fea..a871b8f 100644
--- a/.gitlab/rules.yml
+++ b/.gitlab/rules.yml
@@ -15,9 +15,10 @@
           when: never
         - if: '$CI_MERGE_REQUEST_ID'
           when: manual
+        - if: '$CI_PROJECT_PATH == "cmake/cmake" && $CI_PIPELINE_SOURCE == "schedule"'
+          when: on_success
         - if: '$CI_PROJECT_PATH == "cmake/cmake"'
-          when: delayed
-          start_in: 5 minutes
+          when: manual
         - when: never
 
 .run_automatically:
@@ -34,6 +35,8 @@
           when: never
         - if: '$CI_MERGE_REQUEST_ID'
           when: on_success
+        - if: '$CI_PROJECT_PATH == "cmake/cmake" && $CI_PIPELINE_SOURCE == "schedule"'
+          when: on_success
         - if: '$CI_PROJECT_PATH == "cmake/cmake"'
           when: delayed
           start_in: 5 minutes
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index d2a04fe..7a3e4ed 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -160,6 +160,7 @@
             \ ENABLED_LANGUAGES
             \ ENABLE_EXPORTS
             \ ENVIRONMENT
+            \ ENVIRONMENT_MODIFICATION
             \ EXCLUDE_FROM_ALL
             \ EXCLUDE_FROM_DEFAULT_BUILD
             \ EXPORT_NAME
@@ -2807,6 +2808,7 @@
             \ ABI
             \ BUNDLE
             \ CMAKE_DISABLE_FIND_PACKAGE_
+            \ CMAKE_REQUIRE_FIND_PACKAGE_
             \ CMAKE_FIND_ROOT_PATH_BOTH
             \ COMPONENTS
             \ CONFIG
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9944ea4..fdfe456 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
-cmake_minimum_required(VERSION 3.1...3.19 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.1...3.20 FATAL_ERROR)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake)
 project(CMake)
@@ -815,8 +815,12 @@
 
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
   if(NOT CMake_VERSION_IS_RELEASE)
-    if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND
-        NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS 4.2)
+    if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND
+        NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS 4.2) OR
+       (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND
+        NOT "${CMAKE_C_COMPILER_VERSION}" VERSION_LESS 3.0 AND
+        NOT "x${CMAKE_C_SIMULATE_ID}" STREQUAL "xMSVC") OR
+       CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
       set(C_FLAGS_LIST -Wcast-align -Werror-implicit-function-declaration -Wchar-subscripts
                        -Wall -W -Wpointer-arith -Wwrite-strings -Wformat-security
                        -Wmissing-format-attribute -fno-common -Wundef
diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst
index 2b902a9..998e146 100644
--- a/Help/command/cmake_host_system_information.rst
+++ b/Help/command/cmake_host_system_information.rst
@@ -13,46 +13,236 @@
 
 ``<key>`` can be one of the following values:
 
-============================= ================================================
-Key                           Description
-============================= ================================================
-``NUMBER_OF_LOGICAL_CORES``   Number of logical cores
-``NUMBER_OF_PHYSICAL_CORES``  Number of physical cores
-``HOSTNAME``                  Hostname
-``FQDN``                      Fully qualified domain name
-``TOTAL_VIRTUAL_MEMORY``      Total virtual memory in MiB [#mebibytes]_
-``AVAILABLE_VIRTUAL_MEMORY``  Available virtual memory in MiB [#mebibytes]_
-``TOTAL_PHYSICAL_MEMORY``     Total physical memory in MiB [#mebibytes]_
-``AVAILABLE_PHYSICAL_MEMORY`` Available physical memory in MiB [#mebibytes]_
-============================= ================================================
+``NUMBER_OF_LOGICAL_CORES``
+  Number of logical cores
 
-.. versionadded:: 3.10
-  Additional ``<key>`` values are available:
+``NUMBER_OF_PHYSICAL_CORES``
+  Number of physical cores
 
-============================= ================================================
-Key                           Description
-============================= ================================================
-``IS_64BIT``                  One if processor is 64Bit
-``HAS_FPU``                   One if processor has floating point unit
-``HAS_MMX``                   One if processor supports MMX instructions
-``HAS_MMX_PLUS``              One if processor supports Ext. MMX instructions
-``HAS_SSE``                   One if processor supports SSE instructions
-``HAS_SSE2``                  One if processor supports SSE2 instructions
-``HAS_SSE_FP``                One if processor supports SSE FP instructions
-``HAS_SSE_MMX``               One if processor supports SSE MMX instructions
-``HAS_AMD_3DNOW``             One if processor supports 3DNow instructions
-``HAS_AMD_3DNOW_PLUS``        One if processor supports 3DNow+ instructions
-``HAS_IA64``                  One if IA64 processor emulating x86
-``HAS_SERIAL_NUMBER``         One if processor has serial number
-``PROCESSOR_SERIAL_NUMBER``   Processor serial number
-``PROCESSOR_NAME``            Human readable processor name
-``PROCESSOR_DESCRIPTION``     Human readable full processor description
-``OS_NAME``                   See :variable:`CMAKE_HOST_SYSTEM_NAME`
-``OS_RELEASE``                The OS sub-type e.g. on Windows ``Professional``
-``OS_VERSION``                The OS build ID
-``OS_PLATFORM``               See :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`
-============================= ================================================
+``HOSTNAME``
+  Hostname
+
+``FQDN``
+  Fully qualified domain name
+
+``TOTAL_VIRTUAL_MEMORY``
+  Total virtual memory in MiB [#mebibytes]_
+
+``AVAILABLE_VIRTUAL_MEMORY``
+  Available virtual memory in MiB [#mebibytes]_
+
+``TOTAL_PHYSICAL_MEMORY``
+  Total physical memory in MiB [#mebibytes]_
+
+``AVAILABLE_PHYSICAL_MEMORY``
+  Available physical memory in MiB [#mebibytes]_
+
+``IS_64BIT``
+  .. versionadded:: 3.10
+
+  One if processor is 64Bit
+
+``HAS_FPU``
+  .. versionadded:: 3.10
+
+  One if processor has floating point unit
+
+``HAS_MMX``
+  .. versionadded:: 3.10
+
+  One if processor supports MMX instructions
+
+``HAS_MMX_PLUS``
+  .. versionadded:: 3.10
+
+  One if processor supports Ext. MMX instructions
+
+``HAS_SSE``
+  .. versionadded:: 3.10
+
+  One if processor supports SSE instructions
+
+``HAS_SSE2``
+  .. versionadded:: 3.10
+
+  One if processor supports SSE2 instructions
+
+``HAS_SSE_FP``
+  .. versionadded:: 3.10
+
+  One if processor supports SSE FP instructions
+
+``HAS_SSE_MMX``
+  .. versionadded:: 3.10
+
+  One if processor supports SSE MMX instructions
+
+``HAS_AMD_3DNOW``
+  .. versionadded:: 3.10
+
+  One if processor supports 3DNow instructions
+
+``HAS_AMD_3DNOW_PLUS``
+  .. versionadded:: 3.10
+
+  One if processor supports 3DNow+ instructions
+
+``HAS_IA64``
+  .. versionadded:: 3.10
+
+  One if IA64 processor emulating x86
+
+``HAS_SERIAL_NUMBER``
+  .. versionadded:: 3.10
+
+  One if processor has serial number
+
+``PROCESSOR_SERIAL_NUMBER``
+  .. versionadded:: 3.10
+
+  Processor serial number
+
+``PROCESSOR_NAME``
+  .. versionadded:: 3.10
+
+  Human readable processor name
+
+``PROCESSOR_DESCRIPTION``
+  .. versionadded:: 3.10
+
+  Human readable full processor description
+
+``OS_NAME``
+  .. versionadded:: 3.10
+
+  See :variable:`CMAKE_HOST_SYSTEM_NAME`
+
+``OS_RELEASE``
+  .. versionadded:: 3.10
+
+  The OS sub-type e.g. on Windows ``Professional``
+
+``OS_VERSION``
+  .. versionadded:: 3.10
+
+  The OS build ID
+
+``OS_PLATFORM``
+  .. versionadded:: 3.10
+
+  See :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`
+
+``DISTRIB_INFO``
+  .. versionadded:: 3.22
+
+  Read :file:`/etc/os-release` file and define the given ``<variable>``
+  into a list of read variables
+
+``DISTRIB_<name>``
+  .. versionadded:: 3.22
+
+  Get the ``<name>`` variable (see `man 5 os-release`_) if it exists in the
+  :file:`/etc/os-release` file
+
+  Example:
+
+  .. code-block:: cmake
+
+      cmake_host_system_information(RESULT PRETTY_NAME QUERY DISTRIB_PRETTY_NAME)
+      message(STATUS "${PRETTY_NAME}")
+
+      cmake_host_system_information(RESULT DISTRO QUERY DISTRIB_INFO)
+
+      foreach(VAR IN LISTS DISTRO)
+        message(STATUS "${VAR}=`${${VAR}}`")
+      endforeach()
+
+
+  Output::
+
+    -- Ubuntu 20.04.2 LTS
+    -- DISTRO_BUG_REPORT_URL=`https://bugs.launchpad.net/ubuntu/`
+    -- DISTRO_HOME_URL=`https://www.ubuntu.com/`
+    -- DISTRO_ID=`ubuntu`
+    -- DISTRO_ID_LIKE=`debian`
+    -- DISTRO_NAME=`Ubuntu`
+    -- DISTRO_PRETTY_NAME=`Ubuntu 20.04.2 LTS`
+    -- DISTRO_PRIVACY_POLICY_URL=`https://www.ubuntu.com/legal/terms-and-policies/privacy-policy`
+    -- DISTRO_SUPPORT_URL=`https://help.ubuntu.com/`
+    -- DISTRO_UBUNTU_CODENAME=`focal`
+    -- DISTRO_VERSION=`20.04.2 LTS (Focal Fossa)`
+    -- DISTRO_VERSION_CODENAME=`focal`
+    -- DISTRO_VERSION_ID=`20.04`
+
+If :file:`/etc/os-release` file is not found, the command tries to gather OS
+identification via fallback scripts.  The fallback script can use `various
+distribution-specific files`_ to collect OS identification data and map it
+into `man 5 os-release`_ variables.
+
+Fallback Interface Variables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
+
+  In addition to the scripts shipped with CMake, a user may append full
+  paths to his script(s) to the this list.  The script filename has the
+  following format: ``NNN-<name>.cmake``, where ``NNN`` is three digits
+  used to apply collected scripts in a specific order.
+
+.. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname>
+
+  Variables collected by the user provided fallback script
+  ought to be assigned to CMake variables using this naming
+  convention.  Example, the ``ID`` variable from the manual becomes
+  ``CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID``.
+
+.. variable:: CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
+
+  The fallback script ought to store names of all assigned
+  ``CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_<varname>`` variables in this list.
+
+Example:
+
+.. code-block:: cmake
+
+  # Try to detect some old distribution
+  # See also
+  # - http://linuxmafia.com/faq/Admin/release-files.html
+  #
+  if(NOT EXISTS "${CMAKE_SYSROOT}/etc/foobar-release")
+    return()
+  endif()
+  # Get the first string only
+  file(
+      STRINGS "${CMAKE_SYSROOT}/etc/foobar-release" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
+      LIMIT_COUNT 1
+    )
+  #
+  # Example:
+  #
+  #   Foobar distribution release 1.2.3 (server)
+  #
+  if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "Foobar distribution release ([0-9\.]+) .*")
+    set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME Foobar)
+    set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT}")
+    set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID foobar)
+    set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_MATCH_1})
+    set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_MATCH_1})
+    list(
+        APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
+        CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
+        CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
+        CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
+        CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
+        CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
+      )
+  endif()
+  unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)
+
 
 .. rubric:: Footnotes
 
 .. [#mebibytes] One MiB (mebibyte) is equal to 1024x1024 bytes.
+
+.. _man 5 os-release: https://www.freedesktop.org/software/systemd/man/os-release.html
+.. _various distribution-specific files: http://linuxmafia.com/faq/Admin/release-files.html
diff --git a/Help/command/configure_file.rst b/Help/command/configure_file.rst
index 086668c..1d81423 100644
--- a/Help/command/configure_file.rst
+++ b/Help/command/configure_file.rst
@@ -96,6 +96,7 @@
   with respect to the value of :variable:`CMAKE_CURRENT_BINARY_DIR`.
   If the path names an existing directory the output file is placed
   in that directory with the same file name as the input file.
+  If the path contains non-existent directories, they are created.
 
 ``NO_SOURCE_PERMISSIONS``
   .. versionadded:: 3.19
diff --git a/Help/command/ctest_submit.rst b/Help/command/ctest_submit.rst
index e6d277f..d661825 100644
--- a/Help/command/ctest_submit.rst
+++ b/Help/command/ctest_submit.rst
@@ -30,7 +30,8 @@
     Build      = ctest_build results, in Build.xml
     Test       = ctest_test results, in Test.xml
     Coverage   = ctest_coverage results, in Coverage.xml
-    MemCheck   = ctest_memcheck results, in DynamicAnalysis.xml
+    MemCheck   = ctest_memcheck results, in DynamicAnalysis.xml and
+                 DynamicAnalysis-Test.xml
     Notes      = Files listed by CTEST_NOTES_FILES, in Notes.xml
     ExtraFiles = Files listed by CTEST_EXTRA_SUBMIT_FILES
     Upload     = Files prepared for upload by ctest_upload(), in Upload.xml
diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst
index 89787d1..6a9a6a0 100644
--- a/Help/command/ctest_test.rst
+++ b/Help/command/ctest_test.rst
@@ -190,29 +190,34 @@
 <https://github.com/Kitware/CDash/blob/master/docs/test_measurements.md>`_
 for more information on the types of test measurements that CDash recognizes.
 
+.. versionadded: 3.22
+  CTest can parse custom measurements from tags named
+  ``<CTestMeasurement>`` or ``<CTestMeasurementFile>``. The older names
+  ``<DartMeasurement>`` and ``<DartMeasurementFile>`` are still supported.
+
 The following example demonstrates how to output a variety of custom test
 measurements.
 
 .. code-block:: c++
 
    std::cout <<
-     "<DartMeasurement type=\"numeric/double\" name=\"score\">28.3</DartMeasurement>"
+     "<CTestMeasurement type=\"numeric/double\" name=\"score\">28.3</CTestMeasurement>"
      << std::endl;
 
    std::cout <<
-     "<DartMeasurement type=\"text/string\" name=\"color\">red</DartMeasurement>"
+     "<CTestMeasurement type=\"text/string\" name=\"color\">red</CTestMeasurement>"
      << std::endl;
 
    std::cout <<
-     "<DartMeasurement type=\"text/link\" name=\"CMake URL\">https://cmake.org</DartMeasurement>"
+     "<CTestMeasurement type=\"text/link\" name=\"CMake URL\">https://cmake.org</CTestMeasurement>"
      << std::endl;
 
    std::cout <<
-     "<DartMeasurement type=\"text/preformatted\" name=\"Console Output\">" <<
+     "<CTestMeasurement type=\"text/preformatted\" name=\"Console Output\">" <<
      "line 1.\n" <<
      "  \033[31;1m line 2. Bold red, and indented!\033[0;0ml\n" <<
      "line 3. Not bold or indented...\n" <<
-     "</DartMeasurement>" << std::endl;
+     "</CTestMeasurement>" << std::endl;
 
 Image Measurements
 """"""""""""""""""
@@ -222,16 +227,16 @@
 .. code-block:: c++
 
    std::cout <<
-     "<DartMeasurementFile type=\"image/jpg\" name=\"TestImage\">" <<
-     "/dir/to/test_img.jpg</DartMeasurementFile>" << std::endl;
+     "<CTestMeasurementFile type=\"image/jpg\" name=\"TestImage\">" <<
+     "/dir/to/test_img.jpg</CTestMeasurementFile>" << std::endl;
 
    std::cout <<
-     "<DartMeasurementFile type=\"image/gif\" name=\"ValidImage\">" <<
-     "/dir/to/valid_img.gif</DartMeasurementFile>" << std::endl;
+     "<CTestMeasurementFile type=\"image/gif\" name=\"ValidImage\">" <<
+     "/dir/to/valid_img.gif</CTestMeasurementFile>" << std::endl;
 
    std::cout <<
-     "<DartMeasurementFile type=\"image/png\" name=\"AlgoResult\"> <<
-     "/dir/to/img.png</DartMeasurementFile>"
+     "<CTestMeasurementFile type=\"image/png\" name=\"AlgoResult\"> <<
+     "/dir/to/img.png</CTestMeasurementFile>"
      << std::endl;
 
 Images will be displayed together in an interactive comparison mode on CDash
@@ -252,13 +257,17 @@
 Attached Files
 """"""""""""""
 
+.. versionadded:: 3.21
+
 The following example demonstrates how to upload non-image files to CDash.
 
 .. code-block:: c++
 
    std::cout <<
-     "<DartMeasurementFile type=\"file\" name=\"MyTestInputData\">" <<
-     "/dir/to/data.csv</DartMeasurementFile>" << std::endl;
+     "<CTestMeasurementFile type=\"file\" name=\"TestInputData1\">" <<
+     "/dir/to/data1.csv</CTestMeasurementFile>\n"                   <<
+     "<CTestMeasurementFile type=\"file\" name=\"TestInputData2\">" <<
+     "/dir/to/data2.csv</CTestMeasurementFile>"                     << std::endl;
 
 If the name of the file to upload is known at configure time, you can use the
 :prop_test:`ATTACHED_FILES` or :prop_test:`ATTACHED_FILES_ON_FAIL` test
@@ -267,6 +276,8 @@
 Custom Details
 """"""""""""""
 
+.. versionadded:: 3.21
+
 The following example demonstrates how to specify a custom value for the
 ``Test Details`` field displayed on CDash.
 
@@ -274,3 +285,22 @@
 
    std::cout <<
      "<CTestDetails>My Custom Details Value</CTestDetails>" << std::endl;
+
+.. _`Additional Labels`:
+
+Additional Labels
+"""""""""""""""""
+
+.. versionadded:: 3.22
+
+The following example demonstrates how to add additional labels to a test
+at runtime.
+
+.. code-block:: c++
+
+   std::cout <<
+     "<CTestLabel>Custom Label 1</CTestLabel>\n" <<
+     "<CTestLabel>Custom Label 2</CTestLabel>"   << std::endl;
+
+Use the :prop_test:`LABELS` test property instead for labels that can be
+determined at configure time.
diff --git a/Help/command/file.rst b/Help/command/file.rst
index f038871..799b6ff 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -838,11 +838,16 @@
 files.
 
 The ``INSTALL`` signature differs slightly from ``COPY``: it prints
-status messages (subject to the :variable:`CMAKE_INSTALL_MESSAGE` variable),
-and ``NO_SOURCE_PERMISSIONS`` is default.
+status messages, and ``NO_SOURCE_PERMISSIONS`` is default.
+
 Installation scripts generated by the :command:`install` command
 use this signature (with some undocumented options for internal use).
 
+.. versionchanged:: 3.22
+
+  The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
+  default copying behavior of :command:`file(INSTALL)`.
+
 .. _SIZE:
 
 .. code-block:: cmake
@@ -1068,7 +1073,7 @@
   .. versionadded:: 3.11
 
   Specify whether the .netrc file is to be used for operation.  If this
-  option is not specified, the value of the ``CMAKE_NETRC`` variable
+  option is not specified, the value of the :variable:`CMAKE_NETRC` variable
   will be used instead.
   Valid levels are:
 
@@ -1087,29 +1092,28 @@
 
   Specify an alternative .netrc file to the one in your home directory,
   if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
-  is not specified, the value of the ``CMAKE_NETRC_FILE`` variable will
+  is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable will
   be used instead.
 
-If neither ``NETRC`` option is given CMake will check variables
-``CMAKE_NETRC`` and ``CMAKE_NETRC_FILE``, respectively.
-
 ``TLS_VERIFY <ON|OFF>``
   Specify whether to verify the server certificate for ``https://`` URLs.
-  The default is to *not* verify.
+  The default is to *not* verify. If this option is not specified, the value
+  of the :variable:`CMAKE_TLS_VERIFY` variable will be used instead.
 
   .. versionadded:: 3.18
     Added support to ``file(UPLOAD)``.
 
 ``TLS_CAINFO <file>``
-  Specify a custom Certificate Authority file for ``https://`` URLs.
+  Specify a custom Certificate Authority file for ``https://`` URLs. If this
+  option is not specified, the value of the :variable:`CMAKE_TLS_CAINFO`
+  variable will be used instead.
 
   .. versionadded:: 3.18
     Added support to ``file(UPLOAD)``.
 
 For ``https://`` URLs CMake must be built with OpenSSL support.  ``TLS/SSL``
 certificates are not checked by default.  Set ``TLS_VERIFY`` to ``ON`` to
-check certificates. If neither ``TLS`` option is given CMake will check
-variables :variable:`CMAKE_TLS_VERIFY` and ``CMAKE_TLS_CAINFO``, respectively.
+check certificates.
 
 Additional options to ``DOWNLOAD`` are:
 
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index 3dfd62f..1a79a8a 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -5,12 +5,74 @@
 
    .. contents::
 
-Find an external project, and load its settings.
+Find a package (usually provided by something external to the project),
+and load its package-specific details.
+
+Search Modes
+^^^^^^^^^^^^
+
+The command has two very distinct ways of conducting the search:
+
+**Module mode**
+  In this mode, CMake searches for a file called ``Find<PackageName>.cmake``,
+  looking first in the locations listed in the :variable:`CMAKE_MODULE_PATH`,
+  then among the :ref:`Find Modules` provided by the CMake installation.
+  If the file is found, it is read and processed by CMake.  It is responsible
+  for finding the package, checking the version, and producing any needed
+  messages.  Some Find modules provide limited or no support for versioning;
+  check the Find module's documentation.
+
+  The ``Find<PackageName>.cmake`` file is not typically provided by the
+  package itself.  Rather, it is normally provided by something external to
+  the package, such as the operating system, CMake itself, or even the project
+  from which the ``find_package()`` command was called.  Being externally
+  provided, :ref:`Find Modules` tend to be heuristic in nature and are
+  susceptible to becoming out-of-date.  They typically search for certain
+  libraries, files and other package artifacts.
+
+  Module mode is only supported by the
+  :ref:`basic command signature <Basic Signature>`.
+
+**Config mode**
+  In this mode, CMake searches for a file called
+  ``<lowercasePackageName>-config.cmake`` or ``<PackageName>Config.cmake``.
+  It will also look for ``<lowercasePackageName>-config-version.cmake`` or
+  ``<PackageName>ConfigVersion.cmake`` if version details were specified
+  (see :ref:`version selection` for an explanation of how these separate
+  version files are used).
+
+  In config mode, the command can be given a list of names to search for
+  as package names.  The locations where CMake searches for the config and
+  version files is considerably more complicated than for Module mode
+  (see :ref:`search procedure`).
+
+  The config and version files are typically installed as part of the
+  package, so they tend to be more reliable than Find modules.  They usually
+  contain direct knowledge of the package contents, so no searching or
+  heuristics are needed within the config or version files themselves.
+
+  Config mode is supported by both the :ref:`basic <Basic Signature>` and
+  :ref:`full <Full Signature>` command signatures.
+
+The command arguments determine which of the above modes is used.  When the
+`basic signature`_ is used, the command searches in Module mode first.
+If the package is not found, the search falls back to Config mode.
+A user may set the :variable:`CMAKE_FIND_PACKAGE_PREFER_CONFIG` variable
+to true to reverse the priority and direct CMake to search using Config mode
+first before falling back to Module mode.  The basic signature can also be
+forced to use only Module mode with a ``MODULE`` keyword.  If the
+`full signature`_ is used, the command only searches in Config mode.
+
+Where possible, user code should generally look for packages using the
+`basic signature`_, since that allows the package to be found with either mode.
+Project maintainers wishing to provide a config package should understand
+the bigger picture, as explained in :ref:`Full Signature` and all subsequent
+sections on this page.
 
 .. _`basic signature`:
 
-Basic Signature and Module Mode
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Basic Signature
+^^^^^^^^^^^^^^^
 
 .. code-block:: cmake
 
@@ -19,20 +81,39 @@
                [OPTIONAL_COMPONENTS components...]
                [NO_POLICY_SCOPE])
 
-Finds and loads settings from an external project.  ``<PackageName>_FOUND``
-will be set to indicate whether the package was found.  When the
-package is found package-specific information is provided through
-variables and :ref:`Imported Targets` documented by the package itself.  The
+The basic signature is supported by both Module and Config modes.
+The ``MODULE`` keyword implies that only Module mode can be used to find
+the package, with no fallback to Config mode.
+
+Regardless of the mode used, a ``<PackageName>_FOUND`` variable will be
+set to indicate whether the package was found.  When the package is found,
+package-specific information may be provided through other variables and
+:ref:`Imported Targets` documented by the package itself.  The
 ``QUIET`` option disables informational messages, including those indicating
 that the package cannot be found if it is not ``REQUIRED``.  The ``REQUIRED``
 option stops processing with an error message if the package cannot be found.
 
 A package-specific list of required components may be listed after the
-``COMPONENTS`` option (or after the ``REQUIRED`` option if present).
+``COMPONENTS`` keyword.  If any of these components are not able to be
+satisfied, the package overall is considered to be not found.  If the
+``REQUIRED`` option is also present, this is treated as a fatal error,
+otherwise execution still continues.  As a form of shorthand, if the
+``REQUIRED`` option is present, the ``COMPONENTS`` keyword can be omitted
+and the required components can be listed directly after ``REQUIRED``.
+
 Additional optional components may be listed after
-``OPTIONAL_COMPONENTS``.  Available components and their influence on
-whether a package is considered to be found are defined by the target
-package.
+``OPTIONAL_COMPONENTS``.  If these cannot be satisfied, the package overall
+can still be considered found, as long as all required components are
+satisfied.
+
+The set of available components and their meaning are defined by the
+target package.  Formally, it is up to the target package how to
+interpret the component information given to it, but it should follow
+the expectations stated above.  For calls where no components are specified,
+there is no single expected behavior and target packages should clearly
+define what occurs in such cases.  Common arrangements include assuming it
+should find all components, no components or some well-defined subset of the
+available components.
 
 .. _FIND_PACKAGE_VERSION_FORMAT:
 
@@ -40,12 +121,13 @@
 should be compatible. There are two possible forms in which it may be
 specified:
 
-  * A single version with the format ``major[.minor[.patch[.tweak]]]``.
+  * 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 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.
+    ``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.
 
 The ``EXACT`` option requests that the version be matched exactly. This option
 is incompatible with the specification of a version range.
@@ -62,36 +144,10 @@
 See the :command:`cmake_policy` command documentation for discussion
 of the ``NO_POLICY_SCOPE`` option.
 
-The command has two modes by which it searches for packages: "Module"
-mode and "Config" mode.  The above signature selects Module mode.
-If no module is found the command falls back to Config mode, described
-below. This fall back is disabled if the ``MODULE`` option is given.
+.. _`full signature`:
 
-In Module mode, CMake searches for a file called ``Find<PackageName>.cmake``.
-The file is first searched in the :variable:`CMAKE_MODULE_PATH`,
-then among the :ref:`Find Modules` provided by the CMake installation.
-If the file is found, it is read and processed by CMake.  It is responsible
-for finding the package, checking the version, and producing any needed
-messages.  Some find-modules provide limited or no support for versioning;
-check the module documentation.
-
-If the ``MODULE`` option is not specified in the above signature,
-CMake first searches for the package using Module mode. Then, if the
-package is not found, it searches again using Config mode. A user
-may set the variable :variable:`CMAKE_FIND_PACKAGE_PREFER_CONFIG` to
-``TRUE`` to direct CMake first search using Config mode before falling
-back to Module mode.
-
-Full Signature and Config Mode
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-User code should generally look for packages using the above `basic
-signature`_.  The remainder of this command documentation specifies the
-full command signature and details of the search process.  Project
-maintainers wishing to provide a package to be found by this command
-are encouraged to read on.
-
-The complete Config mode command signature is
+Full Signature
+^^^^^^^^^^^^^^
 
 .. code-block:: cmake
 
@@ -129,18 +185,19 @@
 searches for a package with the name ``<PackageName>``.  If the ``NAMES`` option
 is given the names following it are used instead of ``<PackageName>``.
 The command searches for a file called ``<PackageName>Config.cmake`` or
-``<lower-case-package-name>-config.cmake`` for each name specified.
+``<lowercasePackageName>-config.cmake`` for each name specified.
 A replacement set of possible configuration file names may be given
-using the ``CONFIGS`` option.  The search procedure is specified below.
-Once found, the configuration file is read and processed by CMake.
+using the ``CONFIGS`` option.  The :ref:`search procedure` is specified below.
+Once found, any :ref:`version constraint <version selection>` is checked,
+and if satisfied, the configuration file is read and processed by CMake.
 Since the file is provided by the package it already knows the
 location of package contents.  The full path to the configuration file
 is stored in the cmake variable ``<PackageName>_CONFIG``.
 
 All configuration files which have been considered by CMake while
-searching for an installation of the package with an appropriate
-version are stored in the cmake variable ``<PackageName>_CONSIDERED_CONFIGS``,
-the associated versions in ``<PackageName>_CONSIDERED_VERSIONS``.
+searching for the package with an appropriate version are stored in the
+``<PackageName>_CONSIDERED_CONFIGS`` variable, and the associated versions
+in the ``<PackageName>_CONSIDERED_VERSIONS`` variable.
 
 If the package configuration file cannot be found CMake will generate
 an error describing the problem unless the ``QUIET`` argument is
@@ -150,143 +207,18 @@
 configuration file CMake will ignore it and search from scratch.
 
 Package maintainers providing CMake package configuration files are
-encouraged to name and install them such that the `Search Procedure`_
+encouraged to name and install them such that the :ref:`search procedure`
 outlined below will find them without requiring use of additional options.
 
-Version Selection
-^^^^^^^^^^^^^^^^^
+.. _`search procedure`:
 
-When the ``[version]`` argument is given, Config mode will only find a
-version of the package that claims compatibility with the requested
-version (see :ref:`format specification <FIND_PACKAGE_VERSION_FORMAT>`). If the
-``EXACT`` option is given, only a version of the package claiming an exact match
-of the requested version may be found.  CMake does not establish any
-convention for the meaning of version numbers.  Package version
-numbers are checked by "version" files provided by the packages
-themselves.  For a candidate package configuration file
-``<config-file>.cmake`` the corresponding version file is located next
-to it and named either ``<config-file>-version.cmake`` or
-``<config-file>Version.cmake``.  If no such version file is available
-then the configuration file is assumed to not be compatible with any
-requested version.  A basic version file containing generic version
-matching code can be created using the
-:module:`CMakePackageConfigHelpers` module.  When a version file
-is found it is loaded to check the requested version number.  The
-version file is loaded in a nested scope in which the following
-variables have been defined:
+Config Mode Search Procedure
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-``PACKAGE_FIND_NAME``
-  The ``<PackageName>``
-``PACKAGE_FIND_VERSION``
-  Full requested version string
-``PACKAGE_FIND_VERSION_MAJOR``
-  Major version if requested, else 0
-``PACKAGE_FIND_VERSION_MINOR``
-  Minor version if requested, else 0
-``PACKAGE_FIND_VERSION_PATCH``
-  Patch version if requested, else 0
-``PACKAGE_FIND_VERSION_TWEAK``
-  Tweak version if requested, else 0
-``PACKAGE_FIND_VERSION_COUNT``
-  Number of version components, 0 to 4
-
-When a version range is specified, the above version variables will hold
-values based on the lower end of the version range.  This is to preserve
-compatibility with packages that have not been implemented to expect version
-ranges.  In addition, the version range will be described by the following
-variables:
-
-``PACKAGE_FIND_VERSION_RANGE``
-  Full requested version range string
-``PACKAGE_FIND_VERSION_RANGE_MIN``
-  This specifies whether the lower end point of the version range should be
-  included or excluded.  Currently, the only supported value for this variable
-  is ``INCLUDE``.
-``PACKAGE_FIND_VERSION_RANGE_MAX``
-  This specifies whether the upper end point of the version range should be
-  included or excluded.  The supported values for this variable are
-  ``INCLUDE`` and ``EXCLUDE``.
-
-``PACKAGE_FIND_VERSION_MIN``
-  Full requested version string of the lower end point of the range
-``PACKAGE_FIND_VERSION_MIN_MAJOR``
-  Major version of the lower end point if requested, else 0
-``PACKAGE_FIND_VERSION_MIN_MINOR``
-  Minor version of the lower end point if requested, else 0
-``PACKAGE_FIND_VERSION_MIN_PATCH``
-  Patch version of the lower end point if requested, else 0
-``PACKAGE_FIND_VERSION_MIN_TWEAK``
-  Tweak version of the lower end point if requested, else 0
-``PACKAGE_FIND_VERSION_MIN_COUNT``
-  Number of version components of the lower end point, 0 to 4
-
-``PACKAGE_FIND_VERSION_MAX``
-  Full requested version string of the upper end point of the range
-``PACKAGE_FIND_VERSION_MAX_MAJOR``
-  Major version of the upper end point if requested, else 0
-``PACKAGE_FIND_VERSION_MAX_MINOR``
-  Minor version of the upper end point if requested, else 0
-``PACKAGE_FIND_VERSION_MAX_PATCH``
-  Patch version of the upper end point if requested, else 0
-``PACKAGE_FIND_VERSION_MAX_TWEAK``
-  Tweak version of the upper end point if requested, else 0
-``PACKAGE_FIND_VERSION_MAX_COUNT``
-  Number of version components of the upper end point, 0 to 4
-
-Regardless of whether a single version or a version range is specified, the
-variable ``PACKAGE_FIND_VERSION_COMPLETE`` will be defined and will hold
-the full requested version string as specified.
-
-The version file checks whether it satisfies the requested version and
-sets these variables:
-
-``PACKAGE_VERSION``
-  Full provided version string
-``PACKAGE_VERSION_EXACT``
-  True if version is exact match
-``PACKAGE_VERSION_COMPATIBLE``
-  True if version is compatible
-``PACKAGE_VERSION_UNSUITABLE``
-  True if unsuitable as any version
-
-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:
-
-``<PackageName>_VERSION``
-  Full provided version string
-``<PackageName>_VERSION_MAJOR``
-  Major version if provided, else 0
-``<PackageName>_VERSION_MINOR``
-  Minor version if provided, else 0
-``<PackageName>_VERSION_PATCH``
-  Patch version if provided, else 0
-``<PackageName>_VERSION_TWEAK``
-  Tweak version if provided, else 0
-``<PackageName>_VERSION_COUNT``
-  Number of version components, 0 to 4
-
-and the corresponding package configuration file is loaded.
-When multiple package configuration files are available whose version files
-claim compatibility with the version requested it is unspecified which
-one is chosen: unless the variable :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER`
-is set no attempt is made to choose a highest or closest version number.
-
-To control the order in which ``find_package`` checks for compatibility use
-the two variables :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER` and
-:variable:`CMAKE_FIND_PACKAGE_SORT_DIRECTION`.
-For instance in order to select the highest version one can set
-
-.. code-block:: cmake
-
-  SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
-  SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
-
-before calling ``find_package``.
-
-Search Procedure
-^^^^^^^^^^^^^^^^
+.. note::
+  When Config mode is used, this search procedure is applied regardless of
+  whether the :ref:`full <full signature>` or :ref:`basic <basic signature>`
+  signature was given.
 
 CMake constructs a set of possible installation prefixes for the
 package.  Under each prefix several directories are searched for a
@@ -432,7 +364,7 @@
    hard-coded guesses.
 
 .. versionadded:: 3.16
-   Added the ``CMAKE_FIND_USE_<CATEGORY>_PATH`` variables to globally disable
+   Added the ``CMAKE_FIND_USE_<CATEGORY>`` variables to globally disable
    various search locations.
 
 .. |FIND_XXX| replace:: find_package
@@ -448,8 +380,154 @@
 variable may be set to ``TRUE`` before calling ``find_package`` in order
 to resolve symbolic links and store the real path to the file.
 
-Every non-REQUIRED ``find_package`` call can be disabled by setting the
-:variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable to ``TRUE``.
+Every non-REQUIRED ``find_package`` call can be disabled or made REQUIRED:
+
+* Setting the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable
+  to ``TRUE`` disables the package.
+
+* Setting the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable
+  to ``TRUE`` makes the package REQUIRED.
+
+Setting both variables to ``TRUE`` simultaneously is an error.
+
+.. _`version selection`:
+
+Config Mode Version Selection
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note::
+  When Config mode is used, this version selection process is applied
+  regardless of whether the :ref:`full <full signature>` or
+  :ref:`basic <basic signature>` signature was given.
+
+When the ``[version]`` argument is given, Config mode will only find a
+version of the package that claims compatibility with the requested
+version (see :ref:`format specification <FIND_PACKAGE_VERSION_FORMAT>`). If the
+``EXACT`` option is given, only a version of the package claiming an exact match
+of the requested version may be found.  CMake does not establish any
+convention for the meaning of version numbers.  Package version
+numbers are checked by "version" files provided by the packages
+themselves.  For a candidate package configuration file
+``<config-file>.cmake`` the corresponding version file is located next
+to it and named either ``<config-file>-version.cmake`` or
+``<config-file>Version.cmake``.  If no such version file is available
+then the configuration file is assumed to not be compatible with any
+requested version.  A basic version file containing generic version
+matching code can be created using the
+:module:`CMakePackageConfigHelpers` module.  When a version file
+is found it is loaded to check the requested version number.  The
+version file is loaded in a nested scope in which the following
+variables have been defined:
+
+``PACKAGE_FIND_NAME``
+  The ``<PackageName>``
+``PACKAGE_FIND_VERSION``
+  Full requested version string
+``PACKAGE_FIND_VERSION_MAJOR``
+  Major version if requested, else 0
+``PACKAGE_FIND_VERSION_MINOR``
+  Minor version if requested, else 0
+``PACKAGE_FIND_VERSION_PATCH``
+  Patch version if requested, else 0
+``PACKAGE_FIND_VERSION_TWEAK``
+  Tweak version if requested, else 0
+``PACKAGE_FIND_VERSION_COUNT``
+  Number of version components, 0 to 4
+
+When a version range is specified, the above version variables will hold
+values based on the lower end of the version range.  This is to preserve
+compatibility with packages that have not been implemented to expect version
+ranges.  In addition, the version range will be described by the following
+variables:
+
+``PACKAGE_FIND_VERSION_RANGE``
+  Full requested version range string
+``PACKAGE_FIND_VERSION_RANGE_MIN``
+  This specifies whether the lower end point of the version range should be
+  included or excluded.  Currently, the only supported value for this variable
+  is ``INCLUDE``.
+``PACKAGE_FIND_VERSION_RANGE_MAX``
+  This specifies whether the upper end point of the version range should be
+  included or excluded.  The supported values for this variable are
+  ``INCLUDE`` and ``EXCLUDE``.
+
+``PACKAGE_FIND_VERSION_MIN``
+  Full requested version string of the lower end point of the range
+``PACKAGE_FIND_VERSION_MIN_MAJOR``
+  Major version of the lower end point if requested, else 0
+``PACKAGE_FIND_VERSION_MIN_MINOR``
+  Minor version of the lower end point if requested, else 0
+``PACKAGE_FIND_VERSION_MIN_PATCH``
+  Patch version of the lower end point if requested, else 0
+``PACKAGE_FIND_VERSION_MIN_TWEAK``
+  Tweak version of the lower end point if requested, else 0
+``PACKAGE_FIND_VERSION_MIN_COUNT``
+  Number of version components of the lower end point, 0 to 4
+
+``PACKAGE_FIND_VERSION_MAX``
+  Full requested version string of the upper end point of the range
+``PACKAGE_FIND_VERSION_MAX_MAJOR``
+  Major version of the upper end point if requested, else 0
+``PACKAGE_FIND_VERSION_MAX_MINOR``
+  Minor version of the upper end point if requested, else 0
+``PACKAGE_FIND_VERSION_MAX_PATCH``
+  Patch version of the upper end point if requested, else 0
+``PACKAGE_FIND_VERSION_MAX_TWEAK``
+  Tweak version of the upper end point if requested, else 0
+``PACKAGE_FIND_VERSION_MAX_COUNT``
+  Number of version components of the upper end point, 0 to 4
+
+Regardless of whether a single version or a version range is specified, the
+variable ``PACKAGE_FIND_VERSION_COMPLETE`` will be defined and will hold
+the full requested version string as specified.
+
+The version file checks whether it satisfies the requested version and
+sets these variables:
+
+``PACKAGE_VERSION``
+  Full provided version string
+``PACKAGE_VERSION_EXACT``
+  True if version is exact match
+``PACKAGE_VERSION_COMPATIBLE``
+  True if version is compatible
+``PACKAGE_VERSION_UNSUITABLE``
+  True if unsuitable as any version
+
+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:
+
+``<PackageName>_VERSION``
+  Full provided version string
+``<PackageName>_VERSION_MAJOR``
+  Major version if provided, else 0
+``<PackageName>_VERSION_MINOR``
+  Minor version if provided, else 0
+``<PackageName>_VERSION_PATCH``
+  Patch version if provided, else 0
+``<PackageName>_VERSION_TWEAK``
+  Tweak version if provided, else 0
+``<PackageName>_VERSION_COUNT``
+  Number of version components, 0 to 4
+
+and the corresponding package configuration file is loaded.
+When multiple package configuration files are available whose version files
+claim compatibility with the version requested it is unspecified which
+one is chosen: unless the variable :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER`
+is set no attempt is made to choose a highest or closest version number.
+
+To control the order in which ``find_package`` checks for compatibility use
+the two variables :variable:`CMAKE_FIND_PACKAGE_SORT_ORDER` and
+:variable:`CMAKE_FIND_PACKAGE_SORT_DIRECTION`.
+For instance in order to select the highest version one can set
+
+.. code-block:: cmake
+
+  SET(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
+  SET(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
+
+before calling ``find_package``.
 
 Package File Interface Variables
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -479,7 +557,7 @@
 ``<PackageName>_FIND_VERSION_EXACT``
   True if ``EXACT`` option was given
 ``<PackageName>_FIND_COMPONENTS``
-  List of requested components
+  List of specified components (required and optional)
 ``<PackageName>_FIND_REQUIRED_<c>``
   True if component ``<c>`` is required,
   false if component ``<c>`` is optional
diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst
index f77d8af..46da285 100644
--- a/Help/command/get_property.rst
+++ b/Help/command/get_property.rst
@@ -9,7 +9,7 @@
                <GLOBAL             |
                 DIRECTORY [<dir>]  |
                 TARGET    <target> |
-                SOURCE    <source> |
+                SOURCE    <source>
                           [DIRECTORY <dir> | TARGET_DIRECTORY <target>] |
                 INSTALL   <file>   |
                 TEST      <test>   |
diff --git a/Help/command/if.rst b/Help/command/if.rst
index fbf3e36..5dba13e 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -56,12 +56,23 @@
  ``NO``, ``FALSE``, ``N``, ``IGNORE``, ``NOTFOUND``, the empty string,
  or ends in the suffix ``-NOTFOUND``.  Named boolean constants are
  case-insensitive.  If the argument is not one of these specific
- constants, it is treated as a variable or string and the following
- signature is used.
+ constants, it is treated as a variable or string (see `Variable Expansion`_
+ further below) and one of the following two forms applies.
 
-``if(<variable|string>)``
+``if(<variable>)``
  True if given a variable that is defined to a value that is not a false
- constant.  False otherwise.  (Note macro arguments are not variables.)
+ constant.  False otherwise, including if the variable is undefined.
+ Note that macro arguments are not variables.
+ Environment variables also cannot be tested this way, e.g.
+ ``if(ENV{some_var})`` will always evaluate to false.
+
+``if(<string>)``
+ A quoted string always evaluates to false unless:
+
+ * The string's value is one of the true constants, or
+ * Policy :policy:`CMP0054` is not set to ``NEW`` and the string's value
+   happens to be a variable name that is affected by :policy:`CMP0054`'s
+   behavior.
 
 Logic Operators
 """""""""""""""
@@ -171,7 +182,7 @@
 
 ``if(<variable|string> MATCHES regex)``
  True if the given string or variable's value matches the given regular
- condition.  See :ref:`Regex Specification` for regex format.
+ expression.  See :ref:`Regex Specification` for regex format.
 
  .. versionadded:: 3.9
   ``()`` groups are captured in :variable:`CMAKE_MATCH_<n>` variables.
diff --git a/Help/command/install.rst b/Help/command/install.rst
index c6af489..1236f1d 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -30,6 +30,10 @@
   with those in the parent directory to run in the order declared (see
   policy :policy:`CMP0082`).
 
+.. versionchanged:: 3.22
+  The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
+  default copying behavior of :command:`install()`.
+
 There are multiple signatures for this command.  Some of them define
 installation options for files and targets.  Options common to
 multiple signatures are covered here but they are valid only for
diff --git a/Help/command/list.rst b/Help/command/list.rst
index f46641f..9b49cb4 100644
--- a/Help/command/list.rst
+++ b/Help/command/list.rst
@@ -161,8 +161,9 @@
 .. versionadded:: 3.15
 
 If no variable name is given, removes exactly one element. Otherwise,
-assign the last element's value to the given variable and removes it,
-up to the last variable name given.
+with `N` variable names provided, assign the last `N` elements' values
+to the given variables and then remove the last `N` values from
+``<list>``.
 
 .. _POP_FRONT:
 
@@ -173,8 +174,9 @@
 .. versionadded:: 3.15
 
 If no variable name is given, removes exactly one element. Otherwise,
-assign the first element's value to the given variable and removes it,
-up to the last variable name given.
+with `N` variable names provided, assign the first `N` elements' values
+to the given variables and then remove the first `N` values from
+``<list>``.
 
 .. _PREPEND:
 
diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst
index bf437b4..555520d 100644
--- a/Help/command/set_property.rst
+++ b/Help/command/set_property.rst
@@ -9,8 +9,8 @@
                 DIRECTORY [<dir>]           |
                 TARGET    [<target1> ...]   |
                 SOURCE    [<src1> ...]
-                          [DIRECTORY <dirs> ...] |
-                          [TARGET_DIRECTORY <targets> ...]
+                          [DIRECTORY <dirs> ...]
+                          [TARGET_DIRECTORY <targets> ...] |
                 INSTALL   [<file1> ...]     |
                 TEST      [<test1> ...]     |
                 CACHE     [<entry1> ...]    >
diff --git a/Help/command/source_group.rst b/Help/command/source_group.rst
index a4b5bf1..83ae286 100644
--- a/Help/command/source_group.rst
+++ b/Help/command/source_group.rst
@@ -22,7 +22,8 @@
  CMake will automatically detect, from ``<src>`` files paths, source groups
  it needs to create, to keep structure of source groups analogically to the
  actual files and directories structure in the project. Paths of ``<src>``
- files will be cut to be relative to ``<root>``.
+ files will be cut to be relative to ``<root>``. The command fails if the
+ paths within ``src`` do not start with ``root``.
 
 ``PREFIX``
  .. versionadded:: 3.8
diff --git a/Help/command/string.rst b/Help/command/string.rst
index 8ad0089..29ad082 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -449,38 +449,73 @@
 The optional ``<format_string>`` may contain the following format
 specifiers:
 
-::
+``%%``
+  .. versionadded:: 3.8
 
-   %%        A literal percent sign (%).
-   %d        The day of the current month (01-31).
-   %H        The hour on a 24-hour clock (00-23).
-   %I        The hour on a 12-hour clock (01-12).
-   %j        The day of the current year (001-366).
-   %m        The month of the current year (01-12).
-   %b        Abbreviated month name (e.g. Oct).
-   %B        Full month name (e.g. October).
-   %M        The minute of the current hour (00-59).
-   %s        Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
-   %S        The second of the current minute.
-             60 represents a leap second. (00-60)
-   %U        The week number of the current year (00-53).
-   %w        The day of the current week. 0 is Sunday. (0-6)
-   %a        Abbreviated weekday name (e.g. Fri).
-   %A        Full weekday name (e.g. Friday).
-   %y        The last two digits of the current year (00-99)
-   %Y        The current year.
+  A literal percent sign (%).
 
-.. versionadded:: 3.6
-  ``%s`` format specifier (UNIX time).
+``%d``
+  The day of the current month (01-31).
 
-.. versionadded:: 3.7
-  ``%a`` and ``%b`` format specifiers (abbreviated month and weekday names).
+``%H``
+  The hour on a 24-hour clock (00-23).
 
-.. versionadded:: 3.8
-  ``%%`` specifier (literal ``%``).
+``%I``
+  The hour on a 12-hour clock (01-12).
 
-.. versionadded:: 3.7
-  ``%A`` and ``%B`` format specifiers (full month and weekday names).
+``%j``
+  The day of the current year (001-366).
+
+``%m``
+  The month of the current year (01-12).
+
+``%b``
+  .. versionadded:: 3.7
+
+  Abbreviated month name (e.g. Oct).
+
+``%B``
+  .. versionadded:: 3.10
+
+  Full month name (e.g. October).
+
+``%M``
+  The minute of the current hour (00-59).
+
+``%s``
+  .. versionadded:: 3.6
+
+  Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
+
+``%S``
+  The second of the current minute.  60 represents a leap second. (00-60)
+
+``%U``
+  The week number of the current year (00-53).
+
+``%V``
+  .. versionadded:: 3.22
+
+  The ISO 8601 week number of the current year (01-53).
+
+``%w``
+  The day of the current week. 0 is Sunday. (0-6)
+
+``%a``
+  .. versionadded:: 3.7
+
+  Abbreviated weekday name (e.g. Fri).
+
+``%A``
+  .. versionadded:: 3.10
+
+  Full weekday name (e.g. Friday).
+
+``%y``
+  The last two digits of the current year (00-99).
+
+``%Y``
+  The current year.
 
 Unknown format specifiers will be ignored and copied to the output
 as-is.
diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst
index d8d53ec..f96ca32 100644
--- a/Help/cpack_gen/deb.rst
+++ b/Help/cpack_gen/deb.rst
@@ -274,10 +274,23 @@
 
  Possible values are:
 
- - lzma
- - xz
- - bzip2
- - gzip
+  ``lzma``
+    Lempel–Ziv–Markov chain algorithm
+
+  ``xz``
+    XZ Utils compression
+
+  ``bzip2``
+    bzip2 Burrows–Wheeler algorithm
+
+  ``gzip``
+    GNU Gzip compression
+
+  ``zstd``
+    .. versionadded:: 3.22
+
+    Zstandard compression
+
 
 .. variable:: CPACK_DEBIAN_PACKAGE_PRIORITY
               CPACK_DEBIAN_<COMPONENT>_PACKAGE_PRIORITY
@@ -652,9 +665,18 @@
 
 .. note::
 
+ Setting this also strips the ELF files in the generated non-dbgsym package,
+ which results in debuginfo only being available in the dbgsym package.
+
+.. note::
+
  Binaries must contain debug symbols before packaging so use either ``Debug``
  or ``RelWithDebInfo`` for :variable:`CMAKE_BUILD_TYPE` variable value.
 
+ Additionally, if :variable:`CPACK_STRIP_FILES` is set, the files will be stripped before
+ they get to the DEB generator, so will not contain debug symbols and
+ a dbgsym package will not get built. Do not use with :variable:`CPACK_STRIP_FILES`.
+
 Building Debian packages on Windows
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/Help/cpack_gen/nsis.rst b/Help/cpack_gen/nsis.rst
index 02e33ba..299cfec 100644
--- a/Help/cpack_gen/nsis.rst
+++ b/Help/cpack_gen/nsis.rst
@@ -3,8 +3,8 @@
 
 CPack Nullsoft Scriptable Install System (NSIS) generator specific options.
 
-.. versionchanged:: 3.17
- The NSIS generator requires NSIS 3.0 or newer.
+.. versionchanged:: 3.22
+ The NSIS generator requires NSIS 3.03 or newer.
 
 Variables specific to CPack NSIS generator
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -201,3 +201,9 @@
  .. versionadded:: 3.21
 
  If set, specify the name of the NSIS executable. Default is ``makensis``.
+
+.. variable:: CPACK_NSIS_IGNORE_LICENSE_PAGE
+
+ .. versionadded:: 3.22
+
+ If set, do not display the page containing the license during installation.
diff --git a/Help/cpack_gen/rpm.rst b/Help/cpack_gen/rpm.rst
index 322ab68..0d287fc 100644
--- a/Help/cpack_gen/rpm.rst
+++ b/Help/cpack_gen/rpm.rst
@@ -840,6 +840,10 @@
  Binaries must contain debug symbols before packaging so use either ``Debug``
  or ``RelWithDebInfo`` for :variable:`CMAKE_BUILD_TYPE` variable value.
 
+ Additionally, if :variable:`CPACK_STRIP_FILES` is set, the files will be stripped before
+ they get to the RPM generator, so will not contain debug symbols and
+ a debuginfo package will not get built. Do not use with :variable:`CPACK_STRIP_FILES`.
+
 .. note::
 
  Packages generated from packages without binary files, with binary files but
@@ -1023,7 +1027,7 @@
  * Mandatory : YES
  * Default   : "/"
 
-.. VARIABLE:: CPACK_RPM_BUILDREQUIRES
+.. variable:: CPACK_RPM_BUILDREQUIRES
 
  List of source rpm build dependencies.
 
@@ -1035,3 +1039,16 @@
  example::
 
   set(CPACK_RPM_BUILDREQUIRES "python >= 2.5.0, cmake >= 2.8")
+
+.. variable:: CPACK_RPM_REQUIRES_EXCLUDE_FROM
+
+ .. versionadded:: 3.22
+
+ * Mandatory : NO
+ * Default   : -
+
+ May be used to keep the dependency generator from scanning specific files
+ or directories for dependencies.  Note that you can use a regular
+ expression that matches all of the directories or files, for example::
+
+  set(CPACK_RPM_REQUIRES_EXCLUDE_FROM "bin/libqsqloci.*\\.so.*")
diff --git a/Help/dev/documentation.rst b/Help/dev/documentation.rst
index 29fc880..4a2a5d9 100644
--- a/Help/dev/documentation.rst
+++ b/Help/dev/documentation.rst
@@ -92,6 +92,11 @@
  the referenced documents inline as part of the referencing
  document.
 
+``versionadded``, ``versionchanged`` directives
+ Specify that something was added or changed by a named CMake version.
+ The command-line help processor prints the block content as if the lines
+ were normal paragraph text with interpretation.
+
 Inline markup constructs not listed above are printed literally in the
 command-line help output.  We prefer to use inline markup constructs that
 look correct in source form, so avoid use of \\-escapes in favor of inline
diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst
index 664b7a4..54d627d 100644
--- a/Help/dev/maint.rst
+++ b/Help/dev/maint.rst
@@ -245,12 +245,12 @@
   Release versions do not have the development topic section of
   the CMake Release Notes index page.
 
-Update ``.gitlab-ci.yml`` to drop the ``upload:`` jobs from the
+Update ``.gitlab-ci.yml`` to drop the upload jobs from the
 packaging pipeline by renaming them to start in ``.``:
 
 .. code-block:: shell
 
-  sed -i 's/^upload:/.upload:/' .gitlab-ci.yml
+  sed -i 's/^u:/.u:/' .gitlab-ci.yml
 
 Commit with a message such as::
 
@@ -298,7 +298,7 @@
   git checkout origin/master -- \
     Source/CMakeVersion.cmake Help/release/dev/0-sample-topic.rst
   sed -i $'/^Releases/ i\\\n.. include:: dev.txt\\\n' Help/release/index.rst
-  sed -i 's/^\.upload:/upload:/' .gitlab-ci.yml
+  sed -i 's/^\.u:/u:/' .gitlab-ci.yml
 
 Update ``Source/CMakeVersion.cmake`` to set the version to
 ``$major.$minor.$date``:
diff --git a/Help/dev/review.rst b/Help/dev/review.rst
index 198e100..34796a1 100644
--- a/Help/dev/review.rst
+++ b/Help/dev/review.rst
@@ -324,6 +324,19 @@
     * ``failed``: Restart jobs which have completed, but without success.
     * ``completed``: Restart all completed jobs.
 
+
+In order to keep job names shorter and keep as much information visible on the
+GitLab web interface as possible, jobs have a short prefix which indicates
+what its main purpose is:
+
+  * ``b:`` jobs build CMake for the purposes of running the
+    test suite.
+  * ``l:`` jobs perform "linting" on the CMake source tree such as static
+    analysis.
+  * ``p:`` jobs perform preparatory tasks for use in other jobs.
+  * ``t:`` jobs perform testing of CMake.
+  * ``u:`` jobs upload other job results to permanent locations.
+
 If the merge request topic branch is updated by a push, a new manual trigger
 using one of the above methods is needed to start CI again.
 
diff --git a/Help/envvar/CMAKE_BUILD_TYPE.rst b/Help/envvar/CMAKE_BUILD_TYPE.rst
new file mode 100644
index 0000000..f798aff
--- /dev/null
+++ b/Help/envvar/CMAKE_BUILD_TYPE.rst
@@ -0,0 +1,10 @@
+CMAKE_BUILD_TYPE
+----------------
+
+.. versionadded:: 3.22
+
+.. include:: ENV_VAR.txt
+
+The ``CMAKE_BUILD_TYPE`` environment variable specifies a default value
+for the :variable:`CMAKE_BUILD_TYPE` variable when there is no explicit
+configuration given on the first run while creating a new build tree.
diff --git a/Help/envvar/CMAKE_CONFIGURATION_TYPES.rst b/Help/envvar/CMAKE_CONFIGURATION_TYPES.rst
new file mode 100644
index 0000000..833aa4a
--- /dev/null
+++ b/Help/envvar/CMAKE_CONFIGURATION_TYPES.rst
@@ -0,0 +1,11 @@
+CMAKE_CONFIGURATION_TYPES
+-------------------------
+
+.. versionadded:: 3.22
+
+.. include:: ENV_VAR.txt
+
+The ``CMAKE_CONFIGURATION_TYPES`` environment variable specifies a
+default value for the :variable:`CMAKE_CONFIGURATION_TYPES` variable
+when there is no explicit configuration given on the first run while
+creating a new build tree.
diff --git a/Help/envvar/CMAKE_INSTALL_MODE.rst b/Help/envvar/CMAKE_INSTALL_MODE.rst
new file mode 100644
index 0000000..4549ea1
--- /dev/null
+++ b/Help/envvar/CMAKE_INSTALL_MODE.rst
@@ -0,0 +1,116 @@
+CMAKE_INSTALL_MODE
+------------------
+
+.. versionadded:: 3.22
+
+.. include:: ENV_VAR.txt
+
+The ``CMAKE_INSTALL_MODE`` environment variable allows users to operate
+CMake in an alternate mode of :command:`file(INSTALL)` and :command:`install()`.
+
+The default behavior for an installation is to copy a source file from a
+source directory into a destination directory. This environment variable
+however allows the user to override this behavior, causing CMake to create
+symbolic links instead.
+
+Usage Scenarios
+^^^^^^^^^^^^^^^
+
+Installing symbolic links rather than copying files can help in the following
+ways:
+
+* Conserving storage space because files do not have to be duplicated on disk.
+* Changes to the source of the symbolic link are seen at the install
+  destination without having to re-run the install step.
+* Editing through the link at the install destination will modify the source
+  of the link. This may be useful when dealing with CMake project hierarchies,
+  i.e. using :module:`ExternalProject` and consistent source navigation and
+  refactoring is desired across projects.
+
+Allowed Values
+^^^^^^^^^^^^^^
+
+The following values are allowed for ``CMAKE_INSTALL_MODE``:
+
+``COPY``, empty or unset
+  Duplicate the file at its destination.  This is the default behavior.
+
+``ABS_SYMLINK``
+  Create an *absolute* symbolic link to the source file at the destination.
+  Halt with an error if the link cannot be created.
+
+``ABS_SYMLINK_OR_COPY``
+  Like ``ABS_SYMLINK`` but fall back to silently copying if the symlink
+  couldn't be created.
+
+``REL_SYMLINK``
+  Create a *relative* symbolic link to the source file at the destination.
+  Halt with an error if the link cannot be created.
+
+``REL_SYMLINK_OR_COPY``
+  Like ``REL_SYMLINK`` but fall back to silently copying if the symlink
+  couldn't be created.
+
+``SYMLINK``
+  Try as if through ``REL_SYMLINK`` and fall back to ``ABS_SYMLINK`` if the
+  referenced file cannot be expressed using a relative path.
+  Halt with an error if the link cannot be created.
+
+``SYMLINK_OR_COPY``
+  Like ``SYMLINK`` but fall back to silently copying if the symlink couldn't
+  be created.
+
+.. note::
+  A symbolic link consists of a reference file path rather than contents of its
+  own, hence there are two ways to express the relation, either by a *relative*
+  or an *absolute* path.
+
+When To Set The Environment Variable
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For the environment variable to take effect, it must be set during the correct
+build phase(s).
+
+* If the project calls :command:`file(INSTALL)` directly, the environment
+  variable must be set during the configuration phase.
+* In order to apply to :command:`install()`, the environment variable must be
+  set during installation.  This could be during a build if using the
+  ``install`` or ``package`` build targets, or separate from the build when
+  invoking an install or running :manual:`cpack <cpack(1)>` from the command
+  line.
+* When using :module:`ExternalProject`, it might be required during the build
+  phase, since the external project's own configure, build and install steps
+  will execute during the main project's build phase.
+
+Given the above, it is recommended to set the environment variable consistently
+across all phases (configure, build and install).
+
+Caveats
+^^^^^^^
+
+Use this environment variable with caution. The following highlights some
+points to be considered:
+
+* ``CMAKE_INSTALL_MODE`` only affects files, not directories.
+
+* Symbolic links are not available on all platforms.
+
+* The way this environment variable interacts with the install step of
+  :module:`ExternalProject` is more complex. For further details, see that
+  module's documentation.
+
+* A symbolic link ties the destination to the source in a persistent way.
+  Writing to either of the two affects both file system objects.
+  This is in contrast to normal install behavior which only copies files as
+  they were at the time the install was performed, with no enduring
+  relationship between the source and destination of the install.
+
+* Combining ``CMAKE_INSTALL_MODE`` with :prop_tgt:`IOS_INSTALL_COMBINED` is
+  not supported.
+
+* Changing ``CMAKE_INSTALL_MODE`` from what it was on a previous run can lead
+  to unexpected results.  Moving from a non-symlinking mode to a symlinking
+  mode will discard any previous file at the destination, but the reverse is
+  not true.  Once a symlink exists at the destination, even if you switch to a
+  non-symlink mode, the symlink will continue to exist at the destination and
+  will not be replaced by an actual file.
diff --git a/Help/generator/Visual Studio 10 2010.rst b/Help/generator/Visual Studio 10 2010.rst
index c065550..9ec33c3 100644
--- a/Help/generator/Visual Studio 10 2010.rst
+++ b/Help/generator/Visual Studio 10 2010.rst
@@ -1,7 +1,14 @@
 Visual Studio 10 2010
 ---------------------
 
-Generates Visual Studio 10 (VS 2010) project files.
+Deprecated.  Generates Visual Studio 10 (VS 2010) project files.
+
+.. note::
+  This generator is deprecated and will be removed in a future version
+  of CMake.  It will still be possible to build with VS 10 2010 tools
+  using the :generator:`Visual Studio 11 2012` (or above) generator
+  with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v100``, or by
+  using the :generator:`NMake Makefiles` generator.
 
 For compatibility with CMake versions prior to 3.0, one may specify this
 generator using the name ``Visual Studio 10`` without the year component.
diff --git a/Help/generator/Visual Studio 15 2017.rst b/Help/generator/Visual Studio 15 2017.rst
index a4f5f98..b4d6f6d 100644
--- a/Help/generator/Visual Studio 15 2017.rst
+++ b/Help/generator/Visual Studio 15 2017.rst
@@ -15,20 +15,20 @@
 Instance Selection
 ^^^^^^^^^^^^^^^^^^
 
-.. versionadded:: 3.9
-  VS 2017 supports multiple installations on the same machine.
-  The :variable:`CMAKE_GENERATOR_INSTANCE` variable may be set as a
-  cache entry containing the absolute path to a Visual Studio instance.
-  If the value is not specified explicitly by the user or a toolchain file,
-  CMake queries the Visual Studio Installer to locate VS instances, chooses
-  one, and sets the variable as a cache entry to hold the value persistently.
-
 .. versionadded:: 3.11
-  When CMake first chooses an instance, if the ``VS150COMNTOOLS`` environment
-  variable is set and points to the ``Common7/Tools`` directory within
-  one of the instances, that instance will be used.  Otherwise, if more
-  than one instance is installed we do not define which one is chosen
-  by default.
+
+VS 2017 supports multiple installations on the same machine.
+The :variable:`CMAKE_GENERATOR_INSTANCE` variable may be set as a
+cache entry containing the absolute path to a Visual Studio instance.
+If the value is not specified explicitly by the user or a toolchain file,
+CMake queries the Visual Studio Installer to locate VS instances, chooses
+one, and sets the variable as a cache entry to hold the value persistently.
+
+When CMake first chooses an instance, if the ``VS150COMNTOOLS`` environment
+variable is set and points to the ``Common7/Tools`` directory within
+one of the instances, that instance will be used.  Otherwise, if more
+than one instance is installed we do not define which one is chosen
+by default.
 
 Platform Selection
 ^^^^^^^^^^^^^^^^^^
diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst
index 41e8479..cf1ec01 100644
--- a/Help/guide/tutorial/A Basic Starting Point.rst
+++ b/Help/guide/tutorial/A Basic Starting Point.rst
@@ -24,6 +24,45 @@
 code for ``tutorial.cxx`` is provided in the ``Step1`` directory and can be
 used to compute the square root of a number.
 
+Build and Run
+-------------
+
+That's all that is needed - we can build and run our project now! First, run
+the :manual:`cmake <cmake(1)>` executable or the
+:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
+with your chosen build tool.
+
+For example, from the command line we could navigate to the
+``Help/guide/tutorial`` directory of the CMake source code tree and create a
+build directory:
+
+.. code-block:: console
+
+  mkdir Step1_build
+
+Next, navigate to the build directory and run CMake to configure the project
+and generate a native build system:
+
+.. code-block:: console
+
+  cd Step1_build
+  cmake ../Step1
+
+Then call that build system to actually compile/link the project:
+
+.. code-block:: console
+
+  cmake --build .
+
+Finally, try to use the newly built ``Tutorial`` with these commands:
+
+.. code-block:: console
+
+  Tutorial 4294967296
+  Tutorial 10
+  Tutorial
+
+
 Adding a Version Number and Configured Header File
 --------------------------------------------------
 
@@ -113,39 +152,24 @@
   :language: cmake
   :end-before: # configure a header file to pass some of the CMake settings
 
-Build and Test
---------------
+Rebuild
+-------
 
-Run the :manual:`cmake <cmake(1)>` executable or the
-:manual:`cmake-gui <cmake-gui(1)>` to configure the project and then build it
-with your chosen build tool.
-
-For example, from the command line we could navigate to the
-``Help/guide/tutorial`` directory of the CMake source code tree and create a
-build directory:
-
-.. code-block:: console
-
-  mkdir Step1_build
-
-Next, navigate to the build directory and run CMake to configure the project
-and generate a native build system:
+Let's build our project again. We already created a build directory and ran
+CMake, so we can skip to the build step:
 
 .. code-block:: console
 
   cd Step1_build
-  cmake ../Step1
-
-Then call that build system to actually compile/link the project:
-
-.. code-block:: console
-
   cmake --build .
 
-Finally, try to use the newly built ``Tutorial`` with these commands:
+Now we can try to use the newly built ``Tutorial`` with same commands as before:
 
 .. code-block:: console
 
   Tutorial 4294967296
   Tutorial 10
   Tutorial
+
+Check that the version number is now reported when running the executable without
+any arguments.
diff --git a/Help/guide/tutorial/Adding Export Configuration.rst b/Help/guide/tutorial/Adding Export Configuration.rst
index e5ab6a2..3bd6d64 100644
--- a/Help/guide/tutorial/Adding Export Configuration.rst
+++ b/Help/guide/tutorial/Adding Export Configuration.rst
@@ -12,10 +12,10 @@
 
 The first step is to update our :command:`install(TARGETS)` commands to not
 only specify a ``DESTINATION`` but also an ``EXPORT``. The ``EXPORT`` keyword
-generates and installs a CMake file containing code to import all targets
-listed in the install command from the installation tree. So let's go ahead and
-explicitly ``EXPORT`` the ``MathFunctions`` library by updating the ``install``
-command in ``MathFunctions/CMakeLists.txt`` to look like:
+generates a CMake file containing code to import all targets listed in the
+install command from the installation tree. So let's go ahead and explicitly
+``EXPORT`` the ``MathFunctions`` library by updating the ``install`` command
+in ``MathFunctions/CMakeLists.txt`` to look like:
 
 .. literalinclude:: Complete/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
@@ -82,6 +82,46 @@
   :name: CMakeLists.txt-install-Config.cmake
   :language: cmake
   :start-after: # install the configuration targets
+  :end-before: # generate the config file
+
+
+Next, we execute the :command:`configure_package_config_file`.  This command
+will configure a provided file but with a few specific differences from the
+standard :command:`configure_file` way.
+To properly utilize this function, the input file should have a single line
+with the text ``@PACKAGE_INIT@`` in addition to the content that is desired.
+That variable will be replaced with a block of code which turns set values into
+relative paths.  These values which are new can be referenced by the same name
+but prepended with a ``PACKAGE_`` prefix.
+
+.. literalinclude:: Step12/CMakeLists.txt
+  :caption: CMakeLists.txt
+  :name: CMakeLists.txt-configure-package-config.cmake
+  :language: cmake
+  :start-after: # install the configuration targets
+  :end-before: # generate the version file
+
+The :command:`write_basic_package_version_file` is next.  This command writes
+a file which is used by the "find_package" document the version and
+compatibility of the desired package.  Here, we use the ``Tutorial_VERSION_*``
+variables and say that it is compatible with ``AnyNewerVersion``, which
+denotes that this version or any higher one are compatible with the requested
+version.
+
+.. literalinclude:: Step12/CMakeLists.txt
+  :caption: CMakeLists.txt
+  :name: CMakeLists.txt-basic-version-file.cmake
+  :language: cmake
+  :start-after: # generate the version file
+  :end-before: # install the generated configuration files
+
+Finally, set both generated files to be installed:
+
+.. literalinclude:: Step12/CMakeLists.txt
+  :caption: CMakeLists.txt
+  :name: CMakeLists.txt-install-configured-files.cmake
+  :language: cmake
+  :start-after: # install the generated configuration files
   :end-before: # generate the export
 
 At this point, we have generated a relocatable CMake Configuration for our
diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst
index 55acb34..7fcc97f 100644
--- a/Help/guide/tutorial/Adding Generator Expressions.rst
+++ b/Help/guide/tutorial/Adding Generator Expressions.rst
@@ -53,6 +53,16 @@
   :start-after: project(Tutorial VERSION 1.0)
   :end-before: # add compiler warning flags just when building this project via
 
+**Note**:  This upcoming section will require a change to the
+:command:`cmake_minimum_required` usage in the code.  The Generator Expression
+that is about to be used was introduced in `3.15`.  Update the call to require
+that more recent version:
+
+.. code-block:: cmake
+  :caption: CMakeLists.txt
+  :name: CMakeLists.txt-version-update
+
+  cmake_minimum_required(VERSION 3.15)
 
 Next we add the desired compiler warning flags that we want for our project. As
 warning flags vary based on the compiler we use the ``COMPILE_LANG_AND_ID``
diff --git a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
index 26aae4f..c6e0fd0 100644
--- a/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
+++ b/Help/guide/tutorial/Adding Support for a Testing Dashboard.rst
@@ -28,9 +28,23 @@
 The :module:`CTest` module will automatically call ``enable_testing()``, so we
 can remove it from our CMake files.
 
-We will also need to create a ``CTestConfig.cmake`` file in the top-level
-directory where we can specify the name of the project and where to submit the
-dashboard.
+We will also need to acquire a ``CTestConfig.cmake`` file to be placed in the
+top-level directory where we can specify information to CTest about the
+project. It contains:
+
+* The project name
+
+* The project "Nightly" start time
+
+  *  The time when a 24 hour "day" starts for this project.
+
+* The URL of the CDash instance where the submission's generated documents
+  will be sent
+
+One has been provided for you in this directory.  It would normally be
+downloaded from the ``Settings`` page of the project on the CDash
+instance that will host and display the test results.  Once downloaded from
+CDash, the file should not be modified locally.
 
 .. literalinclude:: Step9/CTestConfig.cmake
   :caption: CTestConfig.cmake
diff --git a/Help/guide/tutorial/Adding System Introspection.rst b/Help/guide/tutorial/Adding System Introspection.rst
index 7210a8d..e149110 100644
--- a/Help/guide/tutorial/Adding System Introspection.rst
+++ b/Help/guide/tutorial/Adding System Introspection.rst
@@ -14,11 +14,14 @@
 the ``m`` library. If ``log`` and ``exp`` are not initially found, require the
 ``m`` library and try again.
 
+Add the checks for ``log`` and ``exp`` to ``MathFunctions/CMakeLists.txt``,
+after the call to :command:`target_include_directories`:
+
 .. literalinclude:: Step6/MathFunctions/CMakeLists.txt
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-check_symbol_exists
   :language: cmake
-  :start-after: # does this system provide the log and exp functions?
+  :start-after: # to find MathFunctions.h, while we don't.
   :end-before: # add compile definitions
 
 If available, use :command:`target_compile_definitions` to specify
diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst
index 1806361..ed03448 100644
--- a/Help/guide/tutorial/Adding a Library.rst
+++ b/Help/guide/tutorial/Adding a Library.rst
@@ -64,8 +64,13 @@
 each time they run CMake on a build directory.
 
 The next change is to make building and linking the ``MathFunctions`` library
-conditional. To do this we change the end of the top-level ``CMakeLists.txt``
-file to look like the following:
+conditional. To do this,  we will create an ``if`` statement which checks the
+value of the option.  Inside the ``if`` block, put the
+:command:`add_subdirectory` command from above with some additional list
+commands to store information needed to link to the library and add the
+subdirectory as an include directory in the ``Tutorial`` target.
+The end of the top-level ``CMakeLists.txt`` file will now look like the
+following:
 
 .. literalinclude:: Step3/CMakeLists.txt
   :caption: CMakeLists.txt
diff --git a/Help/guide/tutorial/Complete/CMakeLists.txt b/Help/guide/tutorial/Complete/CMakeLists.txt
index 4d8a3ad..ac1d083 100644
--- a/Help/guide/tutorial/Complete/CMakeLists.txt
+++ b/Help/guide/tutorial/Complete/CMakeLists.txt
@@ -73,7 +73,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -81,7 +81,7 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
 include(InstallRequiredSystemLibraries)
diff --git a/Help/guide/tutorial/Installing and Testing.rst b/Help/guide/tutorial/Installing and Testing.rst
index 2fc3202..394c986 100644
--- a/Help/guide/tutorial/Installing and Testing.rst
+++ b/Help/guide/tutorial/Installing and Testing.rst
@@ -88,6 +88,8 @@
 Rebuild the application and then cd to the binary directory and run the
 :manual:`ctest <ctest(1)>` executable: ``ctest -N`` and ``ctest -VV``. For
 multi-config generators (e.g. Visual Studio), the configuration type must be
-specified. To run tests in Debug mode, for example, use ``ctest -C Debug -VV``
-from the build directory (not the Debug subdirectory!). Alternatively, build
-the ``RUN_TESTS`` target from the IDE.
+specified with the ``-C <mode>`` flag.  For example, to run tests in Debug
+mode use ``ctest -C Debug -VV`` from the binary directory
+(not the Debug subdirectory!). Release mode would be executed from the same
+location but with a ``-C Release``.  Alternatively, build the ``RUN_TESTS``
+target from the IDE.
diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt
index 34ae70c..dc9a0e8 100644
--- a/Help/guide/tutorial/Step10/CMakeLists.txt
+++ b/Help/guide/tutorial/Step10/CMakeLists.txt
@@ -55,7 +55,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -63,7 +63,7 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
 include(InstallRequiredSystemLibraries)
diff --git a/Help/guide/tutorial/Step11/CMakeLists.txt b/Help/guide/tutorial/Step11/CMakeLists.txt
index 4763951..10f35ce 100644
--- a/Help/guide/tutorial/Step11/CMakeLists.txt
+++ b/Help/guide/tutorial/Step11/CMakeLists.txt
@@ -63,7 +63,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -71,7 +71,7 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
 include(InstallRequiredSystemLibraries)
diff --git a/Help/guide/tutorial/Step12/CMakeLists.txt b/Help/guide/tutorial/Step12/CMakeLists.txt
index 1b0c826..634b84c 100644
--- a/Help/guide/tutorial/Step12/CMakeLists.txt
+++ b/Help/guide/tutorial/Step12/CMakeLists.txt
@@ -69,7 +69,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -77,7 +77,7 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
 include(InstallRequiredSystemLibraries)
@@ -107,7 +107,7 @@
   COMPATIBILITY AnyNewerVersion
 )
 
-# install the configuration file
+# install the generated configuration files
 install(FILES
   ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
   ${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake
diff --git a/Help/guide/tutorial/Step5/CMakeLists.txt b/Help/guide/tutorial/Step5/CMakeLists.txt
index c3b375a..82d00c8 100644
--- a/Help/guide/tutorial/Step5/CMakeLists.txt
+++ b/Help/guide/tutorial/Step5/CMakeLists.txt
@@ -54,7 +54,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -62,5 +62,5 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Help/guide/tutorial/Step6/CMakeLists.txt b/Help/guide/tutorial/Step6/CMakeLists.txt
index c3b375a..82d00c8 100644
--- a/Help/guide/tutorial/Step6/CMakeLists.txt
+++ b/Help/guide/tutorial/Step6/CMakeLists.txt
@@ -54,7 +54,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -62,5 +62,5 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Help/guide/tutorial/Step7/CMakeLists.txt b/Help/guide/tutorial/Step7/CMakeLists.txt
index c3b375a..82d00c8 100644
--- a/Help/guide/tutorial/Step7/CMakeLists.txt
+++ b/Help/guide/tutorial/Step7/CMakeLists.txt
@@ -54,7 +54,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -62,5 +62,5 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
diff --git a/Help/guide/tutorial/Step8/CMakeLists.txt b/Help/guide/tutorial/Step8/CMakeLists.txt
index 19b9913..4ae898f 100644
--- a/Help/guide/tutorial/Step8/CMakeLists.txt
+++ b/Help/guide/tutorial/Step8/CMakeLists.txt
@@ -54,7 +54,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -62,7 +62,7 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
 # setup installer
diff --git a/Help/guide/tutorial/Step8/CTestConfig.cmake b/Help/guide/tutorial/Step8/CTestConfig.cmake
new file mode 100644
index 0000000..73efdb1
--- /dev/null
+++ b/Help/guide/tutorial/Step8/CTestConfig.cmake
@@ -0,0 +1,7 @@
+set(CTEST_PROJECT_NAME "CMakeTutorial")
+set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
+
+set(CTEST_DROP_METHOD "http")
+set(CTEST_DROP_SITE "my.cdash.org")
+set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
+set(CTEST_DROP_SITE_CDASH TRUE)
diff --git a/Help/guide/tutorial/Step9/CMakeLists.txt b/Help/guide/tutorial/Step9/CMakeLists.txt
index d5f1cc8..130bc9a 100644
--- a/Help/guide/tutorial/Step9/CMakeLists.txt
+++ b/Help/guide/tutorial/Step9/CMakeLists.txt
@@ -54,7 +54,7 @@
   set_tests_properties(Comp${arg}
     PROPERTIES PASS_REGULAR_EXPRESSION ${result}
     )
-endfunction(do_test)
+endfunction()
 
 # do a bunch of result based tests
 do_test(Tutorial 4 "4 is 2")
@@ -62,7 +62,7 @@
 do_test(Tutorial 5 "5 is 2.236")
 do_test(Tutorial 7 "7 is 2.645")
 do_test(Tutorial 25 "25 is 5")
-do_test(Tutorial -25 "-25 is [-nan|nan|0]")
+do_test(Tutorial -25 "-25 is (-nan|nan|0)")
 do_test(Tutorial 0.0001 "0.0001 is 0.01")
 
 include(InstallRequiredSystemLibraries)
diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst
index 7008383..2f43070 100644
--- a/Help/manual/cmake-buildsystem.7.rst
+++ b/Help/manual/cmake-buildsystem.7.rst
@@ -21,9 +21,9 @@
 
 Executables and libraries are defined using the :command:`add_executable`
 and :command:`add_library` commands.  The resulting binary files have
-appropriate :prop_tgt:`PREFIX`, :prop_tgt:`SUFFIX` and extensions for the platform targeted.
-Dependencies between binary targets are expressed using the
-:command:`target_link_libraries` command:
+appropriate :prop_tgt:`PREFIX`, :prop_tgt:`SUFFIX` and extensions for the
+platform targeted. Dependencies between binary targets are expressed using
+the :command:`target_link_libraries` command:
 
 .. code-block:: cmake
 
@@ -530,38 +530,6 @@
 In this case, the ``exe1`` source files will be compiled with
 ``-DCONTAINER_SIZE=200``.
 
-Configuration determined build specifications may be conveniently set using
-the ``CONFIG`` generator expression.
-
-.. code-block:: cmake
-
-  target_compile_definitions(exe1 PRIVATE
-      $<$<CONFIG:Debug>:DEBUG_BUILD>
-  )
-
-The ``CONFIG`` parameter is compared case-insensitively with the configuration
-being built.  In the presence of :prop_tgt:`IMPORTED` targets, the content of
-:prop_tgt:`MAP_IMPORTED_CONFIG_DEBUG <MAP_IMPORTED_CONFIG_<CONFIG>>` is also
-accounted for by this expression.
-
-Some buildsystems generated by :manual:`cmake(1)` have a predetermined
-build-configuration set in the :variable:`CMAKE_BUILD_TYPE` variable.  The
-buildsystem for the IDEs such as Visual Studio and Xcode are generated
-independent of the build-configuration, and the actual build configuration
-is not known until build-time.  Therefore, code such as
-
-.. code-block:: cmake
-
-  string(TOLOWER ${CMAKE_BUILD_TYPE} _type)
-  if (_type STREQUAL debug)
-    target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
-  endif()
-
-may appear to work for :ref:`Makefile Generators` and :generator:`Ninja`
-generators, but is not portable to IDE generators.  Additionally,
-the :prop_tgt:`IMPORTED` configuration-mappings are not accounted for
-with code like this, so it should be avoided.
-
 The unary ``TARGET_PROPERTY`` generator expression and the ``TARGET_POLICY``
 generator expression are evaluated with the consuming target context.  This
 means that a usage requirement specification may be evaluated differently based
@@ -840,6 +808,121 @@
 a similar function, but operate at directory scope instead of target
 scope for convenience.
 
+.. _`Build Configurations`:
+
+Build Configurations
+====================
+
+Configurations determine specifications for a certain type of build, such
+as ``Release`` or ``Debug``.  The way this is specified depends on the type
+of :manual:`generator <cmake-generators(7)>` being used.  For single
+configuration generators like  :ref:`Makefile Generators` and
+:generator:`Ninja`, the configuration is specified at configure time by the
+:variable:`CMAKE_BUILD_TYPE` variable. For multi-configuration generators
+like :ref:`Visual Studio <Visual Studio Generators>`, :generator:`Xcode`, and
+:generator:`Ninja Multi-Config`, the configuration is chosen by the user at
+build time and :variable:`CMAKE_BUILD_TYPE` is ignored.  In the
+multi-configuration case, the set of *available* configurations is specified
+at configure time by the :variable:`CMAKE_CONFIGURATION_TYPES` variable,
+but the actual configuration used cannot be known until the build stage.
+This difference is often misunderstood, leading to problematic code like the
+following:
+
+.. code-block:: cmake
+
+  # WARNING: This is wrong for multi-config generators because they don't use
+  #          and typically don't even set CMAKE_BUILD_TYPE
+  string(TOLOWER ${CMAKE_BUILD_TYPE} build_type)
+  if (build_type STREQUAL debug)
+    target_compile_definitions(exe1 PRIVATE DEBUG_BUILD)
+  endif()
+
+:manual:`Generator expressions <cmake-generator-expressions(7)>` should be
+used instead to handle configuration-specific logic correctly, regardless of
+the generator used.  For example:
+
+.. code-block:: cmake
+
+  # Works correctly for both single and multi-config generators
+  target_compile_definitions(exe1 PRIVATE
+    $<$<CONFIG:Debug>:DEBUG_BUILD>
+  )
+
+In the presence of :prop_tgt:`IMPORTED` targets, the content of
+:prop_tgt:`MAP_IMPORTED_CONFIG_DEBUG <MAP_IMPORTED_CONFIG_<CONFIG>>` is also
+accounted for by the above ``$<CONFIG:Debug>`` expression.
+
+
+Case Sensitivity
+----------------
+
+:variable:`CMAKE_BUILD_TYPE` and :variable:`CMAKE_CONFIGURATION_TYPES` are
+just like other variables in that any string comparisons made with their
+values will be case-sensitive.  The ``$<CONFIG>`` generator expression also
+preserves the casing of the configuration as set by the user or CMake defaults.
+For example:
+
+.. code-block:: cmake
+
+  # NOTE: Don't use these patterns, they are for illustration purposes only.
+
+  set(CMAKE_BUILD_TYPE Debug)
+  if(CMAKE_BUILD_TYPE STREQUAL DEBUG)
+    # ... will never get here, "Debug" != "DEBUG"
+  endif()
+  add_custom_target(print_config ALL
+    # Prints "Config is Debug" in this single-config case
+    COMMAND ${CMAKE_COMMAND} -E echo "Config is $<CONFIG>"
+    VERBATIM
+  )
+
+  set(CMAKE_CONFIGURATION_TYPES Debug Release)
+  if(DEBUG IN_LIST CMAKE_CONFIGURATION_TYPES)
+    # ... will never get here, "Debug" != "DEBUG"
+  endif()
+
+In contrast, CMake treats the configuration type case-insensitively when
+using it internally in places that modify behavior based on the configuration.
+For example, the ``$<CONFIG:Debug>`` generator expression will evaluate to 1
+for a configuration of not only ``Debug``, but also ``DEBUG``, ``debug`` or
+even ``DeBuG``.  Therefore, you can specify configuration types in
+:variable:`CMAKE_BUILD_TYPE` and :variable:`CMAKE_CONFIGURATION_TYPES` with
+any mixture of upper and lowercase, although there are strong conventions
+(see the next section).  If you must test the value in string comparisons,
+always convert the value to upper or lowercase first and adjust the test
+accordingly.
+
+Default And Custom Configurations
+---------------------------------
+
+By default, CMake defines a number of standard configurations:
+
+* ``Debug``
+* ``Release``
+* ``RelWithDebInfo``
+* ``MinSizeRel``
+
+In multi-config generators, the :variable:`CMAKE_CONFIGURATION_TYPES` variable
+will be populated with (potentially a subset of) the above list by default,
+unless overridden by the project or user.  The actual configuration used is
+selected by the user at build time.
+
+For single-config generators, the configuration is specified with the
+:variable:`CMAKE_BUILD_TYPE` variable at configure time and cannot be changed
+at build time.  The default value will often be none of the above standard
+configurations and will instead be an empty string.  A common misunderstanding
+is that this is the same as ``Debug``, but that is not the case.  Users should
+always explicitly specify the build type instead to avoid this common problem.
+
+The above standard configuration types provide reasonable behavior on most
+platforms, but they can be extended to provide other types.  Each configuration
+defines a set of compiler and linker flag variables for the language in use.
+These variables follow the convention :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>`,
+where ``<CONFIG>`` is always the uppercase configuration name.  When defining
+a custom configuration type, make sure these variables are set appropriately,
+typically as cache variables.
+
+
 Pseudo Targets
 ==============
 
diff --git a/Help/manual/cmake-compile-features.7.rst b/Help/manual/cmake-compile-features.7.rst
index 2b0de11..67b0f6e 100644
--- a/Help/manual/cmake-compile-features.7.rst
+++ b/Help/manual/cmake-compile-features.7.rst
@@ -118,13 +118,12 @@
 Availability of Compiler Extensions
 -----------------------------------
 
-Because the :prop_tgt:`CXX_EXTENSIONS` target property is ``ON`` by default,
-CMake uses extended variants of language dialects by default, such as
-``-std=gnu++11`` instead of ``-std=c++11``.  That target property may be
-set to ``OFF`` to use the non-extended variant of the dialect flag.  Note
-that because most compilers enable extensions by default, this could
-expose cross-platform bugs in user code or in the headers of third-party
-dependencies.
+The :prop_tgt:`<LANG>_EXTENSIONS` target property defaults to the compiler's
+default (see :variable:`CMAKE_<LANG>_EXTENSIONS_DEFAULT`). Note that because
+most compilers enable extensions by default, this may expose portability bugs
+in user code or in the headers of third-party dependencies.
+
+:prop_tgt:`<LANG>_EXTENSIONS` used to default to ``ON``. See :policy:`CMP0128`.
 
 Optional Compile Features
 =========================
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst
index fa38588..0799fdd 100644
--- a/Help/manual/cmake-env-variables.7.rst
+++ b/Help/manual/cmake-env-variables.7.rst
@@ -30,12 +30,15 @@
 
    /envvar/CMAKE_APPLE_SILICON_PROCESSOR
    /envvar/CMAKE_BUILD_PARALLEL_LEVEL
+   /envvar/CMAKE_BUILD_TYPE
+   /envvar/CMAKE_CONFIGURATION_TYPES
    /envvar/CMAKE_CONFIG_TYPE
    /envvar/CMAKE_EXPORT_COMPILE_COMMANDS
    /envvar/CMAKE_GENERATOR
    /envvar/CMAKE_GENERATOR_INSTANCE
    /envvar/CMAKE_GENERATOR_PLATFORM
    /envvar/CMAKE_GENERATOR_TOOLSET
+   /envvar/CMAKE_INSTALL_MODE
    /envvar/CMAKE_LANG_COMPILER_LAUNCHER
    /envvar/CMAKE_LANG_LINKER_LAUNCHER
    /envvar/CMAKE_MSVCIDE_RUN_PATH
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index d3cbd49..df13dd0 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -134,8 +134,8 @@
 
 .. genex:: $<CONFIG:cfgs>
 
-  ``1`` if config is any one of the entries in ``cfgs``, else ``0``. This is a
-  case-insensitive comparison. The mapping in
+  ``1`` if config is any one of the entries in comma-separated list
+  ``cfgs``, else ``0``. This is a case-insensitive comparison. The mapping in
   :prop_tgt:`MAP_IMPORTED_CONFIG_<CONFIG>` is also considered by this
   expression when it is evaluated on a property on an :prop_tgt:`IMPORTED`
   target.
diff --git a/Help/manual/cmake-packages.7.rst b/Help/manual/cmake-packages.7.rst
index 4b2934a..5c109ff 100644
--- a/Help/manual/cmake-packages.7.rst
+++ b/Help/manual/cmake-packages.7.rst
@@ -74,7 +74,9 @@
 
 By setting the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable to
 ``TRUE``, the ``<PackageName>`` package will not be searched, and will always
-be ``NOTFOUND``.
+be ``NOTFOUND``. Likewise, setting the
+:variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` to ``TRUE`` will make the
+package REQUIRED.
 
 .. _`Config File Packages`:
 
@@ -447,7 +449,7 @@
   set(_supported_components Plot Table)
 
   foreach(_comp ${ClimbingStats_FIND_COMPONENTS})
-    if (NOT ";${_supported_components};" MATCHES _comp)
+    if (NOT ";${_supported_components};" MATCHES ";${_comp};")
       set(ClimbingStats_FOUND False)
       set(ClimbingStats_NOT_FOUND_MESSAGE "Unsupported component: ${_comp}")
     endif()
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index b9e3d45..3df4f9f 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,15 @@
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.22
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0128: Selection of language standard and extension flags improved. </policy/CMP0128>
+   CMP0127: cmake_dependent_option() supports full Condition Syntax. </policy/CMP0127>
+
 Policies Introduced by CMake 3.21
 =================================
 
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 5f18a82..bb5dba3 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -202,6 +202,7 @@
    /prop_tgt/EXPORT_NAME
    /prop_tgt/EXPORT_PROPERTIES
    /prop_tgt/FOLDER
+   /prop_tgt/Fortran_BUILDING_INSTRINSIC_MODULES
    /prop_tgt/Fortran_FORMAT
    /prop_tgt/Fortran_MODULE_DIRECTORY
    /prop_tgt/Fortran_PREPROCESS
@@ -214,6 +215,9 @@
    /prop_tgt/GNUtoMS
    /prop_tgt/HAS_CXX
    /prop_tgt/HIP_ARCHITECTURES
+   /prop_tgt/HIP_EXTENSIONS
+   /prop_tgt/HIP_STANDARD
+   /prop_tgt/HIP_STANDARD_REQUIRED
    /prop_tgt/IMPLICIT_DEPENDS_INCLUDE_TRANSFORM
    /prop_tgt/IMPORTED
    /prop_tgt/IMPORTED_COMMON_LANGUAGE_RUNTIME
@@ -273,8 +277,11 @@
    /prop_tgt/LANG_COMPILER_LAUNCHER
    /prop_tgt/LANG_CPPCHECK
    /prop_tgt/LANG_CPPLINT
+   /prop_tgt/LANG_EXTENSIONS
    /prop_tgt/LANG_INCLUDE_WHAT_YOU_USE
    /prop_tgt/LANG_LINKER_LAUNCHER
+   /prop_tgt/LANG_STANDARD
+   /prop_tgt/LANG_STANDARD_REQUIRED
    /prop_tgt/LANG_VISIBILITY_PRESET
    /prop_tgt/LIBRARY_OUTPUT_DIRECTORY
    /prop_tgt/LIBRARY_OUTPUT_DIRECTORY_CONFIG
@@ -450,6 +457,7 @@
    /prop_test/DEPENDS
    /prop_test/DISABLED
    /prop_test/ENVIRONMENT
+   /prop_test/ENVIRONMENT_MODIFICATION
    /prop_test/FAIL_REGULAR_EXPRESSION
    /prop_test/FIXTURES_CLEANUP
    /prop_test/FIXTURES_REQUIRED
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 9cea0fb..4ed0b2e 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -71,6 +71,7 @@
    /variable/CMAKE_JOB_POOL_PRECOMPILE_HEADER
    /variable/CMAKE_JOB_POOLS
    /variable/CMAKE_LANG_COMPILER_AR
+   /variable/CMAKE_LANG_COMPILER_FRONTEND_VARIANT
    /variable/CMAKE_LANG_COMPILER_RANLIB
    /variable/CMAKE_LANG_LINK_LIBRARY_SUFFIX
    /variable/CMAKE_LINK_LIBRARY_SUFFIX
@@ -124,6 +125,9 @@
    /variable/CMAKE_VS_PLATFORM_TOOLSET_CUDA_CUSTOM_DIR
    /variable/CMAKE_VS_PLATFORM_TOOLSET_HOST_ARCHITECTURE
    /variable/CMAKE_VS_PLATFORM_TOOLSET_VERSION
+   /variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION
+   /variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER
+   /variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
    /variable/CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM
    /variable/CMAKE_XCODE_BUILD_SYSTEM
@@ -231,6 +235,7 @@
    /variable/CMAKE_PROJECT_INCLUDE_BEFORE
    /variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE
    /variable/CMAKE_PROJECT_PROJECT-NAME_INCLUDE_BEFORE
+   /variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName
    /variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY
    /variable/CMAKE_STAGING_PREFIX
    /variable/CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS
@@ -246,6 +251,7 @@
    /variable/CMAKE_SYSTEM_LIBRARY_PATH
    /variable/CMAKE_SYSTEM_PREFIX_PATH
    /variable/CMAKE_SYSTEM_PROGRAM_PATH
+   /variable/CMAKE_TLS_CAINFO
    /variable/CMAKE_TLS_VERIFY
    /variable/CMAKE_USER_MAKE_RULES_OVERRIDE
    /variable/CMAKE_WARN_DEPRECATED
@@ -428,6 +434,7 @@
    /variable/CMAKE_LANG_LINKER_LAUNCHER
    /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_FLAG
+   /variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG
    /variable/CMAKE_LANG_VISIBILITY_PRESET
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY
    /variable/CMAKE_LIBRARY_OUTPUT_DIRECTORY_CONFIG
@@ -438,6 +445,7 @@
    /variable/CMAKE_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LINK_LIBRARY_FLAG
    /variable/CMAKE_LINK_WHAT_YOU_USE
+   /variable/CMAKE_LINK_WHAT_YOU_USE_CHECK
    /variable/CMAKE_MACOSX_BUNDLE
    /variable/CMAKE_MACOSX_RPATH
    /variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG
@@ -525,6 +533,9 @@
    /variable/CMAKE_Fortran_MODDIR_FLAG
    /variable/CMAKE_Fortran_MODOUT_FLAG
    /variable/CMAKE_HIP_ARCHITECTURES
+   /variable/CMAKE_HIP_EXTENSIONS
+   /variable/CMAKE_HIP_STANDARD
+   /variable/CMAKE_HIP_STANDARD_REQUIRED
    /variable/CMAKE_ISPC_HEADER_DIRECTORY
    /variable/CMAKE_ISPC_HEADER_SUFFIX
    /variable/CMAKE_ISPC_INSTRUCTION_SETS
@@ -546,6 +557,8 @@
    /variable/CMAKE_LANG_CREATE_SHARED_LIBRARY
    /variable/CMAKE_LANG_CREATE_SHARED_MODULE
    /variable/CMAKE_LANG_CREATE_STATIC_LIBRARY
+   /variable/CMAKE_LANG_EXTENSIONS
+   /variable/CMAKE_LANG_EXTENSIONS_DEFAULT
    /variable/CMAKE_LANG_FLAGS
    /variable/CMAKE_LANG_FLAGS_CONFIG
    /variable/CMAKE_LANG_FLAGS_CONFIG_INIT
@@ -574,8 +587,11 @@
    /variable/CMAKE_LANG_SIMULATE_VERSION
    /variable/CMAKE_LANG_SIZEOF_DATA_PTR
    /variable/CMAKE_LANG_SOURCE_FILE_EXTENSIONS
+   /variable/CMAKE_LANG_STANDARD
+   /variable/CMAKE_LANG_STANDARD_DEFAULT
    /variable/CMAKE_LANG_STANDARD_INCLUDE_DIRECTORIES
    /variable/CMAKE_LANG_STANDARD_LIBRARIES
+   /variable/CMAKE_LANG_STANDARD_REQUIRED
    /variable/CMAKE_OBJC_EXTENSIONS
    /variable/CMAKE_OBJC_STANDARD
    /variable/CMAKE_OBJC_STANDARD_REQUIRED
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 03d8bf6..d66c5a9 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -464,6 +464,10 @@
 associated with the tests run. The label time summary will not include labels
 that are mapped to subprojects.
 
+.. versionadded:: 3.22
+  Labels added dynamically during test execution are also reported in the
+  timing summary.  See :ref:`Additional Labels`.
+
 When the :prop_test:`PROCESSORS` test property is set, CTest will display a
 weighted test timing result in label and subproject summaries. The time is
 reported with `sec*proc` instead of just `sec`.
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index 767e80b..7852550 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -124,7 +124,7 @@
               },
               {
                 "type": "array",
-                "description": "An optional array of strings representing the names of presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.",
+                "description": "An optional array of strings representing the names of presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.",
                 "items": {
                   "type": "string",
                   "description": "An optional string representing the name of the preset to inherit from.",
@@ -446,7 +446,7 @@
               },
               {
                 "type": "array",
-                "description": "An optional array of strings representing the names of build presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.",
+                "description": "An optional array of strings representing the names of build presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.",
                 "items": {
                   "type": "string",
                   "description": "An optional string representing the name of the preset to inherit from.",
@@ -641,7 +641,7 @@
               },
               {
                 "type": "array",
-                "description": "An optional array of strings representing the names of test presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.",
+                "description": "An optional array of strings representing the names of test presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and displayName), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json must not inherit from presets in CMakeUserPresets.json.",
                 "items": {
                   "type": "string",
                   "description": "An optional string representing the name of the preset to inherit from.",
diff --git a/Help/policy/CMP0077.rst b/Help/policy/CMP0077.rst
index d8744a9..482125a 100644
--- a/Help/policy/CMP0077.rst
+++ b/Help/policy/CMP0077.rst
@@ -53,6 +53,8 @@
 This policy was introduced in CMake version 3.13.  CMake version
 |release| warns when the policy is not set and uses ``OLD`` behavior.
 Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
-explicitly.
+explicitly within a project.  Use the :variable:`CMAKE_POLICY_DEFAULT_CMP0077
+<CMAKE_POLICY_DEFAULT_CMP\<NNNN\>>` variable to set the policy for
+a third-party project in a subdirectory without modifying it.
 
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0126.rst b/Help/policy/CMP0126.rst
index ba027b3..1b69957 100644
--- a/Help/policy/CMP0126.rst
+++ b/Help/policy/CMP0126.rst
@@ -22,7 +22,10 @@
 already exists and :policy:`CMP0077` is set to ``NEW``.
 
 Policy ``CMP0126`` was introduced in CMake version 3.21. Use the
-:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly
+within a project.  Use the :variable:`CMAKE_POLICY_DEFAULT_CMP0126
+<CMAKE_POLICY_DEFAULT_CMP\<NNNN\>>` variable to set the policy for
+a third-party project in a subdirectory without modifying it.
 Unlike many policies, CMake version |release| does *not* warn when the policy
 is not set and simply uses ``OLD`` behavior.  See documentation of the
 :variable:`CMAKE_POLICY_WARNING_CMP0126 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
diff --git a/Help/policy/CMP0127.rst b/Help/policy/CMP0127.rst
new file mode 100644
index 0000000..2106110
--- /dev/null
+++ b/Help/policy/CMP0127.rst
@@ -0,0 +1,34 @@
+CMP0127
+-------
+
+.. versionadded:: 3.22
+
+:command:`cmake_dependent_option` supports full :ref:`Condition Syntax`.
+
+The ``<depends>`` parameter accepts a :ref:`semicolon-separated list <CMake
+Language Lists>` of conditions.  CMake 3.21 and lower evaluates each
+``condition`` as ``if(${condition})``, which does not properly handle
+conditions with nested paren groups.  CMake 3.22 and above instead prefer
+to evaluate each ``condition`` as ``if(<condition>)``, where ``<condition>``
+is re-parsed as if literally written in a call to :command:`if`.  This
+allows expressions like::
+
+  "A AND (B OR C)"
+
+but requires expressions like::
+
+  "FOO MATCHES (UPPER|lower)"
+
+to be re-written as::
+
+  "FOO MATCHES \"(UPPER|lower)\""
+
+Policy ``CMP0127`` provides compatibility for projects that have not
+been updated to expect the new behavior.
+
+This policy was introduced in CMake version 3.22.  CMake version
+|release| warns when the policy is not set and uses ``OLD`` behavior.
+Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
+explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0128.rst b/Help/policy/CMP0128.rst
new file mode 100644
index 0000000..604a146
--- /dev/null
+++ b/Help/policy/CMP0128.rst
@@ -0,0 +1,71 @@
+CMP0128
+-------
+
+.. versionadded:: 3.22
+
+When this policy is set to ``NEW``:
+
+* :prop_tgt:`<LANG>_EXTENSIONS` is initialized to
+  :variable:`CMAKE_<LANG>_EXTENSIONS` if set, otherwise falling back to
+  :variable:`CMAKE_<LANG>_EXTENSIONS_DEFAULT`.
+
+* Extensions are correctly enabled/disabled if :prop_tgt:`<LANG>_STANDARD` is
+  unset or satisfied by the default.
+
+* Standard mode-affecting flags aren't added unless necessary to achieve the
+  specified mode.
+
+The ``OLD`` behavior:
+
+* Initializes :prop_tgt:`<LANG>_EXTENSIONS` to
+  :variable:`CMAKE_<LANG>_EXTENSIONS` if set, otherwise falling back to ``ON``.
+
+* Always adds a flag if :prop_tgt:`<LANG>_STANDARD` is set and
+  :prop_tgt:`<LANG>_STANDARD_REQUIRED` is ``OFF``.
+
+* If :prop_tgt:`<LANG>_STANDARD` is unset:
+
+  * Doesn't disable extensions even if :prop_tgt:`<LANG>_EXTENSIONS` is
+    ``OFF``.
+
+  * Fails to enable extensions if :prop_tgt:`<LANG>_EXTENSIONS` is ``ON``
+    except for the ``IAR`` compiler.
+
+Code may need to be updated for the ``NEW`` behavior in the following cases:
+
+* If a standard mode flag previously overridden by CMake's and not used during
+  compiler detection now takes effect due to CMake no longer adding one as the
+  default detected is appropriate.
+
+  Such code should be converted to either:
+
+  * Use :prop_tgt:`<LANG>_STANDARD` and :prop_tgt:`<LANG>_EXTENSIONS` instead
+    of manually adding flags.
+
+  * Or ensure the manually-specified flags are used during compiler detection.
+
+* If extensions were disabled without :prop_tgt:`<LANG>_STANDARD` being set
+  CMake previously wouldn't actually disable extensions.
+
+  Such code should be updated to not disable extensions if they are required.
+
+* If extensions were enabled/disabled when :prop_tgt:`<LANG>_STANDARD` was
+  satisfied by the compiler's default CMake previously wouldn't actually
+  enable/disable extensions.
+
+  Such code should be updated to set the correct extensions mode.
+
+If compiler flags affecting the standard mode are used during compiler
+detection (for example in :manual:`a toolchain file <cmake-toolchains(7)>`
+using :variable:`CMAKE_<LANG>_FLAGS_INIT`) then they will affect the detected
+default :variable:`standard <CMAKE_<LANG>_STANDARD_DEFAULT>` and
+:variable:`extensions <CMAKE_<LANG>_EXTENSIONS_DEFAULT>`.
+
+Unlike many policies, CMake version |release| does *not* warn when the policy
+is not set and simply uses the ``OLD`` behavior. Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+See documentation of the
+:variable:`CMAKE_POLICY_WARNING_CMP0128 <CMAKE_POLICY_WARNING_CMP<NNNN>>`
+variable to control the warning.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst b/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst
index 6bbb870..3a17973 100644
--- a/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst
+++ b/Help/prop_gbl/CMAKE_CUDA_KNOWN_FEATURES.rst
@@ -32,4 +32,6 @@
   Compiler mode is at least CUDA/C++ 20.
 
 ``cuda_std_23``
+  .. versionadded:: 3.20
+
   Compiler mode is at least CUDA/C++ 23.
diff --git a/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst b/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst
index 73c0b34..1a913fb 100644
--- a/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst
+++ b/Help/prop_gbl/CMAKE_CXX_KNOWN_FEATURES.rst
@@ -18,6 +18,8 @@
 High level meta features indicating C++ standard support
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+.. versionadded:: 3.8
+
 The following meta features indicate general support for the associated
 language standard.  It reflects the language support claimed by the compiler,
 but it does not necessarily imply complete conformance to that standard.
@@ -35,9 +37,13 @@
   Compiler mode is at least C++ 17.
 
 ``cxx_std_20``
+  .. versionadded:: 3.12
+
   Compiler mode is at least C++ 20.
 
 ``cxx_std_23``
+  .. versionadded:: 3.20
+
   Compiler mode is at least C++ 23.
 
 Low level individual compile features
diff --git a/Help/prop_gbl/CMAKE_C_KNOWN_FEATURES.rst b/Help/prop_gbl/CMAKE_C_KNOWN_FEATURES.rst
index 2bd0feb..97da697 100644
--- a/Help/prop_gbl/CMAKE_C_KNOWN_FEATURES.rst
+++ b/Help/prop_gbl/CMAKE_C_KNOWN_FEATURES.rst
@@ -13,7 +13,12 @@
 command.  See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
 
-The features known to this version of CMake are:
+The features known to this version of CMake are listed below.
+
+High level meta features indicating C standard support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.8
 
 ``c_std_90``
   Compiler mode is at least C 90.
@@ -25,11 +30,18 @@
   Compiler mode is at least C 11.
 
 ``c_std_17``
+  .. versionadded:: 3.21
+
   Compiler mode is at least C 17.
 
 ``c_std_23``
+  .. versionadded:: 3.21
+
   Compiler mode is at least C 23.
 
+Low level individual compile features
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 ``c_function_prototypes``
   Function prototypes, as defined in ``ISO/IEC 9899:1990``.
 
diff --git a/Help/prop_sf/VS_SETTINGS.rst b/Help/prop_sf/VS_SETTINGS.rst
index 322f5a6..871e36e 100644
--- a/Help/prop_sf/VS_SETTINGS.rst
+++ b/Help/prop_sf/VS_SETTINGS.rst
@@ -3,7 +3,12 @@
 
 .. versionadded:: 3.18
 
-Set any item metadata on a non-built file.
+Set any item metadata on a file.
+
+.. versionadded:: 3.22
+
+  This property is honored for all source file types.
+  Previously it worked only for non-built files.
 
 Takes a list of ``Key=Value`` pairs. Tells the Visual Studio generator to set
 ``Key`` to ``Value`` as item metadata on the file.
diff --git a/Help/prop_test/ENVIRONMENT.rst b/Help/prop_test/ENVIRONMENT.rst
index 102c792..43bcdbe 100644
--- a/Help/prop_test/ENVIRONMENT.rst
+++ b/Help/prop_test/ENVIRONMENT.rst
@@ -5,5 +5,5 @@
 
 If set to a list of environment variables and values of the form
 ``MYVAR=value`` those environment variables will be defined while running
-the test.  The environment is restored to its previous state after the
-test is done.
+the test.  The environment changes from this property do not affect other
+tests.
diff --git a/Help/prop_test/ENVIRONMENT_MODIFICATION.rst b/Help/prop_test/ENVIRONMENT_MODIFICATION.rst
new file mode 100644
index 0000000..1e17329
--- /dev/null
+++ b/Help/prop_test/ENVIRONMENT_MODIFICATION.rst
@@ -0,0 +1,38 @@
+ENVIRONMENT_MODIFICATION
+------------------------
+
+.. versionadded:: 3.22
+
+Specify environment variables that should be modified for running a test. Note
+that the operations performed by this property are performed after the
+:prop_test:`ENVIRONMENT` property is already applied.
+
+If set to a list of environment variables and values of the form
+``MYVAR=OP:VALUE``, where ``MYVAR`` is the case-sensitive name of an
+environment variable to be modified. Entries are considered in the
+order specified in the property's value. The ``OP`` may be one of:
+
+  - ``reset``: Reset to the unmodified value, ignoring all modifications to
+    ``MYVAR`` prior to this entry. Note that this will reset the variable to
+    the value set by :prop_test:`ENVIRONMENT`, if it was set, and otherwise
+    to its state from the rest of the CTest execution.
+  - ``set``: Replaces the current value of ``MYVAR`` with ``VALUE``.
+  - ``unset``: Unsets the current value of ``MYVAR``.
+  - ``string_append``: Appends ``VALUE`` to the current value of ``MYVAR``.
+  - ``string_prepend``: Prepends ``VALUE`` to the current value of ``MYVAR``.
+  - ``path_list_append``: Appends ``VALUE`` to the current value of ``MYVAR``
+    using the host platform's path list separator (``;`` on Windows and ``:``
+    elsewhere).
+  - ``path_list_prepend``: Prepends ``VALUE`` to the current value of
+    ``MYVAR`` using the host platform's path list separator (``;`` on Windows
+    and ``:`` elsewhere).
+  - ``cmake_list_append``: Appends ``VALUE`` to the current value of ``MYVAR``
+    using ``;`` as the separator.
+  - ``cmake_list_prepend``: Prepends ``VALUE`` to the current value of
+    ``MYVAR`` using ``;`` as the separator.
+
+Unrecognized ``OP`` values will result in the test failing before it is
+executed. This is so that future operations may be added without changing
+valid behavior of existing tests.
+
+The environment changes from this property do not affect other tests.
diff --git a/Help/prop_test/LABELS.rst b/Help/prop_test/LABELS.rst
index 8d75570..a06f152 100644
--- a/Help/prop_test/LABELS.rst
+++ b/Help/prop_test/LABELS.rst
@@ -1,6 +1,10 @@
 LABELS
 ------
 
-Specify a list of text labels associated with a test.
+Specify a list of text labels associated with a test.  The labels are
+reported in both the ``ctest`` output summary and in dashboard submissions.
+They can also be used to filter the set of tests to be executed (see the
+``ctest -L`` and ``ctest -LE`` :ref:`CTest Options`).
 
-The list is reported in dashboard submissions.
+See :ref:`Additional Labels` for adding labels to a test dynamically during
+test execution.
diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst
index 52d96e0..ed8b262 100644
--- a/Help/prop_tgt/AUTOMOC.rst
+++ b/Help/prop_tgt/AUTOMOC.rst
@@ -4,9 +4,9 @@
 Should the target be processed with auto-moc (for Qt projects).
 
 :prop_tgt:`AUTOMOC` is a boolean specifying whether CMake will handle the Qt
-``moc`` preprocessor automatically, i.e.  without having to use the
-:module:`QT4_WRAP_CPP() <FindQt4>` or ``QT5_WRAP_CPP()`` macro.
-Currently Qt4 and Qt5 are supported.
+``moc`` preprocessor automatically, i.e.  without having to use commands like
+:module:`QT4_WRAP_CPP() <FindQt4>`, ``QT5_WRAP_CPP()``, etc.
+Currently, Qt versions 4 to 6 are supported.
 
 This property is initialized by the value of the :variable:`CMAKE_AUTOMOC`
 variable if it is set when a target is created.
@@ -148,10 +148,13 @@
 
 :prop_tgt:`AUTOMOC` enabled targets need to know the Qt major and minor
 version they're working with.  The major version usually is provided by the
-``INTERFACE_QT_MAJOR_VERSION`` property of the ``Qt[45]Core`` library,
+``INTERFACE_QT_MAJOR_VERSION`` property of the ``Qt[456]Core`` library,
 that the target links to.  To find the minor version, CMake builds a list of
 available Qt versions from
 
+- ``Qt6Core_VERSION_MAJOR`` and ``Qt6Core_VERSION_MINOR`` variables
+  (usually set by ``find_package(Qt6...)``)
+- ``Qt6Core_VERSION_MAJOR`` and ``Qt6Core_VERSION_MINOR`` directory properties
 - ``Qt5Core_VERSION_MAJOR`` and ``Qt5Core_VERSION_MINOR`` variables
   (usually set by ``find_package(Qt5...)``)
 - ``Qt5Core_VERSION_MAJOR`` and ``Qt5Core_VERSION_MINOR`` directory properties
@@ -167,13 +170,13 @@
 If  ``INTERFACE_QT_MAJOR_VERSION`` is not a valid number, the first
 entry in the list is taken.
 
-A ``find_package(Qt[45]...)`` call sets the ``QT/Qt5Core_VERSION_MAJOR/MINOR``
+A ``find_package(Qt[456]...)`` call sets the ``QT/Qt[56]Core_VERSION_MAJOR/MINOR``
 variables.  If the call is in a different context than the
 :command:`add_executable` or :command:`add_library` call, e.g. in a function,
 then the version variables might not be available to the :prop_tgt:`AUTOMOC`
 enabled target.
 In that case the version variables can be forwarded from the
-``find_package(Qt[45]...)`` calling context to the :command:`add_executable`
+``find_package(Qt[456]...)`` calling context to the :command:`add_executable`
 or :command:`add_library` calling context as directory properties.
 The following Qt5 example demonstrates the procedure.
 
diff --git a/Help/prop_tgt/AUTORCC.rst b/Help/prop_tgt/AUTORCC.rst
index 9a98f44..0a0c2a1 100644
--- a/Help/prop_tgt/AUTORCC.rst
+++ b/Help/prop_tgt/AUTORCC.rst
@@ -5,8 +5,8 @@
 
 :prop_tgt:`AUTORCC` is a boolean specifying whether CMake will handle
 the Qt ``rcc`` code generator automatically, i.e. without having to use
-the :module:`QT4_ADD_RESOURCES() <FindQt4>` or ``QT5_ADD_RESOURCES()``
-macro.  Currently Qt4 and Qt5 are supported.
+commands like :module:`QT4_ADD_RESOURCES() <FindQt4>`, ``QT5_ADD_RESOURCES()``,
+etc.  Currently, Qt versions 4 to 6 are supported.
 
 When this property is ``ON``, CMake will handle ``.qrc`` files added
 as target sources at build time and invoke ``rcc`` accordingly.
diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst
index cd24f5e..e0cea97 100644
--- a/Help/prop_tgt/AUTOUIC.rst
+++ b/Help/prop_tgt/AUTOUIC.rst
@@ -5,8 +5,8 @@
 
 :prop_tgt:`AUTOUIC` is a boolean specifying whether CMake will handle
 the Qt ``uic`` code generator automatically, i.e. without having to use
-the :module:`QT4_WRAP_UI() <FindQt4>` or ``QT5_WRAP_UI()`` macro. Currently
-Qt4 and Qt5 are supported.
+commands like :module:`QT4_WRAP_UI() <FindQt4>`, ``QT5_WRAP_UI()``, etc.
+Currently, Qt versions 4 to 6 are supported.
 
 This property is initialized by the value of the :variable:`CMAKE_AUTOUIC`
 variable if it is set when a target is created.
diff --git a/Help/prop_tgt/CUDA_EXTENSIONS.rst b/Help/prop_tgt/CUDA_EXTENSIONS.rst
index 2ddba0b..ab467ac 100644
--- a/Help/prop_tgt/CUDA_EXTENSIONS.rst
+++ b/Help/prop_tgt/CUDA_EXTENSIONS.rst
@@ -15,5 +15,7 @@
 compile features and a list of supported compilers.
 
 This property is initialized by the value of
-the :variable:`CMAKE_CUDA_EXTENSIONS` variable if it is set when a target
-is created.
+the :variable:`CMAKE_CUDA_EXTENSIONS` variable if set when a target is created
+and otherwise by the value of
+:variable:`CMAKE_CUDA_EXTENSIONS_DEFAULT <CMAKE_<LANG>_EXTENSIONS_DEFAULT>`
+(see :policy:`CMP0128`).
diff --git a/Help/prop_tgt/CUDA_STANDARD.rst b/Help/prop_tgt/CUDA_STANDARD.rst
index 5ef57be..950ba12 100644
--- a/Help/prop_tgt/CUDA_STANDARD.rst
+++ b/Help/prop_tgt/CUDA_STANDARD.rst
@@ -12,21 +12,27 @@
 Supported values are:
 
 ``98``
-  CUDA C++98
+  CUDA C++98. Note that this maps to the same as ``03`` internally.
+
+``03``
+  CUDA C++03
 
 ``11``
   CUDA C++11
 
 ``14``
-  CUDA C++14
+  CUDA C++14. While CMake 3.8 and later *recognize* ``14`` as a valid value,
+  CMake 3.9 was the first version to include support for any compiler.
 
 ``17``
-  CUDA C++17
+  CUDA C++17. While CMake 3.8 and later *recognize* ``17`` as a valid value,
+  CMake 3.18 was the first version to include support for any compiler.
 
 ``20``
   .. versionadded:: 3.12
 
-  CUDA C++20
+  CUDA C++20. While CMake 3.12 and later *recognize* ``20`` as a valid value,
+  CMake 3.18 was the first version to include support for any compiler.
 
 ``23``
   .. versionadded:: 3.20
@@ -43,7 +49,7 @@
 
 with a compiler which does not support ``-std=gnu++11`` or an equivalent
 flag will not result in an error or warning, but will instead add the
-``-std=gnu++98`` flag if supported.  This "decay" behavior may be controlled
+``-std=gnu++03`` flag if supported.  This "decay" behavior may be controlled
 with the :prop_tgt:`CUDA_STANDARD_REQUIRED` target property.
 Additionally, the :prop_tgt:`CUDA_EXTENSIONS` target property may be used to
 control whether compiler-specific extensions are enabled on a per-target basis.
diff --git a/Help/prop_tgt/CXX_EXTENSIONS.rst b/Help/prop_tgt/CXX_EXTENSIONS.rst
index bda531e..9b52504 100644
--- a/Help/prop_tgt/CXX_EXTENSIONS.rst
+++ b/Help/prop_tgt/CXX_EXTENSIONS.rst
@@ -15,5 +15,7 @@
 compile features and a list of supported compilers.
 
 This property is initialized by the value of
-the :variable:`CMAKE_CXX_EXTENSIONS` variable if it is set when a target
-is created.
+the :variable:`CMAKE_CXX_EXTENSIONS` variable if set when a target is created
+and otherwise by the value of
+:variable:`CMAKE_CXX_EXTENSIONS_DEFAULT <CMAKE_<LANG>_EXTENSIONS_DEFAULT>` (see
+:policy:`CMP0128`).
diff --git a/Help/prop_tgt/C_EXTENSIONS.rst b/Help/prop_tgt/C_EXTENSIONS.rst
index b2abb46..a53090c 100644
--- a/Help/prop_tgt/C_EXTENSIONS.rst
+++ b/Help/prop_tgt/C_EXTENSIONS.rst
@@ -15,5 +15,7 @@
 compile features and a list of supported compilers.
 
 This property is initialized by the value of
-the :variable:`CMAKE_C_EXTENSIONS` variable if it is set when a target
-is created.
+the :variable:`CMAKE_C_EXTENSIONS` variable if set when a target is created and
+otherwise by the value of
+:variable:`CMAKE_C_EXTENSIONS_DEFAULT <CMAKE_<LANG>_EXTENSIONS_DEFAULT>` (see
+:policy:`CMP0128`).
diff --git a/Help/prop_tgt/Fortran_BUILDING_INSTRINSIC_MODULES.rst b/Help/prop_tgt/Fortran_BUILDING_INSTRINSIC_MODULES.rst
new file mode 100644
index 0000000..3533ef9
--- /dev/null
+++ b/Help/prop_tgt/Fortran_BUILDING_INSTRINSIC_MODULES.rst
@@ -0,0 +1,16 @@
+Fortran_BUILDING_INSTRINSIC_MODULES
+-----------------------------------
+
+.. versionadded:: 3.22
+
+Instructs the CMake Fortran preprocessor that the target is building
+Fortran intrinsics for building a Fortran compiler.
+
+This property is off by default and should be turned only on projects
+that build a Fortran compiler. It should not be turned on for projects
+that use a Fortran compiler.
+
+Turning this property on will correctly add dependencies for building
+Fortran intrinsic modules whereas turning the property off will ignore
+Fortran intrinsic modules in the dependency graph as they are supplied
+by the compiler itself.
diff --git a/Help/prop_tgt/HIP_EXTENSIONS.rst b/Help/prop_tgt/HIP_EXTENSIONS.rst
new file mode 100644
index 0000000..d1475c9
--- /dev/null
+++ b/Help/prop_tgt/HIP_EXTENSIONS.rst
@@ -0,0 +1,21 @@
+HIP_EXTENSIONS
+--------------
+
+.. versionadded:: 3.21
+
+Boolean specifying whether compiler specific extensions are requested.
+
+This property specifies whether compiler specific extensions should be
+used.  For some compilers, this results in adding a flag such
+as ``-std=gnu++11`` instead of ``-std=c++11`` to the compile line.  This
+property is ``ON`` by default. The basic HIP/C++ standard level is
+controlled by the :prop_tgt:`HIP_STANDARD` target property.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
+
+This property is initialized by the value of
+the :variable:`CMAKE_HIP_EXTENSIONS` variable if set when a target is created
+and otherwise by the value of
+:variable:`CMAKE_HIP_EXTENSIONS_DEFAULT <CMAKE_<LANG>_EXTENSIONS_DEFAULT>` (see
+:policy:`CMP0128`).
diff --git a/Help/prop_tgt/HIP_STANDARD.rst b/Help/prop_tgt/HIP_STANDARD.rst
new file mode 100644
index 0000000..0c767c6
--- /dev/null
+++ b/Help/prop_tgt/HIP_STANDARD.rst
@@ -0,0 +1,48 @@
+HIP_STANDARD
+------------
+
+.. versionadded:: 3.21
+
+The HIP/C++ standard requested to build this target.
+
+Supported values are:
+
+``98``
+  HIP C++98
+
+``11``
+  HIP C++11
+
+``14``
+  HIP C++14
+
+``17``
+  HIP C++17
+
+``20``
+  HIP C++20
+
+``23``
+  HIP C++23
+
+If the value requested does not result in a compile flag being added for
+the compiler in use, a previous standard flag will be added instead.  This
+means that using:
+
+.. code-block:: cmake
+
+  set_property(TARGET tgt PROPERTY HIP_STANDARD 11)
+
+with a compiler which does not support ``-std=gnu++11`` or an equivalent
+flag will not result in an error or warning, but will instead add the
+``-std=gnu++98`` flag if supported.  This "decay" behavior may be controlled
+with the :prop_tgt:`HIP_STANDARD_REQUIRED` target property.
+Additionally, the :prop_tgt:`HIP_EXTENSIONS` target property may be used to
+control whether compiler-specific extensions are enabled on a per-target basis.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
+
+This property is initialized by the value of
+the :variable:`CMAKE_HIP_STANDARD` variable if it is set when a target
+is created.
diff --git a/Help/prop_tgt/HIP_STANDARD_REQUIRED.rst b/Help/prop_tgt/HIP_STANDARD_REQUIRED.rst
new file mode 100644
index 0000000..e56209a
--- /dev/null
+++ b/Help/prop_tgt/HIP_STANDARD_REQUIRED.rst
@@ -0,0 +1,19 @@
+HIP_STANDARD_REQUIRED
+---------------------
+
+.. versionadded:: 3.21
+
+Boolean describing whether the value of :prop_tgt:`HIP_STANDARD` is a requirement.
+
+If this property is set to ``ON``, then the value of the
+:prop_tgt:`HIP_STANDARD` target property is treated as a requirement.  If this
+property is ``OFF`` or unset, the :prop_tgt:`HIP_STANDARD` target property is
+treated as optional and may "decay" to a previous standard if the requested is
+not available.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
+
+This property is initialized by the value of
+the :variable:`CMAKE_HIP_STANDARD_REQUIRED` variable if it is set when a
+target is created.
diff --git a/Help/prop_tgt/LANG_EXTENSIONS.rst b/Help/prop_tgt/LANG_EXTENSIONS.rst
new file mode 100644
index 0000000..afc2ad3
--- /dev/null
+++ b/Help/prop_tgt/LANG_EXTENSIONS.rst
@@ -0,0 +1,24 @@
+<LANG>_EXTENSIONS
+-----------------
+
+The variations are:
+
+* :prop_tgt:`C_EXTENSIONS`
+* :prop_tgt:`CXX_EXTENSIONS`
+* :prop_tgt:`CUDA_EXTENSIONS`
+* :prop_tgt:`HIP_EXTENSIONS`
+* :prop_tgt:`OBJC_EXTENSIONS`
+* :prop_tgt:`OBJCXX_EXTENSIONS`
+
+These properties specify whether compiler-specific extensions are requested.
+
+These properties are initialized by the value of the
+:variable:`CMAKE_<LANG>_EXTENSIONS` variable if it is set when a target is
+created and otherwise by the value of
+:variable:`CMAKE_<LANG>_EXTENSIONS_DEFAULT` (see :policy:`CMP0128`).
+
+For supported CMake versions see the respective pages.
+To control language standard versions see :prop_tgt:`<LANG>_STANDARD`.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/prop_tgt/LANG_STANDARD.rst b/Help/prop_tgt/LANG_STANDARD.rst
new file mode 100644
index 0000000..bd377ec
--- /dev/null
+++ b/Help/prop_tgt/LANG_STANDARD.rst
@@ -0,0 +1,26 @@
+<LANG>_STANDARD
+---------------
+
+The variations are:
+
+* :prop_tgt:`C_STANDARD`
+* :prop_tgt:`CXX_STANDARD`
+* :prop_tgt:`CUDA_STANDARD`
+* :prop_tgt:`HIP_STANDARD`
+* :prop_tgt:`OBJC_STANDARD`
+* :prop_tgt:`OBJCXX_STANDARD`
+
+These properties specify language standard versions which are requested. When a
+newer standard is specified than is supported by the compiler, then it will
+fallback to the latest supported standard. This "decay" behavior may be
+controlled with the :prop_tgt:`<LANG>_STANDARD_REQUIRED` target property.
+
+These properties are initialized by the value of the
+:variable:`CMAKE_<LANG>_STANDARD` variable if it is set when a target is
+created.
+
+For supported values and CMake versions see the respective pages.
+To control compiler-specific extensions see :prop_tgt:`<LANG>_EXTENSIONS`.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst b/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst
new file mode 100644
index 0000000..56ecef8
--- /dev/null
+++ b/Help/prop_tgt/LANG_STANDARD_REQUIRED.rst
@@ -0,0 +1,26 @@
+<LANG>_STANDARD_REQUIRED
+------------------------
+
+The variations are:
+
+* :prop_tgt:`C_STANDARD_REQUIRED`
+* :prop_tgt:`CXX_STANDARD_REQUIRED`
+* :prop_tgt:`CUDA_STANDARD_REQUIRED`
+* :prop_tgt:`HIP_STANDARD_REQUIRED`
+* :prop_tgt:`OBJC_STANDARD_REQUIRED`
+* :prop_tgt:`OBJCXX_STANDARD_REQUIRED`
+
+These properties specify whether the value of :prop_tgt:`<LANG>_STANDARD` is a
+requirement. When ``OFF`` or unset, the :prop_tgt:`<LANG>_STANDARD` target
+property is treated as optional and may "decay" to a previous standard if the
+requested is not available.
+
+These properties are initialized by the value of the
+:variable:`CMAKE_<LANG>_STANDARD_REQUIRED` variable if it is set when a target
+is created.
+
+For supported CMake versions see the respective pages.
+To control language standard versions see :prop_tgt:`<LANG>_STANDARD`.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/prop_tgt/LINK_WHAT_YOU_USE.rst b/Help/prop_tgt/LINK_WHAT_YOU_USE.rst
index 2ed93ad..d6de0d4 100644
--- a/Help/prop_tgt/LINK_WHAT_YOU_USE.rst
+++ b/Help/prop_tgt/LINK_WHAT_YOU_USE.rst
@@ -1,16 +1,22 @@
 LINK_WHAT_YOU_USE
----------------------------
+-----------------
 
 .. versionadded:: 3.7
 
-This is a boolean option that when set to ``TRUE`` will automatically run
-``ldd -r -u`` on the target after it is linked. In addition, the linker flag
-``-Wl,--no-as-needed`` will be passed to the target with the link command so
-that all libraries specified on the command line will be linked into the
-target. This will result in the link producing a list of libraries that
-provide no symbols used by this target but are being linked to it.
-This is only applicable to executable and shared library targets and
-will only work when ld and ldd accept the flags used.
+This is a boolean option that, when set to ``TRUE``, will automatically run
+contents of variable :variable:`CMAKE_LINK_WHAT_YOU_USE_CHECK` on the target
+after it is linked. In addition, the linker flag specified by variable
+:variable:`CMAKE_<LANG>_LINK_WHAT_YOU_USE_FLAG`  will be passed to the target
+with the link command so that all libraries specified on the command line will
+be linked into the target. This will result in the link producing a list of
+libraries that provide no symbols used by this target but are being linked to
+it.
+
+.. note::
+
+  For now, it is only supported for ``ELF`` platforms and is only applicable to
+  executable and shared or module library targets. This property will be
+  ignored for any other targets and configurations.
 
 This property is initialized by the value of
 the :variable:`CMAKE_LINK_WHAT_YOU_USE` variable if it is set
diff --git a/Help/prop_tgt/OBJCXX_EXTENSIONS.rst b/Help/prop_tgt/OBJCXX_EXTENSIONS.rst
index 8a254f2..2a15dec 100644
--- a/Help/prop_tgt/OBJCXX_EXTENSIONS.rst
+++ b/Help/prop_tgt/OBJCXX_EXTENSIONS.rst
@@ -18,5 +18,7 @@
 the value of :prop_tgt:`CXX_EXTENSIONS` is set for :prop_tgt:`OBJCXX_EXTENSIONS`.
 
 This property is initialized by the value of
-the :variable:`CMAKE_OBJCXX_EXTENSIONS` variable if it is set when a target
-is created.
+the :variable:`CMAKE_OBJCXX_EXTENSIONS` variable if set when a target is
+created and otherwise by the value of
+:variable:`CMAKE_OBJCXX_EXTENSIONS_DEFAULT <CMAKE_<LANG>_EXTENSIONS_DEFAULT>`
+(see :policy:`CMP0128`).
diff --git a/Help/prop_tgt/OBJC_EXTENSIONS.rst b/Help/prop_tgt/OBJC_EXTENSIONS.rst
index ef1c754..cd72e5f 100644
--- a/Help/prop_tgt/OBJC_EXTENSIONS.rst
+++ b/Help/prop_tgt/OBJC_EXTENSIONS.rst
@@ -18,5 +18,7 @@
 compile features and a list of supported compilers.
 
 This property is initialized by the value of
-the :variable:`CMAKE_OBJC_EXTENSIONS` variable if it is set when a target
-is created.
+the :variable:`CMAKE_OBJC_EXTENSIONS` variable if set when a target is created
+and otherwise by the value of
+:variable:`CMAKE_OBJC_EXTENSIONS_DEFAULT <CMAKE_<LANG>_EXTENSIONS_DEFAULT>`
+(see :policy:`CMP0128`).
diff --git a/Help/release/3.22.rst b/Help/release/3.22.rst
new file mode 100644
index 0000000..1f773e6
--- /dev/null
+++ b/Help/release/3.22.rst
@@ -0,0 +1,166 @@
+CMake 3.22 Release Notes
+************************
+
+.. only:: html
+
+  .. contents::
+
+Changes made since CMake 3.21 include the following.
+
+New Features
+============
+
+Commands
+--------
+
+* The :command:`cmake_host_system_information` command can now query
+  `OS identification variables`_ from the :file:`/etc/os-release` file.
+
+* The :command:`string(TIMESTAMP)` command now supports the ``%V``
+  specifier for ISO 8601 week numbers.
+
+.. _`OS identification variables`: https://www.freedesktop.org/software/systemd/man/os-release.html
+
+Variables
+---------
+
+* The :envvar:`CMAKE_BUILD_TYPE` environment variable was added to
+  provide a default value for the :variable:`CMAKE_BUILD_TYPE` variable.
+
+* The :envvar:`CMAKE_CONFIGURATION_TYPES` environment variable was added to
+  provide a default value for the :variable:`CMAKE_CONFIGURATION_TYPES`
+  variable.
+
+* The :envvar:`CMAKE_INSTALL_MODE` environment variable was added to
+  tell :command:`install` rules (implemented by :command:`file(INSTALL)`)
+  to install symbolic links instead of copying of files.
+
+* The :variable:`CMAKE_<LANG>_LINK_WHAT_YOU_USE_FLAG` and
+  :variable:`CMAKE_LINK_WHAT_YOU_USE_CHECK` variables were added to
+  control the linker flag and check used by the
+  :prop_tgt:`LINK_WHAT_YOU_USE` target property.
+
+* The :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable
+  was added to turn a non-REQUIRED :command:`find_package` call into
+  a REQUIRED one.
+
+Properties
+----------
+
+* The :prop_tgt:`<LANG>_EXTENSIONS` target property is now initialized to
+  :variable:`CMAKE_<LANG>_EXTENSIONS_DEFAULT`, detected from the compiler.
+  See :policy:`CMP0128`.
+
+* The :prop_sf:`VS_SETTINGS` source file property is now supported for
+  all source file types.  Previously it worked only for non-built sources.
+
+Modules
+-------
+
+* The :module:`CMakeDependentOption` module :command:`cmake_dependent_option`
+  macro now supports full :ref:`Condition Syntax`.
+  See policy :policy:`CMP0127`.
+
+* The :module:`FetchContent` module now passes through the
+  :variable:`CMAKE_TLS_VERIFY`, :variable:`CMAKE_TLS_CAINFO`,
+  :variable:`CMAKE_NETRC` and :variable:`CMAKE_NETRC_FILE` variables (when
+  defined) to the underlying :module:`ExternalProject` sub-build.
+  Previously, those variables were silently ignored by :module:`FetchContent`.
+
+* The :module:`FindBLAS` and :module:`FindLAPACK` modules gained
+  a ``BLA_SIZEOF_INTEGER`` option to find a BLAS/LAPACK whose ABI
+  uses a specific integer size.
+
+* The :module:`FindJasper` module now provides an imported target.
+
+* The :module:`FindMatlab` module now provides imported targets.
+
+* The :module:`FindPkgConfig` module gained a :variable:`PKG_CONFIG_ARGN`
+  variable to specify arguments to ``pkg-config`` calls.
+
+* The :module:`GoogleTest` module :command:`gtest_discover_tests`
+  function gained a ``TEST_FILTER`` option to filter tests using
+  ``--gtest_filter`` during test discovery.
+
+* The :module:`UseSWIG` module, for :ref:`Visual Studio Generators`,
+  can now use the ``swig`` tool to generate implicit dependencies.
+
+CTest
+-----
+
+* :manual:`ctest(1)` learned to recognize labels attached to a test at run time.
+  Previously it was only possible to attach labels to tests at configure time
+  by using the :prop_test:`LABELS` test property.
+  See :ref:`Additional Test Measurements` for more information.
+
+* :manual:`ctest(1)` learned to be able to modify the environment for a test
+  through the :prop_test:`ENVIRONMENT_MODIFICATION` property. This is allows
+  for updates to environment variables based on the environment present at
+  test time.
+
+* The :command:`ctest_memcheck` command now also generates a
+  :file:`DynamicAnalysis-Test.xml` file which may be used to submit test
+  results to CDash.
+
+CPack
+-----
+
+* The :cpack_gen:`CPack DEB Generator` gained the
+  option to set :variable:`CPACK_DEBIAN_COMPRESSION_TYPE` to ``zstd``,
+  which enables Zstandard compression for deb packages.
+
+* The :cpack_gen:`CPack NSIS Generator` gained a new
+  :variable:`CPACK_NSIS_IGNORE_LICENSE_PAGE` variable to suppress
+  the license page in the installer.
+
+* The :cpack_gen:`CPack RPM Generator` gained the
+  :variable:`CPACK_RPM_REQUIRES_EXCLUDE_FROM` option to avoid scanning
+  specific paths for dependencies.
+
+Deprecated and Removed Features
+===============================
+
+* The :generator:`Visual Studio 10 2010` generator is now deprecated
+  and will be removed in a future version of CMake.
+
+Other Changes
+=============
+
+* The :manual:`Compile Features <cmake-compile-features(7)>` functionality now
+  correctly disables or enables compiler extensions when no standard level is
+  specified and avoids unnecessarily adding language standard flags if the
+  requested settings match the compiler's defaults. See :policy:`CMP0128`.
+
+* The :manual:`Compile Features <cmake-compile-features(7)>` functionality
+  now ignores features for languages that are not enabled.
+
+* The :ref:`Ninja Generators` now implement the ``edit_cache`` target
+  using :manual:`ccmake(1)` if available.
+
+* The :generator:`Ninja` and :generator:`NMake Makefiles` generators
+  now use the MSVC ``-external:I`` flag for system includes.
+  This became available as of VS 16.10 (toolchain version 14.29.30037).
+
+* The :cpack_gen:`CPack NSIS Generator` now requires NSIS 3.03 or later.
+
+3.22.1
+------
+
+This version made no changes to documented features or interfaces.
+Some implementation updates were made to support ecosystem changes
+and/or fix regressions.
+
+3.22.2
+------
+
+* The ``OLD`` behavior of :policy:`CMP0128` was fixed to add flags even when
+  the specified standard matches the compiler default.
+
+3.22.3
+------
+
+* The :command:`while` command again ignores errors in condition evaluation
+  as CMake 3.21 and below did.  This bug was fixed in 3.22.0, but exposed
+  errors in existing projects.  The fix has been reverted to restore
+  compatibility.  The fix may be restored in a future version of CMake
+  via a policy.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index 75667e5..3d2ed43 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -13,6 +13,7 @@
 .. toctree::
    :maxdepth: 1
 
+   3.22 <3.22>
    3.21 <3.21>
    3.20 <3.20>
    3.19 <3.19>
diff --git a/Help/variable/CMAKE_BUILD_TYPE.rst b/Help/variable/CMAKE_BUILD_TYPE.rst
index 405f7d5..bb95436 100644
--- a/Help/variable/CMAKE_BUILD_TYPE.rst
+++ b/Help/variable/CMAKE_BUILD_TYPE.rst
@@ -1,25 +1,21 @@
 CMAKE_BUILD_TYPE
 ----------------
 
-Specifies the build type on single-configuration generators.
+Specifies the build type on single-configuration generators (e.g.
+:ref:`Makefile Generators` or :generator:`Ninja`).  Typical values include
+``Debug``, ``Release``, ``RelWithDebInfo`` and ``MinSizeRel``, but custom
+build types can also be defined.
 
-This statically specifies what build type (configuration) will be
-built in this build tree.  Possible values are empty, ``Debug``, ``Release``,
-``RelWithDebInfo``, ``MinSizeRel``, ...  This variable is only meaningful to
-single-configuration generators (such as :ref:`Makefile Generators` and
-:generator:`Ninja`) i.e.  those which choose a single configuration when CMake
-runs to generate a build tree as opposed to multi-configuration generators
-which offer selection of the build configuration within the generated build
-environment.  There are many per-config properties and variables
-(usually following clean ``SOME_VAR_<CONFIG>`` order conventions), such as
-``CMAKE_C_FLAGS_<CONFIG>``, specified as uppercase:
-``CMAKE_C_FLAGS_[DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|...]``.  For example,
-in a build tree configured to build type ``Debug``, CMake will see to
-having :variable:`CMAKE_C_FLAGS_DEBUG <CMAKE_<LANG>_FLAGS_DEBUG>` settings get
-added to the :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>` settings.  See
-also :variable:`CMAKE_CONFIGURATION_TYPES`.
+This variable is initialized by the first :command:`project` or
+:command:`enable_language` command called in a project when a new build
+tree is first created.  If the :envvar:`CMAKE_BUILD_TYPE` environment
+variable is set, its value is used.  Otherwise, a toolchain-specific
+default is chosen when a language is enabled.  The default value is often
+an empty string, but this is usually not desirable and one of the other
+standard build types is usually more appropriate.
 
-Note that configuration names are case-insensitive.  The value of this
-variable will be the same as it is specified when invoking CMake.
-For instance, if ``-DCMAKE_BUILD_TYPE=ReLeAsE`` is specified, then the
-value of ``CMAKE_BUILD_TYPE`` will be ``ReLeAsE``.
+Depending on the situation, the value of this variable may be treated
+case-sensitively or case-insensitively.  See :ref:`Build Configurations`
+for discussion of this and other related topics.
+
+For multi-config generators, see :variable:`CMAKE_CONFIGURATION_TYPES`.
diff --git a/Help/variable/CMAKE_CONFIGURATION_TYPES.rst b/Help/variable/CMAKE_CONFIGURATION_TYPES.rst
index 8fcc798..75ff8a1 100644
--- a/Help/variable/CMAKE_CONFIGURATION_TYPES.rst
+++ b/Help/variable/CMAKE_CONFIGURATION_TYPES.rst
@@ -1,12 +1,20 @@
 CMAKE_CONFIGURATION_TYPES
 -------------------------
 
-Specifies the available build types on multi-config generators.
+Specifies the available build types (configurations) on multi-config
+generators (e.g. :ref:`Visual Studio <Visual Studio Generators>`,
+:generator:`Xcode`, or :generator:`Ninja Multi-Config`).  Typical values
+include ``Debug``, ``Release``, ``RelWithDebInfo`` and ``MinSizeRel``,
+but custom build types can also be defined.
 
-This specifies what build types (configurations) will be available
-such as ``Debug``, ``Release``, ``RelWithDebInfo`` etc.  This has reasonable
-defaults on most platforms, but can be extended to provide other build
-types.
+This variable is initialized by the first :command:`project` or
+:command:`enable_language` command called in a project when a new build
+tree is first created.  If the :envvar:`CMAKE_CONFIGURATION_TYPES`
+environment variable is set, its value is used.  Otherwise, the default
+value is generator-specific.
 
-See :variable:`CMAKE_BUILD_TYPE` for specifying the configuration with
-single-config generators.
+Depending on the situation, the values in this variable may be treated
+case-sensitively or case-insensitively.  See :ref:`Build Configurations`
+for discussion of this and other related topics.
+
+For single-config generators, see :variable:`CMAKE_BUILD_TYPE`.
diff --git a/Help/variable/CMAKE_CUDA_EXTENSIONS.rst b/Help/variable/CMAKE_CUDA_EXTENSIONS.rst
index b86c0ea..12749ce 100644
--- a/Help/variable/CMAKE_CUDA_EXTENSIONS.rst
+++ b/Help/variable/CMAKE_CUDA_EXTENSIONS.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.8
 
-Default value for :prop_tgt:`CUDA_EXTENSIONS` property of targets.
-
-This variable is used to initialize the :prop_tgt:`CUDA_EXTENSIONS`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`CUDA_EXTENSIONS` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_CUDA_STANDARD.rst b/Help/variable/CMAKE_CUDA_STANDARD.rst
index 798ab1e..ee9395a 100644
--- a/Help/variable/CMAKE_CUDA_STANDARD.rst
+++ b/Help/variable/CMAKE_CUDA_STANDARD.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.8
 
-Default value for :prop_tgt:`CUDA_STANDARD` property of targets.
-
-This variable is used to initialize the :prop_tgt:`CUDA_STANDARD`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`CUDA_STANDARD` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_CUDA_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_CUDA_STANDARD_REQUIRED.rst
index ae2f52f..0c85a04d 100644
--- a/Help/variable/CMAKE_CUDA_STANDARD_REQUIRED.rst
+++ b/Help/variable/CMAKE_CUDA_STANDARD_REQUIRED.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.8
 
-Default value for :prop_tgt:`CUDA_STANDARD_REQUIRED` property of targets.
-
-This variable is used to initialize the :prop_tgt:`CUDA_STANDARD_REQUIRED`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`CUDA_STANDARD_REQUIRED` target property if set
+when a target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_CXX_EXTENSIONS.rst b/Help/variable/CMAKE_CXX_EXTENSIONS.rst
index ea8c4be..017f503 100644
--- a/Help/variable/CMAKE_CXX_EXTENSIONS.rst
+++ b/Help/variable/CMAKE_CXX_EXTENSIONS.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.1
 
-Default value for :prop_tgt:`CXX_EXTENSIONS` property of targets.
-
-This variable is used to initialize the :prop_tgt:`CXX_EXTENSIONS`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`CXX_EXTENSIONS` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_CXX_STANDARD.rst b/Help/variable/CMAKE_CXX_STANDARD.rst
index 8ef8c80..33eb89e 100644
--- a/Help/variable/CMAKE_CXX_STANDARD.rst
+++ b/Help/variable/CMAKE_CXX_STANDARD.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.1
 
-Default value for :prop_tgt:`CXX_STANDARD` property of targets.
-
-This variable is used to initialize the :prop_tgt:`CXX_STANDARD`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`CXX_STANDARD` target property if set when a target
+is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_CXX_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_CXX_STANDARD_REQUIRED.rst
index f7b2ae9..1c6c0cc 100644
--- a/Help/variable/CMAKE_CXX_STANDARD_REQUIRED.rst
+++ b/Help/variable/CMAKE_CXX_STANDARD_REQUIRED.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.1
 
-Default value for :prop_tgt:`CXX_STANDARD_REQUIRED` property of targets.
-
-This variable is used to initialize the :prop_tgt:`CXX_STANDARD_REQUIRED`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`CXX_STANDARD_REQUIRED` target property if set when
+a target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_C_EXTENSIONS.rst b/Help/variable/CMAKE_C_EXTENSIONS.rst
index fce8fc7..6680521 100644
--- a/Help/variable/CMAKE_C_EXTENSIONS.rst
+++ b/Help/variable/CMAKE_C_EXTENSIONS.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.1
 
-Default value for :prop_tgt:`C_EXTENSIONS` property of targets.
-
-This variable is used to initialize the :prop_tgt:`C_EXTENSIONS`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`C_EXTENSIONS` target property if set when a target
+is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_C_STANDARD.rst b/Help/variable/CMAKE_C_STANDARD.rst
index 64ef8ce..ec1e513 100644
--- a/Help/variable/CMAKE_C_STANDARD.rst
+++ b/Help/variable/CMAKE_C_STANDARD.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.1
 
-Default value for :prop_tgt:`C_STANDARD` property of targets.
-
-This variable is used to initialize the :prop_tgt:`C_STANDARD`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`C_STANDARD` target property if set when a target
+is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_C_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_C_STANDARD_REQUIRED.rst
index e70b6bd..2bd9d51 100644
--- a/Help/variable/CMAKE_C_STANDARD_REQUIRED.rst
+++ b/Help/variable/CMAKE_C_STANDARD_REQUIRED.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.1
 
-Default value for :prop_tgt:`C_STANDARD_REQUIRED` property of targets.
-
-This variable is used to initialize the :prop_tgt:`C_STANDARD_REQUIRED`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`C_STANDARD_REQUIRED` target property if set when
+a target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst b/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
index ed60020..f77e939 100644
--- a/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
+++ b/Help/variable/CMAKE_DISABLE_FIND_PACKAGE_PackageName.rst
@@ -14,3 +14,5 @@
 variables which have been stored in the cache will still be there.  In
 that case it is recommended to remove the cache variables for this
 package from the cache using the cache editor or :manual:`cmake(1)` ``-U``
+
+See also the :variable:`CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>` variable.
diff --git a/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst b/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst
index 4a64e33..c2c2609 100644
--- a/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst
+++ b/Help/variable/CMAKE_FIND_LIBRARY_SUFFIXES.rst
@@ -5,5 +5,8 @@
 
 This specifies what suffixes to add to library names when the
 :command:`find_library` command looks for libraries.  On Windows systems this
-is typically ``.lib`` and ``.dll``, meaning that when trying to find the
-``foo`` library it will look for ``foo.dll`` etc.
+is typically ``.lib`` and, depending on the compiler, ``.dll.a``, ``.a``
+(e.g. GCC and Clang), so when it tries to find the ``foo`` library, it will
+look for ``[<prefix>]foo.lib`` and/or ``[<prefix>]foo[.dll].a``, depending on
+the compiler used and the ``<prefix>`` specified in the
+:variable:`CMAKE_FIND_LIBRARY_PREFIXES`.
diff --git a/Help/variable/CMAKE_HIP_EXTENSIONS.rst b/Help/variable/CMAKE_HIP_EXTENSIONS.rst
new file mode 100644
index 0000000..13987dc
--- /dev/null
+++ b/Help/variable/CMAKE_HIP_EXTENSIONS.rst
@@ -0,0 +1,10 @@
+CMAKE_HIP_EXTENSIONS
+--------------------
+
+.. versionadded:: 3.21
+
+Default value for :prop_tgt:`HIP_EXTENSIONS` target property if set when a
+target is created.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_HIP_STANDARD.rst b/Help/variable/CMAKE_HIP_STANDARD.rst
new file mode 100644
index 0000000..5f15145
--- /dev/null
+++ b/Help/variable/CMAKE_HIP_STANDARD.rst
@@ -0,0 +1,10 @@
+CMAKE_HIP_STANDARD
+------------------
+
+.. versionadded:: 3.21
+
+Default value for :prop_tgt:`HIP_STANDARD` target property if set when a target
+is created.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_HIP_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_HIP_STANDARD_REQUIRED.rst
new file mode 100644
index 0000000..5d35e4e
--- /dev/null
+++ b/Help/variable/CMAKE_HIP_STANDARD_REQUIRED.rst
@@ -0,0 +1,10 @@
+CMAKE_HIP_STANDARD_REQUIRED
+---------------------------
+
+.. versionadded:: 3.21
+
+Default value for :prop_tgt:`HIP_STANDARD_REQUIRED` target property if set when
+a target is created.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_LANG_COMPILER_FRONTEND_VARIANT.rst b/Help/variable/CMAKE_LANG_COMPILER_FRONTEND_VARIANT.rst
new file mode 100644
index 0000000..128b1fb
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_COMPILER_FRONTEND_VARIANT.rst
@@ -0,0 +1,18 @@
+CMAKE_<LANG>_COMPILER_FRONTEND_VARIANT
+--------------------------------------
+
+.. versionadded:: 3.14
+
+Identification string of the compiler frontend variant.
+
+Some compilers have multiple, different frontends for accepting command
+line options.  (For example ``Clang`` originally only had a frontend
+compatible with the ``GNU`` compiler but since its port to Windows
+(``Clang-Cl``) it now also supports a frontend compatible with ``MSVC``.)
+When CMake detects such a compiler it sets this
+variable to what would have been the :variable:`CMAKE_<LANG>_COMPILER_ID` for
+the compiler whose frontend it resembles.
+
+.. note::
+  In other words, this variable describes what command line options
+  and language extensions the compiler frontend expects.
diff --git a/Help/variable/CMAKE_LANG_EXTENSIONS.rst b/Help/variable/CMAKE_LANG_EXTENSIONS.rst
new file mode 100644
index 0000000..3a32002
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_EXTENSIONS.rst
@@ -0,0 +1,20 @@
+CMAKE_<LANG>_EXTENSIONS
+-----------------------
+
+The variations are:
+
+* :variable:`CMAKE_C_EXTENSIONS`
+* :variable:`CMAKE_CXX_EXTENSIONS`
+* :variable:`CMAKE_CUDA_EXTENSIONS`
+* :variable:`CMAKE_HIP_EXTENSIONS`
+* :variable:`CMAKE_OBJC_EXTENSIONS`
+* :variable:`CMAKE_OBJCXX_EXTENSIONS`
+
+Default values for :prop_tgt:`<LANG>_EXTENSIONS` target properties if set when
+a target is created.  For the compiler's default setting see
+:variable:`CMAKE_<LANG>_EXTENSIONS_DEFAULT`.
+
+For supported CMake versions see the respective pages.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_LANG_EXTENSIONS_DEFAULT.rst b/Help/variable/CMAKE_LANG_EXTENSIONS_DEFAULT.rst
new file mode 100644
index 0000000..a66453a
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_EXTENSIONS_DEFAULT.rst
@@ -0,0 +1,10 @@
+CMAKE_<LANG>_EXTENSIONS_DEFAULT
+-------------------------------
+
+.. versionadded:: 3.22
+
+Compiler's default extensions mode. Used as the default for the
+:prop_tgt:`<LANG>_EXTENSIONS` target property when
+:variable:`CMAKE_<LANG>_EXTENSIONS` is not set (see :policy:`CMP0128`).
+
+This variable is read-only.  Modifying it is undefined behavior.
diff --git a/Help/variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES.rst b/Help/variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES.rst
index e9e04be..081c4da 100644
--- a/Help/variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES.rst
+++ b/Help/variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES.rst
@@ -6,8 +6,20 @@
 Compilers typically pass directories containing language runtime
 libraries and default library search paths when they invoke a linker.
 These paths are implicit linker search directories for the compiler's
-language.  CMake automatically detects these directories for each
-language and reports the results in this variable.
+language.  For each language enabled by the :command:`project` or
+:command:`enable_language` command, CMake automatically detects these
+directories and reports the results in this variable.
+
+When linking to a static library, CMake adds the implicit link directories
+from this variable for each language used in the static library (except
+the language whose compiler is used to drive linking).  In the case of an
+imported static library, the :prop_tgt:`IMPORTED_LINK_INTERFACE_LANGUAGES`
+target property lists the languages whose implicit link information is
+needed.  If any of the languages is not enabled, its value for the
+``CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES`` variable may instead be provided
+by the project.  Or, a :variable:`toolchain file <CMAKE_TOOLCHAIN_FILE>`
+may set the variable to a value known for the specified toolchain.  It will
+either be overridden when the language is enabled, or used as a fallback.
 
 Some toolchains read implicit directories from an environment variable such as
 ``LIBRARY_PATH``.  If using such an environment variable, keep its value
@@ -18,3 +30,5 @@
 of these directories is given by full path to :command:`target_link_libraries`
 CMake will generate the ``-l<name>`` form on link lines for historical
 purposes.
+
+See also the :variable:`CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES` variable.
diff --git a/Help/variable/CMAKE_LANG_IMPLICIT_LINK_LIBRARIES.rst b/Help/variable/CMAKE_LANG_IMPLICIT_LINK_LIBRARIES.rst
index ec16477..0c25489 100644
--- a/Help/variable/CMAKE_LANG_IMPLICIT_LINK_LIBRARIES.rst
+++ b/Help/variable/CMAKE_LANG_IMPLICIT_LINK_LIBRARIES.rst
@@ -5,6 +5,20 @@
 
 Compilers typically pass language runtime library names and other
 flags when they invoke a linker.  These flags are implicit link
-options for the compiler's language.  CMake automatically detects
-these libraries and flags for each language and reports the results in
-this variable.
+options for the compiler's language.  For each language enabled
+by the :command:`project` or :command:`enable_language` command,
+CMake automatically detects these libraries and flags and reports
+the results in this variable.
+
+When linking to a static library, CMake adds the implicit link libraries and
+flags from this variable for each language used in the static library (except
+the language whose compiler is used to drive linking).  In the case of an
+imported static library, the :prop_tgt:`IMPORTED_LINK_INTERFACE_LANGUAGES`
+target property lists the languages whose implicit link information is
+needed.  If any of the languages is not enabled, its value for the
+``CMAKE_<LANG>_IMPLICIT_LINK_LIBRARIES`` variable may instead be provided
+by the project.  Or, a :variable:`toolchain file <CMAKE_TOOLCHAIN_FILE>`
+may set the variable to a value known for the specified toolchain.  It will
+either be overridden when the language is enabled, or used as a fallback.
+
+See also the :variable:`CMAKE_<LANG>_IMPLICIT_LINK_DIRECTORIES` variable.
diff --git a/Help/variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG.rst b/Help/variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG.rst
new file mode 100644
index 0000000..5004530
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_WHAT_YOU_USE_FLAG.rst
@@ -0,0 +1,9 @@
+CMAKE_<LANG>_LINK_WHAT_YOU_USE_FLAG
+-----------------------------------
+
+.. versionadded:: 3.22
+
+Linker flag to be used to configure linker so that all specified libraries on
+the command line will be linked into the target.
+
+See also variable :variable:`CMAKE_LINK_WHAT_YOU_USE_CHECK`.
diff --git a/Help/variable/CMAKE_LANG_SIMULATE_ID.rst b/Help/variable/CMAKE_LANG_SIMULATE_ID.rst
index 15c87a1..3226352 100644
--- a/Help/variable/CMAKE_LANG_SIMULATE_ID.rst
+++ b/Help/variable/CMAKE_LANG_SIMULATE_ID.rst
@@ -1,9 +1,13 @@
 CMAKE_<LANG>_SIMULATE_ID
 ------------------------
 
-Identification string of "simulated" compiler.
+Identification string of the "simulated" compiler.
 
 Some compilers simulate other compilers to serve as drop-in
 replacements.  When CMake detects such a compiler it sets this
 variable to what would have been the :variable:`CMAKE_<LANG>_COMPILER_ID` for
 the simulated compiler.
+
+.. note::
+  In other words, this variable describes the ABI compatibility
+  of the generated code.
diff --git a/Help/variable/CMAKE_LANG_STANDARD.rst b/Help/variable/CMAKE_LANG_STANDARD.rst
new file mode 100644
index 0000000..0c41e19
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_STANDARD.rst
@@ -0,0 +1,19 @@
+CMAKE_<LANG>_STANDARD
+---------------------
+
+The variations are:
+
+* :variable:`CMAKE_C_STANDARD`
+* :variable:`CMAKE_CXX_STANDARD`
+* :variable:`CMAKE_CUDA_STANDARD`
+* :variable:`CMAKE_HIP_STANDARD`
+* :variable:`CMAKE_OBJC_STANDARD`
+* :variable:`CMAKE_OBJCXX_STANDARD`
+
+Default values for :prop_tgt:`<LANG>_STANDARD` target properties if set when a
+target is created.
+
+For supported CMake versions see the respective pages.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_LANG_STANDARD_DEFAULT.rst b/Help/variable/CMAKE_LANG_STANDARD_DEFAULT.rst
new file mode 100644
index 0000000..eb63d20
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_STANDARD_DEFAULT.rst
@@ -0,0 +1,7 @@
+CMAKE_<LANG>_STANDARD_DEFAULT
+-----------------------------
+
+.. versionadded:: 3.9
+
+The compiler's default standard for the language ``<LANG>``. Empty if the
+compiler has no conception of standard levels.
diff --git a/Help/variable/CMAKE_LANG_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_LANG_STANDARD_REQUIRED.rst
new file mode 100644
index 0000000..d7fa9b6
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_STANDARD_REQUIRED.rst
@@ -0,0 +1,19 @@
+CMAKE_<LANG>_STANDARD_REQUIRED
+------------------------------
+
+The variations are:
+
+* :variable:`CMAKE_C_STANDARD_REQUIRED`
+* :variable:`CMAKE_CXX_STANDARD_REQUIRED`
+* :variable:`CMAKE_CUDA_STANDARD_REQUIRED`
+* :variable:`CMAKE_HIP_STANDARD_REQUIRED`
+* :variable:`CMAKE_OBJC_STANDARD_REQUIRED`
+* :variable:`CMAKE_OBJCXX_STANDARD_REQUIRED`
+
+Default values for :prop_tgt:`<LANG>_STANDARD_REQUIRED` target properties if
+set when a target is created.
+
+For supported CMake versions see the respective pages.
+
+See the :manual:`cmake-compile-features(7)` manual for information on
+compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_LINK_WHAT_YOU_USE.rst b/Help/variable/CMAKE_LINK_WHAT_YOU_USE.rst
index 06b3aa8..bca4519 100644
--- a/Help/variable/CMAKE_LINK_WHAT_YOU_USE.rst
+++ b/Help/variable/CMAKE_LINK_WHAT_YOU_USE.rst
@@ -1,5 +1,5 @@
 CMAKE_LINK_WHAT_YOU_USE
----------------------------------
+-----------------------
 
 .. versionadded:: 3.7
 
diff --git a/Help/variable/CMAKE_LINK_WHAT_YOU_USE_CHECK.rst b/Help/variable/CMAKE_LINK_WHAT_YOU_USE_CHECK.rst
new file mode 100644
index 0000000..e626641
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_WHAT_YOU_USE_CHECK.rst
@@ -0,0 +1,10 @@
+CMAKE_LINK_WHAT_YOU_USE_CHECK
+-----------------------------
+
+.. versionadded:: 3.22
+
+Defines the command executed after the link step to check libraries usage.
+This check is currently only defined on ``ELF`` platforms with value
+``ldd -u -r``.
+
+See also :variable:`CMAKE_<LANG>_LINK_WHAT_YOU_USE_FLAG` variables.
diff --git a/Help/variable/CMAKE_NETRC.rst b/Help/variable/CMAKE_NETRC.rst
index 2c64a81..bddfca5 100644
--- a/Help/variable/CMAKE_NETRC.rst
+++ b/Help/variable/CMAKE_NETRC.rst
@@ -3,9 +3,11 @@
 
 .. versionadded:: 3.11
 
-This variable is used to initialize the ``NETRC`` option for
-:command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands and the
-module :module:`ExternalProject`. See those commands for additional
-information.
+This variable is used to initialize the ``NETRC`` option for the
+:command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands.
+See those commands for additional information.
+
+This variable is also used by the :module:`ExternalProject` and
+:module:`FetchContent` modules for internal calls to :command:`file(DOWNLOAD)`.
 
 The local option takes precedence over this variable.
diff --git a/Help/variable/CMAKE_NETRC_FILE.rst b/Help/variable/CMAKE_NETRC_FILE.rst
index 97a645e..af98170 100644
--- a/Help/variable/CMAKE_NETRC_FILE.rst
+++ b/Help/variable/CMAKE_NETRC_FILE.rst
@@ -3,9 +3,11 @@
 
 .. versionadded:: 3.11
 
-This variable is used to initialize the ``NETRC_FILE`` option for
-:command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands and the
-module :module:`ExternalProject`. See those commands for additional
-information.
+This variable is used to initialize the ``NETRC_FILE`` option for the
+:command:`file(DOWNLOAD)` and :command:`file(UPLOAD)` commands.
+See those commands for additional information.
+
+This variable is also used by the :module:`ExternalProject` and
+:module:`FetchContent` modules for internal calls to :command:`file(DOWNLOAD)`.
 
 The local option takes precedence over this variable.
diff --git a/Help/variable/CMAKE_OBJCXX_EXTENSIONS.rst b/Help/variable/CMAKE_OBJCXX_EXTENSIONS.rst
index b5225ea..46736aa 100644
--- a/Help/variable/CMAKE_OBJCXX_EXTENSIONS.rst
+++ b/Help/variable/CMAKE_OBJCXX_EXTENSIONS.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.16
 
-Default value for :prop_tgt:`OBJCXX_EXTENSIONS` property of targets.
-
-This variable is used to initialize the :prop_tgt:`OBJCXX_EXTENSIONS`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`OBJCXX_EXTENSIONS` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_OBJCXX_STANDARD.rst b/Help/variable/CMAKE_OBJCXX_STANDARD.rst
index 98e4624..0913382 100644
--- a/Help/variable/CMAKE_OBJCXX_STANDARD.rst
+++ b/Help/variable/CMAKE_OBJCXX_STANDARD.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.16
 
-Default value for :prop_tgt:`OBJCXX_STANDARD` property of targets.
-
-This variable is used to initialize the :prop_tgt:`OBJCXX_STANDARD`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`OBJCXX_STANDARD` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_OBJCXX_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_OBJCXX_STANDARD_REQUIRED.rst
index 4b5e77c..9afc4ea 100644
--- a/Help/variable/CMAKE_OBJCXX_STANDARD_REQUIRED.rst
+++ b/Help/variable/CMAKE_OBJCXX_STANDARD_REQUIRED.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.16
 
-Default value for :prop_tgt:`OBJCXX_STANDARD_REQUIRED` property of targets.
-
-This variable is used to initialize the :prop_tgt:`OBJCXX_STANDARD_REQUIRED`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`OBJCXX_STANDARD_REQUIRED` target property if set
+when a target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_OBJC_EXTENSIONS.rst b/Help/variable/CMAKE_OBJC_EXTENSIONS.rst
index d6e3c7d..9df11f1 100644
--- a/Help/variable/CMAKE_OBJC_EXTENSIONS.rst
+++ b/Help/variable/CMAKE_OBJC_EXTENSIONS.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.16
 
-Default value for :prop_tgt:`OBJC_EXTENSIONS` property of targets.
-
-This variable is used to initialize the :prop_tgt:`OBJC_EXTENSIONS`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`OBJC_EXTENSIONS` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_OBJC_STANDARD.rst b/Help/variable/CMAKE_OBJC_STANDARD.rst
index fde367d..0b9b63f 100644
--- a/Help/variable/CMAKE_OBJC_STANDARD.rst
+++ b/Help/variable/CMAKE_OBJC_STANDARD.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.16
 
-Default value for :prop_tgt:`OBJC_STANDARD` property of targets.
-
-This variable is used to initialize the :prop_tgt:`OBJC_STANDARD`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`OBJC_STANDARD` target property if set when a
+target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_OBJC_STANDARD_REQUIRED.rst b/Help/variable/CMAKE_OBJC_STANDARD_REQUIRED.rst
index 8d26d95..c74db55 100644
--- a/Help/variable/CMAKE_OBJC_STANDARD_REQUIRED.rst
+++ b/Help/variable/CMAKE_OBJC_STANDARD_REQUIRED.rst
@@ -3,11 +3,8 @@
 
 .. versionadded:: 3.16
 
-Default value for :prop_tgt:`OBJC_STANDARD_REQUIRED` property of targets.
-
-This variable is used to initialize the :prop_tgt:`OBJC_STANDARD_REQUIRED`
-property on all targets.  See that target property for additional
-information.
+Default value for :prop_tgt:`OBJC_STANDARD_REQUIRED` target property if set
+when a target is created.
 
 See the :manual:`cmake-compile-features(7)` manual for information on
 compile features and a list of supported compilers.
diff --git a/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst b/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst
index 43582be..d643fb8 100644
--- a/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst
+++ b/Help/variable/CMAKE_POLICY_DEFAULT_CMPNNNN.rst
@@ -9,9 +9,16 @@
 or ``NEW`` to specify the default for policy ``CMP<NNNN>``, where ``<NNNN>``
 is the policy number.
 
-This variable should not be set by a project in CMake code; use
-:command:`cmake_policy(SET)` instead.  Users running CMake may set this
-variable in the cache (e.g. ``-DCMAKE_POLICY_DEFAULT_CMP<NNNN>=<OLD|NEW>``)
-to set a policy not otherwise set by the project.  Set to ``OLD`` to quiet a
-policy warning while using old behavior or to ``NEW`` to try building the
-project with new behavior.
+This variable should not be set by a project in CMake code as a way to
+set its own policies; use :command:`cmake_policy(SET)` instead.  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_DEFAULT_CMP<NNNN>=<OLD|NEW>``).  Set it to ``OLD``
+  to quiet a policy warning while using old behavior or to ``NEW`` to
+  try building the project with new behavior.
+
+* 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.
diff --git a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
index b991db2..0231668 100644
--- a/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
+++ b/Help/variable/CMAKE_POLICY_WARNING_CMPNNNN.rst
@@ -2,8 +2,9 @@
 ------------------------------
 
 Explicitly enable or disable the warning when CMake Policy ``CMP<NNNN>``
-is not set.  This is meaningful only for the few policies that do not
-warn by default:
+has not been set explicitly by :command:`cmake_policy` or implicitly
+by :command:`cmake_minimum_required`. This is meaningful
+only for the policies that do not warn by default:
 
 * ``CMAKE_POLICY_WARNING_CMP0025`` controls the warning for
   policy :policy:`CMP0025`.
@@ -31,6 +32,8 @@
   policy :policy:`CMP0116`.
 * ``CMAKE_POLICY_WARNING_CMP0126`` controls the warning for
   policy :policy:`CMP0126`.
+* ``CMAKE_POLICY_WARNING_CMP0128`` controls the warning for
+  policy :policy:`CMP0128`.
 
 This variable should not be set by a project in CMake code.  Project
 developers running CMake may set this variable in their cache to
diff --git a/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst b/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst
new file mode 100644
index 0000000..893f1ae
--- /dev/null
+++ b/Help/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName.rst
@@ -0,0 +1,14 @@
+CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>
+----------------------------------------
+
+.. versionadded:: 3.22
+
+Variable for making :command:`find_package` call ``REQUIRED``.
+
+Every non-``REQUIRED`` :command:`find_package` call in a project can be
+turned into ``REQUIRED`` by setting the variable
+``CMAKE_REQUIRE_FIND_PACKAGE_<PackageName>`` to ``TRUE``.
+This can be used to assert assumptions about build environment and to
+ensure the build will fail early if they do not hold.
+
+See also the :variable:`CMAKE_DISABLE_FIND_PACKAGE_<PackageName>` variable.
diff --git a/Help/variable/CMAKE_TLS_CAINFO.rst b/Help/variable/CMAKE_TLS_CAINFO.rst
new file mode 100644
index 0000000..07f6366
--- /dev/null
+++ b/Help/variable/CMAKE_TLS_CAINFO.rst
@@ -0,0 +1,9 @@
+CMAKE_TLS_CAINFO
+----------------
+
+Specify the default value for the :command:`file(DOWNLOAD)` and
+:command:`file(UPLOAD)` commands' ``TLS_CAINFO`` options.
+It is unset by default.
+
+This variable is also used by the :module:`ExternalProject` and
+:module:`FetchContent` modules for internal calls to :command:`file(DOWNLOAD)`.
diff --git a/Help/variable/CMAKE_TLS_VERIFY.rst b/Help/variable/CMAKE_TLS_VERIFY.rst
index 24f8a25..b22f1ce 100644
--- a/Help/variable/CMAKE_TLS_VERIFY.rst
+++ b/Help/variable/CMAKE_TLS_VERIFY.rst
@@ -5,8 +5,8 @@
 :command:`file(UPLOAD)` commands' ``TLS_VERIFY`` options.
 If not set, the default is *off*.
 
-This setting is also used by the :module:`ExternalProject` module
-for internal calls to :command:`file(DOWNLOAD)`.
+This variable is also used by the :module:`ExternalProject` and
+:module:`FetchContent` modules for internal calls to :command:`file(DOWNLOAD)`.
 
 TLS verification can help provide confidence that one is connecting
 to the desired server.  When downloading known content, one should
diff --git a/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER.rst b/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER.rst
new file mode 100644
index 0000000..5ad5897
--- /dev/null
+++ b/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER.rst
@@ -0,0 +1,13 @@
+CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER
+------------------------------------
+
+.. versionadded:: 3.22
+
+Visual Studio target framework identifier.
+
+In some cases, the :ref:`Visual Studio Generators` may use an explicit value
+for the MSBuild ``TargetFrameworkIdentifier`` setting in ``.csproj`` files.
+CMake provides the chosen value in this variable.
+
+See also :variable:`CMAKE_VS_TARGET_FRAMEWORK_VERSION` and
+:variable:`CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION`.
diff --git a/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION.rst b/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION.rst
new file mode 100644
index 0000000..8dcb3a7
--- /dev/null
+++ b/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION.rst
@@ -0,0 +1,13 @@
+CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION
+-----------------------------------------
+
+.. versionadded:: 3.22
+
+Visual Studio target framework targets version.
+
+In some cases, the :ref:`Visual Studio Generators` may use an explicit value
+for the MSBuild ``TargetFrameworkTargetsVersion`` setting in ``.csproj`` files.
+CMake provides the chosen value in this variable.
+
+See also :variable:`CMAKE_VS_TARGET_FRAMEWORK_VERSION` and
+:variable:`CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER`.
diff --git a/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION.rst b/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION.rst
new file mode 100644
index 0000000..5489f79
--- /dev/null
+++ b/Help/variable/CMAKE_VS_TARGET_FRAMEWORK_VERSION.rst
@@ -0,0 +1,17 @@
+CMAKE_VS_TARGET_FRAMEWORK_VERSION
+---------------------------------
+
+.. versionadded:: 3.22
+
+Visual Studio target framework version.
+
+In some cases, the :ref:`Visual Studio Generators` may use an explicit value
+for the MSBuild ``TargetFrameworkVersion`` setting in ``.csproj`` files.
+CMake provides the chosen value in this variable.
+
+See the :variable:`CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION` variable
+and :prop_tgt:`DOTNET_TARGET_FRAMEWORK_VERSION` target property to
+specify custom ``TargetFrameworkVersion`` values for project targets.
+
+See also :variable:`CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER` and
+:variable:`CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION`.
diff --git a/Modules/CMakeASM_MASMInformation.cmake b/Modules/CMakeASM_MASMInformation.cmake
index 6d1e174..656b75e 100644
--- a/Modules/CMakeASM_MASMInformation.cmake
+++ b/Modules/CMakeASM_MASMInformation.cmake
@@ -8,7 +8,7 @@
 
 set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm)
 
-set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "<CMAKE_ASM${ASM_DIALECT}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> /c /Fo <OBJECT> <SOURCE>")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "<CMAKE_ASM${ASM_DIALECT}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -c -Fo <OBJECT> <SOURCE>")
 
 # The ASM_MASM compiler id for this compiler is "MSVC", so fill out the runtime library table.
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
diff --git a/Modules/CMakeASM_NASMInformation.cmake b/Modules/CMakeASM_NASMInformation.cmake
index dad2750..a72575b 100644
--- a/Modules/CMakeASM_NASMInformation.cmake
+++ b/Modules/CMakeASM_NASMInformation.cmake
@@ -35,7 +35,7 @@
 endif()
 
 if(NOT CMAKE_ASM_NASM_COMPILE_OBJECT)
-  set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
+  set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
 endif()
 
 if(CMAKE_ASM_NASM_COMPILER_ID STREQUAL "NASM")
diff --git a/Modules/CMakeCCompiler.cmake.in b/Modules/CMakeCCompiler.cmake.in
index 754f235..2b24ff2 100644
--- a/Modules/CMakeCCompiler.cmake.in
+++ b/Modules/CMakeCCompiler.cmake.in
@@ -5,6 +5,7 @@
 set(CMAKE_C_COMPILER_VERSION_INTERNAL "@CMAKE_C_COMPILER_VERSION_INTERNAL@")
 set(CMAKE_C_COMPILER_WRAPPER "@CMAKE_C_COMPILER_WRAPPER@")
 set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "@CMAKE_C_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_C_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_C_EXTENSIONS_COMPUTED_DEFAULT@")
 set(CMAKE_C_COMPILE_FEATURES "@CMAKE_C_COMPILE_FEATURES@")
 set(CMAKE_C90_COMPILE_FEATURES "@CMAKE_C90_COMPILE_FEATURES@")
 set(CMAKE_C99_COMPILE_FEATURES "@CMAKE_C99_COMPILE_FEATURES@")
@@ -30,18 +31,9 @@
 set(CMAKE_C_COMPILER_LOADED 1)
 set(CMAKE_C_COMPILER_WORKS @CMAKE_C_COMPILER_WORKS@)
 set(CMAKE_C_ABI_COMPILED @CMAKE_C_ABI_COMPILED@)
-set(CMAKE_COMPILER_IS_MINGW @CMAKE_COMPILER_IS_MINGW@)
-set(CMAKE_COMPILER_IS_CYGWIN @CMAKE_COMPILER_IS_CYGWIN@)
-if(CMAKE_COMPILER_IS_CYGWIN)
-  set(CYGWIN 1)
-  set(UNIX 1)
-endif()
 
 set(CMAKE_C_COMPILER_ENV_VAR "CC")
 
-if(CMAKE_COMPILER_IS_MINGW)
-  set(MINGW 1)
-endif()
 set(CMAKE_C_COMPILER_ID_RUN 1)
 set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m)
 set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
diff --git a/Modules/CMakeCCompilerId.c.in b/Modules/CMakeCCompilerId.c.in
index 1f19c00..82d56cf 100644
--- a/Modules/CMakeCCompilerId.c.in
+++ b/Modules/CMakeCCompilerId.c.in
@@ -41,23 +41,33 @@
 
 #if !defined(__STDC__) && !defined(__clang__)
 # if defined(_MSC_VER) || defined(__ibmxl__) || defined(__IBMC__)
-#  define C_DIALECT "90"
+#  define C_VERSION "90"
 # else
-#  define C_DIALECT
+#  define C_VERSION
 # endif
 #elif __STDC_VERSION__ > 201710L
-# define C_DIALECT "23"
+# define C_VERSION "23"
 #elif __STDC_VERSION__ >= 201710L
-# define C_DIALECT "17"
+# define C_VERSION "17"
 #elif __STDC_VERSION__ >= 201000L
-# define C_DIALECT "11"
+# define C_VERSION "11"
 #elif __STDC_VERSION__ >= 199901L
-# define C_DIALECT "99"
+# define C_VERSION "99"
 #else
-# define C_DIALECT "90"
+# define C_VERSION "90"
 #endif
-const char* info_language_dialect_default =
-  "INFO" ":" "dialect_default[" C_DIALECT "]";
+const char* info_language_standard_default =
+  "INFO" ":" "standard_default[" C_VERSION "]";
+
+const char* info_language_extensions_default = "INFO" ":" "extensions_default["
+#if (defined(__clang__) || defined(__GNUC__) || defined(__xlC__) ||           \
+     defined(__TI_COMPILER_VERSION__)) &&                                     \
+  !defined(__STRICT_ANSI__)
+  "ON"
+#else
+  "OFF"
+#endif
+"]";
 
 /*--------------------------------------------------------------------------*/
 
@@ -89,7 +99,8 @@
 #if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
   require += info_cray[argc];
 #endif
-  require += info_language_dialect_default[argc];
+  require += info_language_standard_default[argc];
+  require += info_language_extensions_default[argc];
   (void)argv;
   return require;
 }
diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake
index 6be1865..665f309 100644
--- a/Modules/CMakeCInformation.cmake
+++ b/Modules/CMakeCInformation.cmake
@@ -91,6 +91,14 @@
   set(CMAKE_USER_MAKE_RULES_OVERRIDE_C "${_override}")
 endif()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_C_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_C_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
 
 # for most systems a module is the same as a shared library
 # so unless the variable CMAKE_MODULE_EXISTS is set just
@@ -196,5 +204,3 @@
 endif()
 
 set(CMAKE_C_INFORMATION_LOADED 1)
-
-
diff --git a/Modules/CMakeCUDACompiler.cmake.in b/Modules/CMakeCUDACompiler.cmake.in
index 56ae732..2f3e9a8 100644
--- a/Modules/CMakeCUDACompiler.cmake.in
+++ b/Modules/CMakeCUDACompiler.cmake.in
@@ -6,6 +6,7 @@
 set(CMAKE_CUDA_DEVICE_LINKER "@CMAKE_CUDA_DEVICE_LINKER@")
 set(CMAKE_CUDA_FATBINARY "@CMAKE_CUDA_FATBINARY@")
 set(CMAKE_CUDA_STANDARD_COMPUTED_DEFAULT "@CMAKE_CUDA_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_CUDA_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_CUDA_EXTENSIONS_COMPUTED_DEFAULT@")
 set(CMAKE_CUDA_COMPILE_FEATURES "@CMAKE_CUDA_COMPILE_FEATURES@")
 set(CMAKE_CUDA03_COMPILE_FEATURES "@CMAKE_CUDA03_COMPILE_FEATURES@")
 set(CMAKE_CUDA11_COMPILE_FEATURES "@CMAKE_CUDA11_COMPILE_FEATURES@")
diff --git a/Modules/CMakeCUDACompilerId.cu.in b/Modules/CMakeCUDACompilerId.cu.in
index 91039e5..d5a3b03 100644
--- a/Modules/CMakeCUDACompilerId.cu.in
+++ b/Modules/CMakeCUDACompilerId.cu.in
@@ -16,7 +16,7 @@
 @CMAKE_CUDA_COMPILER_ID_PLATFORM_CONTENT@
 @CMAKE_CUDA_COMPILER_ID_ERROR_FOR_TEST@
 
-const char* info_language_dialect_default = "INFO" ":" "dialect_default["
+const char* info_language_standard_default = "INFO" ":" "standard_default["
 #if __cplusplus > 202002L
   "23"
 #elif __cplusplus > 201703L
@@ -32,6 +32,15 @@
 #endif
 "]";
 
+const char* info_language_extensions_default = "INFO" ":" "extensions_default["
+#if (defined(__clang__) || defined(__GNUC__)) &&                              \
+  !defined(__STRICT_ANSI__)
+  "ON"
+#else
+  "OFF"
+#endif
+"]";
+
 /*--------------------------------------------------------------------------*/
 
 int main(int argc, char* argv[])
@@ -48,7 +57,8 @@
 #ifdef SIMULATE_VERSION_MAJOR
   require += info_simulate_version[argc];
 #endif
-  require += info_language_dialect_default[argc];
+  require += info_language_standard_default[argc];
+  require += info_language_extensions_default[argc];
   (void)argv;
   return require;
 }
diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake
index a2946f4..e9cfed6 100644
--- a/Modules/CMakeCUDAInformation.cmake
+++ b/Modules/CMakeCUDAInformation.cmake
@@ -100,6 +100,15 @@
   set(CMAKE_SHARED_MODULE_CREATE_CUDA_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS})
 endif()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
diff --git a/Modules/CMakeCXXCompiler.cmake.in b/Modules/CMakeCXXCompiler.cmake.in
index d0ce77a..534e960 100644
--- a/Modules/CMakeCXXCompiler.cmake.in
+++ b/Modules/CMakeCXXCompiler.cmake.in
@@ -5,6 +5,7 @@
 set(CMAKE_CXX_COMPILER_VERSION_INTERNAL "@CMAKE_CXX_COMPILER_VERSION_INTERNAL@")
 set(CMAKE_CXX_COMPILER_WRAPPER "@CMAKE_CXX_COMPILER_WRAPPER@")
 set(CMAKE_CXX_STANDARD_COMPUTED_DEFAULT "@CMAKE_CXX_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_CXX_EXTENSIONS_COMPUTED_DEFAULT@")
 set(CMAKE_CXX_COMPILE_FEATURES "@CMAKE_CXX_COMPILE_FEATURES@")
 set(CMAKE_CXX98_COMPILE_FEATURES "@CMAKE_CXX98_COMPILE_FEATURES@")
 set(CMAKE_CXX11_COMPILE_FEATURES "@CMAKE_CXX11_COMPILE_FEATURES@")
@@ -31,18 +32,9 @@
 set(CMAKE_CXX_COMPILER_LOADED 1)
 set(CMAKE_CXX_COMPILER_WORKS @CMAKE_CXX_COMPILER_WORKS@)
 set(CMAKE_CXX_ABI_COMPILED @CMAKE_CXX_ABI_COMPILED@)
-set(CMAKE_COMPILER_IS_MINGW @CMAKE_COMPILER_IS_MINGW@)
-set(CMAKE_COMPILER_IS_CYGWIN @CMAKE_COMPILER_IS_CYGWIN@)
-if(CMAKE_COMPILER_IS_CYGWIN)
-  set(CYGWIN 1)
-  set(UNIX 1)
-endif()
 
 set(CMAKE_CXX_COMPILER_ENV_VAR "CXX")
 
-if(CMAKE_COMPILER_IS_MINGW)
-  set(MINGW 1)
-endif()
 set(CMAKE_CXX_COMPILER_ID_RUN 1)
 set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;m;mm;mpp;CPP;ixx;cppm)
 set(CMAKE_CXX_IGNORE_EXTENSIONS inl;h;hpp;HPP;H;o;O;obj;OBJ;def;DEF;rc;RC)
diff --git a/Modules/CMakeCXXCompilerId.cpp.in b/Modules/CMakeCXXCompilerId.cpp.in
index 7362a08..f19bc97 100644
--- a/Modules/CMakeCXXCompilerId.cpp.in
+++ b/Modules/CMakeCXXCompilerId.cpp.in
@@ -49,7 +49,7 @@
 #  define CXX_STD __cplusplus
 #endif
 
-const char* info_language_dialect_default = "INFO" ":" "dialect_default["
+const char* info_language_standard_default = "INFO" ":" "standard_default["
 #if CXX_STD > 202002L
   "23"
 #elif CXX_STD > 201703L
@@ -65,6 +65,16 @@
 #endif
 "]";
 
+const char* info_language_extensions_default = "INFO" ":" "extensions_default["
+#if (defined(__clang__) || defined(__GNUC__) || defined(__xlC__) ||           \
+     defined(__TI_COMPILER_VERSION__)) &&                                     \
+  !defined(__STRICT_ANSI__)
+  "ON"
+#else
+  "OFF"
+#endif
+"]";
+
 /*--------------------------------------------------------------------------*/
 
 int main(int argc, char* argv[])
@@ -87,7 +97,8 @@
 #if defined(__CRAYXT_COMPUTE_LINUX_TARGET)
   require += info_cray[argc];
 #endif
-  require += info_language_dialect_default[argc];
+  require += info_language_standard_default[argc];
+  require += info_language_extensions_default[argc];
   (void)argv;
   return require;
 }
diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake
index 944d236..53abf37 100644
--- a/Modules/CMakeCXXInformation.cmake
+++ b/Modules/CMakeCXXInformation.cmake
@@ -193,6 +193,15 @@
   endif()
 endforeach()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
diff --git a/Modules/CMakeCheckCompilerFlagCommonPatterns.cmake b/Modules/CMakeCheckCompilerFlagCommonPatterns.cmake
index 05174de..bda1d71 100644
--- a/Modules/CMakeCheckCompilerFlagCommonPatterns.cmake
+++ b/Modules/CMakeCheckCompilerFlagCommonPatterns.cmake
@@ -32,5 +32,6 @@
     FAIL_REGEX "Warning: illegal option"                   # SunStudio 12
     FAIL_REGEX "[Ww]arning: Invalid suboption"             # Fujitsu
     FAIL_REGEX "An invalid option .* appears on the command line" # Cray
+    FAIL_REGEX "WARNING: invalid compiler option"          # TI armcl
   )
 endmacro ()
diff --git a/Modules/CMakeDependentOption.cmake b/Modules/CMakeDependentOption.cmake
index 96855d2..b7c478f 100644
--- a/Modules/CMakeDependentOption.cmake
+++ b/Modules/CMakeDependentOption.cmake
@@ -10,44 +10,62 @@
 This macro presents an option to the user only if a set of other
 conditions are true.
 
-Usage:
+.. command:: cmake_dependent_option
 
-.. code-block:: cmake
+  .. code-block:: cmake
 
-  cmake_dependent_option(<option> "<help_text>" <value> <depends> <force>)
+    cmake_dependent_option(<option> "<help_text>" <value> <depends> <force>)
 
-Where ``<option>`` is available to the user if ``<depends>`` is true. When
-``<option>`` is available, the given ``<help_text>`` and initial ``<value>``
-are used. If the ``<depends>`` condition is not true, ``<option>`` will not be
-presented and will always have the value given by ``<force>``. Any value set by
-the user is preserved for when the option is presented again. Each element in
-the fourth parameter is evaluated as an if-condition, so
-:ref:`Condition Syntax` can be used.
+  Makes ``<option>`` available to the user if ``<depends>`` is true. When
+  ``<option>`` is available, the given ``<help_text>`` and initial ``<value>``
+  are used. If the ``<depends>`` condition is not true, ``<option>`` will not be
+  presented and will always have the value given by ``<force>``. Any value set by
+  the user is preserved for when the option is presented again. In case ``<depends>``
+  is a :ref:`semicolon-separated list <CMake Language Lists>`, all elements must
+  be true in order to initialize ``<option>`` with ``<value>``.
 
 Example invocation:
 
 .. code-block:: cmake
 
-  cmake_dependent_option(USE_FOO "Use Foo" ON
-                         "USE_BAR;NOT USE_ZOT" OFF)
+  cmake_dependent_option(USE_FOO "Use Foo" ON "USE_BAR;NOT USE_ZOT" OFF)
 
 If ``USE_BAR`` is true and ``USE_ZOT`` is false, this provides an option called
 ``USE_FOO`` that defaults to ON. Otherwise, it sets ``USE_FOO`` to OFF and
 hides the option from the user. If the status of ``USE_BAR`` or ``USE_ZOT``
 ever changes, any value for the ``USE_FOO`` option is saved so that when the
 option is re-enabled it retains its old value.
+
+.. versionadded:: 3.22
+
+  Full :ref:`Condition Syntax` is now supported.  See policy :policy:`CMP0127`.
+
 #]=======================================================================]
 
 macro(CMAKE_DEPENDENT_OPTION option doc default depends force)
+  cmake_policy(GET CMP0127 _CDO_CMP0127
+    PARENT_SCOPE # undocumented, do not use outside of CMake
+    )
   if(${option}_ISSET MATCHES "^${option}_ISSET$")
     set(${option}_AVAILABLE 1)
-    foreach(d ${depends})
-      string(REGEX REPLACE " +" ";" CMAKE_DEPENDENT_OPTION_DEP "${d}")
-      if(${CMAKE_DEPENDENT_OPTION_DEP})
-      else()
-        set(${option}_AVAILABLE 0)
-      endif()
-    endforeach()
+    if("x${_CDO_CMP0127}x" STREQUAL "xNEWx")
+      foreach(d ${depends})
+        cmake_language(EVAL CODE "
+          if (${d})
+          else()
+            set(${option}_AVAILABLE 0)
+          endif()"
+        )
+      endforeach()
+    else()
+      foreach(d ${depends})
+        string(REGEX REPLACE " +" ";" CMAKE_DEPENDENT_OPTION_DEP "${d}")
+        if(${CMAKE_DEPENDENT_OPTION_DEP})
+        else()
+          set(${option}_AVAILABLE 0)
+        endif()
+      endforeach()
+    endif()
     if(${option}_AVAILABLE)
       option(${option} "${doc}" "${default}")
       set(${option} "${${option}}" CACHE BOOL "${doc}" FORCE)
@@ -61,4 +79,9 @@
   else()
     set(${option} "${${option}_ISSET}")
   endif()
+  if("x${_CDO_CMP0127}x" STREQUAL "xx" AND "x${depends}x" MATCHES "[^A-Za-z0-9_; ]")
+    cmake_policy(GET_WARNING CMP0127 _CDO_CMP0127_WARNING)
+    message(AUTHOR_WARNING "${_CDO_CMP0127_WARNING}")
+  endif()
+  unset(_CDO_CMP0127)
 endmacro()
diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake
index e8b9db7..d03cbef 100644
--- a/Modules/CMakeDetermineASMCompiler.cmake
+++ b/Modules/CMakeDetermineASMCompiler.cmake
@@ -104,7 +104,7 @@
 
   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)")
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_ARMCC "(ARM Compiler)|(ARM Assembler)|(Arm Compiler)")
 
   list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS NASM)
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_NASM "-v")
@@ -125,6 +125,7 @@
   include(CMakeDetermineCompilerId)
   set(userflags)
   CMAKE_DETERMINE_COMPILER_ID_VENDOR(ASM${ASM_DIALECT} "${userflags}")
+  set(_variant "")
   if("x${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}" STREQUAL "xIAR")
     # primary necessary to detect architecture, so the right archiver and linker can be picked
     # eg. "IAR Assembler V8.10.1.12857/W32 for ARM" or "IAR Assembler V4.11.1.4666 for Renesas RX"
@@ -137,6 +138,19 @@
     if(_all_compileid_matches)
       list(GET _all_compileid_matches "-1" CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID)
     endif()
+  elseif("x${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}" STREQUAL "xClang")
+    # Test whether an MSVC-like command-line option works.
+    execute_process(COMMAND ${CMAKE_ASM${ASM_DIALECT}_COMPILER} -?
+      OUTPUT_VARIABLE _clang_output
+      ERROR_VARIABLE _clang_output
+      RESULT_VARIABLE _clang_result)
+      if(_clang_result EQUAL 0)
+        set(CMAKE_ASM${ASM_DIALECT}_COMPILER_FRONTEND_VARIANT "MSVC")
+        set(CMAKE_ASM${ASM_DIALECT}_SIMULATE_ID MSVC)
+      else()
+        set(CMAKE_ASM${ASM_DIALECT}_COMPILER_FRONTEND_VARIANT "GNU")
+      endif()
+      set(_variant " with ${CMAKE_ASM${ASM_DIALECT}_COMPILER_FRONTEND_VARIANT}-like command-line")
   endif()
 
   _cmake_find_compiler_sysroot(ASM${ASM_DIALECT})
@@ -144,6 +158,8 @@
   unset(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_OUTPUT)
   unset(_all_compileid_matches)
   unset(_compileid)
+  unset(_clang_result)
+  unset(_clang_output)
 endif()
 
 if(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID)
@@ -157,9 +173,10 @@
   else()
     set(_archid "")
   endif()
-  message(STATUS "The ASM${ASM_DIALECT} compiler identification is ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}${_archid}${_version}")
+  message(STATUS "The ASM${ASM_DIALECT} compiler identification is ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}${_archid}${_version}${_variant}")
   unset(_archid)
   unset(_version)
+  unset(_variant)
 else()
   message(STATUS "The ASM${ASM_DIALECT} compiler identification is unknown")
 endif()
diff --git a/Modules/CMakeDetermineCCompiler.cmake b/Modules/CMakeDetermineCCompiler.cmake
index 0f80f9c..15eab0f 100644
--- a/Modules/CMakeDetermineCCompiler.cmake
+++ b/Modules/CMakeDetermineCCompiler.cmake
@@ -128,11 +128,6 @@
   if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
     set(CMAKE_COMPILER_IS_GNUCC 1)
   endif()
-  if(CMAKE_C_PLATFORM_ID MATCHES "MinGW")
-    set(CMAKE_COMPILER_IS_MINGW 1)
-  elseif(CMAKE_C_PLATFORM_ID MATCHES "Cygwin")
-    set(CMAKE_COMPILER_IS_CYGWIN 1)
-  endif()
 else()
   if(NOT DEFINED CMAKE_C_COMPILER_FRONTEND_VARIANT)
     # Some toolchain files set our internal CMAKE_C_COMPILER_ID_RUN
diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake
index e360d31..d06315e 100644
--- a/Modules/CMakeDetermineCUDACompiler.cmake
+++ b/Modules/CMakeDetermineCUDACompiler.cmake
@@ -192,17 +192,31 @@
       get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY)
       get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY)
     endif()
+
+    if(_CUDA_NVCC_OUT MATCHES "\\#\\$ NVVMIR_LIBRARY_DIR=([^\r\n]*)")
+      get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${CMAKE_MATCH_1}" ABSOLUTE)
+
+      #We require the path to end in `/nvvm/libdevice'
+      if(_CUDA_NVVMIR_LIBRARY_DIR MATCHES "nvvm/libdevice$")
+        get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}/../.." ABSOLUTE)
+        set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}")
+      endif()
+
+      unset(_CUDA_NVVMIR_LIBRARY_DIR)
+      unset(_cuda_nvvmir_dir_name)
+    endif()
     unset(_CUDA_NVCC_OUT)
 
     set(CMAKE_CUDA_DEVICE_LINKER "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/bin/nvlink${CMAKE_EXECUTABLE_SUFFIX}")
     set(CMAKE_CUDA_FATBINARY "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/bin/fatbinary${CMAKE_EXECUTABLE_SUFFIX}")
 
-
     # In a non-scattered installation the following are equivalent to CMAKE_CUDA_COMPILER_TOOLKIT_ROOT.
     # We first check for a non-scattered installation to prefer it over a scattered installation.
 
     # CMAKE_CUDA_COMPILER_LIBRARY_ROOT contains the device library.
-    if(EXISTS "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/nvvm/libdevice")
+    if(DEFINED CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR)
+      set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR}")
+    elseif(EXISTS "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/nvvm/libdevice")
       set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}")
     elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/nvvm/libdevice")
       set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/cuda")
@@ -211,6 +225,7 @@
     else()
       message(FATAL_ERROR "Couldn't find CUDA library root.")
     endif()
+    unset(CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR)
 
     # CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT contains the linking stubs necessary for device linking and other low-level library files.
     if(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub")
@@ -426,9 +441,10 @@
       if("${_nvcc_output_line}" MATCHES "^ *nvlink")
         string(APPEND _nvcc_log "    ignoring nvlink line\n")
       elseif(_nvcc_libraries)
-        if("${_nvcc_output_line}" MATCHES "(@\"?tmp/a\\.exe\\.res\"?)")
+        if("${_nvcc_output_line}" MATCHES "(@\"?((tmp/)?a\\.exe\\.res)\"?)")
           set(_nvcc_link_res_arg "${CMAKE_MATCH_1}")
-          set(_nvcc_link_res "${CMAKE_PLATFORM_INFO_DIR}/CompilerIdCUDA/tmp/a.exe.res")
+          set(_nvcc_link_res_file "${CMAKE_MATCH_2}")
+          set(_nvcc_link_res "${CMAKE_PLATFORM_INFO_DIR}/CompilerIdCUDA/${_nvcc_link_res_file}")
           if(EXISTS "${_nvcc_link_res}")
             file(READ "${_nvcc_link_res}" _nvcc_link_res_content)
             string(REPLACE "${_nvcc_link_res_arg}" "${_nvcc_link_res_content}" _nvcc_output_line "${_nvcc_output_line}")
diff --git a/Modules/CMakeDetermineCXXCompiler.cmake b/Modules/CMakeDetermineCXXCompiler.cmake
index 556518f..72dc8d3 100644
--- a/Modules/CMakeDetermineCXXCompiler.cmake
+++ b/Modules/CMakeDetermineCXXCompiler.cmake
@@ -125,11 +125,6 @@
   if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
     set(CMAKE_COMPILER_IS_GNUCXX 1)
   endif()
-  if(CMAKE_CXX_PLATFORM_ID MATCHES "MinGW")
-    set(CMAKE_COMPILER_IS_MINGW 1)
-  elseif(CMAKE_CXX_PLATFORM_ID MATCHES "Cygwin")
-    set(CMAKE_COMPILER_IS_CYGWIN 1)
-  endif()
 else()
   if(NOT DEFINED CMAKE_CXX_COMPILER_FRONTEND_VARIANT)
     # Some toolchain files set our internal CMAKE_CXX_COMPILER_ID_RUN
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index e564517..916f60c 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -267,6 +267,7 @@
   set(CMAKE_${lang}_SIMULATE_ID "${CMAKE_${lang}_SIMULATE_ID}" PARENT_SCOPE)
   set(CMAKE_${lang}_SIMULATE_VERSION "${CMAKE_${lang}_SIMULATE_VERSION}" PARENT_SCOPE)
   set(CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT "${CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT}" PARENT_SCOPE)
+  set(CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT "${CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT}" PARENT_SCOPE)
   set(CMAKE_${lang}_COMPILER_PRODUCED_OUTPUT "${COMPILER_${lang}_PRODUCED_OUTPUT}" PARENT_SCOPE)
   set(CMAKE_${lang}_COMPILER_PRODUCED_FILES "${COMPILER_${lang}_PRODUCED_FILES}" PARENT_SCOPE)
 endfunction()
@@ -418,6 +419,15 @@
     if(CMAKE_VS_PLATFORM_TOOLSET_VCTARGETS_CUSTOM_DIR)
       set(id_ToolsetVCTargetsDir "<VCTargetsPath>${CMAKE_VS_PLATFORM_TOOLSET_VCTARGETS_CUSTOM_DIR}</VCTargetsPath>")
     endif()
+    if(CMAKE_VS_TARGET_FRAMEWORK_VERSION)
+      set(id_TargetFrameworkVersion "<TargetFrameworkVersion>${CMAKE_VS_TARGET_FRAMEWORK_VERSION}</TargetFrameworkVersion>")
+    endif()
+    if(CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER)
+      set(id_TargetFrameworkIdentifier "<TargetFrameworkIdentifier>${CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER}</TargetFrameworkIdentifier>")
+    endif()
+    if(CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION)
+      set(id_TargetFrameworkTargetsVersion "<TargetFrameworkTargetsVersion>${CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION}</TargetFrameworkTargetsVersion>")
+    endif()
     set(id_CustomGlobals "")
     foreach(pair IN LISTS CMAKE_VS_GLOBALS)
       if("${pair}" MATCHES "([^=]+)=(.*)$")
@@ -536,7 +546,8 @@
     else()
       set(id_toolset "")
     endif()
-    if("${lang}" STREQUAL "Swift")
+    set(id_lang_version "")
+    if("x${lang}" STREQUAL "xSwift")
       if(CMAKE_Swift_LANGUAGE_VERSION)
         set(id_lang_version "SWIFT_VERSION = ${CMAKE_Swift_LANGUAGE_VERSION};")
       elseif(XCODE_VERSION VERSION_GREATER_EQUAL 10.2)
@@ -546,8 +557,14 @@
       else()
         set(id_lang_version "SWIFT_VERSION = 2.3;")
       endif()
-    else()
-      set(id_lang_version "")
+    elseif("x${lang}" STREQUAL "xC" OR "x${lang}" STREQUAL "xOBJC")
+      if(CMAKE_${lang}_COMPILER_ID_FLAGS MATCHES "(^| )(-std=[^ ]+)( |$)")
+        set(id_lang_version "OTHER_CFLAGS = \"${CMAKE_MATCH_2}\";")
+      endif()
+    elseif("x${lang}" STREQUAL "xCXX" OR "x${lang}" STREQUAL "xOBJCXX")
+      if(CMAKE_${lang}_COMPILER_ID_FLAGS MATCHES "(^| )(-std=[^ ]+)( |$)")
+        set(id_lang_version "OTHER_CPLUSPLUSFLAGS = \"${CMAKE_MATCH_2}\";")
+      endif()
     endif()
     if(CMAKE_OSX_DEPLOYMENT_TARGET)
       set(id_deployment_target
@@ -685,7 +702,7 @@
   # Check the result of compilation.
   if(CMAKE_${lang}_COMPILER_ID_RESULT
      # Intel Fortran warns and ignores preprocessor lines without /fpp
-     OR CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES "Bad # preprocessor line"
+     OR CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES "warning #5117: Bad # preprocessor line"
      )
     # Compilation failed.
     set(MSG
@@ -696,7 +713,10 @@
 ${CMAKE_${lang}_COMPILER_ID_OUTPUT}
 
 ")
-    file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "${MSG}")
+    # Log the output unless we recognize it as a known-bad case.
+    if(NOT CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES "warning #5117: Bad # preprocessor line")
+      file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "${MSG}")
+    endif()
 
     # Some languages may know the correct/desired set of flags and want to fail right away if they don't work.
     # This is currently only used by CUDA.
@@ -876,9 +896,12 @@
       if("${info}" MATCHES "INFO:qnxnto\\[\\]")
         set(COMPILER_QNXNTO 1)
       endif()
-      if("${info}" MATCHES "INFO:dialect_default\\[([^]\"]*)\\]")
+      if("${info}" MATCHES "INFO:standard_default\\[([^]\"]*)\\]")
         set(CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT "${CMAKE_MATCH_1}")
       endif()
+      if("${info}" MATCHES "INFO:extensions_default\\[([^]\"]*)\\]")
+        set(CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT "${CMAKE_MATCH_1}")
+      endif()
     endforeach()
 
     # Construct compiler version from components if needed.
@@ -988,6 +1011,7 @@
   set(CMAKE_${lang}_SIMULATE_VERSION "${CMAKE_${lang}_SIMULATE_VERSION}" PARENT_SCOPE)
   set(COMPILER_QNXNTO "${COMPILER_QNXNTO}" PARENT_SCOPE)
   set(CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT "${CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT}" PARENT_SCOPE)
+  set(CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT "${CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT}" PARENT_SCOPE)
 endfunction()
 
 #-----------------------------------------------------------------------------
diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake
index d7d032c..6a8984b 100644
--- a/Modules/CMakeDetermineFortranCompiler.cmake
+++ b/Modules/CMakeDetermineFortranCompiler.cmake
@@ -235,11 +235,6 @@
   if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU")
     set(CMAKE_COMPILER_IS_GNUG77 1)
   endif()
-  if(CMAKE_Fortran_PLATFORM_ID MATCHES "MinGW")
-    set(CMAKE_COMPILER_IS_MINGW 1)
-  elseif(CMAKE_Fortran_PLATFORM_ID MATCHES "Cygwin")
-    set(CMAKE_COMPILER_IS_CYGWIN 1)
-  endif()
 endif()
 
 if (NOT _CMAKE_TOOLCHAIN_LOCATION)
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index 84fde49..a6bd0d1 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -118,7 +118,7 @@
   endfunction()
 
   __resolve_IAR_hints("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER}" _CMAKE_TOOLCHAIN_LOCATION)
-  set(_CMAKE_IAR_ITOOLS "ARM" "RX" "RH850" "RL78" "RISCV" "STM8")
+  set(_CMAKE_IAR_ITOOLS "ARM" "RX" "RH850" "RL78" "RISCV" "RISC-V" "STM8")
   set(_CMAKE_IAR_XTOOLS "AVR" "MSP430" "V850" "8051")
 
   if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_ITOOLS)
diff --git a/Modules/CMakeFindPackageMode.cmake b/Modules/CMakeFindPackageMode.cmake
index 815dfc9..726e2a2 100644
--- a/Modules/CMakeFindPackageMode.cmake
+++ b/Modules/CMakeFindPackageMode.cmake
@@ -78,7 +78,7 @@
   # from the outside
   if(NOT CMAKE_SIZEOF_VOID_P)
     set(CMAKE_SIZEOF_VOID_P 4)
-    if(EXISTS /usr/lib64)
+    if(EXISTS ${CMAKE_SYSROOT}/usr/lib64)
       set(CMAKE_SIZEOF_VOID_P 8)
     else()
       # use the file utility to check whether itself is 64 bit:
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in
index d474f96..97f891e 100644
--- a/Modules/CMakeFortranCompiler.cmake.in
+++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -19,20 +19,11 @@
 set(CMAKE_Fortran_COMPILER_LOADED 1)
 set(CMAKE_Fortran_COMPILER_WORKS @CMAKE_Fortran_COMPILER_WORKS@)
 set(CMAKE_Fortran_ABI_COMPILED @CMAKE_Fortran_ABI_COMPILED@)
-set(CMAKE_COMPILER_IS_MINGW @CMAKE_COMPILER_IS_MINGW@)
-set(CMAKE_COMPILER_IS_CYGWIN @CMAKE_COMPILER_IS_CYGWIN@)
-if(CMAKE_COMPILER_IS_CYGWIN)
-  set(CYGWIN 1)
-  set(UNIX 1)
-endif()
 
 set(CMAKE_Fortran_COMPILER_ENV_VAR "FC")
 
 set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 @CMAKE_Fortran_COMPILER_SUPPORTS_F90@)
 
-if(CMAKE_COMPILER_IS_MINGW)
-  set(MINGW 1)
-endif()
 set(CMAKE_Fortran_COMPILER_ID_RUN 1)
 set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95)
 set(CMAKE_Fortran_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
diff --git a/Modules/CMakeFortranCompilerABI.F90 b/Modules/CMakeFortranCompilerABI.F90
new file mode 100644
index 0000000..4a17153
--- /dev/null
+++ b/Modules/CMakeFortranCompilerABI.F90
@@ -0,0 +1,48 @@
+program CMakeFortranCompilerABI
+
+implicit none
+
+integer :: i(1) = 0
+where (i==0) i=1
+if (any(i/=1)) stop 1
+! showing Fortran 90 syntax is OK
+
+#if 0
+! Address Size
+#endif
+#if defined(_LP64)
+PRINT *, 'INFO:sizeof_dptr[8]'
+#elif defined(_M_IA64)
+PRINT *, 'INFO:sizeof_dptr[8]'
+#elif defined(_M_X64)
+PRINT *, 'INFO:sizeof_dptr[8]'
+#elif defined(_M_AMD64)
+PRINT *, 'INFO:sizeof_dptr[8]'
+#elif defined(__x86_64__)
+PRINT *, 'INFO:sizeof_dptr[8]'
+
+#elif defined(_ILP32)
+PRINT *, 'INFO:sizeof_dptr[4]'
+#elif defined(_M_IX86)
+PRINT *, 'INFO:sizeof_dptr[4]'
+#elif defined(__i386__)
+PRINT *, 'INFO:sizeof_dptr[4]'
+
+#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8
+PRINT *, 'INFO:sizeof_dptr[8]'
+#elif defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 4
+PRINT *, 'INFO:sizeof_dptr[4]'
+#elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ == 8
+PRINT *, 'INFO:sizeof_dptr[8]'
+#elif defined(__SIZEOF_SIZE_T__) && __SIZEOF_SIZE_T__ == 4
+PRINT *, 'INFO:sizeof_dptr[4]'
+#endif
+
+#if 0
+! Application Binary Interface
+#endif
+#if defined(__ELF__)
+PRINT *, 'INFO:abi[ELF]'
+#endif
+PRINT *, 'ABI Detection'
+end program
diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake
index 9a4ce63..0f71c6f 100644
--- a/Modules/CMakeFortranInformation.cmake
+++ b/Modules/CMakeFortranInformation.cmake
@@ -157,6 +157,15 @@
   set(CMAKE_INCLUDE_FLAG_Fortran ${CMAKE_INCLUDE_FLAG_C})
 endif()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 set(CMAKE_VERBOSE_MAKEFILE FALSE CACHE BOOL "If this value is on, makefiles will be generated without the .SILENT directive, and all commands will be echoed to the console during the make.  This is useful for debugging only. With Visual Studio IDE projects all commands are done without /nologo.")
 
 set(CMAKE_Fortran_FLAGS_INIT "$ENV{FFLAGS} ${CMAKE_Fortran_FLAGS_INIT}")
diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in
index 17633a8..ce4e2cf 100644
--- a/Modules/CMakeHIPCompiler.cmake.in
+++ b/Modules/CMakeHIPCompiler.cmake.in
@@ -2,6 +2,7 @@
 set(CMAKE_HIP_COMPILER_ID "@CMAKE_HIP_COMPILER_ID@")
 set(CMAKE_HIP_COMPILER_VERSION "@CMAKE_HIP_COMPILER_VERSION@")
 set(CMAKE_HIP_STANDARD_COMPUTED_DEFAULT "@CMAKE_HIP_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_HIP_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_HIP_EXTENSIONS_COMPUTED_DEFAULT@")
 set(CMAKE_HIP_COMPILE_FEATURES "@CMAKE_HIP_COMPILE_FEATURES@")
 set(CMAKE_HIP98_COMPILE_FEATURES "@CMAKE_HIP03_COMPILE_FEATURES@")
 set(CMAKE_HIP11_COMPILE_FEATURES "@CMAKE_HIP11_COMPILE_FEATURES@")
diff --git a/Modules/CMakeHIPCompilerId.hip.in b/Modules/CMakeHIPCompilerId.hip.in
index 5258efb..3c4a1d4 100644
--- a/Modules/CMakeHIPCompilerId.hip.in
+++ b/Modules/CMakeHIPCompilerId.hip.in
@@ -16,7 +16,7 @@
 @CMAKE_HIP_COMPILER_ID_PLATFORM_CONTENT@
 @CMAKE_HIP_COMPILER_ID_ERROR_FOR_TEST@
 
-const char* info_language_dialect_default = "INFO" ":" "dialect_default["
+const char* info_language_standard_default = "INFO" ":" "standard_default["
 #if __cplusplus > 202002L
   "23"
 #elif __cplusplus > 201703L
@@ -32,6 +32,14 @@
 #endif
 "]";
 
+const char* info_language_extensions_default = "INFO" ":" "extensions_default["
+#if (defined(__clang__) || defined(__GNUC__)) && !defined(__STRICT_ANSI__)
+  "ON"
+#else
+  "OFF"
+#endif
+"]";
+
 /*--------------------------------------------------------------------------*/
 
 int main(int argc, char* argv[])
@@ -48,7 +56,8 @@
 #ifdef SIMULATE_VERSION_MAJOR
   require += info_simulate_version[argc];
 #endif
-  require += info_language_dialect_default[argc];
+  require += info_language_standard_default[argc];
+  require += info_language_extensions_default[argc];
   (void)argv;
   return require;
 }
diff --git a/Modules/CMakeHIPInformation.cmake b/Modules/CMakeHIPInformation.cmake
index 28c5f18..4c57677 100644
--- a/Modules/CMakeHIPInformation.cmake
+++ b/Modules/CMakeHIPInformation.cmake
@@ -68,6 +68,15 @@
   set(CMAKE_SHARED_MODULE_CREATE_HIP_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_HIP_FLAGS})
 endif()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
diff --git a/Modules/CMakeOBJCCompiler.cmake.in b/Modules/CMakeOBJCCompiler.cmake.in
index ab67bf3..36f6ec1 100644
--- a/Modules/CMakeOBJCCompiler.cmake.in
+++ b/Modules/CMakeOBJCCompiler.cmake.in
@@ -5,6 +5,7 @@
 set(CMAKE_OBJC_COMPILER_VERSION_INTERNAL "@CMAKE_OBJC_COMPILER_VERSION_INTERNAL@")
 set(CMAKE_OBJC_COMPILER_WRAPPER "@CMAKE_OBJC_COMPILER_WRAPPER@")
 set(CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJC_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_OBJC_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_OBJC_EXTENSIONS_COMPUTED_DEFAULT@")
 set(CMAKE_OBJC_COMPILE_FEATURES "@CMAKE_OBJC_COMPILE_FEATURES@")
 set(CMAKE_OBJC90_COMPILE_FEATURES "@CMAKE_OBJC90_COMPILE_FEATURES@")
 set(CMAKE_OBJC99_COMPILE_FEATURES "@CMAKE_OBJC99_COMPILE_FEATURES@")
diff --git a/Modules/CMakeOBJCCompilerId.m.in b/Modules/CMakeOBJCCompilerId.m.in
index 418fd48..89bfe02 100644
--- a/Modules/CMakeOBJCCompilerId.m.in
+++ b/Modules/CMakeOBJCCompilerId.m.in
@@ -23,23 +23,31 @@
 #if !defined(__STDC__)
 # if (defined(_MSC_VER) && !defined(__clang__)) \
   || (defined(__ibmxl__) || defined(__IBMC__))
-#  define C_DIALECT "90"
+#  define C_VERSION "90"
 # else
-#  define C_DIALECT
+#  define C_VERSION
 # endif
 #elif __STDC_VERSION__ > 201710L
-# define C_DIALECT "23"
+# define C_VERSION "23"
 #elif __STDC_VERSION__ >= 201710L
-# define C_DIALECT "17"
+# define C_VERSION "17"
 #elif __STDC_VERSION__ >= 201000L
-# define C_DIALECT "11"
+# define C_VERSION "11"
 #elif __STDC_VERSION__ >= 199901L
-# define C_DIALECT "99"
+# define C_VERSION "99"
 #else
-# define C_DIALECT "90"
+# define C_VERSION "90"
 #endif
-const char* info_language_dialect_default =
-  "INFO" ":" "dialect_default[" C_DIALECT "]";
+const char* info_language_standard_default =
+  "INFO" ":" "standard_default[" C_VERSION "]";
+
+const char* info_language_extensions_default = "INFO" ":" "extensions_default["
+#if (defined(__clang__) || defined(__GNUC__)) && !defined(__STRICT_ANSI__)
+  "ON"
+#else
+  "OFF"
+#endif
+"]";
 
 /*--------------------------------------------------------------------------*/
 
@@ -61,7 +69,8 @@
 #ifdef SIMULATE_VERSION_MAJOR
   require += info_simulate_version[argc];
 #endif
-  require += info_language_dialect_default[argc];
+  require += info_language_standard_default[argc];
+  require += info_language_extensions_default[argc];
   (void)argv;
   return require;
 }
diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake
index ac67d01..4c697da 100644
--- a/Modules/CMakeOBJCInformation.cmake
+++ b/Modules/CMakeOBJCInformation.cmake
@@ -91,6 +91,15 @@
   set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC "${_override}")
 endif()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_OBJC_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_OBJC_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 
 # for most systems a module is the same as a shared library
 # so unless the variable CMAKE_MODULE_EXISTS is set just
diff --git a/Modules/CMakeOBJCXXCompiler.cmake.in b/Modules/CMakeOBJCXXCompiler.cmake.in
index a24582b..4f27100 100644
--- a/Modules/CMakeOBJCXXCompiler.cmake.in
+++ b/Modules/CMakeOBJCXXCompiler.cmake.in
@@ -5,6 +5,7 @@
 set(CMAKE_OBJCXX_COMPILER_VERSION_INTERNAL "@CMAKE_OBJCXX_COMPILER_VERSION_INTERNAL@")
 set(CMAKE_OBJCXX_COMPILER_WRAPPER "@CMAKE_OBJCXX_COMPILER_WRAPPER@")
 set(CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT "@CMAKE_OBJCXX_STANDARD_COMPUTED_DEFAULT@")
+set(CMAKE_OBJCXX_EXTENSIONS_COMPUTED_DEFAULT "@CMAKE_OBJCXX_EXTENSIONS_COMPUTED_DEFAULT@")
 set(CMAKE_OBJCXX_COMPILE_FEATURES "@CMAKE_OBJCXX_COMPILE_FEATURES@")
 set(CMAKE_OBJCXX98_COMPILE_FEATURES "@CMAKE_OBJCXX98_COMPILE_FEATURES@")
 set(CMAKE_OBJCXX11_COMPILE_FEATURES "@CMAKE_OBJCXX11_COMPILE_FEATURES@")
diff --git a/Modules/CMakeOBJCXXCompilerId.mm.in b/Modules/CMakeOBJCXXCompilerId.mm.in
index e2ac35d..2145b40 100644
--- a/Modules/CMakeOBJCXXCompilerId.mm.in
+++ b/Modules/CMakeOBJCXXCompilerId.mm.in
@@ -29,7 +29,7 @@
 #define CXX_STD __cplusplus
 #endif
 
-const char* info_language_dialect_default = "INFO" ":" "dialect_default["
+const char* info_language_standard_default = "INFO" ":" "standard_default["
 #if CXX_STD > 202002L
   "23"
 #elfif CXX_STD > 201703L
@@ -45,6 +45,14 @@
 #endif
 "]";
 
+const char* info_language_extensions_default = "INFO" ":" "extensions_default["
+#if (defined(__clang__) || defined(__GNUC__)) && !defined(__STRICT_ANSI__)
+  "ON"
+#else
+  "OFF"
+#endif
+"]";
+
 /*--------------------------------------------------------------------------*/
 
 int main(int argc, char* argv[])
@@ -64,7 +72,8 @@
 #ifdef SIMULATE_VERSION_MAJOR
   require += info_simulate_version[argc];
 #endif
-  require += info_language_dialect_default[argc];
+  require += info_language_standard_default[argc];
+  require += info_language_extensions_default[argc];
   (void)argv;
   return require;
 }
diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake
index 70e8579..a6d824f 100644
--- a/Modules/CMakeOBJCXXInformation.cmake
+++ b/Modules/CMakeOBJCXXInformation.cmake
@@ -189,6 +189,15 @@
   endif()
 endforeach()
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_OBJCXX_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_OBJCXX_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
diff --git a/Modules/CMakeParseLibraryArchitecture.cmake b/Modules/CMakeParseLibraryArchitecture.cmake
index 6fb9c6b..f7a4b29 100644
--- a/Modules/CMakeParseLibraryArchitecture.cmake
+++ b/Modules/CMakeParseLibraryArchitecture.cmake
@@ -23,7 +23,7 @@
 
     foreach(obj IN LISTS implicit_objs)
       get_filename_component(dir "${obj}" DIRECTORY)
-      if("${dir}" MATCHES "(/usr)+/lib/${CMAKE_LIBRARY_ARCHITECTURE_REGEX}$")
+      if("${dir}" MATCHES "(/usr)?/lib/${CMAKE_LIBRARY_ARCHITECTURE_REGEX}$")
         get_filename_component(arch "${dir}" NAME)
         set(library_arch "${arch}")
         break()
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index 8f0909c..ecad1d5 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -70,6 +70,15 @@
 set(CMAKE_Swift_FLAGS_RELWITHDEBINFO_INIT "-O -g")
 set(CMAKE_Swift_FLAGS_MINSIZEREL_INIT "-Osize")
 
+if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+  if(NOT DEFINED CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG)
+    set(CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+  endif()
+  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+  endif()
+endif()
+
 cmake_initialize_per_config_variable(CMAKE_Swift_FLAGS "Swift Compiler Flags")
 
 # NOTE(compnerd) we do not have an object compile rule since we build the objects as part of the link step
diff --git a/Modules/CMakeTestFortranCompiler.cmake b/Modules/CMakeTestFortranCompiler.cmake
index f25788d..579f83f 100644
--- a/Modules/CMakeTestFortranCompiler.cmake
+++ b/Modules/CMakeTestFortranCompiler.cmake
@@ -17,11 +17,18 @@
 
 # Try to identify the ABI and configure it into CMakeFortranCompiler.cmake
 include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerABI.cmake)
-CMAKE_DETERMINE_COMPILER_ABI(Fortran ${CMAKE_ROOT}/Modules/CMakeFortranCompilerABI.F)
+CMAKE_DETERMINE_COMPILER_ABI(Fortran ${CMAKE_ROOT}/Modules/CMakeFortranCompilerABI.F90)
 if(CMAKE_Fortran_ABI_COMPILED)
   # The compiler worked so skip dedicated test below.
   set(CMAKE_Fortran_COMPILER_WORKS TRUE)
+  set(CMAKE_Fortran_COMPILER_SUPPORTS_F90 1)
   message(STATUS "Check for working Fortran compiler: ${CMAKE_Fortran_COMPILER} - skipped")
+else()
+  cmake_determine_compiler_abi(Fortran ${CMAKE_ROOT}/Modules/CMakeFortranCompilerABI.F)
+  if(CMAKE_Fortran_ABI_COMPILED)
+    set(CMAKE_Fortran_COMPILER_WORKS TRUE)
+    message(STATUS "Check for working Fortran 77 compiler: ${CMAKE_Fortran_COMPILER} - skipped")
+  endif()
 endif()
 
 # This file is used by EnableLanguage in cmGlobalGenerator to
diff --git a/Modules/CMakeTestHIPCompiler.cmake b/Modules/CMakeTestHIPCompiler.cmake
index 5acd806..ecbfa7f 100644
--- a/Modules/CMakeTestHIPCompiler.cmake
+++ b/Modules/CMakeTestHIPCompiler.cmake
@@ -10,7 +10,7 @@
 endif()
 
 set(__CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS}")
-string(APPEND CMAKE_HIP_FLAGS "--cuda-host-only")
+string(APPEND CMAKE_HIP_FLAGS " --cuda-host-only")
 
 include(CMakeTestCompilerCommon)
 
diff --git a/Modules/CPackIFW.cmake b/Modules/CPackIFW.cmake
index 2e68770..85108db 100644
--- a/Modules/CPackIFW.cmake
+++ b/Modules/CPackIFW.cmake
@@ -439,6 +439,7 @@
   "QtIFW-")
 
 set(_CPACK_IFW_VERSIONS
+  "4.2"
   "4.1"
   "4.0"
   "3.2"
diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake
index b4da4fa..7b13c3a 100644
--- a/Modules/CheckCXXSymbolExists.cmake
+++ b/Modules/CheckCXXSymbolExists.cmake
@@ -25,7 +25,7 @@
   as a function or variable then the symbol must also be available for
   linking.  If the symbol is a type, enum value, or C++ template it will
   not be recognized: consider using the :module:`CheckTypeSize`
-  or :module:`CheckCXXSourceCompiles` module instead.
+  or :module:`CheckSourceCompiles` module instead.
 
 .. note::
 
diff --git a/Modules/CheckFortranFunctionExists.cmake b/Modules/CheckFortranFunctionExists.cmake
index ad72e2f..8f1ca9d 100644
--- a/Modules/CheckFortranFunctionExists.cmake
+++ b/Modules/CheckFortranFunctionExists.cmake
@@ -20,6 +20,12 @@
   ``<result>``
     variable to store the result; will be created as an internal cache variable.
 
+.. note::
+
+  This command does not detect functions in Fortran modules. In general it is
+  recommended to use :module:`CheckSourceCompiles` instead to determine if a
+  Fortran function or subroutine is available.
+
 The following variables may be set before calling this macro to modify
 the way the check is run:
 
diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake
index 559c103..52f707c 100644
--- a/Modules/CheckLanguage.cmake
+++ b/Modules/CheckLanguage.cmake
@@ -46,7 +46,7 @@
     file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang})
 
     set(extra_compiler_variables)
-    if(${lang} STREQUAL CUDA)
+    if(${lang} STREQUAL CUDA AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
       set(extra_compiler_variables "set(CMAKE_CUDA_HOST_COMPILER \\\"\${CMAKE_CUDA_HOST_COMPILER}\\\")")
     endif()
 
diff --git a/Modules/CheckLinkerFlag.cmake b/Modules/CheckLinkerFlag.cmake
index 28ac2e3..e85e43e 100644
--- a/Modules/CheckLinkerFlag.cmake
+++ b/Modules/CheckLinkerFlag.cmake
@@ -41,6 +41,10 @@
 
 include(CMakeCheckCompilerFlagCommonPatterns)
 
+cmake_policy(PUSH)
+cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
+cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
+
 function(CHECK_LINKER_FLAG _lang _flag _var)
   get_property (_supported_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
   if (NOT _lang IN_LIST _supported_languages)
@@ -82,3 +86,5 @@
   endforeach()
   set(${_var} "${${_var}}" PARENT_SCOPE)
 endfunction()
+
+cmake_policy(POP)
diff --git a/Modules/CheckSymbolExists.cmake b/Modules/CheckSymbolExists.cmake
index f8ca584..48ee3c4 100644
--- a/Modules/CheckSymbolExists.cmake
+++ b/Modules/CheckSymbolExists.cmake
@@ -24,7 +24,7 @@
 as a function or variable then the symbol must also be available for
 linking (so intrinsics may not be detected).
 If the symbol is a type, enum value, or intrinsic it will not be recognized
-(consider using :module:`CheckTypeSize` or :module:`CheckCSourceCompiles`).
+(consider using :module:`CheckTypeSize` or :module:`CheckSourceCompiles`).
 If the check needs to be done in C++, consider using
 :module:`CheckCXXSymbolExists` instead.
 
diff --git a/Modules/Compiler/ARMClang-C.cmake b/Modules/Compiler/ARMClang-C.cmake
index 0a64a8a..01c4cea 100644
--- a/Modules/Compiler/ARMClang-C.cmake
+++ b/Modules/Compiler/ARMClang-C.cmake
@@ -2,6 +2,14 @@
 include(Compiler/ARMClang)
 __compiler_armclang(C)
 
+if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+    AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+    AND CMAKE_DEPFILE_FLAGS_C)
+  # dependencies are computed by the compiler itself
+  set(CMAKE_C_DEPFILE_FORMAT gcc)
+  set(CMAKE_C_DEPENDS_USE_COMPILER TRUE)
+endif()
+
 set(CMAKE_C90_STANDARD_COMPILE_OPTION "-std=c90")
 set(CMAKE_C90_EXTENSION_COMPILE_OPTION "-std=gnu90")
 set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
diff --git a/Modules/Compiler/ARMClang-CXX.cmake b/Modules/Compiler/ARMClang-CXX.cmake
index 5dfb401..045b783 100644
--- a/Modules/Compiler/ARMClang-CXX.cmake
+++ b/Modules/Compiler/ARMClang-CXX.cmake
@@ -1,3 +1,11 @@
 include(Compiler/Clang-CXX)
 include(Compiler/ARMClang)
 __compiler_armclang(CXX)
+
+if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+    AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
+    AND CMAKE_DEPFILE_FLAGS_CXX)
+  # dependencies are computed by the compiler itself
+  set(CMAKE_CXX_DEPFILE_FORMAT gcc)
+  set(CMAKE_CXX_DEPENDS_USE_COMPILER TRUE)
+endif()
diff --git a/Modules/Compiler/CMakeCommonCompilerMacros.cmake b/Modules/Compiler/CMakeCommonCompilerMacros.cmake
index c86af98..2f220d4 100644
--- a/Modules/Compiler/CMakeCommonCompilerMacros.cmake
+++ b/Modules/Compiler/CMakeCommonCompilerMacros.cmake
@@ -38,20 +38,26 @@
   # support for language standards, then don't bother.
   if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "${stdver1}")
     if (NOT CMAKE_${lang}_COMPILER_FORCED)
-      if (NOT CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT)
-        message(FATAL_ERROR "CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT should be set for ${CMAKE_${lang}_COMPILER_ID} (${CMAKE_${lang}_COMPILER}) version ${CMAKE_${lang}_COMPILER_VERSION}")
+      if (NOT CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT OR NOT DEFINED CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT)
+        message(FATAL_ERROR "CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT and CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT should be set for ${CMAKE_${lang}_COMPILER_ID} (${CMAKE_${lang}_COMPILER}) version ${CMAKE_${lang}_COMPILER_VERSION}")
       endif ()
       set(CMAKE_${lang}_STANDARD_DEFAULT ${CMAKE_${lang}_STANDARD_COMPUTED_DEFAULT})
+      set(CMAKE_${lang}_EXTENSIONS_DEFAULT ${CMAKE_${lang}_EXTENSIONS_COMPUTED_DEFAULT})
     else ()
       list(REVERSE __std_ver_pairs)
       foreach (__std_ver_pair IN LISTS __std_ver_pairs)
         string(REGEX MATCH "([^ ]+) (.+)" __std_ver_pair "${__std_ver_pair}")
         set(__stdver ${CMAKE_MATCH_1})
         set(__std ${CMAKE_MATCH_2})
-        if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL __stdver AND
-          NOT DEFINED CMAKE_${lang}_STANDARD_DEFAULT)
-          # Compiler id was forced so just guess the default standard level.
-          set(CMAKE_${lang}_STANDARD_DEFAULT ${__std})
+        # Compiler id was forced so just guess the defaults.
+        if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL __stdver)
+          if(NOT DEFINED CMAKE_${lang}_EXTENSIONS_DEFAULT)
+            # Currently known compilers default to enabling extensions.
+            set(CMAKE_${lang}_EXTENSIONS_DEFAULT ON)
+          endif()
+          if(NOT DEFINED CMAKE_${lang}_STANDARD_DEFAULT)
+            set(CMAKE_${lang}_STANDARD_DEFAULT ${__std})
+          endif()
         endif ()
         unset(__std)
         unset(__stdver)
diff --git a/Modules/Compiler/Clang-C.cmake b/Modules/Compiler/Clang-C.cmake
index cf493d7..1b765ad 100644
--- a/Modules/Compiler/Clang-C.cmake
+++ b/Modules/Compiler/Clang-C.cmake
@@ -9,6 +9,7 @@
 if("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
   set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
   set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
+  set(CMAKE_C_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
       AND CMAKE_DEPFILE_FLAGS_C)
diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake
index 98828e0..84b05d7 100644
--- a/Modules/Compiler/Clang-CXX.cmake
+++ b/Modules/Compiler/Clang-CXX.cmake
@@ -22,6 +22,7 @@
 
 if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
   set(CMAKE_CXX_CLANG_TIDY_DRIVER_MODE "cl")
+  set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles"
       AND CMAKE_DEPFILE_FLAGS_CXX)
diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake
index e3ca16e..bae0fbd 100644
--- a/Modules/Compiler/IAR-ASM.cmake
+++ b/Modules/Compiler/IAR-ASM.cmake
@@ -1,57 +1,45 @@
-# This file is processed when the IAR compiler is used for an assembler file
+# This file is processed when the IAR Assembler is used
 
 include(Compiler/IAR)
 
-if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ilink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
+cmake_policy(PUSH)
+cmake_policy(SET CMP0057 NEW) # if IN_LIST
 
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ilink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
+set(_CMAKE_IAR_ITOOLS "ARM" "RH850"  "RL78" "RX" "RISC-V" "STM8")
+set(_CMAKE_IAR_XTOOLS "AVR" "MSP430" "V850" "8051")
 
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ilink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
+set(_CMAKE_IAR_ASM_SILENT   "RH850"  "RL78" "RX" "RISC-V" "STM8")
+if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_ASM_SILENT)
+  set(_CMAKE_IAR_SILENCER_FLAG " --silent")
+else()
+  set(_CMAKE_IAR_SILENCER_FLAG " -S")
+endif()
 
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ilink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
+string(APPEND CMAKE_ASM_FLAGS_INIT " ")
+string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
+string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
+string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
 
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RISC-V")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ilink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
+set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> ${_CMAKE_IAR_SILENCER_FLAG} <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
 
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_ITOOLS)
+  __compiler_iar_ilink(ASM)
+  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa;S)
+
+elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" IN_LIST _CMAKE_IAR_XTOOLS)
   __compiler_iar_xlink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;asm;msa)
-
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_xlink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s43;asm;msa)
-
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "V850")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_xlink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s85;asm;msa)
-
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "8051")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_xlink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s51;asm;msa)
-
-elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "STM8")
-  set(CMAKE_ASM_COMPILE_OBJECT  "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
-  __compiler_iar_ilink(ASM)
-  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa)
+  # AVR=s90, MSP430=s43, V850=s85, 8051=s51
+  set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;s43;s85;s51;asm;msa)
 
 else()
   message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.")
+
 endif()
+
+unset(_CMAKE_IAR_ITOOLS)
+unset(_CMAKE_IAR_XTOOLS)
+unset(_CMAKE_IAR_ASM_SILENT)
+unset(_CMAKE_IAR_SILENCER_FLAG)
+
+cmake_policy(POP)
diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake
index 054ee74..0ef1a2a 100644
--- a/Modules/Compiler/IAR-C.cmake
+++ b/Modules/Compiler/IAR-C.cmake
@@ -1,54 +1,61 @@
-# This file is processed when the IAR compiler is used for a C file
-
+# This file is processed when the IAR C Compiler is used
+#
+# C Language Specification support
+#  - Newer versions of the IAR C Compiler require the --c89 flag to build a file under the C90 standard.
+#  - Earlier versions of the compiler had C90 by default, not requiring the backward-compatibility flag.
+#
+# The IAR Language Extensions
+#  - The IAR Language Extensions can be enabled by -e flag
+#
 include(Compiler/IAR)
 include(Compiler/CMakeCommonCompilerMacros)
 
-# Common
-if(NOT CMAKE_C_COMPILER_VERSION)
+if(NOT DEFINED CMAKE_C_COMPILER_VERSION)
   message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected.  This should be automatic.")
 endif()
 
+# Unused after CMP0128
 set(CMAKE_C_EXTENSION_COMPILE_OPTION -e)
 
 if(CMAKE_C_COMPILER_VERSION_INTERNAL VERSION_GREATER 7)
   set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89)
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e)
-  set(CMAKE_C99_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e)
 else()
   set(CMAKE_C90_STANDARD_COMPILE_OPTION "")
   set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e)
 endif()
 
-if(CMAKE_C_COMPILER_VERSION_INTERNAL VERSION_GREATER 8)
-  set(CMAKE_C11_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_C11_EXTENSION_COMPILE_OPTION -e)
-endif()
+set(CMAKE_C${CMAKE_C_STANDARD_COMPUTED_DEFAULT}_STANDARD_COMPILE_OPTION "")
+set(CMAKE_C${CMAKE_C_STANDARD_COMPUTED_DEFAULT}_EXTENSION_COMPILE_OPTION -e)
 
 # Architecture specific
 if("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-  if(CMAKE_C_COMPILER_VERSION_INTERNAL VERSION_LESS 7)
-    # IAR ARM 4.X uses xlink.exe, detection is not yet implemented
-    message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION = ${CMAKE_C_COMPILER_VERSION} not supported by CMake.")
+  if (CMAKE_C_COMPILER_VERSION VERSION_LESS 5)
+    # IAR C Compiler for Arm prior version 5.xx uses XLINK. Support in CMake is not implemented.
+    message(FATAL_ERROR "IAR C Compiler for Arm version ${CMAKE_C_COMPILER_VERSION} not supported by CMake.")
   endif()
   __compiler_iar_ilink(C)
-  __compiler_check_default_language_standard(C 1.10 90 6.10 99 8.10 11)
+  __compiler_check_default_language_standard(C 5.10 90 6.10 99 8.10 11 8.40 17)
 
 elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
   __compiler_iar_ilink(C)
-  __compiler_check_default_language_standard(C 1.10 90 2.10 99 4.10 11)
+  __compiler_check_default_language_standard(C 1.10 90 2.10 99 4.10 11 4.20 17)
 
 elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850")
   __compiler_iar_ilink(C)
-  __compiler_check_default_language_standard(C 1.10 90 1.10 99 2.10 11)
+  __compiler_check_default_language_standard(C 1.10 90 1.10 99 2.10 11 2.21 17)
 
 elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78")
+  if(CMAKE_C_COMPILER_VERSION VERSION_LESS 2)
+    # IAR C Compiler for RL78 prior version 2.xx uses XLINK. Support in CMake is not implemented.
+    message(FATAL_ERROR "IAR C Compiler for RL78 version ${CMAKE_C_COMPILER_VERSION} not supported by CMake.")
+  endif()
   __compiler_iar_ilink(C)
-  __compiler_check_default_language_standard(C 1.10 90 1.10 99 4.10 11)
+  __compiler_check_default_language_standard(C 2.10 90 2.10 99 4.10 11 4.20 17)
 
 elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RISCV")
   __compiler_iar_ilink(C)
-  __compiler_check_default_language_standard(C 1.10 90 1.10 99 1.10 11)
+  __compiler_check_default_language_standard(C 1.10 90 1.10 99 1.10 11 1.21 17)
 
 elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
   __compiler_iar_xlink(C)
diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake
index d93b272..7df74ad 100644
--- a/Modules/Compiler/IAR-CXX.cmake
+++ b/Modules/Compiler/IAR-CXX.cmake
@@ -1,63 +1,68 @@
-# This file is processed when the IAR compiler is used for a C++ file
-
+# This file is processed when the IAR C++ Compiler is used
+#
+# C++ Language Specification support
+#  - Newer versions of the IAR C++ Compiler require the --c++ flag to build a C++ file.
+#    Earlier versions for non-ARM architectures provided Embedded C++, enabled with the --eec++ flag.
+#
+# The IAR Language Extensions
+#  - The IAR Language Extensions can be enabled by -e flag
+#
 include(Compiler/IAR)
 include(Compiler/CMakeCommonCompilerMacros)
 
-# Common
-if(NOT CMAKE_CXX_COMPILER_VERSION)
+if(NOT DEFINED CMAKE_CXX_COMPILER_VERSION)
   message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.")
 endif()
 
+# Whenever needed, override this default behavior using CMAKE_IAR_CXX_FLAG in your toolchain file.
 if(NOT CMAKE_IAR_CXX_FLAG)
-  # The --c++ flag was introduced in platform version 9 for all architectures except ARM where it was introduced already in version 7
-  if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8 OR
-    (CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 6 AND "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") )
-    set(CMAKE_IAR_CXX_FLAG --c++)
+  cmake_policy(PUSH)
+  cmake_policy(SET CMP0057 NEW) # if IN_LIST
+
+  if(${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT} IN_LIST "14;17" OR
+     ("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM" AND ${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT} EQUAL 98))
+    string(PREPEND CMAKE_CXX_FLAGS "--c++ ")
   else()
-    set(CMAKE_IAR_CXX_FLAG --eec++)
+    string(PREPEND CMAKE_CXX_FLAGS "--eec++ ")
   endif()
+
+  cmake_policy(POP)
 endif()
 
-set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e)
+set(CMAKE_CXX_STANDARD_COMPILE_OPTION "")
+set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e) # Unused after CMP0128
 
-if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 7)
-  set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e)
-  set(CMAKE_CXX03_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CXX03_EXTENSION_COMPILE_OPTION -e)
-endif()
-
-if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8)
-  set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -e)
-  set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -e)
-endif()
+set(CMAKE_CXX${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT}_STANDARD_COMPILE_OPTION "")
+set(CMAKE_CXX${CMAKE_CXX_STANDARD_COMPUTED_DEFAULT}_EXTENSION_COMPILE_OPTION -e)
 
 # Architecture specific
 if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM")
-  if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_LESS 7)
-    # IAR ARM 4.X uses xlink.exe, detection is not yet implemented
-    message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION = ${CMAKE_C_COMPILER_VERSION} not supported by CMake.")
+  if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
+    # IAR C++ Compiler for Arm prior version 5.xx uses XLINK. Support in CMake is not implemented.
+    message(FATAL_ERROR "IAR C++ Compiler for Arm version ${CMAKE_CXX_COMPILER_VERSION} not supported by CMake.")
   endif()
   __compiler_iar_ilink(CXX)
-  __compiler_check_default_language_standard(CXX 6.10 98 8.10 14)
+  __compiler_check_default_language_standard(CXX 5.10 98 8.10 14 8.40 17)
 
 elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX")
   __compiler_iar_ilink(CXX)
-  __compiler_check_default_language_standard(CXX 2.10 98 4.10 14)
+  __compiler_check_default_language_standard(CXX 2.10 98 4.10 14 4.20 17)
 
 elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850")
   __compiler_iar_ilink(CXX)
-  __compiler_check_default_language_standard(CXX 1.10 98 2.10 14)
+  __compiler_check_default_language_standard(CXX 1.10 98 2.10 14 2.21 17)
 
 elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78")
+  if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 2)
+    # # IAR C++ Compiler for RL78 prior version 2.xx uses XLINK. Support in CMake is not implemented.
+    message(FATAL_ERROR "IAR C++ Compiler for RL78 version ${CMAKE_CXX_COMPILER_VERSION} not supported by CMake.")
+  endif()
   __compiler_iar_ilink(CXX)
-  __compiler_check_default_language_standard(CXX 1.10 98 4.10 14)
+  __compiler_check_default_language_standard(CXX 2.10 98 4.10 14 4.20 17)
 
 elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RISCV")
   __compiler_iar_ilink(CXX)
-  __compiler_check_default_language_standard(CXX 1.10 98 1.10 14)
+  __compiler_check_default_language_standard(CXX 1.10 98 1.10 14 1.21 17)
 
 elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR")
   __compiler_iar_xlink(CXX)
diff --git a/Modules/Compiler/IAR-DetermineCompiler.cmake b/Modules/Compiler/IAR-DetermineCompiler.cmake
index 443b09c..b03fd1f 100644
--- a/Modules/Compiler/IAR-DetermineCompiler.cmake
+++ b/Modules/Compiler/IAR-DetermineCompiler.cmake
@@ -1,28 +1,26 @@
-# IAR Systems compiler for ARM embedded systems.
-#   http://www.iar.com
-#   http://supp.iar.com/FilesPublic/UPDINFO/004916/arm/doc/EWARM_DevelopmentGuide.ENU.pdf
+# IAR C/C++ Compiler (https://www.iar.com)
+# CPU <arch> supported in CMake: 8051, Arm, AVR, MSP430, RH850, RISC-V, RL78, RX and V850
 #
-# __IAR_SYSTEMS_ICC__ An integer that identifies the IAR compiler platform:
-#                       9 and higher means C11 and C++14 as language default
-#                       8 means C99 and C++03 as language default
-#                       7 and lower means C89 and EC++ as language default.
-# __ICCARM__          An integer that is set to 1 when the code is compiled with the IAR C/C++ Compiler for ARM
-# __VER__             An integer that identifies the version number of the IAR compiler in use. For example,
-#                     version 5.11.3 is returned as 5011003.
+# IAR C/C++ Compiler for <arch> internal integer symbols used in CMake:
 #
-# IAR Systems Compiler for AVR embedded systems
-#  http://supp.iar.com/FilesPublic/UPDINFO/007051/ew/doc/EWAVR_CompilerReference.pdf
+# __IAR_SYSTEMS_ICC__
+#           Provides the compiler internal platform version
+# __ICC<arch>__
+#           Provides 1 for the current <arch> in use
+# __VER__
+#           Provides the current version in use
+#            The semantic version of the compiler is architecture-dependent
+#            When <arch> is ARM:
+#               CMAKE_<LANG>_COMPILER_VERSION_MAJOR = (__VER__ / 1E6)
+#               CMAKE_<LANG>_COMPILER_VERSION_MINOR = (__VER__ / 1E3) % 1E3
+#               CMAKE_<LANG>_COMPILER_VERSION_PATCH = (__VER__ % 1E3)
+#            When <arch> is non-ARM:
+#               CMAKE_<LANG>_COMPILER_VERSION_MAJOR = (__VER__ / 1E2)
+#               CMAKE_<LANG>_COMPILER_VERSION_MINOR = (__VER__ - ((__VER__/ 1E2) * 1E2))
+#               CMAKE_<LANG>_COMPILER_VERSION_PATCH = (__SUBVERSION__)
+# __SUBVERSION__
+#           Provides the version's patch level for non-ARM <arch>
 #
-# __IAR_SYSTEMS_ICC__ An integer that identifies the IAR compiler platform.
-# __ICCAVR__          An integer that is set to 1 when the code is compiled with the IAR C/C++ Compiler for AVR
-# __VER__             An integer that identifies the version number of the IAR compiler in use.
-#                     The value is calculated by (100 * VERSION_MAJOR + VERSION_MINOR). For example the version
-#                     3.34 is given as 334
-# __SUBVERSION__      An integer that identifies the subversion number of the compiler version number
-#                     for example 3 in 1.2.3.4.  THis is used as the patch version, as seen when running iccavr
-#                     from the command line
-#
-
 set(_compiler_id_pp_test "defined(__IAR_SYSTEMS_ICC__) || defined(__IAR_SYSTEMS_ICC)")
 
 set(_compiler_id_version_compute "
diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake
index 2200a21..53456f5 100644
--- a/Modules/Compiler/IAR.cmake
+++ b/Modules/Compiler/IAR.cmake
@@ -1,47 +1,15 @@
-# This file is processed when the IAR compiler is used for a C or C++ file
-# Documentation can be downloaded here: http://www.iar.com/website1/1.0.1.0/675/1/
-# The initial feature request is here: https://gitlab.kitware.com/cmake/cmake/-/issues/10176
-# It also contains additional links and information.
-# See USER GUIDES -> C/C++ Development Guide and ReleaseNotes for EWARM:
-# version 6.30.8: http://supp.iar.com/FilesPublic/UPDINFO/006607/arm/doc/infocenter/index.ENU.html
-# version 7.60.1: http://supp.iar.com/FilesPublic/UPDINFO/011006/arm/doc/infocenter/index.ENU.html
-# version 8.10.1: http://netstorage.iar.com/SuppDB/Public/UPDINFO/011854/arm/doc/infocenter/index.ENU.html
-
-# The IAR internal compiler platform generations (Predefined symbol __IAR_SYSTEMS_ICC__):
-#  9 and higher means C11 and C++14 as language default (EWARM v8.x, EWRX v4.x and higher)
-#  8 means C99 and C++03 as language default (EWARM v6.x, v7.x. EWRX v2.x, 3.x)
-#  7 and lower means C89 and EC++ as language default. (EWARM v5.x and lower)
-
-# C/C++ Standard versions
+# This file is processed when the IAR C/C++ Compiler is used
 #
-# IAR typically only supports one C and C++ Standard version,
-# the exception is C89 which is always supported and can be selected
-# if its not the default
+# CPU <arch> supported in CMake: 8051, Arm, AVR, MSP430, RH850, RISC-V, RL78, RX and V850
 #
-# C++ is trickier, there were historically 3 switches,
-# and some IAR versions support multiple of those.
-# they are --eec++, --ec++ and --c++ and where used to
-# enable various language features like exceptions
+# The compiler user documentation is architecture-dependent
+# and it can found with the product installation under <arch>/doc/{EW,BX}<arch>_DevelopmentGuide.ENU.pdf
 #
-# recent versions only have --c++ for full support
-# but can choose to disable features with further arguments
 #
-# C/C++ Standard compliance
-#
-# IAR has 3 modes: default, strict and extended
-# the extended mode is needed for popular libraries like CMSIS
-#
-# "Silent" Operation
-#
-# this really is different to most programs I know.
-# nothing meaningful from the operation is lost, just some redundant
-# code and data size printouts (that can be inspected with common tools).
-
-# This module is shared by multiple languages; use include blocker.
 include_guard()
 
 macro(__compiler_iar_ilink lang)
-  set(CMAKE_EXECUTABLE_SUFFIX_${lang} ".elf")
+  set(CMAKE_EXECUTABLE_SUFFIX ".elf")
   set(CMAKE_${lang}_OUTPUT_EXTENSION ".o")
   if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
     set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
@@ -58,14 +26,6 @@
     string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
   endif()
 
-  if (${lang} STREQUAL "ASM")
-    string(APPEND CMAKE_ASM_FLAGS_INIT " ")
-    string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
-    string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
-    string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
-    string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
-  endif()
-
   set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_LINKER> --silent <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <TARGET> --create <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> <TARGET> --create <LINK_FLAGS> <OBJECTS>")
@@ -74,7 +34,7 @@
 endmacro()
 
 macro(__compiler_iar_xlink lang)
-  set(CMAKE_EXECUTABLE_SUFFIX_${lang} ".bin")
+  set(CMAKE_EXECUTABLE_SUFFIX ".bin")
   if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX")
 
     set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
@@ -91,14 +51,6 @@
     string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG")
   endif()
 
-  if (${lang} STREQUAL "ASM")
-    string(APPEND CMAKE_ASM_FLAGS_INIT " ")
-    string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r")
-    string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
-    string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG")
-    string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG")
-  endif()
-
   set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_LINKER> -S <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>")
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> <TARGET> <LINK_FLAGS> <OBJECTS>")
diff --git a/Modules/Compiler/Intel-C.cmake b/Modules/Compiler/Intel-C.cmake
index ead9069..9884b58 100644
--- a/Modules/Compiler/Intel-C.cmake
+++ b/Modules/Compiler/Intel-C.cmake
@@ -17,6 +17,7 @@
 
   set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
   set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
+  set(CMAKE_C_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
 
   if (NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 16.0.0)
     set(CMAKE_C11_STANDARD_COMPILE_OPTION "-Qstd=c11")
diff --git a/Modules/Compiler/Intel-CXX.cmake b/Modules/Compiler/Intel-CXX.cmake
index 37f339a..7c9cca9 100644
--- a/Modules/Compiler/Intel-CXX.cmake
+++ b/Modules/Compiler/Intel-CXX.cmake
@@ -16,6 +16,7 @@
 if("x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
 
   set(CMAKE_CXX_CLANG_TIDY_DRIVER_MODE "cl")
+  set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
 
   if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0.0)
     set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-Qstd=c++20")
diff --git a/Modules/Compiler/IntelLLVM-C.cmake b/Modules/Compiler/IntelLLVM-C.cmake
index d69d064..d7346f6 100644
--- a/Modules/Compiler/IntelLLVM-C.cmake
+++ b/Modules/Compiler/IntelLLVM-C.cmake
@@ -4,6 +4,7 @@
 if("x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
   set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
   set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
+  set(CMAKE_C_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
       AND CMAKE_DEPFILE_FLAGS_C)
diff --git a/Modules/Compiler/IntelLLVM-CXX.cmake b/Modules/Compiler/IntelLLVM-CXX.cmake
index 9799888..cae1f11 100644
--- a/Modules/Compiler/IntelLLVM-CXX.cmake
+++ b/Modules/Compiler/IntelLLVM-CXX.cmake
@@ -4,6 +4,7 @@
 if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC")
   set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TP)
   set(CMAKE_CXX_CLANG_TIDY_DRIVER_MODE "cl")
+  set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
   if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
       AND CMAKE_GENERATOR MATCHES "Makefiles|WMake"
       AND CMAKE_DEPFILE_FLAGS_CXX)
diff --git a/Modules/Compiler/MSVC-C.cmake b/Modules/Compiler/MSVC-C.cmake
index 9a5104b..df3691c 100644
--- a/Modules/Compiler/MSVC-C.cmake
+++ b/Modules/Compiler/MSVC-C.cmake
@@ -11,6 +11,22 @@
   set(CMAKE_C11_STANDARD_COMPILE_OPTION "-std:c11")
   set(CMAKE_C11_EXTENSION_COMPILE_OPTION "-std:c11")
 
+  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28)
+    set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+    set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
+    set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
+    set(CMAKE_C17_STANDARD_COMPILE_OPTION "-std:c17")
+    set(CMAKE_C17_EXTENSION_COMPILE_OPTION "-std:c17")
+  else()
+    # Special case for 19.27 (VS 16.7): C11 has partial support.
+    macro(cmake_record_c_compile_features)
+      _has_compiler_features_c(90)
+      _has_compiler_features_c(99)
+      list(APPEND CMAKE_C11_COMPILE_FEATURES c_std_11)
+      set(_result 0) # expected by cmake_determine_compile_features
+    endmacro()
+  endif()
+
   __compiler_check_default_language_standard(C 19.27 99)
 else()
   # MSVC has no specific options to set C language standards, but set them as
@@ -25,41 +41,42 @@
 
   # There is no meaningful default for this
   set(CMAKE_C_STANDARD_DEFAULT "")
+
+  # There are no C compiler modes so we hard-code the known compiler supported
+  # features. Override the default macro for this special case.  Pretend that
+  # all language standards are available so that at least compilation
+  # can be attempted.
+  macro(cmake_record_c_compile_features)
+    list(APPEND CMAKE_C_COMPILE_FEATURES
+      c_std_90
+      c_std_99
+      c_std_11
+      c_std_17
+      c_std_23
+      c_function_prototypes
+      )
+    list(APPEND CMAKE_C90_COMPILE_FEATURES c_std_90 c_function_prototypes)
+    list(APPEND CMAKE_C99_COMPILE_FEATURES c_std_99)
+    list(APPEND CMAKE_C11_COMPILE_FEATURES c_std_11)
+    if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0)
+      list(APPEND CMAKE_C_COMPILE_FEATURES c_variadic_macros)
+      list(APPEND CMAKE_C99_COMPILE_FEATURES c_variadic_macros)
+    endif()
+    set(_result 0) # expected by cmake_determine_compile_features
+  endmacro()
 endif()
 
 set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -TC)
 set(CMAKE_C_CLANG_TIDY_DRIVER_MODE "cl")
-
-# There are no C compiler modes so we hard-code the known compiler supported
-# features. Override the default macro for this special case.  Pretend that
-# all language standards are available so that at least compilation
-# can be attempted.
-macro(cmake_record_c_compile_features)
-  list(APPEND CMAKE_C_COMPILE_FEATURES
-    c_std_90
-    c_std_99
-    c_std_11
-    c_function_prototypes
-    )
-  list(APPEND CMAKE_C90_COMPILE_FEATURES c_std_90 c_function_prototypes)
-  list(APPEND CMAKE_C99_COMPILE_FEATURES c_std_99)
-  list(APPEND CMAKE_C11_COMPILE_FEATURES c_std_11)
-  if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0)
-    list(APPEND CMAKE_C_COMPILE_FEATURES c_variadic_macros)
-    list(APPEND CMAKE_C99_COMPILE_FEATURES c_variadic_macros)
-  endif()
-  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.27)
-    list(APPEND CMAKE_C_COMPILE_FEATURES c_restrict)
-    list(APPEND CMAKE_C99_COMPILE_FEATURES c_restrict)
-  endif()
-  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28)
-    list(APPEND CMAKE_C_COMPILE_FEATURES c_static_assert)
-    list(APPEND CMAKE_C11_COMPILE_FEATURES c_static_assert)
-  endif()
-  set(_result 0) # expected by cmake_determine_compile_features
-endmacro()
+set(CMAKE_C_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
 
 # /JMC "Just My Code" is only supported by MSVC 19.05 onward.
 if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   set(CMAKE_C_COMPILE_OPTIONS_JMC "-JMC")
 endif()
+
+# The `/external:I` flag was made non-experimental in 19.29.30036.3.
+if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30036.3)
+  set(CMAKE_INCLUDE_SYSTEM_FLAG_C "-external:I")
+  set(_CMAKE_INCLUDE_SYSTEM_FLAG_C_WARNING "-external:W0 ")
+endif ()
diff --git a/Modules/Compiler/MSVC-CXX.cmake b/Modules/Compiler/MSVC-CXX.cmake
index 5df5c1e..17cbc3c 100644
--- a/Modules/Compiler/MSVC-CXX.cmake
+++ b/Modules/Compiler/MSVC-CXX.cmake
@@ -4,6 +4,7 @@
 include(Compiler/CMakeCommonCompilerMacros)
 
 set(CMAKE_CXX_CLANG_TIDY_DRIVER_MODE "cl")
+set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE_DRIVER_MODE "cl")
 
 if ((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0.24215.1 AND
      CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) OR
@@ -80,3 +81,9 @@
 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.05)
   set(CMAKE_CXX_COMPILE_OPTIONS_JMC "-JMC")
 endif()
+
+# The `/external:I` flag was made non-experimental in 19.29.30036.3.
+if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.29.30036.3)
+  set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-external:I")
+  set(_CMAKE_INCLUDE_SYSTEM_FLAG_CXX_WARNING "-external:W0 ")
+endif ()
diff --git a/Modules/Compiler/NVHPC.cmake b/Modules/Compiler/NVHPC.cmake
index 7048670..b51bb43 100644
--- a/Modules/Compiler/NVHPC.cmake
+++ b/Modules/Compiler/NVHPC.cmake
@@ -12,4 +12,5 @@
 
 macro(__compiler_nvhpc lang)
   # Logic specific to NVHPC.
+  set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
 endmacro()
diff --git a/Modules/Compiler/PGI.cmake b/Modules/Compiler/PGI.cmake
index 4f8b90b..d111be9 100644
--- a/Modules/Compiler/PGI.cmake
+++ b/Modules/Compiler/PGI.cmake
@@ -26,7 +26,7 @@
   endif()
 
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
-  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG ",")
+  set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
 
   set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
   if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le AND (NOT CMAKE_HOST_WIN32 OR CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 16.3))
diff --git a/Modules/CompilerId/VS-10.csproj.in b/Modules/CompilerId/VS-10.csproj.in
index ed5e847..fba74ff 100644
--- a/Modules/CompilerId/VS-10.csproj.in
+++ b/Modules/CompilerId/VS-10.csproj.in
@@ -6,6 +6,9 @@
     <Keyword>Win32Proj</Keyword>
     @id_system@
     @id_system_version@
+    @id_TargetFrameworkVersion@
+    @id_TargetFrameworkIdentifier@
+    @id_TargetFrameworkTargetsVersion@
     @id_WindowsTargetPlatformVersion@
     @id_WindowsSDKDesktopARMSupport@
   </PropertyGroup>
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 9e25bee..4004ea4 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -224,14 +224,14 @@
       ``TLS_CAINFO <file>``
         Specify a custom certificate authority file to use if ``TLS_VERIFY``
         is enabled. If this option is not specified, the value of the
-        ``CMAKE_TLS_CAINFO`` variable will be used instead (see
+        :variable:`CMAKE_TLS_CAINFO` variable will be used instead (see
         :command:`file(DOWNLOAD)`)
 
       ``NETRC <level>``
         .. versionadded:: 3.11
 
         Specify whether the ``.netrc`` file is to be used for operation.
-        If this option is not specified, the value of the ``CMAKE_NETRC``
+        If this option is not specified, the value of the :variable:`CMAKE_NETRC`
         variable will be used instead (see :command:`file(DOWNLOAD)`)
         Valid levels are:
 
@@ -251,8 +251,8 @@
 
         Specify an alternative ``.netrc`` file to the one in your home directory
         if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
-        is not specified, the value of the ``CMAKE_NETRC_FILE`` variable will
-        be used instead (see :command:`file(DOWNLOAD)`)
+        is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable
+        will be used instead (see :command:`file(DOWNLOAD)`)
 
       .. versionadded:: 3.1
         Added support for `tbz2`, `.tar.xz`, `.txz`, and `.7z` extensions.
@@ -637,6 +637,22 @@
       supported). Passing an empty string as the ``<cmd>`` makes the install
       step do nothing.
 
+    .. note::
+      If the :envvar:`CMAKE_INSTALL_MODE` environment variable is set when the
+      main project is built, it will only have an effect if the following
+      conditions are met:
+
+      * The main project's configure step assumed the external project uses
+        CMake as its build system.
+      * The external project's install command actually runs. Note that due
+        to the way ``ExternalProject`` may use timestamps internally, if
+        nothing the install step depends on needs to be re-executed, the
+        install command might also not need to run.
+
+      Note also that ``ExternalProject`` does not check whether the
+      :envvar:`CMAKE_INSTALL_MODE` environment variable changes from one run
+      to another.
+
   **Test Step Options:**
     The test step is only defined if at least one of the following ``TEST_...``
     options are provided.
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index bd82a90..be75689 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -21,13 +21,14 @@
 configure step to use the content in commands like :command:`add_subdirectory`,
 :command:`include` or :command:`file` operations.
 
-Content population details would normally be defined separately from the
-command that performs the actual population.  This separation ensures that
-all of the dependency details are defined before anything may try to use those
-details to populate content.  This is particularly important in more complex
-project hierarchies where dependencies may be shared between multiple projects.
+Content population details should be defined separately from the command that
+performs the actual population.  This separation ensures that all the
+dependency details are defined before anything might try to use them to
+populate content.  This is particularly important in more complex project
+hierarchies where dependencies may be shared between multiple projects.
 
-The following shows a typical example of declaring content details:
+The following shows a typical example of declaring content details for some
+dependencies and then ensuring they are populated with a separate call:
 
 .. code-block:: cmake
 
@@ -36,57 +37,67 @@
     GIT_REPOSITORY https://github.com/google/googletest.git
     GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
   )
+  FetchContent_Declare(
+    myCompanyIcons
+    URL      https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
+    URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
+  )
 
-For most typical cases, populating the content can then be done with a single
-command like so:
+  FetchContent_MakeAvailable(googletest secret_sauce)
 
-.. code-block:: cmake
+The :command:`FetchContent_MakeAvailable` command ensures the named
+dependencies have been populated, either by an earlier call or by populating
+them itself.  When performing the population, it will also add them to the
+main build, if possible, so that the main build can use the populated
+projects' targets, etc.  See the command's documentation for how these steps
+are performed.
 
-  FetchContent_MakeAvailable(googletest)
-
-The above command not only populates the content, it also adds it to the main
-build (if possible) so that the main build can use the populated project's
-targets, etc.  In some cases, the main project may need to have more precise
-control over the population or may be required to explicitly define the
-population steps (e.g. if CMake versions earlier than 3.14 need to be
-supported).  The typical pattern of such custom steps looks like this:
-
-.. code-block:: cmake
-
-  FetchContent_GetProperties(googletest)
-  if(NOT googletest_POPULATED)
-    FetchContent_Populate(googletest)
-    add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
-  endif()
-
-Regardless of which population method is used, when using the
-declare-populate pattern with a hierarchical project arrangement, projects at
-higher levels in the hierarchy are able to override the population details of
-content specified anywhere lower in the project hierarchy.  The ability to
-detect whether content has already been populated ensures that even if
-multiple child projects want certain content to be available, the first one
-to populate it wins.  The other child project can simply make use of the
-already available content instead of repeating the population for itself.
-See the :ref:`Examples <fetch-content-examples>` section which demonstrates
+When using a hierarchical project arrangement, projects at higher levels in
+the hierarchy are able to override the declared details of content specified
+anywhere lower in the project hierarchy.  The first details to be declared
+for a given dependency take precedence, regardless of where in the project
+hierarchy that occurs.  Similarly, the first call that tries to populate a
+dependency "wins", with subsequent populations reusing the result of the
+first instead of repeating the population again.
+See the :ref:`Examples <fetch-content-examples>` which demonstrate
 this scenario.
 
+In some cases, the main project may need to have more precise control over
+the population, or it may be required to explicitly define the population
+steps in a way that cannot be captured by the declared details alone.
+For such situations, the lower level :command:`FetchContent_GetProperties` and
+:command:`FetchContent_Populate` commands can be used.  These lack the richer
+features provided by :command:`FetchContent_MakeAvailable` though, so their
+direct use should be considered a last resort.  The typical pattern of such
+custom steps looks like this:
+
+.. code-block:: cmake
+
+  # NOTE: Where possible, prefer to use FetchContent_MakeAvailable()
+  #       instead of custom logic like this
+
+  # Check if population has already been performed
+  FetchContent_GetProperties(depname)
+  if(NOT depname_POPULATED)
+    # Fetch the content using previously declared details
+    FetchContent_Populate(depname)
+
+    # Set custom variables, policies, etc.
+    # ...
+
+    # Bring the populated content into the build
+    add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
+  endif()
+
 The ``FetchContent`` module also supports defining and populating
 content in a single call, with no check for whether the content has been
-populated elsewhere in the project already.  This is a more low level
-operation and would not normally be the way the module is used, but it is
-sometimes useful as part of implementing some higher level feature or to
-populate some content in CMake's script mode.
-
-.. versionchanged:: 3.14
-  ``FetchContent`` commands can access the terminal. This is necessary
-  for password prompts and real-time progress displays to work.
+populated elsewhere already.  This should not be done in projects, but may
+be appropriate for populating content in CMake's script mode.
+See :command:`FetchContent_Populate` for details.
 
 Commands
 ^^^^^^^^
 
-Declaring Content Details
-"""""""""""""""""""""""""
-
 .. command:: FetchContent_Declare
 
   .. code-block:: cmake
@@ -94,7 +105,7 @@
     FetchContent_Declare(<name> <contentOptions>...)
 
   The ``FetchContent_Declare()`` function records the options that describe
-  how to populate the specified content, but if such details have already
+  how to populate the specified content.  If such details have already
   been recorded earlier in this project (regardless of where in the project
   hierarchy), this and all later calls for the same content ``<name>`` are
   ignored.  This "first to record, wins" approach is what allows hierarchical
@@ -110,7 +121,7 @@
   projects needing that same content will use the same name, leading to
   the content being populated multiple times.
 
-  The ``<contentOptions>`` can be any of the download or update/patch options
+  The ``<contentOptions>`` can be any of the download, update or patch options
   that the :command:`ExternalProject_Add` command understands.  The configure,
   build, install and test steps are explicitly disabled and therefore options
   related to them will be ignored.  The ``SOURCE_SUBDIR`` option is an
@@ -146,47 +157,95 @@
   than a branch or tag name.  A commit hash is more secure and helps to
   confirm that the downloaded contents are what you expected.
 
-Populating The Content
-""""""""""""""""""""""
+  .. versionchanged:: 3.14
+    Commands for the download, update or patch steps can access the terminal.
+    This may be needed for things like password prompts or real-time display
+    of command progress.
 
-For most common scenarios, population means making content available to the
-main build according to previously declared details for that dependency.
-There are two main patterns for populating content, one based on calling
-:command:`FetchContent_GetProperties` and
-:command:`FetchContent_Populate` for more precise control and the other on
-calling :command:`FetchContent_MakeAvailable` for a simpler, more automated
-approach.  The former generally follows this canonical pattern:
+  .. versionadded:: 3.22
+    The :variable:`CMAKE_TLS_VERIFY`, :variable:`CMAKE_TLS_CAINFO`,
+    :variable:`CMAKE_NETRC` and :variable:`CMAKE_NETRC_FILE` variables now
+    provide the defaults for their corresponding content options, just like
+    they do for :command:`ExternalProject_Add`. Previously, these variables
+    were ignored by the ``FetchContent`` module.
 
-.. _`fetch-content-canonical-pattern`:
+.. command:: FetchContent_MakeAvailable
 
-.. code-block:: cmake
-
-  # Check if population has already been performed
-  FetchContent_GetProperties(<name>)
-  string(TOLOWER "<name>" lcName)
-  if(NOT ${lcName}_POPULATED)
-    # Fetch the content using previously declared details
-    FetchContent_Populate(<name>)
-
-    # Set custom variables, policies, etc.
-    # ...
-
-    # Bring the populated content into the build
-    add_subdirectory(${${lcName}_SOURCE_DIR} ${${lcName}_BINARY_DIR})
-  endif()
-
-The above is such a common pattern that, where no custom steps are needed
-between the calls to :command:`FetchContent_Populate` and
-:command:`add_subdirectory`, equivalent logic can be obtained by calling
-:command:`FetchContent_MakeAvailable` instead.  Where it meets the needs of
-the project, :command:`FetchContent_MakeAvailable` should be preferred, as it
-is simpler and provides additional features over the pattern above.
-
-.. command:: FetchContent_Populate
+  .. versionadded:: 3.14
 
   .. code-block:: cmake
 
-    FetchContent_Populate( <name> )
+    FetchContent_MakeAvailable(<name1> [<name2>...])
+
+  This command ensures that each of the named dependencies are populated and
+  potentially added to the build by the time it returns.  It iterates over
+  the list, and for each dependency, the following logic is applied:
+
+  * If the dependency has already been populated earlier in this run, set
+    the ``<lowercaseName>_POPULATED``, ``<lowercaseName>_SOURCE_DIR`` and
+    ``<lowercaseName>_BINARY_DIR`` variables in the same way as a call to
+    :command:`FetchContent_GetProperties`, then skip the remaining steps
+    below and move on to the next dependency in the list.
+
+  * Call :command:`FetchContent_Populate` to populate the dependency using
+    the details recorded by an earlier call to :command:`FetchContent_Declare`.
+    Halt with a fatal error if no such details have been recorded.
+    :variable:`FETCHCONTENT_SOURCE_DIR_<uppercaseName>` can be used to override
+    the declared details and use content provided at the specified location
+    instead.
+
+  * If the top directory of the populated content contains a ``CMakeLists.txt``
+    file, call :command:`add_subdirectory` to add it to the main build.
+    It is not an error for there to be no ``CMakeLists.txt`` file, which
+    allows the command to be used for dependencies that make downloaded
+    content available at a known location, but which do not need or support
+    being added directly to the build.
+
+    .. versionadded:: 3.18
+      The ``SOURCE_SUBDIR`` option can be given in the declared details to
+      look somewhere below the top directory instead (i.e. the same way that
+      ``SOURCE_SUBDIR`` is used by the :command:`ExternalProject_Add`
+      command).  The path provided with ``SOURCE_SUBDIR`` must be relative
+      and will be treated as relative to the top directory.  It can also
+      point to a directory that does not contain a ``CMakeLists.txt`` file
+      or even to a directory that doesn't exist.  This can be used to avoid
+      adding a project that contains a ``CMakeLists.txt`` file in its top
+      directory.
+
+  Projects should aim to declare the details of all dependencies they might
+  use before they call ``FetchContent_MakeAvailable()`` for any of them.
+  This ensures that if any of the dependencies are also sub-dependencies of
+  one or more of the others, the main project still controls the details
+  that will be used (because it will declare them first before the
+  dependencies get a chance to).  In the following code samples, assume that
+  the ``uses_other`` dependency also uses ``FetchContent`` to add the ``other``
+  dependency internally:
+
+  .. code-block:: cmake
+
+    # WRONG: Should declare all details first
+    FetchContent_Declare(uses_other ...)
+    FetchContent_MakeAvailable(uses_other)
+
+    FetchContent_Declare(other ...)    # Will be ignored, uses_other beat us to it
+    FetchContent_MakeAvailable(other)  # Would use details declared by uses_other
+
+  .. code-block:: cmake
+
+    # CORRECT: All details declared first, so they will take priority
+    FetchContent_Declare(uses_other ...)
+    FetchContent_Declare(other ...)
+    FetchContent_MakeAvailable(uses_other other)
+
+.. command:: FetchContent_Populate
+
+  .. note::
+    Where possible, prefer to use :command:`FetchContent_MakeAvailable`
+    instead of implementing population manually with this command.
+
+  .. code-block:: cmake
+
+    FetchContent_Populate(<name>)
 
   In most cases, the only argument given to ``FetchContent_Populate()`` is the
   ``<name>``.  When used this way, the command assumes the content details have
@@ -211,88 +270,29 @@
   ``FetchContent_Populate()``.
 
   ``FetchContent_Populate()`` will set three variables in the scope of the
-  caller; ``<lcName>_POPULATED``, ``<lcName>_SOURCE_DIR`` and
-  ``<lcName>_BINARY_DIR``, where ``<lcName>`` is the lowercased ``<name>``.
-  ``<lcName>_POPULATED`` will always be set to ``True`` by the call.
-  ``<lcName>_SOURCE_DIR`` is the location where the
-  content can be found upon return (it will have already been populated), while
-  ``<lcName>_BINARY_DIR`` is a directory intended for use as a corresponding
-  build directory.  The main use case for the two directory variables is to
-  call :command:`add_subdirectory` immediately after population, i.e.:
+  caller:
+
+  ``<lowercaseName>_POPULATED``
+    This will always be set to ``TRUE`` by the call.
+
+  ``<lowercaseName>_SOURCE_DIR``
+    The location where the populated content can be found upon return.
+
+  ``<lowercaseName>_BINARY_DIR``
+    A directory intended for use as a corresponding build directory.
+
+  The main use case for the ``<lowercaseName>_SOURCE_DIR`` and
+  ``<lowercaseName>_BINARY_DIR`` variables is to call
+  :command:`add_subdirectory` immediately after population:
 
   .. code-block:: cmake
 
-    FetchContent_Populate(FooBar ...)
+    FetchContent_Populate(FooBar)
     add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR})
 
   The values of the three variables can also be retrieved from anywhere in the
   project hierarchy using the :command:`FetchContent_GetProperties` command.
 
-  A number of cache variables influence the behavior of all content population
-  performed using details saved from a :command:`FetchContent_Declare` call:
-
-  ``FETCHCONTENT_BASE_DIR``
-    In most cases, the saved details do not specify any options relating to the
-    directories to use for the internal sub-build, final source and build areas.
-    It is generally best to leave these decisions up to the ``FetchContent``
-    module to handle on the project's behalf.  The ``FETCHCONTENT_BASE_DIR``
-    cache variable controls the point under which all content population
-    directories are collected, but in most cases developers would not need to
-    change this.  The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if
-    developers change this value, they should aim to keep the path short and
-    just below the top level of the build tree to avoid running into path
-    length problems on Windows.
-
-  ``FETCHCONTENT_QUIET``
-    The logging output during population can be quite verbose, making the
-    configure stage quite noisy.  This cache option (``ON`` by default) hides
-    all population output unless an error is encountered.  If experiencing
-    problems with hung downloads, temporarily switching this option off may
-    help diagnose which content population is causing the issue.
-
-  ``FETCHCONTENT_FULLY_DISCONNECTED``
-    When this option is enabled, no attempt is made to download or update
-    any content.  It is assumed that all content has already been populated in
-    a previous run or the source directories have been pointed at existing
-    contents the developer has provided manually (using options described
-    further below).  When the developer knows that no changes have been made to
-    any content details, turning this option ``ON`` can significantly speed up
-    the configure stage.  It is ``OFF`` by default.
-
-  ``FETCHCONTENT_UPDATES_DISCONNECTED``
-    This is a less severe download/update control compared to
-    ``FETCHCONTENT_FULLY_DISCONNECTED``.  Instead of bypassing all download and
-    update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the
-    update stage.  Therefore, if content has not been downloaded previously,
-    it will still be downloaded when this option is enabled.  This can speed up
-    the configure stage, but not as much as
-    ``FETCHCONTENT_FULLY_DISCONNECTED``.  It is ``OFF`` by default.
-
-  In addition to the above cache variables, the following cache variables are
-  also defined for each content name (``<ucName>`` is the uppercased value of
-  ``<name>``):
-
-  ``FETCHCONTENT_SOURCE_DIR_<ucName>``
-    If this is set, no download or update steps are performed for the specified
-    content and the ``<lcName>_SOURCE_DIR`` variable returned to the caller is
-    pointed at this location.  This gives developers a way to have a separate
-    checkout of the content that they can modify freely without interference
-    from the build.  The build simply uses that existing source, but it still
-    defines ``<lcName>_BINARY_DIR`` to point inside its own build area.
-    Developers are strongly encouraged to use this mechanism rather than
-    editing the sources populated in the default location, as changes to
-    sources in the default location can be lost when content population details
-    are changed by the project.
-
-  ``FETCHCONTENT_UPDATES_DISCONNECTED_<ucName>``
-    This is the per-content equivalent of
-    ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option
-    is ``ON``, then updates will be disabled for the named content.
-    Disabling updates for individual content can be useful for content whose
-    details rarely change, while still leaving other frequently changing
-    content with updates enabled.
-
-
   The ``FetchContent_Populate()`` command also supports a syntax allowing the
   content details to be specified directly rather than using any saved
   details.  This is more low-level and use of this form is generally to be
@@ -303,7 +303,8 @@
 
   .. code-block:: cmake
 
-    FetchContent_Populate( <name>
+    FetchContent_Populate(
+      <name>
       [QUIET]
       [SUBBUILD_DIR <subBuildDir>]
       [SOURCE_DIR <srcDir>]
@@ -325,16 +326,17 @@
   - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and
     ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored.
 
-  The ``<lcName>_SOURCE_DIR`` and ``<lcName>_BINARY_DIR`` variables are still
-  returned to the caller, but since these locations are not stored as global
-  properties when this form is used, they are only available to the calling
-  scope and below rather than the entire project hierarchy.  No
-  ``<lcName>_POPULATED`` variable is set in the caller's scope with this form.
+  The ``<lowercaseName>_SOURCE_DIR`` and ``<lowercaseName>_BINARY_DIR``
+  variables are still returned to the caller, but since these locations are
+  not stored as global properties when this form is used, they are only
+  available to the calling scope and below rather than the entire project
+  hierarchy.  No ``<lowercaseName>_POPULATED`` variable is set in the caller's
+  scope with this form.
 
   The supported options for ``FetchContent_Populate()`` are the same as those
   for :command:`FetchContent_Declare()`.  Those few options shown just
   above are either specific to ``FetchContent_Populate()`` or their behavior is
-  slightly modified from how :command:`ExternalProject_Add` treats them.
+  slightly modified from how :command:`ExternalProject_Add` treats them:
 
   ``QUIET``
     The ``QUIET`` option can be given to hide the output associated with
@@ -347,9 +349,9 @@
   ``SUBBUILD_DIR``
     The ``SUBBUILD_DIR`` argument can be provided to change the location of the
     sub-build created to perform the population.  The default value is
-    ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-subbuild`` and it would be unusual
-    to need to override this default.  If a relative path is specified, it will
-    be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`.
+    ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-subbuild`` and it would be
+    unusual to need to override this default.  If a relative path is specified,
+    it will be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`.
     This option should not be confused with the ``SOURCE_SUBDIR`` option which
     only affects the :command:`FetchContent_MakeAvailable` command.
 
@@ -357,9 +359,9 @@
     The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by
     :command:`ExternalProject_Add`, but different default values are used by
     ``FetchContent_Populate()``.  ``SOURCE_DIR`` defaults to
-    ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-src`` and ``BINARY_DIR`` defaults to
-    ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-build``.  If a relative path is
-    specified, it will be interpreted as relative to
+    ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-src`` and ``BINARY_DIR``
+    defaults to ``${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-build``.
+    If a relative path is specified, it will be interpreted as relative to
     :variable:`CMAKE_CURRENT_BINARY_DIR`.
 
   In addition to the above explicit options, any other unrecognized options are
@@ -380,11 +382,12 @@
   on the command line invoking the script.
 
   .. versionadded:: 3.18
-    Added support for ``DOWNLOAD_NO_EXTRACT`` and ``SOURCE_SUBDIR`` options.
+    Added support for the ``DOWNLOAD_NO_EXTRACT`` option.
 
 .. command:: FetchContent_GetProperties
 
-  When using saved content details, a call to :command:`FetchContent_Populate`
+  When using saved content details, a call to
+  :command:`FetchContent_MakeAvailable` or :command:`FetchContent_Populate`
   records information in global properties which can be queried at any time.
   This information includes the source and binary directories associated with
   the content and also whether or not the content population has been processed
@@ -392,7 +395,8 @@
 
   .. code-block:: cmake
 
-    FetchContent_GetProperties( <name>
+    FetchContent_GetProperties(
+      <name>
       [SOURCE_DIR <srcDirVar>]
       [BINARY_DIR <binDirVar>]
       [POPULATED <doneVar>]
@@ -403,49 +407,104 @@
   which is the name of the variable in which to store that property.  Most of
   the time though, only ``<name>`` is given, in which case the call will then
   set the same variables as a call to
-  :command:`FetchContent_Populate(name) <FetchContent_Populate>`.  This allows
-  the following canonical pattern to be used, which ensures that the relevant
-  variables will always be defined regardless of whether or not the population
-  has been performed elsewhere in the project already:
+  :command:`FetchContent_MakeAvailable(name) <FetchContent_MakeAvailable>` or
+  :command:`FetchContent_Populate(name) <FetchContent_Populate>`.
+
+  This command is rarely needed when using
+  :command:`FetchContent_MakeAvailable`.  It is more commonly used as part of
+  implementing the following pattern with :command:`FetchContent_Populate`,
+  which ensures that the relevant variables will always be defined regardless
+  of whether or not the population has been performed elsewhere in the project
+  already:
 
   .. code-block:: cmake
 
-    FetchContent_GetProperties(foobar)
-    if(NOT foobar_POPULATED)
-      FetchContent_Populate(foobar)
-      ...
+    # Check if population has already been performed
+    FetchContent_GetProperties(depname)
+    if(NOT depname_POPULATED)
+      # Fetch the content using previously declared details
+      FetchContent_Populate(depname)
+
+      # Set custom variables, policies, etc.
+      # ...
+
+      # Bring the populated content into the build
+      add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
     endif()
 
-  The above pattern allows other parts of the overall project hierarchy to
-  re-use the same content and ensure that it is only populated once.
+Variables
+^^^^^^^^^
 
+A number of cache variables can influence the behavior where details from a
+:command:`FetchContent_Declare` call are used to populate content.
+The variables are all intended for the developer to customize behavior and
+should not normally be set by the project.
 
-.. command:: FetchContent_MakeAvailable
+.. variable:: FETCHCONTENT_BASE_DIR
 
-  .. code-block:: cmake
+  In most cases, the saved details do not specify any options relating to the
+  directories to use for the internal sub-build, final source and build areas.
+  It is generally best to leave these decisions up to the ``FetchContent``
+  module to handle on the project's behalf.  The ``FETCHCONTENT_BASE_DIR``
+  cache variable controls the point under which all content population
+  directories are collected, but in most cases, developers would not need to
+  change this.  The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if
+  developers change this value, they should aim to keep the path short and
+  just below the top level of the build tree to avoid running into path
+  length problems on Windows.
 
-    FetchContent_MakeAvailable( <name1> [<name2>...] )
+.. variable:: FETCHCONTENT_QUIET
 
-  .. versionadded:: 3.14
+  The logging output during population can be quite verbose, making the
+  configure stage quite noisy.  This cache option (``ON`` by default) hides
+  all population output unless an error is encountered.  If experiencing
+  problems with hung downloads, temporarily switching this option off may
+  help diagnose which content population is causing the issue.
 
-  This command implements the common pattern typically needed for most
-  dependencies.  It iterates over each of the named dependencies in turn
-  and for each one it loosely follows the
-  :ref:`canonical pattern <fetch-content-canonical-pattern>` as
-  presented at the beginning of this section.  An important difference is
-  that :command:`add_subdirectory` will only be called on the
-  populated content if there is a ``CMakeLists.txt`` file in its top level
-  source directory.  This allows the command to be used for dependencies
-  that make downloaded content available at a known location but which do
-  not need or support being added directly to the build.
+.. variable:: FETCHCONTENT_FULLY_DISCONNECTED
 
-  The ``SOURCE_SUBDIR`` option can be given in the declared details to
-  instruct ``FetchContent_MakeAvailable()`` to look for a ``CMakeLists.txt``
-  file in a subdirectory below the top level (i.e. the same way that
-  ``SOURCE_SUBDIR`` is used by the :command:`ExternalProject_Add` command).
-  ``SOURCE_SUBDIR`` must always be a relative path.  See the next section
-  for an example of this option.
+  When this option is enabled, no attempt is made to download or update
+  any content.  It is assumed that all content has already been populated in
+  a previous run or the source directories have been pointed at existing
+  contents the developer has provided manually (using options described
+  further below).  When the developer knows that no changes have been made to
+  any content details, turning this option ``ON`` can significantly speed up
+  the configure stage.  It is ``OFF`` by default.
 
+.. variable:: FETCHCONTENT_UPDATES_DISCONNECTED
+
+  This is a less severe download/update control compared to
+  :variable:`FETCHCONTENT_FULLY_DISCONNECTED`.  Instead of bypassing all
+  download and update logic, ``FETCHCONTENT_UPDATES_DISCONNECTED`` only
+  disables the update stage.  Therefore, if content has not been downloaded
+  previously, it will still be downloaded when this option is enabled.
+  This can speed up the configure stage, but not as much as
+  :variable:`FETCHCONTENT_FULLY_DISCONNECTED`.  It is ``OFF`` by default.
+
+In addition to the above cache variables, the following cache variables are
+also defined for each content name:
+
+.. variable:: FETCHCONTENT_SOURCE_DIR_<uppercaseName>
+
+  If this is set, no download or update steps are performed for the specified
+  content and the ``<lowercaseName>_SOURCE_DIR`` variable returned to the
+  caller is pointed at this location.  This gives developers a way to have a
+  separate checkout of the content that they can modify freely without
+  interference from the build.  The build simply uses that existing source,
+  but it still defines ``<lowercaseName>_BINARY_DIR`` to point inside its own
+  build area.  Developers are strongly encouraged to use this mechanism rather
+  than editing the sources populated in the default location, as changes to
+  sources in the default location can be lost when content population details
+  are changed by the project.
+
+.. variable:: FETCHCONTENT_UPDATES_DISCONNECTED_<uppercaseName>
+
+  This is the per-content equivalent of
+  :variable:`FETCHCONTENT_UPDATES_DISCONNECTED`.  If the global option or
+  this option is ``ON``, then updates will be disabled for the named content.
+  Disabling updates for individual content can be useful for content whose
+  details rarely change, while still leaving other frequently changing content
+  with updates enabled.
 
 .. _`fetch-content-examples`:
 
@@ -470,7 +529,7 @@
   )
 
   # After the following call, the CMake targets defined by googletest and
-  # Catch2 will be defined and available to the rest of the build
+  # Catch2 will be available to the rest of the build
   FetchContent_MakeAvailable(googletest Catch2)
 
 If the sub-project's ``CMakeLists.txt`` file is not at the top level of its
@@ -964,18 +1023,29 @@
     unset(subCMakeOpts)
   endif()
 
-  if(DEFINED CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY)
-    list(APPEND subCMakeOpts
-      "-DCMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY=${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}")
-  endif()
+  set(__FETCHCONTENT_CACHED_INFO "")
+  set(__passthrough_vars
+    CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY
+    CMAKE_TLS_VERIFY
+    CMAKE_TLS_CAINFO
+    CMAKE_NETRC
+    CMAKE_NETRC_FILE
+  )
+  foreach(var IN LISTS __passthrough_vars)
+    if(DEFINED ${var})
+      # Embed directly in the generated CMakeLists.txt file to avoid making
+      # the cmake command line excessively long. It also makes debugging and
+      # testing easier.
+      string(APPEND __FETCHCONTENT_CACHED_INFO "set(${var} [==[${${var}}]==])\n")
+    endif()
+  endforeach()
 
   # Avoid using if(... IN_LIST ...) so we don't have to alter policy settings
-  set(__FETCHCONTENT_CACHED_INFO "")
   list(FIND ARG_UNPARSED_ARGUMENTS GIT_REPOSITORY indexResult)
   if(indexResult GREATER_EQUAL 0)
     find_package(Git QUIET)
-    set(__FETCHCONTENT_CACHED_INFO
-"# Pass through things we've already detected in the main project to avoid
+    string(APPEND __FETCHCONTENT_CACHED_INFO "
+# Pass through things we've already detected in the main project to avoid
 # paying the cost of redetecting them again in ExternalProject_Add()
 set(GIT_EXECUTABLE [==[${GIT_EXECUTABLE}]==])
 set(GIT_VERSION_STRING [==[${GIT_VERSION_STRING}]==])
diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake
index 726ff75..308138f 100644
--- a/Modules/FindBLAS.cmake
+++ b/Modules/FindBLAS.cmake
@@ -35,6 +35,19 @@
   if set ``pkg-config`` will be used to search for a BLAS library first
   and if one is found that is preferred
 
+``BLA_SIZEOF_INTEGER``
+  .. versionadded:: 3.22
+
+  Specify the BLAS/LAPACK library integer size:
+
+  ``4``
+    Search for a BLAS/LAPACK with 32-bit integer interfaces.
+  ``8``
+    Search for a BLAS/LAPACK with 64-bit integer interfaces.
+  ``ANY``
+    Search for any BLAS/LAPACK.
+    Most likely, a BLAS/LAPACK with 32-bit integer interfaces will be found.
+
 Imported targets
 ^^^^^^^^^^^^^^^^
 
@@ -101,15 +114,16 @@
 ``FlexiBLAS``
   .. versionadded:: 3.19
 
-``Fujitsu_SSL2``, ``Fujitsu_SSL2BLAMP``
+``Fujitsu_SSL2``, ``Fujitsu_SSL2BLAMP``, ``Fujitsu_SSL2SVE``, ``Fujitsu_SSL2BLAMPSVE``
   .. versionadded:: 3.20
 
-  Fujitsu SSL2 serial and parallel blas/lapack
+  Fujitsu SSL2 serial and parallel blas/lapack with SVE instructions
 
 ``Goto``
   GotoBLAS
 
-``IBMESSL``
+``IBMESSL``, ``IBMESSL_SMP``
+
   IBM Engineering and Scientific Subroutine Library
 
 ``Intel``
@@ -150,7 +164,7 @@
 ``PhiPACK``
   Portable High Performance ANSI C (PHiPAC)
 
-``SCSL``
+``SCSL``, ``SCSL_mp``
   Scientific Computing Software Library
 
 ``SGIMATH``
@@ -312,9 +326,9 @@
       list(APPEND _libraries "${_library}")
     else()
       string(REGEX REPLACE "[^A-Za-z0-9]" "_" _lib_var "${_library}")
-      set(_combined_name ${_combined_name}_${_lib_var})
+      string(APPEND _combined_name "_${_lib_var}")
       if(NOT "${_deps}" STREQUAL "")
-        set(_combined_name ${_combined_name}_deps)
+        string(APPEND _combined_name "_deps")
       endif()
       if(_libraries_work)
         find_library(${_prefix}_${_lib_var}_LIBRARY
@@ -332,7 +346,7 @@
 
   foreach(_flag ${_flags})
     string(REGEX REPLACE "[^A-Za-z0-9]" "_" _flag_var "${_flag}")
-    set(_combined_name ${_combined_name}_${_flag_var})
+    string(APPEND _combined_name "_${_flag_var}")
   endforeach()
   if(_libraries_work)
     # Test this combination of libraries.
@@ -371,6 +385,17 @@
   endif()
 endif()
 
+if(NOT BLA_SIZEOF_INTEGER)
+  # in the reality we do not know which API of BLAS/LAPACK is masked in library
+  set(_blas_sizeof_integer "ANY")
+elseif((BLA_SIZEOF_INTEGER STREQUAL "ANY") OR
+       (BLA_SIZEOF_INTEGER STREQUAL "4") OR
+       (BLA_SIZEOF_INTEGER STREQUAL "8"))
+  set(_blas_sizeof_integer ${BLA_SIZEOF_INTEGER})
+else()
+  message(FATAL_ERROR "BLA_SIZEOF_INTEGER can have only <no value>, ANY, 4, or 8 values")
+endif()
+
 # Implicitly linked BLAS libraries?
 if(BLA_VENDOR STREQUAL "All")
   if(NOT BLAS_LIBRARIES)
@@ -386,6 +411,8 @@
       )
   endif()
   if(BLAS_WORKS)
+    # Give a more helpful "found" message
+    set(BLAS_WORKS "implicitly linked")
     set(_blas_fphsa_req_var BLAS_WORKS)
   endif()
 endif()
@@ -429,17 +456,23 @@
         find_package(Threads REQUIRED)
       endif()
 
-      if(BLA_VENDOR MATCHES "_64ilp")
+      if(_blas_sizeof_integer EQUAL 8)
         set(BLAS_mkl_ILP_MODE "ilp64")
-      else()
+      elseif(_blas_sizeof_integer EQUAL 4)
         set(BLAS_mkl_ILP_MODE "lp64")
+      else()
+        if(BLA_VENDOR MATCHES "_64ilp")
+          set(BLAS_mkl_ILP_MODE "ilp64")
+        else()
+          set(BLAS_mkl_ILP_MODE "lp64")
+        endif()
       endif()
 
       set(BLAS_SEARCH_LIBS "")
 
       if(BLA_F95)
         set(BLAS_mkl_SEARCH_SYMBOL "sgemm_f95")
-        set(_LIBRARIES BLAS95_LIBRARIES)
+        set(_BLAS_LIBRARIES BLAS95_LIBRARIES)
         if(WIN32)
           # Find the main file (32-bit or 64-bit)
           set(BLAS_SEARCH_LIBS_WIN_MAIN "")
@@ -501,7 +534,7 @@
         endif()
       else()
         set(BLAS_mkl_SEARCH_SYMBOL sgemm)
-        set(_LIBRARIES BLAS_LIBRARIES)
+        set(_BLAS_LIBRARIES BLAS_LIBRARIES)
         if(WIN32)
           # Find the main file (32-bit or 64-bit)
           set(BLAS_SEARCH_LIBS_WIN_MAIN "")
@@ -613,15 +646,15 @@
           "lib/${BLAS_mkl_ARCH_NAME}"
           )
 
-      foreach(IT ${BLAS_SEARCH_LIBS})
-        string(REPLACE " " ";" SEARCH_LIBS ${IT})
-        if(NOT ${_LIBRARIES})
+      foreach(_search ${BLAS_SEARCH_LIBS})
+        string(REPLACE " " ";" _search ${_search})
+        if(NOT ${_BLAS_LIBRARIES})
           check_blas_libraries(
-            ${_LIBRARIES}
+            ${_BLAS_LIBRARIES}
             BLAS
             ${BLAS_mkl_SEARCH_SYMBOL}
             ""
-            "${SEARCH_LIBS}"
+            "${_search}"
             "${CMAKE_THREAD_LIBS_INIT};${BLAS_mkl_LM};${BLAS_mkl_LDL}"
             "${BLAS_mkl_MKLROOT}"
             "${BLAS_mkl_LIB_PATH_SUFFIXES}"
@@ -629,6 +662,7 @@
         endif()
       endforeach()
 
+      unset(_search)
       unset(BLAS_mkl_ILP_MODE)
       unset(BLAS_mkl_INTFACE)
       unset(BLAS_mkl_THREADING)
@@ -671,29 +705,43 @@
 
 # FlexiBLAS? (http://www.mpi-magdeburg.mpg.de/mpcsc/software/FlexiBLAS/)
 if(BLA_VENDOR STREQUAL "FlexiBLAS" OR BLA_VENDOR STREQUAL "All")
+  set(_blas_flexiblas_lib "flexiblas")
+
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_flexiblas_lib "64")
+  endif()
+
   if(NOT BLAS_LIBRARIES)
     check_blas_libraries(
       BLAS_LIBRARIES
       BLAS
       sgemm
       ""
-      "flexiblas"
+      "${_blas_flexiblas_lib}"
       ""
       ""
       ""
       )
   endif()
+
+  unset(_blas_flexiblas_lib)
 endif()
 
 # OpenBLAS? (http://www.openblas.net)
 if(BLA_VENDOR STREQUAL "OpenBLAS" OR BLA_VENDOR STREQUAL "All")
+  set(_blas_openblas_lib "openblas")
+
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_openblas_lib "64")
+  endif()
+
   if(NOT BLAS_LIBRARIES)
     check_blas_libraries(
       BLAS_LIBRARIES
       BLAS
       sgemm
       ""
-      "openblas"
+      "${_blas_openblas_lib}"
       ""
       ""
       ""
@@ -720,59 +768,75 @@
       BLAS
       sgemm
       ""
-      "openblas"
+      "${_blas_openblas_lib}"
       "${_threadlibs}"
       ""
       ""
       )
     unset(_threadlibs)
   endif()
+
+  unset(_blas_openblas_lib)
 endif()
 
 # ArmPL blas library? (https://developer.arm.com/tools-and-software/server-and-hpc/compile/arm-compiler-for-linux/arm-performance-libraries)
 if(BLA_VENDOR MATCHES "Arm" OR BLA_VENDOR STREQUAL "All")
 
    # Check for 64bit Integer support
-   if(BLA_VENDOR MATCHES "_ilp64")
-     set(BLAS_armpl_LIB "armpl_ilp64")
-   else()
-     set(BLAS_armpl_LIB "armpl_lp64")
-   endif()
+  if(_blas_sizeof_integer EQUAL 8)
+    set(_blas_armpl_lib "armpl_ilp64")
+  elseif(_blas_sizeof_integer EQUAL 4)
+    set(_blas_armpl_lib "armpl_lp64")
+  else()
+    if(BLA_VENDOR MATCHES "_ilp64")
+      set(_blas_armpl_lib "armpl_ilp64")
+    else()
+      set(_blas_armpl_lib "armpl_lp64")
+    endif()
+  endif()
 
    # Check for OpenMP support, VIA BLA_VENDOR of Arm_mp or Arm_ipl64_mp
    if(BLA_VENDOR MATCHES "_mp")
-     set(BLAS_armpl_LIB "${BLAS_armpl_LIB}_mp")
+     string(APPEND _blas_armpl_lib "_mp")
    endif()
 
    if(NOT BLAS_LIBRARIES)
-    check_blas_libraries(
-      BLAS_LIBRARIES
-      BLAS
-      sgemm
-      ""
-      "${BLAS_armpl_LIB}"
-      ""
-      ""
-      ""
-      )
+     check_blas_libraries(
+       BLAS_LIBRARIES
+       BLAS
+       sgemm
+       ""
+       "${_blas_armpl_lib}"
+       ""
+       ""
+       ""
+       )
   endif()
-
+  unset(_blas_armpl_lib)
 endif()
 
 # FLAME's blis library? (https://github.com/flame/blis)
 if(BLA_VENDOR STREQUAL "FLAME" OR BLA_VENDOR STREQUAL "All")
+  set(_blas_flame_lib "blis")
+
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_flame_lib "64")
+  endif()
+
   if(NOT BLAS_LIBRARIES)
     check_blas_libraries(
       BLAS_LIBRARIES
       BLAS
       sgemm
       ""
-      "blis"
+      "${_blas_flame_lib}"
       ""
       ""
       ""
       )
   endif()
+
+  unset(_blas_flame_lib)
 endif()
 
 # BLAS in the ATLAS library? (http://math-atlas.sourceforge.net/)
@@ -809,33 +873,45 @@
 
 # BLAS in Alpha CXML library?
 if(BLA_VENDOR STREQUAL "CXML" OR BLA_VENDOR STREQUAL "All")
-  if(NOT BLAS_LIBRARIES)
-    check_blas_libraries(
-      BLAS_LIBRARIES
-      BLAS
-      sgemm
-      ""
-      "cxml"
-      ""
-      ""
-      ""
-      )
+  if(_blas_sizeof_integer EQUAL 8)
+    if(BLA_VENDOR STREQUAL "CXML")
+      message(FATAL_ERROR "CXML does not support Int64 type")
+    endif()
+  else()
+    if(NOT BLAS_LIBRARIES)
+      check_blas_libraries(
+        BLAS_LIBRARIES
+        BLAS
+        sgemm
+        ""
+        "cxml"
+        ""
+        ""
+        ""
+        )
+    endif()
   endif()
 endif()
 
 # BLAS in Alpha DXML library? (now called CXML, see above)
 if(BLA_VENDOR STREQUAL "DXML" OR BLA_VENDOR STREQUAL "All")
-  if(NOT BLAS_LIBRARIES)
-    check_blas_libraries(
-      BLAS_LIBRARIES
-      BLAS
-      sgemm
-      ""
-      "dxml"
-      ""
-      ""
-      ""
-      )
+  if(_blas_sizeof_integer EQUAL 8)
+    if(BLA_VENDOR STREQUAL "DXML")
+      message(FATAL_ERROR "DXML does not support Int64 type")
+    endif()
+  else()
+    if(NOT BLAS_LIBRARIES)
+      check_blas_libraries(
+        BLAS_LIBRARIES
+        BLAS
+        sgemm
+        ""
+        "dxml"
+        ""
+        ""
+        ""
+        )
+    endif()
   endif()
 endif()
 
@@ -859,19 +935,30 @@
 endif()
 
 # BLAS in SCSL library?  (SGI/Cray Scientific Library)
-if(BLA_VENDOR STREQUAL "SCSL" OR BLA_VENDOR STREQUAL "All")
+if(BLA_VENDOR MATCHES "SCSL" OR BLA_VENDOR STREQUAL "All")
+  set(_blas_scsl_lib "scs")
+
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_scsl_lib "_i8")
+  endif()
+  if(BLA_VENDOR MATCHES "_mp")
+    string(APPEND _blas_scsl_lib "_mp")
+  endif()
+
   if(NOT BLAS_LIBRARIES)
     check_blas_libraries(
       BLAS_LIBRARIES
       BLAS
       sgemm
       ""
-      "scsl"
+      "${_blas_scsl_lib}"
       ""
       ""
       ""
       )
   endif()
+
+  unset(_blas_scsl_lib)
 endif()
 
 # BLAS in SGIMATH library?
@@ -890,20 +977,31 @@
   endif()
 endif()
 
-# BLAS in IBM ESSL library? (requires generic BLAS lib, too)
-if(BLA_VENDOR STREQUAL "IBMESSL" OR BLA_VENDOR STREQUAL "All")
+# BLAS in IBM ESSL library?
+if(BLA_VENDOR MATCHES "IBMESSL" OR BLA_VENDOR STREQUAL "All")
+  set(_blas_essl_lib "essl")
+
+  if(BLA_VENDOR MATCHES "_SMP")
+    string(APPEND _blas_essl_lib "smp")
+  endif()
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_essl_lib "6464")
+  endif()
+
   if(NOT BLAS_LIBRARIES)
     check_blas_libraries(
       BLAS_LIBRARIES
       BLAS
       sgemm
       ""
-      "essl;blas"
+      "${_blas_essl_lib}"
       ""
       ""
       ""
       )
   endif()
+
+  unset(_blas_essl_lib)
 endif()
 
 # BLAS in acml library?
@@ -927,7 +1025,7 @@
   list(GET _ACML_GPU_ROOT 0 _ACML_GPU_ROOT)
   if(_ACML_ROOT)
     get_filename_component(_ACML_ROOT ${_ACML_ROOT} PATH)
-    if(SIZEOF_INTEGER EQUAL 8)
+    if(_blas_sizeof_integer EQUAL 8)
       set(_ACML_PATH_SUFFIX "_int64")
     else()
       set(_ACML_PATH_SUFFIX "")
@@ -1054,102 +1152,179 @@
 
 # Apple BLAS library?
 if(BLA_VENDOR STREQUAL "Apple" OR BLA_VENDOR STREQUAL "All")
-  if(NOT BLAS_LIBRARIES)
-    check_blas_libraries(
-      BLAS_LIBRARIES
-      BLAS
-      dgemm
-      ""
-      "Accelerate"
-      ""
-      ""
-      ""
-      )
+  if(_blas_sizeof_integer EQUAL 8)
+    if(BLA_VENDOR STREQUAL "Apple")
+      message(FATAL_ERROR "Accelerate Framework does not support Int64 type")
+    endif()
+  else()
+    if(NOT BLAS_LIBRARIES)
+      check_blas_libraries(
+        BLAS_LIBRARIES
+        BLAS
+        dgemm
+        ""
+        "Accelerate"
+        ""
+        ""
+        ""
+        )
+    endif()
   endif()
 endif()
 
 # Apple NAS (vecLib) library?
 if(BLA_VENDOR STREQUAL "NAS" OR BLA_VENDOR STREQUAL "All")
-  if(NOT BLAS_LIBRARIES)
-    check_blas_libraries(
-      BLAS_LIBRARIES
-      BLAS
-      dgemm
-      ""
-      "vecLib"
-      ""
-      ""
-      ""
-      )
+  if(_blas_sizeof_integer EQUAL 8)
+    if(BLA_VENDOR STREQUAL "NAS")
+      message(FATAL_ERROR "Accelerate Framework does not support Int64 type")
+    endif()
+  else()
+    if(NOT BLAS_LIBRARIES)
+      check_blas_libraries(
+        BLAS_LIBRARIES
+        BLAS
+        dgemm
+        ""
+        "vecLib"
+        ""
+        ""
+        ""
+        )
+    endif()
   endif()
 endif()
 
 # Elbrus Math Library?
 if(BLA_VENDOR MATCHES "EML" OR BLA_VENDOR STREQUAL "All")
 
-   set(BLAS_EML_LIB "eml")
+  set(_blas_eml_lib "eml")
 
-   # Check for OpenMP support, VIA BLA_VENDOR of eml_mt
-   if(BLA_VENDOR MATCHES "_mt")
-     set(BLAS_EML_LIB "${BLAS_EML_LIB}_mt")
-   endif()
-
-   if(NOT BLAS_LIBRARIES)
-    check_blas_libraries(
-      BLAS_LIBRARIES
-      BLAS
-      sgemm
-      ""
-      "${BLAS_EML_LIB}"
-      ""
-      ""
-      ""
-      )
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_eml_lib "_ilp64")
+  endif()
+  # Check for OpenMP support, VIA BLA_VENDOR of eml_mt
+  if(BLA_VENDOR MATCHES "_mt")
+    string(APPEND _blas_eml_lib "_mt")
   endif()
 
-endif()
-
-# Fujitsu SSL2 Library?
-if(NOT BLAS_LIBRARIES
-    AND (BLA_VENDOR MATCHES "Fujitsu_SSL2" OR BLA_VENDOR STREQUAL "All"))
-  if(BLA_VENDOR STREQUAL "Fujitsu_SSL2BLAMP")
-    set(_ssl2_suffix BLAMP)
-  else()
-    set(_ssl2_suffix)
-  endif()
-  check_blas_libraries(
-    BLAS_LIBRARIES
-    BLAS
-    sgemm
-    "-SSL2${_ssl2_suffix}"
-    ""
-    ""
-    ""
-    ""
-    )
-  if(BLAS_LIBRARIES)
-    set(BLAS_LINKER_FLAGS "-SSL2${_ssl2_suffix}")
-    set(_blas_fphsa_req_var BLAS_LINKER_FLAGS)
-  endif()
-  unset(_ssl2_suffix)
-endif()
-
-# Generic BLAS library?
-if(BLA_VENDOR STREQUAL "Generic" OR
-   BLA_VENDOR STREQUAL "NVHPC" OR
-   BLA_VENDOR STREQUAL "All")
   if(NOT BLAS_LIBRARIES)
     check_blas_libraries(
       BLAS_LIBRARIES
       BLAS
       sgemm
       ""
-      "blas"
+      "${_blas_eml_lib}"
       ""
       ""
       ""
       )
   endif()
+  unset(_blas_eml_lib)
+endif()
+
+# Fujitsu SSL2 Library?
+if(NOT BLAS_LIBRARIES
+    AND (BLA_VENDOR MATCHES "^Fujitsu_SSL2" OR BLA_VENDOR STREQUAL "All"))
+  set(_blas_fjlapack_lib "fjlapack")
+  set(_blas_fjlapack_flags "-Kopenmp")
+
+  if(BLA_VENDOR MATCHES "BLAMP")
+    string(APPEND _blas_fjlapack_lib "ex")
+  endif()
+  if(BLA_VENDOR MATCHES "SVE")
+    string(APPEND _blas_fjlapack_lib "sve")
+  endif()
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_fjlapack_lib "_ilp64")
+  endif()
+
+  if(NOT BLAS_LIBRARIES)
+    check_blas_libraries(
+      BLAS_LIBRARIES
+      BLAS
+      sgemm
+      "${_blas_fjlapack_flags}"
+      "${_blas_fjlapack_lib}"
+      ""
+      ""
+      ""
+      )
+    if(BLAS_LIBRARIES)
+      set(BLAS_LINKER_FLAGS ${_blas_fjlapack_flags})
+    endif()
+  endif()
+
+  unset(_blas_fjlapack_flags)
+  unset(_blas_fjlapack_lib)
+endif()
+
+# BLAS in nVidia HPC SDK? (https://developer.nvidia.com/hpc-sdk)
+if(BLA_VENDOR STREQUAL "NVHPC" OR BLA_VENDOR STREQUAL "All")
+  set(_blas_nvhpc_lib "blas")
+
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_nvhpc_lib "_ilp64")
+  elseif(_blas_sizeof_integer EQUAL 4)
+    string(APPEND _blas_nvhpc_lib "_lp64")
+  endif()
+
+  if(NOT BLAS_LIBRARIES)
+    check_blas_libraries(
+      BLAS_LIBRARIES
+      BLAS
+      sgemm
+      ""
+      "${_blas_nvhpc_lib}"
+      ""
+      ""
+      ""
+      )
+  endif()
+
+  # an additional check for NVHPC 2020
+  # which does not have differentiation
+  # between lp64 and ilp64 modes
+  if(NOT BLAS_LIBRARIES AND NOT _blas_sizeof_integer EQUAL 8)
+    set(_blas_nvhpc_lib "blas")
+
+    check_blas_libraries(
+      BLAS_LIBRARIES
+      BLAS
+      sgemm
+      ""
+      "${_blas_nvhpc_lib}"
+      ""
+      ""
+      ""
+      )
+  endif()
+
+  unset(_blas_nvhpc_lib)
+endif()
+
+# Generic BLAS library?
+if(BLA_VENDOR STREQUAL "Generic" OR
+   BLA_VENDOR STREQUAL "All")
+  set(_blas_generic_lib "blas")
+
+  if(_blas_sizeof_integer EQUAL 8)
+    string(APPEND _blas_generic_lib "64")
+  endif()
+
+  if(NOT BLAS_LIBRARIES)
+    check_blas_libraries(
+      BLAS_LIBRARIES
+      BLAS
+      sgemm
+      ""
+      "${_blas_generic_lib}"
+      ""
+      ""
+      ""
+      )
+  endif()
+
+  unset(_blas_generic_lib)
 endif()
 
 # On compilers that implicitly link BLAS (i.e. CrayPrgEnv) we used a
@@ -1163,3 +1338,6 @@
 endif()
 
 _add_blas_target()
+unset(_blas_fphsa_req_var)
+unset(_blas_sizeof_integer)
+unset(_BLAS_LIBRARIES)
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index 38faca2..c3142d6 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -390,7 +390,7 @@
 
 function(_boost_get_existing_target component target_var)
   set(names "${component}")
-  if(component MATCHES "^([a-z_]*)(python|numpy)([1-9])\\.?([0-9])?$")
+  if(component MATCHES "^([a-z_]*)(python|numpy)([1-9])\\.?([0-9]+)?$")
     # handle pythonXY and numpyXY versioned components and also python X.Y, mpi_python etc.
     list(APPEND names
       "${CMAKE_MATCH_1}${CMAKE_MATCH_2}" # python
@@ -407,7 +407,7 @@
       if(TARGET "${prefix}::${name}")
         # The target may be an INTERFACE library that wraps around a single other
         # target for compatibility.  Unwrap this layer so we can extract real info.
-        if("${name}" MATCHES "^(python|numpy|mpi_python)([1-9])([0-9])$")
+        if("${name}" MATCHES "^(python|numpy|mpi_python)([1-9])([0-9]+)$")
           set(name_nv "${CMAKE_MATCH_1}")
           if(TARGET "${prefix}::${name_nv}")
             get_property(type TARGET "${prefix}::${name}" PROPERTY TYPE)
@@ -430,7 +430,7 @@
 
 function(_boost_get_canonical_target_name component target_var)
   string(TOLOWER "${component}" component)
-  if(component MATCHES "^([a-z_]*)(python|numpy)([1-9])\\.?([0-9])?$")
+  if(component MATCHES "^([a-z_]*)(python|numpy)([1-9])\\.?([0-9]+)?$")
     # handle pythonXY and numpyXY versioned components and also python X.Y, mpi_python etc.
     set(${target_var} "Boost::${CMAKE_MATCH_1}${CMAKE_MATCH_2}" PARENT_SCOPE)
   else()
@@ -1004,7 +1004,7 @@
   # against the new release.
 
   # Handle Python version suffixes
-  if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$")
+  if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9]+)\$")
     set(component "${CMAKE_MATCH_1}")
     set(component_python_version "${CMAKE_MATCH_2}")
   endif()
@@ -1350,7 +1350,7 @@
       set(_Boost_TIMER_DEPENDENCIES chrono)
       set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic)
       set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-    else()
+    elseif(Boost_VERSION_STRING VERSION_LESS 1.78.0)
       set(_Boost_CONTRACT_DEPENDENCIES thread chrono)
       set(_Boost_COROUTINE_DEPENDENCIES context)
       set(_Boost_FIBER_DEPENDENCIES context)
@@ -1365,7 +1365,22 @@
       set(_Boost_TIMER_DEPENDENCIES chrono)
       set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic)
       set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-      if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.78.0 AND NOT Boost_NO_WARN_NEW_VERSIONS)
+    else()
+      set(_Boost_CONTRACT_DEPENDENCIES thread chrono)
+      set(_Boost_COROUTINE_DEPENDENCIES context)
+      set(_Boost_FIBER_DEPENDENCIES context)
+      set(_Boost_IOSTREAMS_DEPENDENCIES regex)
+      set(_Boost_JSON_DEPENDENCIES container)
+      set(_Boost_LOG_DEPENDENCIES log_setup filesystem thread regex chrono atomic)
+      set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l)
+      set(_Boost_MPI_DEPENDENCIES serialization)
+      set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
+      set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
+      set(_Boost_THREAD_DEPENDENCIES chrono atomic)
+      set(_Boost_TIMER_DEPENDENCIES chrono)
+      set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono atomic)
+      set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
+      if(Boost_VERSION_STRING VERSION_GREATER_EQUAL 1.79.0 AND NOT Boost_NO_WARN_NEW_VERSIONS)
         message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets")
       endif()
     endif()
@@ -1393,7 +1408,7 @@
 #
 function(_Boost_COMPONENT_HEADERS component _hdrs)
   # Handle Python version suffixes
-  if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$")
+  if(component MATCHES "^(python|mpi_python|numpy)([0-9]+|[0-9]\\.[0-9]+)\$")
     set(component "${CMAKE_MATCH_1}")
     set(component_python_version "${CMAKE_MATCH_2}")
   endif()
@@ -1638,7 +1653,7 @@
   # _Boost_COMPONENT_HEADERS.  See the instructions at the top of
   # _Boost_COMPONENT_DEPENDENCIES.
   set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS}
-    "1.77.0" "1.77" "1.76.0" "1.76" "1.75.0" "1.75" "1.74.0" "1.74"
+    "1.78.0" "1.78" "1.77.0" "1.77" "1.76.0" "1.76" "1.75.0" "1.75" "1.74.0" "1.74"
     "1.73.0" "1.73" "1.72.0" "1.72" "1.71.0" "1.71" "1.70.0" "1.70" "1.69.0" "1.69"
     "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65"
     "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60"
@@ -2150,7 +2165,7 @@
   if(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\$")
     set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}")
     set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}")
-  elseif(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\\.?([0-9])\$")
+  elseif(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\\.?([0-9]+)\$")
     set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}")
     set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}")
     set(COMPONENT_PYTHON_VERSION_MINOR "${CMAKE_MATCH_3}")
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index 89b8c99..d22a676 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -742,6 +742,19 @@
   message(STATUS "Unable to find cuda_runtime.h in \"${CUDAToolkit_TARGET_DIR}/include\" for CUDAToolkit_INCLUDE_DIR.")
 endif()
 
+# The NVHPC layout moves math library headers and libraries to a sibling directory.
+# Create a separate variable so this directory can be selectively added to math targets.
+if(NOT EXISTS "${CUDAToolkit_INCLUDE_DIR}/cublas_v2.h")
+  set(CUDAToolkit_MATH_INCLUDE_DIR "${CUDAToolkit_TARGET_DIR}/../../math_libs/include")
+  cmake_path(NORMAL_PATH CUDAToolkit_MATH_INCLUDE_DIR)
+  if(NOT EXISTS "${CUDAToolkit_MATH_INCLUDE_DIR}/cublas_v2.h")
+    if(NOT CUDAToolkit_FIND_QUIETLY)
+      message(STATUS "Unable to find cublas_v2.h in either \"${CUDAToolkit_INCLUDE_DIR}\" or \"${CUDAToolkit_MATH_INCLUDE_DIR}\"")
+    endif()
+    unset(CUDAToolkit_MATH_INCLUDE_DIR)
+  endif()
+endif()
+
 if(CUDAToolkit_NVCC_EXECUTABLE AND
    CMAKE_CUDA_COMPILER_VERSION AND
    CUDAToolkit_NVCC_EXECUTABLE STREQUAL CMAKE_CUDA_COMPILER)
@@ -844,14 +857,23 @@
       HINTS ${CUDAToolkit_LIBRARY_DIR}
             ENV CUDA_PATH
       PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
+                    # Support NVHPC splayed math library layout
+                    ../../math_libs/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64
+                    ../../math_libs/lib64
     )
 
     mark_as_advanced(CUDA_${lib_name}_LIBRARY)
 
     if (NOT TARGET CUDA::${lib_name} AND CUDA_${lib_name}_LIBRARY)
-      add_library(CUDA::${lib_name} IMPORTED INTERFACE)
+      add_library(CUDA::${lib_name} UNKNOWN IMPORTED)
       target_include_directories(CUDA::${lib_name} SYSTEM INTERFACE "${CUDAToolkit_INCLUDE_DIRS}")
-      target_link_libraries(CUDA::${lib_name} INTERFACE "${CUDA_${lib_name}_LIBRARY}")
+      if(DEFINED CUDAToolkit_MATH_INCLUDE_DIR)
+        string(FIND ${CUDA_${lib_name}_LIBRARY} "math_libs" math_libs)
+        if(NOT ${math_libs} EQUAL -1)
+          target_include_directories(CUDA::${lib_name} SYSTEM INTERFACE "${CUDAToolkit_MATH_INCLUDE_DIR}")
+        endif()
+      endif()
+      set_property(TARGET CUDA::${lib_name} PROPERTY IMPORTED_LOCATION "${CUDA_${lib_name}_LIBRARY}")
       foreach(dep ${arg_DEPS})
         if(TARGET CUDA::${dep})
           target_link_libraries(CUDA::${lib_name} INTERFACE CUDA::${dep})
diff --git a/Modules/FindFLTK.cmake b/Modules/FindFLTK.cmake
index bab7256..d54d2f6 100644
--- a/Modules/FindFLTK.cmake
+++ b/Modules/FindFLTK.cmake
@@ -152,13 +152,17 @@
 endif()
 
 # Check if FLTK was built using CMake
-if(EXISTS ${FLTK_DIR}/FLTKConfig.cmake)
-  set(FLTK_BUILT_WITH_CMAKE 1)
-endif()
+foreach(fltk_include IN LISTS FLTK_DIR)
+  if(EXISTS "${fltk_include}/FLTKConfig.cmake")
+    set(FLTK_BUILT_WITH_CMAKE 1)
+    set(FLTK_CMAKE_PATH "${fltk_include}/FLTKConfig.cmake")
+    break()
+  endif()
+endforeach()
 
 if(FLTK_BUILT_WITH_CMAKE)
   set(FLTK_FOUND 1)
-  include(${FLTK_DIR}/FLTKConfig.cmake)
+  include("${FLTK_CMAKE_PATH}")
 
   # Fluid
   if(FLUID_COMMAND)
diff --git a/Modules/FindGLUT.cmake b/Modules/FindGLUT.cmake
index 2770c60..9c80ad7 100644
--- a/Modules/FindGLUT.cmake
+++ b/Modules/FindGLUT.cmake
@@ -41,8 +41,48 @@
 #]=======================================================================]
 
 include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 
-if (WIN32)
+function(_add_glut_target_simple)
+  if(TARGET GLUT::GLUT)
+    return()
+  endif()
+  add_library(GLUT::GLUT INTERFACE IMPORTED)
+  if(GLUT_INCLUDE_DIRS)
+    target_include_directories(GLUT::GLUT SYSTEM
+      INTERFACE "${GLUT_INCLUDE_DIRS}")
+  endif()
+  if(GLUT_LIBRARIES)
+    target_link_libraries(GLUT::GLUT INTERFACE ${GLUT_LIBRARIES})
+  endif()
+  if(GLUT_LIBRARY_DIRS)
+    target_link_directories(GLUT::GLUT INTERFACE ${GLUT_LIBRARY_DIRS})
+  endif()
+  if(GLUT_LDFLAGS)
+    target_link_options(GLUT::GLUT INTERFACE ${GLUT_LDFLAGS})
+  endif()
+  if(GLUT_CFLAGS)
+    separate_arguments(GLUT_CFLAGS_SPLIT UNIX_COMMAND "${GLUT_CFLAGS}")
+    target_compile_options(GLUT::GLUT INTERFACE ${GLUT_CFLAGS_SPLIT})
+  endif()
+
+  set_property(TARGET GLUT::GLUT APPEND PROPERTY
+    IMPORTED_LOCATION "${GLUT_glut_LIBRARY}")
+endfunction()
+
+find_package(PkgConfig)
+if(PKG_CONFIG_FOUND)
+  pkg_check_modules(GLUT glut)
+  if(GLUT_FOUND)
+    # In the non-pkg-config code path we only provide GLUT_INCLUDE_DIR.
+    set(GLUT_INCLUDE_DIR "${GLUT_INCLUDE_DIRS}")
+    _add_glut_target_simple()
+    FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLUT REQUIRED_VARS GLUT_FOUND)
+    return()
+  endif()
+endif()
+
+if(WIN32)
   find_path( GLUT_INCLUDE_DIR NAMES GL/glut.h
     PATHS  ${GLUT_ROOT_PATH}/include )
   find_library( GLUT_glut_LIBRARY_RELEASE NAMES glut glut32 freeglut
@@ -57,85 +97,75 @@
     )
   mark_as_advanced(GLUT_glut_LIBRARY_RELEASE GLUT_glut_LIBRARY_DEBUG)
   select_library_configurations(GLUT_glut)
-else ()
+elseif(APPLE)
+  find_path(GLUT_INCLUDE_DIR glut.h ${OPENGL_LIBRARY_DIR})
+  find_library(GLUT_glut_LIBRARY GLUT DOC "GLUT library for OSX")
+  find_library(GLUT_cocoa_LIBRARY Cocoa DOC "Cocoa framework for OSX")
+  mark_as_advanced(GLUT_glut_LIBRARY GLUT_cocoa_LIBRARY)
 
-  if (APPLE)
-    find_path(GLUT_INCLUDE_DIR glut.h ${OPENGL_LIBRARY_DIR})
-    find_library(GLUT_glut_LIBRARY GLUT DOC "GLUT library for OSX")
-    find_library(GLUT_cocoa_LIBRARY Cocoa DOC "Cocoa framework for OSX")
-    mark_as_advanced(GLUT_glut_LIBRARY GLUT_cocoa_LIBRARY)
-
-    if(GLUT_cocoa_LIBRARY AND NOT TARGET GLUT::Cocoa)
-      add_library(GLUT::Cocoa UNKNOWN IMPORTED)
-      # Cocoa should always be a Framework, but we check to make sure.
-      if(GLUT_cocoa_LIBRARY MATCHES "/([^/]+)\\.framework$")
-        set(_glut_cocoa "${GLUT_cocoa_LIBRARY}/${CMAKE_MATCH_1}")
-        if(EXISTS "${_glut_cocoa}.tbd")
-          string(APPEND _glut_cocoa ".tbd")
-        endif()
-        set_target_properties(GLUT::Cocoa PROPERTIES
-          IMPORTED_LOCATION "${_glut_cocoa}")
-      else()
-        set_target_properties(GLUT::Cocoa PROPERTIES
-          IMPORTED_LOCATION "${GLUT_cocoa_LIBRARY}")
+  if(GLUT_cocoa_LIBRARY AND NOT TARGET GLUT::Cocoa)
+    add_library(GLUT::Cocoa UNKNOWN IMPORTED)
+    # Cocoa should always be a Framework, but we check to make sure.
+    if(GLUT_cocoa_LIBRARY MATCHES "/([^/]+)\\.framework$")
+      set(_glut_cocoa "${GLUT_cocoa_LIBRARY}/${CMAKE_MATCH_1}")
+      if(EXISTS "${_glut_cocoa}.tbd")
+        string(APPEND _glut_cocoa ".tbd")
       endif()
-    endif()
-  else ()
-
-    if (BEOS)
-
-      set(_GLUT_INC_DIR /boot/develop/headers/os/opengl)
-      set(_GLUT_glut_LIB_DIR /boot/develop/lib/x86)
-
+      set_target_properties(GLUT::Cocoa PROPERTIES
+        IMPORTED_LOCATION "${_glut_cocoa}")
     else()
-
-      find_library( GLUT_Xi_LIBRARY Xi
-        /usr/openwin/lib
-        )
-      mark_as_advanced(GLUT_Xi_LIBRARY)
-
-      find_library( GLUT_Xmu_LIBRARY Xmu
-        /usr/openwin/lib
-        )
-      mark_as_advanced(GLUT_Xmu_LIBRARY)
-
-      if(GLUT_Xi_LIBRARY AND NOT TARGET GLUT::Xi)
-        add_library(GLUT::Xi UNKNOWN IMPORTED)
-        set_target_properties(GLUT::Xi PROPERTIES
-          IMPORTED_LOCATION "${GLUT_Xi_LIBRARY}")
-      endif()
-
-      if(GLUT_Xmu_LIBRARY AND NOT TARGET GLUT::Xmu)
-        add_library(GLUT::Xmu UNKNOWN IMPORTED)
-        set_target_properties(GLUT::Xmu PROPERTIES
-          IMPORTED_LOCATION "${GLUT_Xmu_LIBRARY}")
-      endif()
-
-    endif ()
-
-    find_path( GLUT_INCLUDE_DIR GL/glut.h
-      /usr/include/GL
-      /usr/openwin/share/include
-      /usr/openwin/include
-      /opt/graphics/OpenGL/include
-      /opt/graphics/OpenGL/contrib/libglut
-      ${_GLUT_INC_DIR}
-      )
-
-    find_library( GLUT_glut_LIBRARY glut
+      set_target_properties(GLUT::Cocoa PROPERTIES
+        IMPORTED_LOCATION "${GLUT_cocoa_LIBRARY}")
+    endif()
+  endif()
+else()
+  if(BEOS)
+    set(_GLUT_INC_DIR /boot/develop/headers/os/opengl)
+    set(_GLUT_glut_LIB_DIR /boot/develop/lib/x86)
+  else()
+    find_library( GLUT_Xi_LIBRARY Xi
       /usr/openwin/lib
-      ${_GLUT_glut_LIB_DIR}
       )
-    mark_as_advanced(GLUT_glut_LIBRARY)
+    mark_as_advanced(GLUT_Xi_LIBRARY)
 
-    unset(_GLUT_INC_DIR)
-    unset(_GLUT_glut_LIB_DIR)
+    find_library( GLUT_Xmu_LIBRARY Xmu
+      /usr/openwin/lib
+      )
+    mark_as_advanced(GLUT_Xmu_LIBRARY)
+
+    if(GLUT_Xi_LIBRARY AND NOT TARGET GLUT::Xi)
+      add_library(GLUT::Xi UNKNOWN IMPORTED)
+      set_target_properties(GLUT::Xi PROPERTIES
+        IMPORTED_LOCATION "${GLUT_Xi_LIBRARY}")
+    endif()
+
+    if(GLUT_Xmu_LIBRARY AND NOT TARGET GLUT::Xmu)
+      add_library(GLUT::Xmu UNKNOWN IMPORTED)
+      set_target_properties(GLUT::Xmu PROPERTIES
+        IMPORTED_LOCATION "${GLUT_Xmu_LIBRARY}")
+    endif()
 
   endif ()
 
-endif ()
+  find_path( GLUT_INCLUDE_DIR GL/glut.h
+    /usr/include/GL
+    /usr/openwin/share/include
+    /usr/openwin/include
+    /opt/graphics/OpenGL/include
+    /opt/graphics/OpenGL/contrib/libglut
+    ${_GLUT_INC_DIR}
+    )
 
-include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+  find_library( GLUT_glut_LIBRARY glut
+    /usr/openwin/lib
+    ${_GLUT_glut_LIB_DIR}
+    )
+
+  unset(_GLUT_INC_DIR)
+  unset(_GLUT_glut_LIB_DIR)
+endif()
+mark_as_advanced(GLUT_glut_LIBRARY)
+
 FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLUT REQUIRED_VARS GLUT_glut_LIBRARY GLUT_INCLUDE_DIR)
 
 if (GLUT_FOUND)
diff --git a/Modules/FindGSL.cmake b/Modules/FindGSL.cmake
index 3d4e7f9..485735a 100644
--- a/Modules/FindGSL.cmake
+++ b/Modules/FindGSL.cmake
@@ -139,7 +139,7 @@
   # 2. If gsl-config is not available, try looking in gsl/gsl_version.h
   if( NOT GSL_VERSION AND EXISTS "${GSL_INCLUDE_DIRS}/gsl/gsl_version.h" )
     file( STRINGS "${GSL_INCLUDE_DIRS}/gsl/gsl_version.h" gsl_version_h_contents REGEX "define GSL_VERSION" )
-    string( REGEX REPLACE ".*([0-9]\\.[0-9][0-9]?).*" "\\1" GSL_VERSION ${gsl_version_h_contents} )
+    string( REGEX REPLACE ".*define[ ]+GSL_VERSION[ ]+\"([^\"]*)\".*" "\\1" GSL_VERSION ${gsl_version_h_contents} )
   endif()
 
   # might also try scraping the directory name for a regex match "gsl-X.X"
diff --git a/Modules/FindHDF5.cmake b/Modules/FindHDF5.cmake
index e335355..6cadadb 100644
--- a/Modules/FindHDF5.cmake
+++ b/Modules/FindHDF5.cmake
@@ -161,6 +161,8 @@
 # scope somewhere else. We can't rely on it because different components may
 # have been requested for this call.
 set(HDF5_FOUND OFF)
+set(HDF5_LIBRARIES)
+set(HDF5_HL_LIBRARIES)
 
 # List of the valid HDF5 components
 set(HDF5_VALID_LANGUAGE_BINDINGS C CXX Fortran)
@@ -560,7 +562,7 @@
                 set(HDF5_${_lang}_FOUND TRUE)
             endif()
             if(HDF5_FIND_HL)
-                get_target_property(_lang_hl_location ${HDF5_${_lang}_HL_TARGET}${_suffix} IMPORTED_IMPLIB_${_hdf5_imported_conf} )
+                get_target_property(_hdf5_lang_hl_location ${HDF5_${_lang}_HL_TARGET}${_suffix} IMPORTED_IMPLIB_${_hdf5_imported_conf} )
                 if (NOT _hdf5_lang_hl_location)
                     get_target_property(_hdf5_lang_hl_location ${HDF5_${_lang}_HL_TARGET}${_suffix} LOCATION_${_hdf5_imported_conf})
                     if (NOT _hdf5_hl_lang_location)
@@ -586,6 +588,9 @@
   set(HDF5_COMPILER_NO_INTERROGATE TRUE)
   # Only search for languages we've enabled
   foreach(_lang IN LISTS HDF5_LANGUAGE_BINDINGS)
+    set(HDF5_${_lang}_LIBRARIES)
+    set(HDF5_${_lang}_HL_LIBRARIES)
+
     # First check to see if our regular compiler is one of wrappers
     if(_lang STREQUAL "C")
       _HDF5_test_regular_compiler_C(
@@ -811,6 +816,9 @@
     endif()
 
     foreach(_lang IN LISTS HDF5_LANGUAGE_BINDINGS)
+        set(HDF5_${_lang}_LIBRARIES)
+        set(HDF5_${_lang}_HL_LIBRARIES)
+
         # The "main" library.
         set(_hdf5_main_library "")
 
diff --git a/Modules/FindICU.cmake b/Modules/FindICU.cmake
index 2bb49ad..1bae825 100644
--- a/Modules/FindICU.cmake
+++ b/Modules/FindICU.cmake
@@ -172,7 +172,7 @@
       DOC "ICU ${program} executable"
       NO_PACKAGE_ROOT_PATH
       )
-    mark_as_advanced(cache_var)
+    mark_as_advanced("${cache_var}")
     set("${program_var}" "${${cache_var}}" PARENT_SCOPE)
   endforeach()
 
@@ -301,7 +301,7 @@
       HINTS ${icu_roots}
       PATH_SUFFIXES ${icu_data_suffixes}
       DOC "ICU ${data} data file")
-    mark_as_advanced(cache_var)
+    mark_as_advanced("${cache_var}")
     set("${data_var}" "${${cache_var}}" PARENT_SCOPE)
   endforeach()
 
diff --git a/Modules/FindJasper.cmake b/Modules/FindJasper.cmake
index 729a503..9a62669 100644
--- a/Modules/FindJasper.cmake
+++ b/Modules/FindJasper.cmake
@@ -5,43 +5,86 @@
 FindJasper
 ----------
 
-Try to find the Jasper JPEG2000 library
+Find the Jasper JPEG2000 library.
 
-Once done this will define
+IMPORTED Targets
+^^^^^^^^^^^^^^^^
 
-::
+``Jasper::Jasper``
+  The jasper library, if found.
 
-  JASPER_FOUND - system has Jasper
-  JASPER_INCLUDE_DIR - the Jasper include directory
-  JASPER_LIBRARIES - the libraries needed to use Jasper
-  JASPER_VERSION_STRING - the version of Jasper found (since CMake 2.8.8)
+Result Variables
+^^^^^^^^^^^^^^^^
+
+This module defines the following variables:
+
+``JASPER_FOUND``
+  system has Jasper
+``JASPER_INCLUDE_DIRS``
+  .. versionadded:: 3.22
+
+  the Jasper include directory
+``JASPER_LIBRARIES``
+  the libraries needed to use Jasper
+``JASPER_VERSION_STRING``
+  the version of Jasper found
+
+Cache variables
+^^^^^^^^^^^^^^^
+
+The following cache variables may also be set:
+
+``JASPER_INCLUDE_DIR``
+  where to find jasper/jasper.h, etc.
+``JASPER_LIBRARY_RELEASE``
+  where to find the Jasper library (optimized).
+``JASPER_LIBARRY_DEBUG``
+  where to find the Jasper library (debug).
 #]=======================================================================]
 
 find_path(JASPER_INCLUDE_DIR jasper/jasper.h)
+mark_as_advanced(JASPER_INCLUDE_DIR)
 
-if (NOT JASPER_LIBRARIES)
-    find_package(JPEG)
+if(NOT JASPER_LIBRARIES)
+  find_package(JPEG)
+  find_library(JASPER_LIBRARY_RELEASE NAMES jasper libjasper)
+  find_library(JASPER_LIBRARY_DEBUG NAMES jasperd)
+  include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
+  select_library_configurations(JASPER)
+endif()
 
-    find_library(JASPER_LIBRARY_RELEASE NAMES jasper libjasper)
-    find_library(JASPER_LIBRARY_DEBUG NAMES jasperd)
-
-    include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations.cmake)
-    SELECT_LIBRARY_CONFIGURATIONS(JASPER)
-endif ()
-
-if (JASPER_INCLUDE_DIR AND EXISTS "${JASPER_INCLUDE_DIR}/jasper/jas_config.h")
-    file(STRINGS "${JASPER_INCLUDE_DIR}/jasper/jas_config.h" jasper_version_str REGEX "^#define[\t ]+JAS_VERSION[\t ]+\".*\".*")
-
-    string(REGEX REPLACE "^#define[\t ]+JAS_VERSION[\t ]+\"([^\"]+)\".*" "\\1" JASPER_VERSION_STRING "${jasper_version_str}")
-endif ()
+if(JASPER_INCLUDE_DIR AND EXISTS "${JASPER_INCLUDE_DIR}/jasper/jas_config.h")
+  file(STRINGS "${JASPER_INCLUDE_DIR}/jasper/jas_config.h" jasper_version_str REGEX "^#define[\t ]+JAS_VERSION[\t ]+\".*\".*")
+  string(REGEX REPLACE "^#define[\t ]+JAS_VERSION[\t ]+\"([^\"]+)\".*" "\\1" JASPER_VERSION_STRING "${jasper_version_str}")
+endif()
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(Jasper
+find_package_handle_standard_args(Jasper
                                   REQUIRED_VARS JASPER_LIBRARIES JASPER_INCLUDE_DIR JPEG_LIBRARIES
                                   VERSION_VAR JASPER_VERSION_STRING)
 
-if (JASPER_FOUND)
-  set(JASPER_LIBRARIES ${JASPER_LIBRARIES} ${JPEG_LIBRARIES} )
-endif ()
-
-mark_as_advanced(JASPER_INCLUDE_DIR)
+if(JASPER_FOUND)
+  set(JASPER_LIBRARIES ${JASPER_LIBRARIES} ${JPEG_LIBRARIES})
+  set(JASPER_INCLUDE_DIRS ${JASPER_INCLUDE_DIR})
+  if(NOT TARGET Jasper::Jasper)
+    add_library(Jasper::Jasper UNKNOWN IMPORTED)
+    if(JASPER_INCLUDE_DIRS)
+      set_target_properties(Jasper::Jasper PROPERTIES
+        INTERFACE_INCLUDE_DIRECTORIES "${JASPER_INCLUDE_DIRS}")
+    endif()
+    if(EXISTS "${JASPER_LIBRARY_RELEASE}")
+      set_property(TARGET Jasper::Jasper APPEND PROPERTY
+        IMPORTED CONFIGURATION RELEASE)
+      set_target_properties(Jasper::Jasper PROPERTIES
+        IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
+        IMPORTED_LOCATION "${JASPER_LIBRARY_RELEASE}")
+    endif()
+    if(EXISTS "${JASPER_LIBRARY_DEBUG}")
+      set_property(TARGET Jasper::Jasper APPEND PROPERTY
+        IMPORTED CONFIGURATION DEBUG)
+      set_target_properties(Jasper::Jasper PROPERTIES
+        IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
+        IMPORTED_LOCATION "${JASPER_LIBRARY_DEBUG}")
+    endif()
+  endif()
+endif()
diff --git a/Modules/FindLAPACK.cmake b/Modules/FindLAPACK.cmake
index 3146e06..5540965 100644
--- a/Modules/FindLAPACK.cmake
+++ b/Modules/FindLAPACK.cmake
@@ -35,6 +35,19 @@
   if set ``pkg-config`` will be used to search for a LAPACK library first
   and if one is found that is preferred
 
+``BLA_SIZEOF_INTEGER``
+  .. versionadded:: 3.22
+
+  Specify the BLAS/LAPACK library integer size:
+
+  ``4``
+    Search for a BLAS/LAPACK with 32-bit integer interfaces.
+  ``8``
+    Search for a BLAS/LAPACK with 64-bit integer interfaces.
+  ``ANY``
+    Search for any BLAS/LAPACK.
+    Most likely, a BLAS/LAPACK with 32-bit integer interfaces will be found.
+
 Imported targets
 ^^^^^^^^^^^^^^^^
 
@@ -168,9 +181,9 @@
       list(APPEND _libraries "${_library}")
     else()
       string(REGEX REPLACE "[^A-Za-z0-9]" "_" _lib_var "${_library}")
-      set(_combined_name ${_combined_name}_${_lib_var})
+      string(APPEND _combined_name "_${_lib_var}")
       if(NOT "${_deps}" STREQUAL "")
-        set(_combined_name ${_combined_name}_deps)
+        string(APPEND _combined_name "_deps")
       endif()
       if(_libraries_work)
         find_library(${_prefix}_${_lib_var}_LIBRARY
@@ -188,7 +201,7 @@
 
   foreach(_flag ${_flags})
     string(REGEX REPLACE "[^A-Za-z0-9]" "_" _flag_var "${_flag}")
-    set(_combined_name ${_combined_name}_${_flag_var})
+    string(APPEND _combined_name "_${_flag_var}")
   endforeach()
   if(_libraries_work)
     # Test this combination of libraries.
@@ -247,6 +260,17 @@
     "FindLAPACK requires Fortran, C, or C++ to be enabled.")
 endif()
 
+if(NOT BLA_SIZEOF_INTEGER)
+  # in the reality we do not know which API of BLAS/LAPACK is masked in library
+  set(_lapack_sizeof_integer "ANY")
+elseif((BLA_SIZEOF_INTEGER STREQUAL "ANY") OR
+       (BLA_SIZEOF_INTEGER STREQUAL "4") OR
+       (BLA_SIZEOF_INTEGER STREQUAL "8"))
+  set(_lapack_sizeof_integer ${BLA_SIZEOF_INTEGER})
+else()
+  message(FATAL_ERROR "BLA_SIZEOF_INTEGER can have only <no value>, ANY, 4, or 8 values")
+endif()
+
 # Load BLAS
 if(NOT LAPACK_NOT_FOUND_MESSAGE)
   _lapack_find_dependency(BLAS)
@@ -288,17 +312,23 @@
 
     _lapack_find_dependency(Threads)
 
-    if(BLA_VENDOR MATCHES "_64ilp")
+    if(_lapack_sizeof_integer EQUAL 8)
       set(LAPACK_mkl_ILP_MODE "ilp64")
-    else()
+    elseif(_lapack_sizeof_integer EQUAL 4)
       set(LAPACK_mkl_ILP_MODE "lp64")
+    else()
+      if(BLA_VENDOR MATCHES "_64ilp")
+        set(LAPACK_mkl_ILP_MODE "ilp64")
+      else()
+        set(LAPACK_mkl_ILP_MODE "lp64")
+      endif()
     endif()
 
     set(LAPACK_SEARCH_LIBS "")
 
     if(BLA_F95)
       set(LAPACK_mkl_SEARCH_SYMBOL "cheev_f95")
-      set(_LIBRARIES LAPACK95_LIBRARIES)
+      set(_LAPACK_LIBRARIES LAPACK95_LIBRARIES)
       set(_BLAS_LIBRARIES ${BLAS95_LIBRARIES})
 
       # old
@@ -311,7 +341,7 @@
         "mkl_lapack95_${LAPACK_mkl_ILP_MODE}")
     else()
       set(LAPACK_mkl_SEARCH_SYMBOL "cheev")
-      set(_LIBRARIES LAPACK_LIBRARIES)
+      set(_LAPACK_LIBRARIES LAPACK_LIBRARIES)
       set(_BLAS_LIBRARIES ${BLAS_LIBRARIES})
 
       # old and new >= 10.3
@@ -350,10 +380,10 @@
         "lib/${LAPACK_mkl_ARCH_NAME}"
         )
 
-    # First try empty lapack libs
-    if(NOT ${_LIBRARIES})
+    # First try empty lapack libs (implicitly linked or automatic from BLAS)
+    if(NOT ${_LAPACK_LIBRARIES})
       check_lapack_libraries(
-        ${_LIBRARIES}
+        ${_LAPACK_LIBRARIES}
         LAPACK
         ${LAPACK_mkl_SEARCH_SYMBOL}
         ""
@@ -363,18 +393,23 @@
         "${LAPACK_mkl_LIB_PATH_SUFFIXES}"
         "${_BLAS_LIBRARIES}"
       )
+      if(LAPACK_WORKS AND NOT _BLAS_LIBRARIES)
+        # Give a more helpful "found" message
+        set(LAPACK_WORKS "implicitly linked")
+        set(_lapack_fphsa_req_var LAPACK_WORKS)
+      endif()
     endif()
 
     # Then try the search libs
-    foreach(IT ${LAPACK_SEARCH_LIBS})
-      string(REPLACE " " ";" SEARCH_LIBS ${IT})
-      if(NOT ${_LIBRARIES})
+    foreach(_search ${LAPACK_SEARCH_LIBS})
+      string(REPLACE " " ";" _search ${_search})
+      if(NOT ${_LAPACK_LIBRARIES})
         check_lapack_libraries(
-          ${_LIBRARIES}
+          ${_LAPACK_LIBRARIES}
           LAPACK
           ${LAPACK_mkl_SEARCH_SYMBOL}
           ""
-          "${SEARCH_LIBS}"
+          "${_search}"
           "${CMAKE_THREAD_LIBS_INIT};${LAPACK_mkl_LM};${LAPACK_mkl_LDL}"
           "${LAPACK_mkl_MKLROOT}"
           "${LAPACK_mkl_LIB_PATH_SUFFIXES}"
@@ -383,6 +418,7 @@
       endif()
     endforeach()
 
+    unset(_search)
     unset(LAPACK_mkl_ILP_MODE)
     unset(LAPACK_mkl_SEARCH_SYMBOL)
     unset(LAPACK_mkl_LM)
@@ -412,48 +448,70 @@
   # FlexiBLAS? (http://www.mpi-magdeburg.mpg.de/mpcsc/software/FlexiBLAS/)
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR STREQUAL "FlexiBLAS" OR BLA_VENDOR STREQUAL "All"))
+    set(_lapack_flexiblas_lib "flexiblas")
+
+    if(_lapack_sizeof_integer EQUAL 8)
+      string(APPEND _lapack_flexiblas_lib "64")
+    endif()
+
     check_lapack_libraries(
       LAPACK_LIBRARIES
       LAPACK
       cheev
       ""
-      "flexiblas"
+      "${_lapack_flexiblas_lib}"
       ""
       ""
       ""
       "${BLAS_LIBRARIES}"
     )
+
+    unset(_lapack_flexiblas_lib)
   endif()
 
   # OpenBLAS? (http://www.openblas.net)
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR STREQUAL "OpenBLAS" OR BLA_VENDOR STREQUAL "All"))
+    set(_lapack_openblas_lib "openblas")
+
+    if(_lapack_sizeof_integer EQUAL 8)
+      string(APPEND _lapack_openblas_lib "64")
+    endif()
+
     check_lapack_libraries(
       LAPACK_LIBRARIES
       LAPACK
       cheev
       ""
-      "openblas"
+      "${_lapack_openblas_lib}"
       ""
       ""
       ""
       "${BLAS_LIBRARIES}"
     )
+
+    unset(_lapack_openblas_lib)
   endif()
 
   # ArmPL? (https://developer.arm.com/tools-and-software/server-and-hpc/compile/arm-compiler-for-linux/arm-performance-libraries)
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR MATCHES "Arm" OR BLA_VENDOR STREQUAL "All"))
     # Check for 64bit Integer support
-    if(BLA_VENDOR MATCHES "_ilp64")
+    if(_lapack_sizeof_integer EQUAL 8)
       set(LAPACK_armpl_LIB "armpl_ilp64")
-    else()
+    elseif(_lapack_sizeof_integer EQUAL 4)
       set(LAPACK_armpl_LIB "armpl_lp64")
+    else()
+      if(BLA_VENDOR MATCHES "_ilp64")
+        set(LAPACK_armpl_LIB "armpl_ilp64")
+      else()
+        set(LAPACK_armpl_LIB "armpl_lp64")
+      endif()
     endif()
 
     # Check for OpenMP support, VIA BLA_VENDOR of Arm_mp or Arm_ipl64_mp
     if(BLA_VENDOR MATCHES "_mp")
-     set(LAPACK_armpl_LIB "${LAPACK_armpl_LIB}_mp")
+      string(APPEND LAPACK_armpl_LIB "_mp")
     endif()
 
     check_lapack_libraries(
@@ -472,17 +530,50 @@
   # FLAME's blis library? (https://github.com/flame/blis)
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR STREQUAL "FLAME" OR BLA_VENDOR STREQUAL "All"))
+    if(_lapack_sizeof_integer EQUAL 8)
+      if(BLA_VENDOR STREQUAL "FLAME")
+        message(FATAL_ERROR "libFLAME does not support Int64 type")
+      endif()
+    else()
+      check_lapack_libraries(
+        LAPACK_LIBRARIES
+        LAPACK
+        cheev
+        ""
+        "flame"
+        ""
+        ""
+        ""
+        "${BLAS_LIBRARIES}"
+      )
+    endif()
+  endif()
+
+  # LAPACK in SCSL library? (SGI/Cray Scientific Library)
+  if(NOT LAPACK_LIBRARIES
+      AND (BLA_VENDOR MATCHES "SCSL" OR BLA_VENDOR STREQUAL "All"))
+    set(_lapack_scsl_lib "scs")
+
+    if(_lapack_sizeof_integer EQUAL 8)
+      string(APPEND _lapack_scsl_lib "_i8")
+    endif()
+    # Check for OpenMP support, VIA BLA_VENDOR of scs_mp
+    if(BLA_VENDOR MATCHES "_mp")
+      string(APPEND _lapack_scsl_lib "_mp")
+    endif()
+
     check_lapack_libraries(
       LAPACK_LIBRARIES
       LAPACK
       cheev
       ""
-      "flame"
+      "${_lapack_scsl_lib}"
       ""
       ""
       ""
       "${BLAS_LIBRARIES}"
     )
+    unset(_lapack_scsl_lib)
   endif()
 
   # BLAS in acml library?
@@ -495,105 +586,116 @@
   # Apple LAPACK library?
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR STREQUAL "Apple" OR BLA_VENDOR STREQUAL "All"))
-    check_lapack_libraries(
-      LAPACK_LIBRARIES
-      LAPACK
-      cheev
-      ""
-      "Accelerate"
-      ""
-      ""
-      ""
-      "${BLAS_LIBRARIES}"
-    )
+    if(_lapack_sizeof_integer EQUAL 8)
+      if(BLA_VENDOR STREQUAL "Apple")
+        message(FATAL_ERROR "Accelerate Framework does not support Int64 type")
+      endif()
+    else()
+      check_lapack_libraries(
+        LAPACK_LIBRARIES
+        LAPACK
+        cheev
+        ""
+        "Accelerate"
+        ""
+        ""
+        ""
+        "${BLAS_LIBRARIES}"
+      )
+    endif()
   endif()
 
   # Apple NAS (vecLib) library?
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR STREQUAL "NAS" OR BLA_VENDOR STREQUAL "All"))
-    check_lapack_libraries(
-      LAPACK_LIBRARIES
-      LAPACK
-      cheev
-      ""
-      "vecLib"
-      ""
-      ""
-      ""
-      "${BLAS_LIBRARIES}"
-    )
+    if(_lapack_sizeof_integer EQUAL 8)
+      if(BLA_VENDOR STREQUAL "NAS")
+        message(FATAL_ERROR "Accelerate Framework does not support Int64 type")
+      endif()
+    else()
+      check_lapack_libraries(
+        LAPACK_LIBRARIES
+        LAPACK
+        cheev
+        ""
+        "vecLib"
+        ""
+        ""
+        ""
+        "${BLAS_LIBRARIES}"
+      )
+    endif()
   endif()
 
   # Elbrus Math Library?
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR MATCHES "EML" OR BLA_VENDOR STREQUAL "All"))
-
-    set(LAPACK_EML_LIB "eml")
-
-    # Check for OpenMP support, VIA BLA_VENDOR of eml_mt
-    if(BLA_VENDOR MATCHES "_mt")
-     set(LAPACK_EML_LIB "${LAPACK_EML_LIB}_mt")
+    if(BLAS_LIBRARIES MATCHES "eml.+")
+      set(LAPACK_LIBRARIES ${BLAS_LIBRARIES})
     endif()
-
-    check_lapack_libraries(
-      LAPACK_LIBRARIES
-      LAPACK
-      cheev
-      ""
-      "${LAPACK_EML_LIB}"
-      ""
-      ""
-      ""
-      "${BLAS_LIBRARIES}"
-    )
   endif()
 
   # Fujitsu SSL2 Library?
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR MATCHES "Fujitsu_SSL2" OR BLA_VENDOR STREQUAL "All"))
-    if(BLA_VENDOR STREQUAL "Fujitsu_SSL2BLAMP")
-      set(_ssl2_suffix BLAMP)
-    else()
-      set(_ssl2_suffix)
+    if(BLAS_LIBRARIES MATCHES "fjlapack.+")
+      set(LAPACK_LIBRARIES ${BLAS_LIBRARIES})
+      set(LAPACK_LINKER_FLAGS ${BLAS_LINKER_FLAGS})
     endif()
-    set(_ssl2_blas)
-    if(BLAS_LIBRARIES STREQUAL "")
-      set(_ssl2_blas "${BLAS_LINKER_FLAGS}")
-    else()
-      set(_ssl2_blas "${BLAS_LIBRARIES} ${BLAS_LINKER_FLAGS}")
+  endif()
+
+  # LAPACK in IBM ESSL library?
+  if(NOT LAPACK_LIBRARIES
+      AND (BLA_VENDOR MATCHES "IBMESSL" OR BLA_VENDOR STREQUAL "All"))
+    if(BLAS_LIBRARIES MATCHES "essl.+")
+      set(LAPACK_LIBRARIES ${BLAS_LIBRARIES})
     endif()
-    check_lapack_libraries(
-      LAPACK_LIBRARIES
-      LAPACK
-      cheev
-      "-SSL2${_ssl2_suffix}"
-      ""
-      ""
-      ""
-      ""
-      "${_ssl2_blas}"
-    )
-    if(LAPACK_LIBRARIES)
-      set(LAPACK_LINKER_FLAGS "-SSL2${_ssl2_suffix}")
-      set(_lapack_fphsa_req_var LAPACK_LINKER_FLAGS)
-    endif()
-    unset(_ssl2_suffix)
   endif()
 
   # NVHPC Library?
+
   if(NOT LAPACK_LIBRARIES
       AND (BLA_VENDOR MATCHES "NVHPC" OR BLA_VENDOR STREQUAL "All"))
+    set(_lapack_nvhpc_lib "lapack")
+
+    if(_lapack_sizeof_integer EQUAL 8)
+      string(APPEND _lapack_nvhpc_lib "_ilp64")
+    elseif(_lapack_sizeof_integer EQUAL 4)
+      string(APPEND _lapack_nvhpc_lib "_lp64")
+    endif()
+
     check_lapack_libraries(
       LAPACK_LIBRARIES
       LAPACK
       cheev
       ""
-      "lapack"
+      "${_lapack_nvhpc_lib}"
       "-fortranlibs"
       ""
       ""
       "${BLAS_LIBRARIES}"
     )
+
+    # an additional check for NVHPC 2020
+    # which does not have differentiation
+    # between lp64 and ilp64 modes
+    if(NOT LAPACK_LIBRARIES AND NOT _lapack_sizeof_integer EQUAL 8)
+      set(_lapack_nvhpc_lib "lapack")
+
+      check_lapack_libraries(
+        LAPACK_LIBRARIES
+        LAPACK
+        cheev
+        ""
+        "${_lapack_nvhpc_lib}"
+        "-fortranlibs"
+        ""
+        ""
+        "${BLAS_LIBRARIES}"
+      )
+    endif()
+
+    unset(_lapack_nvhpc_lib)
   endif()
 
   # Generic LAPACK library?
@@ -601,6 +703,7 @@
       AND (BLA_VENDOR STREQUAL "Generic"
            OR BLA_VENDOR STREQUAL "ATLAS"
            OR BLA_VENDOR STREQUAL "All"))
+    set(_lapack_generic_lib "lapack")
     if(BLA_STATIC)
       # We do not know for sure how the LAPACK reference implementation
       # is built on this host.  Guess typical dependencies.
@@ -608,18 +711,25 @@
     else()
       set(_lapack_generic_deps "")
     endif()
+
+    if(_lapack_sizeof_integer EQUAL 8)
+      string(APPEND _lapack_generic_lib "64")
+    endif()
+
     check_lapack_libraries(
       LAPACK_LIBRARIES
       LAPACK
       cheev
       ""
-      "lapack"
+      "${_lapack_generic_lib}"
       "${_lapack_generic_deps}"
       ""
       ""
       "${BLAS_LIBRARIES}"
     )
+
     unset(_lapack_generic_deps)
+    unset(_lapack_generic_lib)
   endif()
 endif()
 
@@ -646,3 +756,6 @@
 endif()
 
 _add_lapack_target()
+unset(_lapack_fphsa_req_var)
+unset(_lapack_sizeof_integer)
+unset(_LAPACK_LIBRARIES)
diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake
index d8f0334..c974d33 100644
--- a/Modules/FindMPI.cmake
+++ b/Modules/FindMPI.cmake
@@ -105,7 +105,7 @@
 
 1. Search for ``MPIEXEC_EXECUTABLE`` and, if found, use its base directory.
 2. Check if the compiler has MPI support built-in. This is the case if the user passed a
-   compiler wrapper as ``CMAKE_<LANG>_COMPILER`` or if they're on a Cray system.
+   compiler wrapper as ``CMAKE_<LANG>_COMPILER`` or if they use Cray system compiler wrappers.
 3. Attempt to find an MPI compiler wrapper and determine the compiler information from it.
 4. Try to find an MPI implementation that does not ship such a wrapper by guessing settings.
    Currently, only Microsoft MPI and MPICH2 on Windows are supported.
@@ -267,6 +267,7 @@
 cmake_policy(SET CMP0057 NEW) # if IN_LIST
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+find_package(PkgConfig QUIET)
 
 # Generic compiler names
 set(_MPI_C_GENERIC_COMPILER_NAMES          mpicc    mpcc      mpicc_r mpcc_r)
@@ -333,6 +334,11 @@
                                            mpixlf77   mpixlf77_r mpxlf77 mpxlf77_r
                                            mpixlf     mpixlf_r   mpxlf   mpxlf_r)
 
+# Cray Compiler names
+set(_MPI_Cray_C_COMPILER_NAMES             cc)
+set(_MPI_Cray_CXX_COMPILER_NAMES           CC)
+set(_MPI_Cray_Fortran_COMPILER_NAMES       ftn)
+
 # Prepend vendor-specific compiler wrappers to the list. If we don't know the compiler,
 # attempt all of them.
 # By attempting vendor-specific compiler names first, we should avoid situations where the compiler wrapper
@@ -488,6 +494,26 @@
     endif()
   endif()
 
+  # Cray compiler wrappers come usually without a separate mpicc/c++/ftn, but offer
+  # --cray-print-opts=...
+  if (NOT MPI_COMPILER_RETURN EQUAL 0)
+    _MPI_check_compiler(${LANG} "--cray-print-opts=cflags"
+                        MPI_COMPILE_CMDLINE MPI_COMPILER_RETURN)
+
+    if (MPI_COMPILER_RETURN EQUAL 0)
+      # Pass --no-as-needed so the mpi library is always linked. Otherwise, the
+      # Cray compiler wrapper puts an --as-needed flag around the mpi library,
+      # and it is not linked unless code directly refers to it.
+      _MPI_check_compiler(${LANG} "--no-as-needed;--cray-print-opts=libs"
+                          MPI_LINK_CMDLINE MPI_COMPILER_RETURN)
+
+      if (NOT MPI_COMPILER_RETURN EQUAL 0)
+        unset(MPI_COMPILE_CMDLINE)
+        unset(MPI_LINK_CMDLINE)
+      endif()
+    endif()
+  endif()
+
   # MPICH, MVAPICH2 and Intel MPI just use "-show". Open MPI also offers this, but the
   # -showme commands are more specialized.
   if (NOT MPI_COMPILER_RETURN EQUAL 0)
@@ -1441,9 +1467,10 @@
   endif()
   if(_MPI_FIND_${LANG})
     if( ${LANG} STREQUAL CXX AND NOT MPICXX IN_LIST MPI_FIND_COMPONENTS )
-      set(MPI_CXX_SKIP_MPICXX FALSE CACHE BOOL "If true, the MPI-2 C++ bindings are disabled using definitions.")
+      option(MPI_CXX_SKIP_MPICXX "If true, the MPI-2 C++ bindings are disabled using definitions." FALSE)
       mark_as_advanced(MPI_CXX_SKIP_MPICXX)
     endif()
+    _MPI_adjust_compile_definitions(${LANG})
     if(NOT (MPI_${LANG}_LIB_NAMES AND (MPI_${LANG}_INCLUDE_PATH OR MPI_${LANG}_INCLUDE_DIRS OR MPI_${LANG}_COMPILER_INCLUDE_DIRS)))
       set(MPI_${LANG}_TRIED_IMPLICIT FALSE)
       set(MPI_${LANG}_WORKS_IMPLICIT FALSE)
@@ -1520,10 +1547,66 @@
           endif()
         endif()
 
+        # We are on a Cray, environment identfier: PE_ENV is set (CRAY), and
+        # have NOT found an mpic++-like compiler wrapper (previous block),
+        # and we do NOT use the Cray cc/CC compiler wrappers as CC/CXX CMake
+        # compiler.
+        # So as a last resort, we now interrogate cc/CC/ftn for MPI flags.
+        if(DEFINED ENV{PE_ENV} AND NOT "${MPI_${LANG}_COMPILER}")
+          set(MPI_PINNED_COMPILER TRUE)
+          find_program(MPI_${LANG}_COMPILER
+            NAMES  ${_MPI_Cray_${LANG}_COMPILER_NAMES}
+            PATH_SUFFIXES bin sbin
+            DOC    "MPI compiler for ${LANG}"
+          )
+
+          # If we haven't made the implicit compiler test yet, perform it now.
+          if(NOT MPI_${LANG}_TRIED_IMPLICIT)
+            _MPI_create_imported_target(${LANG})
+            _MPI_check_lang_works(${LANG} TRUE)
+          endif()
+
+          set(MPI_${LANG}_WORKS_IMPLICIT TRUE)
+          _MPI_interrogate_compiler(${LANG})
+        endif()
+
         if(NOT MPI_PINNED_COMPILER AND NOT MPI_${LANG}_WRAPPER_FOUND)
           # If MPI_PINNED_COMPILER wasn't given, and the MPI compiler we potentially found didn't work, we withdraw it.
           set(MPI_${LANG}_COMPILER "MPI_${LANG}_COMPILER-NOTFOUND" CACHE FILEPATH "MPI compiler for ${LANG}" FORCE)
-          if(NOT MPI_SKIP_GUESSING)
+
+          if(LANG STREQUAL "C")
+            set(_MPI_PKG "mpi-c")
+          elseif(LANG STREQUAL "CXX")
+            set(_MPI_PKG "mpi-cxx")
+          elseif(LANG STREQUAL "Fortran")
+            set(_MPI_PKG "mpi-fort")
+          else()
+            set(_MPI_PKG "")
+          endif()
+          if(_MPI_PKG AND PKG_CONFIG_FOUND)
+            pkg_check_modules("MPI_${LANG}_PKG" "${_MPI_PKG}")
+            if("${MPI_${LANG}_PKG_FOUND}")
+              set(MPI_${LANG}_COMPILE_OPTIONS  ${MPI_${LANG}_PKG_CFLAGS}        CACHE STRING "MPI ${LANG} compilation options"       FORCE)
+              set(MPI_${LANG}_INCLUDE_PATH     ${MPI_${LANG}_PKG_INCLUDE_DIRS}  CACHE STRING "MPI ${LANG} include directories"       FORCE)
+              set(MPI_${LANG}_LINK_FLAGS       ${MPI_${LANG}_PKG_LDFLAGS}       CACHE STRING "MPI ${LANG} linker flags"              FORCE)
+              set(MPI_${LANG}_LIB_NAMES        ${MPI_${LANG}_PKG_LIBRARIES}     CACHE STRING "MPI ${LANG} libraries to link against" FORCE)
+              foreach(_MPI_LIB IN LISTS MPI_${LANG}_LIB_NAMES)
+                if(_MPI_LIB)
+                  get_filename_component(_MPI_PLAIN_LIB_NAME "${_MPI_LIB}" NAME_WE)
+                  get_filename_component(_MPI_LIB_NAME "${_MPI_LIB}" NAME)
+                  get_filename_component(_MPI_LIB_DIR "${_MPI_LIB}" DIRECTORY)
+                  find_library(MPI_${_MPI_PLAIN_LIB_NAME}_LIBRARY
+                    NAMES "${_MPI_LIB_NAME}" "lib${_MPI_LIB_NAME}"
+                    HINTS ${_MPI_LIB_DIR}
+                    DOC "Location of the ${_MPI_PLAIN_LIB_NAME} library for MPI"
+                  )
+                  mark_as_advanced(MPI_${_MPI_PLAIN_LIB_NAME}_LIBRARY)
+                endif()
+              endforeach()
+            endif()
+          endif()
+
+          if(NOT MPI_SKIP_GUESSING AND NOT "${MPI_${LANG}_PKG_FOUND}")
             # For C++, we may use the settings for C. Should a given compiler wrapper for C++ not exist, but one for C does, we copy over the
             # settings for C. An MPI distribution that is in this situation would be IBM Platform MPI.
             if("${LANG}" STREQUAL "CXX" AND MPI_C_WRAPPER_FOUND)
@@ -1548,7 +1631,6 @@
     endif()
     _MPI_assemble_libraries(${LANG})
 
-    _MPI_adjust_compile_definitions(${LANG})
     # We always create imported targets even if they're empty
     _MPI_create_imported_target(${LANG})
 
diff --git a/Modules/FindMPI/test_mpi.c b/Modules/FindMPI/test_mpi.c
index 7c96d54..36b5dfd 100644
--- a/Modules/FindMPI/test_mpi.c
+++ b/Modules/FindMPI/test_mpi.c
@@ -7,15 +7,15 @@
 #endif
 
 #if defined(MPI_VERSION) && defined(MPI_SUBVERSION)
-const char mpiver_str[] = { 'I', 'N',
-                            'F', 'O',
-                            ':', 'M',
-                            'P', 'I',
-                            '-', 'V',
-                            'E', 'R',
-                            '[', ('0' + MPI_VERSION),
-                            '.', ('0' + MPI_SUBVERSION),
-                            ']', '\0' };
+static const char mpiver_str[] = { 'I', 'N',
+                                   'F', 'O',
+                                   ':', 'M',
+                                   'P', 'I',
+                                   '-', 'V',
+                                   'E', 'R',
+                                   '[', ('0' + MPI_VERSION),
+                                   '.', ('0' + MPI_SUBVERSION),
+                                   ']', '\0' };
 #endif
 
 int main(int argc, char* argv[])
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake
index e7cd834..3c7efbc 100644
--- a/Modules/FindMatlab.cmake
+++ b/Modules/FindMatlab.cmake
@@ -93,6 +93,33 @@
   additional versions of Matlab for the automatic retrieval of the installed
   versions.
 
+Imported targets
+^^^^^^^^^^^^^^^^
+
+.. versionadded:: 3.22
+
+This module defines the following :prop_tgt:`IMPORTED` targets:
+
+``Matlab::mex``
+  The ``mex`` library, always available.
+
+``Matlab::mx``
+  The mx library of Matlab (arrays), always available.
+
+``Matlab::eng``
+  Matlab engine library. Available only if the ``ENG_LIBRARY`` component
+  is requested.
+
+``Matlab::mat``
+  Matlab matrix library. Available only if the ``MAT_LIBRARY`` component
+  is requested.
+
+``Matlab::MatlabEngine``
+  Matlab C++ engine library, always available for R2018a and newer.
+
+``Matlab::MatlabDataArray``
+  Matlab C++ data array library, always available for R2018a and newer.
+
 Variables defined by the module
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -451,7 +478,7 @@
 
   if(matlabs_from_registry)
     list(REMOVE_DUPLICATES matlabs_from_registry)
-    list(SORT matlabs_from_registry)
+    list(SORT matlabs_from_registry COMPARE NATURAL)
     list(REVERSE matlabs_from_registry)
   endif()
 
@@ -495,7 +522,7 @@
   # we order from more recent to older
   if(matlab_supported_versions)
     list(REMOVE_DUPLICATES matlab_supported_versions)
-    list(SORT matlab_supported_versions)
+    list(SORT matlab_supported_versions COMPARE NATURAL)
     list(REVERSE matlab_supported_versions)
   endif()
 
@@ -1326,7 +1353,7 @@
              ${versioninfo_string}
             )
 
-      if(CMAKE_MATCH_1 MATCHES "(([0-9])\\.([0-9]))[\\.0-9]*")
+      if(CMAKE_MATCH_1 MATCHES "(([0-9]+)\\.([0-9]+))[\\.0-9]*")
         set(_matlab_version_tmp "${CMAKE_MATCH_1}")
       endif()
     endif()
@@ -1545,6 +1572,30 @@
     list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
     list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
     list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
+  elseif(DEFINED Matlab_FIND_VERSION)
+    set(_list_index -1)
+    foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3)
+      list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version)
+      if(_matlab_root_version VERSION_GREATER_EQUAL Matlab_FIND_VERSION)
+        set(_list_index ${_matlab_root_index})
+        break()
+      endif()
+    endforeach()
+
+    if(_list_index LESS 0)
+      set(_list_index 1)
+    endif()
+
+    math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
+    math(EXPR _matlab_root_dir_index "${_list_index} + 1")
+    list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
+    list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
+    list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
+    # adding a warning in case of ambiguity
+    if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
+      message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
+                      " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
+    endif()
   else()
     list(GET _matlab_possible_roots 0 Matlab_Or_MCR)
     list(GET _matlab_possible_roots 1 Matlab_VERSION_STRING)
@@ -1866,6 +1917,32 @@
   list(APPEND Matlab_LIBRARIES ${Matlab_DATAARRAY_LIBRARY})
 endif()
 
+# internal
+# This small stub permits to add imported targets for the found MATLAB libraries
+function(_Matlab_add_imported_target _matlab_library_variable_name _matlab_library_target_name)
+  if(Matlab_${_matlab_library_variable_name}_LIBRARY)
+    if(NOT TARGET Matlab::${_matlab_library_target_name})
+      add_library(Matlab::${_matlab_library_target_name} UNKNOWN IMPORTED)
+      set_target_properties(Matlab::${_matlab_library_target_name} PROPERTIES
+        INTERFACE_INCLUDE_DIRECTORIES "${Matlab_INCLUDE_DIRS}"
+        IMPORTED_LOCATION "${Matlab_${_matlab_library_variable_name}_LIBRARY}")
+      if(_matlab_library_target_name STREQUAL "mex" OR
+         _matlab_library_target_name STREQUAL "eng" OR
+         _matlab_library_target_name STREQUAL "mat")
+        set_target_properties(Matlab::${_matlab_library_target_name} PROPERTIES
+          INTERFACE_LINK_LIBRARIES Matlab::mx)
+      endif()
+    endif()
+  endif()
+endfunction()
+
+_Matlab_add_imported_target(MX mx)
+_Matlab_add_imported_target(MEX mex)
+_Matlab_add_imported_target(ENG eng)
+_Matlab_add_imported_target(MAT mat)
+_Matlab_add_imported_target(ENGINE MatlabEngine)
+_Matlab_add_imported_target(DATAARRAY MatlabDataArray)
+
 find_package_handle_standard_args(
   Matlab
   FOUND_VAR Matlab_FOUND
diff --git a/Modules/FindPatch.cmake b/Modules/FindPatch.cmake
index 4108651..f4fe4a6 100644
--- a/Modules/FindPatch.cmake
+++ b/Modules/FindPatch.cmake
@@ -43,7 +43,7 @@
 
 # First search the PATH
 find_program(Patch_EXECUTABLE
-  NAME patch
+  NAMES patch
   PATHS ${_patch_path}
   DOC ${_doc}
   )
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index 01ad5ac..547bc52 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -12,11 +12,24 @@
 following variables will also be set:
 
 ``PKG_CONFIG_FOUND``
-  if pkg-config executable was found
-``PKG_CONFIG_EXECUTABLE``
-  pathname of the pkg-config program
+  True if a pkg-config executable was found.
+
 ``PKG_CONFIG_VERSION_STRING``
-  version of pkg-config (since CMake 2.8.8)
+  .. versionadded:: 2.8.8
+
+  The version of pkg-config that was found.
+
+``PKG_CONFIG_EXECUTABLE``
+  The pathname of the pkg-config program.
+
+``PKG_CONFIG_ARGN``
+  .. versionadded:: 3.22
+
+  A list of arguments to pass to pkg-config.
+
+Both ``PKG_CONFIG_EXECUTABLE`` and ``PKG_CONFIG_ARGN`` are initialized by the
+module, but may be overridden by the user.  See `Variables Affecting Behavior`_
+for how these variables are initialized.
 
 #]========================================]
 
@@ -29,13 +42,22 @@
 
 # find pkg-config, use PKG_CONFIG if set
 if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL ""))
-  set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable")
+  separate_arguments(PKG_CONFIG_FROM_ENV_SPLIT NATIVE_COMMAND PROGRAM SEPARATE_ARGS "$ENV{PKG_CONFIG}")
+  list(LENGTH PKG_CONFIG_FROM_ENV_SPLIT PKG_CONFIG_FROM_ENV_SPLIT_ARGC)
+  if(PKG_CONFIG_FROM_ENV_SPLIT_ARGC GREATER 0)
+    list(GET PKG_CONFIG_FROM_ENV_SPLIT 0 PKG_CONFIG_FROM_ENV_ARGV0)
+    if(PKG_CONFIG_FROM_ENV_SPLIT_ARGC GREATER 1)
+      list(SUBLIST PKG_CONFIG_FROM_ENV_SPLIT 1 -1 PKG_CONFIG_ARGN)
+    endif()
+    set(PKG_CONFIG_EXECUTABLE "${PKG_CONFIG_FROM_ENV_ARGV0}" CACHE FILEPATH "pkg-config executable")
+  endif()
 endif()
 
 set(PKG_CONFIG_NAMES "pkg-config")
 if(CMAKE_HOST_WIN32)
   list(PREPEND PKG_CONFIG_NAMES "pkg-config.bat")
 endif()
+list(APPEND PKG_CONFIG_NAMES "pkgconf")
 
 find_program(PKG_CONFIG_EXECUTABLE
   NAMES ${PKG_CONFIG_NAMES}
@@ -43,9 +65,12 @@
   DOC "pkg-config executable")
 mark_as_advanced(PKG_CONFIG_EXECUTABLE)
 
+set(PKG_CONFIG_ARGN "${PKG_CONFIG_ARGN}" CACHE STRING "Arguments to supply to pkg-config")
+mark_as_advanced(PKG_CONFIG_ARGN)
+
 set(_PKG_CONFIG_FAILURE_MESSAGE "")
 if (PKG_CONFIG_EXECUTABLE)
-  execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --version
+  execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_ARGN} --version
     OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE
     ERROR_VARIABLE _PKG_CONFIG_VERSION_ERROR ERROR_STRIP_TRAILING_WHITESPACE
     RESULT_VARIABLE _PKG_CONFIG_VERSION_RESULT
@@ -53,14 +78,18 @@
 
   if (NOT _PKG_CONFIG_VERSION_RESULT EQUAL 0)
     string(REPLACE "\n" "\n    " _PKG_CONFIG_VERSION_ERROR "      ${_PKG_CONFIG_VERSION_ERROR}")
+    if(PKG_CONFIG_ARGN)
+      string(REPLACE ";" " " PKG_CONFIG_ARGN " ${PKG_CONFIG_ARGN}")
+    endif()
     string(APPEND _PKG_CONFIG_FAILURE_MESSAGE
       "The command\n"
-      "      \"${PKG_CONFIG_EXECUTABLE}\" --version\n"
+      "      \"${PKG_CONFIG_EXECUTABLE}\"${PKG_CONFIG_ARGN} --version\n"
       "    failed with output:\n${PKG_CONFIG_VERSION_STRING}\n"
       "    stderr: \n${_PKG_CONFIG_VERSION_ERROR}\n"
       "    result: \n${_PKG_CONFIG_VERSION_RESULT}"
       )
     set(PKG_CONFIG_EXECUTABLE "")
+    set(PKG_CONFIG_ARGN "")
     unset(PKG_CONFIG_VERSION_STRING)
   endif ()
   unset(_PKG_CONFIG_VERSION_RESULT)
@@ -99,7 +128,7 @@
   set(_pkgconfig_invoke_result)
 
   execute_process(
-    COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${_pkglist}
+    COMMAND ${PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_ARGN} ${ARGN} ${_pkglist}
     OUTPUT_VARIABLE _pkgconfig_invoke_result
     RESULT_VARIABLE _pkgconfig_failed
     OUTPUT_STRIP_TRAILING_WHITESPACE)
@@ -393,6 +422,14 @@
     unset(_lib_dirs)
     unset(_pkgconfig_path)
   endif()
+
+  # Tell pkg-config not to strip any -L paths so we can search them all.
+  if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS})
+    set(_pkgconfig_allow_system_libs_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}")
+  else()
+    unset(_pkgconfig_allow_system_libs_old)
+  endif()
+  set(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS} 0)
 endmacro()
 
 macro(_pkg_restore_path_internal)
@@ -400,6 +437,10 @@
     # Restore the environment variable
     set(ENV{PKG_CONFIG_PATH} "${_pkgconfig_path_old}")
   endif()
+  if(DEFINED _pkgconfig_allow_system_libs_old)
+    set(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS} "${_pkgconfig_allow_system_libs_old}")
+    unset(_pkgconfig_allow_system_libs_old)
+  endif()
 
   unset(_extra_paths)
   unset(_pkgconfig_path_old)
@@ -541,7 +582,7 @@
 
       # execute the query
       execute_process(
-        COMMAND ${PKG_CONFIG_EXECUTABLE} ${_pkg_check_modules_exist_query}
+        COMMAND ${PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_ARGN} ${_pkg_check_modules_exist_query}
         RESULT_VARIABLE _pkgconfig_retval
         ERROR_VARIABLE _pkgconfig_error
         ERROR_STRIP_TRAILING_WHITESPACE)
@@ -893,12 +934,30 @@
 
 .. variable:: PKG_CONFIG_EXECUTABLE
 
-  This can be set to the path of the pkg-config executable.  If not provided,
-  it will be set by the module as a result of calling :command:`find_program`
-  internally.
+  This cache variable can be set to the path of the pkg-config executable.
+  :command:`find_program` is called internally by the module with this
+  variable.
 
   .. versionadded:: 3.1
-    The ``PKG_CONFIG`` environment variable can be used as a hint.
+    The ``PKG_CONFIG`` environment variable can be used as a hint if
+    ``PKG_CONFIG_EXECUTABLE`` has not yet been set.
+
+  .. versionchanged:: 3.22
+    If the ``PKG_CONFIG`` environment variable is set, only the first
+    argument is taken from it when using it as a hint.
+
+.. variable:: PKG_CONFIG_ARGN
+
+  .. versionadded:: 3.22
+
+  This cache variable can be set to a list of arguments to additionally pass
+  to pkg-config if needed. If not provided, it will be initialized from the
+  ``PKG_CONFIG`` environment variable, if set. The first argument in that
+  environment variable is assumed to be the pkg-config program, while all
+  remaining arguments after that are used to initialize ``PKG_CONFIG_ARGN``.
+  If no such environment variable is defined, ``PKG_CONFIG_ARGN`` is
+  initialized to an empty string. The module does not update the variable once
+  it has been set in the cache.
 
 .. variable:: PKG_CONFIG_USE_CMAKE_PREFIX_PATH
 
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake
index a4b7a03..375cb70 100644
--- a/Modules/FindPython.cmake
+++ b/Modules/FindPython.cmake
@@ -229,6 +229,11 @@
   * If set to TRUE, search **only** for static libraries.
   * If set to FALSE, search **only** for shared libraries.
 
+  .. note::
+
+    This hint will be ignored on ``Windows`` because static libraries are not
+    available on this platform.
+
 ``Python_FIND_ABI``
   .. versionadded:: 3.16
 
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index ce25cfc..b7a0ef6 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -22,7 +22,7 @@
   message (FATAL_ERROR "FindPython: INTERNAL ERROR")
 endif()
 if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3")
-  set(_${_PYTHON_PREFIX}_VERSIONS 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0)
+  set(_${_PYTHON_PREFIX}_VERSIONS 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0)
 elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "2")
   set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0)
 else()
diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake
index bb965b9..0888fad 100644
--- a/Modules/FindPython2.cmake
+++ b/Modules/FindPython2.cmake
@@ -215,6 +215,11 @@
   * If set to TRUE, search **only** for static libraries.
   * If set to FALSE, search **only** for shared libraries.
 
+  .. note::
+
+    This hint will be ignored on ``Windows`` because static libraries are not
+    available on this platform.
+
 ``Python2_FIND_STRATEGY``
   .. versionadded:: 3.15
 
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake
index f826fcf..145c95e 100644
--- a/Modules/FindPython3.cmake
+++ b/Modules/FindPython3.cmake
@@ -231,6 +231,11 @@
   * If set to TRUE, search **only** for static libraries.
   * If set to FALSE, search **only** for shared libraries.
 
+  .. note::
+
+    This hint will be ignored on ``Windows`` because static libraries are not
+    available on this platform.
+
 ``Python3_FIND_ABI``
   .. versionadded:: 3.16
 
diff --git a/Modules/FindPythonInterp.cmake b/Modules/FindPythonInterp.cmake
index efe0f11..4fc40c8 100644
--- a/Modules/FindPythonInterp.cmake
+++ b/Modules/FindPythonInterp.cmake
@@ -54,7 +54,7 @@
 
 set(_PYTHON1_VERSIONS 1.6 1.5)
 set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0)
-set(_PYTHON3_VERSIONS 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0)
+set(_PYTHON3_VERSIONS 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0)
 
 if(PythonInterp_FIND_VERSION)
     if(PythonInterp_FIND_VERSION_COUNT GREATER 1)
diff --git a/Modules/FindPythonLibs.cmake b/Modules/FindPythonLibs.cmake
index 396f0d2..c0caf34 100644
--- a/Modules/FindPythonLibs.cmake
+++ b/Modules/FindPythonLibs.cmake
@@ -79,7 +79,7 @@
 
 set(_PYTHON1_VERSIONS 1.6 1.5)
 set(_PYTHON2_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0)
-set(_PYTHON3_VERSIONS 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0)
+set(_PYTHON3_VERSIONS 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0)
 
 if(PythonLibs_FIND_VERSION)
     if(PythonLibs_FIND_VERSION_COUNT GREATER 1)
diff --git a/Modules/FindThreads.cmake b/Modules/FindThreads.cmake
index c6a3451..e4d6cf3 100644
--- a/Modules/FindThreads.cmake
+++ b/Modules/FindThreads.cmake
@@ -106,7 +106,7 @@
 # Do NOT even think about using it outside of this file!
 macro(_check_pthreads_flag)
   if(NOT Threads_FOUND)
-    # If we did not found -lpthread, -lpthread, or -lthread, look for -pthread
+    # If we did not find -lpthreads, -lpthread, or -lthread, look for -pthread
     if(NOT DEFINED THREADS_HAVE_PTHREAD_ARG)
       message(CHECK_START "Check if compiler accepts -pthread")
       if(CMAKE_C_COMPILER_LOADED)
diff --git a/Modules/FindX11.cmake b/Modules/FindX11.cmake
index d480fc4..fd5ee53 100644
--- a/Modules/FindX11.cmake
+++ b/Modules/FindX11.cmake
@@ -828,6 +828,12 @@
     X11_Xau_INCLUDE_PATH
     X11_xcb_LIB
     X11_xcb_INCLUDE_PATH
+    X11_xcb_icccm_LIB
+    X11_xcb_icccm_INCLUDE_PATH
+    X11_xcb_util_LIB
+    X11_xcb_util_INCLUDE_PATH
+    X11_xcb_xfixes_LIB
+    X11_xcb_xfixes_INCLUDE_PATH
     X11_xcb_xkb_LIB
     X11_X11_xcb_LIB
     X11_X11_xcb_INCLUDE_PATH
diff --git a/Modules/FortranCInterface/CMakeLists.txt b/Modules/FortranCInterface/CMakeLists.txt
index 13e4498..ce0bc10 100644
--- a/Modules/FortranCInterface/CMakeLists.txt
+++ b/Modules/FortranCInterface/CMakeLists.txt
@@ -102,6 +102,19 @@
 add_executable(FortranCInterface main.F call_sub.f ${call_mod})
 target_link_libraries(FortranCInterface PUBLIC symbols)
 
+# If IPO is enabled here, GCC gfortran >= 12.0 will obfuscate
+# the strings of the return values in the compiled executable,
+# which we use to regex match against later.
+# The static libraries must be build with IPO and non-IPO objects,
+# as that will ensure the verify step will operate on IPO objects,
+# if requested by the system compiler flags.
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND
+  CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER_EQUAL 12)
+  target_compile_options(FortranCInterface PRIVATE "-fno-lto")
+  target_compile_options(myfort PRIVATE "-flto=auto" "-ffat-lto-objects")
+  target_compile_options(symbols PRIVATE "-flto=auto" "-ffat-lto-objects")
+endif()
+
 file(GENERATE OUTPUT exe-$<CONFIG>.cmake CONTENT [[
 set(FortranCInterface_EXE "$<TARGET_FILE:FortranCInterface>")
 ]])
diff --git a/Modules/GNUInstallDirs.cmake b/Modules/GNUInstallDirs.cmake
index 4aaad79..01bd637 100644
--- a/Modules/GNUInstallDirs.cmake
+++ b/Modules/GNUInstallDirs.cmake
@@ -218,6 +218,7 @@
   # Override this default 'lib' with 'lib64' iff:
   #  - we are on Linux system but NOT cross-compiling
   #  - we are NOT on debian
+  #  - we are NOT building for conda
   #  - we are on a 64 bits system
   # reason is: amd64 ABI: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
   # For Debian with multiarch, use 'lib/${CMAKE_LIBRARY_ARCHITECTURE}' if
@@ -239,11 +240,36 @@
       "Unable to determine default CMAKE_INSTALL_LIBDIR directory because no target architecture is known. "
       "Please enable at least one language before including GNUInstallDirs.")
   endif()
+
   if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU)$"
-      AND NOT CMAKE_CROSSCOMPILING
-      AND NOT EXISTS "/etc/alpine-release"
-      AND NOT EXISTS "/etc/arch-release")
-    if (EXISTS "/etc/debian_version") # is this a debian system ?
+      AND NOT CMAKE_CROSSCOMPILING)
+    unset(__system_type_for_install)
+    if(DEFINED ENV{CONDA_BUILD} AND DEFINED ENV{PREFIX})
+      set(conda_prefix "$ENV{PREFIX}")
+      cmake_path(ABSOLUTE_PATH conda_prefix NORMALIZE)
+      if("${CMAKE_INSTALL_PREFIX}" STREQUAL conda_prefix)
+        set(__system_type_for_install "conda")
+      endif()
+    elseif(DEFINED ENV{CONDA_PREFIX})
+      set(conda_prefix "$ENV{CONDA_PREFIX}")
+      cmake_path(ABSOLUTE_PATH conda_prefix NORMALIZE)
+      if("${CMAKE_INSTALL_PREFIX}" STREQUAL conda_prefix AND
+         NOT ("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$" OR
+              "${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$"))
+        set(__system_type_for_install "conda")
+      endif()
+    endif()
+    if(NOT __system_type_for_install)
+      if (EXISTS "/etc/alpine-release")
+        set(__system_type_for_install "alpine")
+      elseif (EXISTS "/etc/arch-release")
+        set(__system_type_for_install "arch linux")
+      elseif (EXISTS "/etc/debian_version")
+        set(__system_type_for_install "debian")
+      endif()
+    endif()
+
+    if(__system_type_for_install STREQUAL "debian")
       if(CMAKE_LIBRARY_ARCHITECTURE)
         if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$")
           set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
@@ -253,7 +279,8 @@
           set(__LAST_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
         endif()
       endif()
-    else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
+    elseif(NOT DEFINED __system_type_for_install)
+      # not debian, alpine, arch, or conda so rely on CMAKE_SIZEOF_VOID_P:
       if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
         set(_LIBDIR_DEFAULT "lib64")
         if(DEFINED _GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX)
@@ -262,6 +289,8 @@
       endif()
     endif()
   endif()
+  unset(__system_type_for_install)
+
   if(NOT DEFINED CMAKE_INSTALL_LIBDIR)
     set(CMAKE_INSTALL_LIBDIR "${_LIBDIR_DEFAULT}" CACHE PATH "Object code libraries (${_LIBDIR_DEFAULT})")
   elseif(DEFINED __LAST_LIBDIR_DEFAULT
diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake
index e90a9b5..53584c6 100644
--- a/Modules/GetPrerequisites.cmake
+++ b/Modules/GetPrerequisites.cmake
@@ -746,7 +746,7 @@
     set(gp_regex_error "")
     set(gp_regex_fallback "")
     set(gp_regex_cmp_count 1)
-  elseif(gp_tool MATCHES "objdump$")
+  elseif(gp_tool MATCHES "objdump(\\.exe)?$")
     set(gp_cmd_args "-p")
     set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
     set(gp_regex_error "")
diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake
index 80d8e23..f5f4f02 100644
--- a/Modules/GoogleTest.cmake
+++ b/Modules/GoogleTest.cmake
@@ -151,6 +151,7 @@
                          [WORKING_DIRECTORY dir]
                          [TEST_PREFIX prefix]
                          [TEST_SUFFIX suffix]
+                         [TEST_FILTER expr]
                          [NO_PRETTY_TYPES] [NO_PRETTY_VALUES]
                          [PROPERTIES name1 value1...]
                          [TEST_LIST var]
@@ -204,6 +205,15 @@
     every discovered test case.  Both ``TEST_PREFIX`` and ``TEST_SUFFIX`` may
     be specified.
 
+  ``TEST_FILTER expr``
+    .. versionadded:: 3.22
+
+    Filter expression to pass as a ``--gtest_filter`` argument during test
+    discovery.  Note that the expression is a wildcard-based format that
+    matches against the original test names as used by gtest.  For type or
+    value-parameterized tests, these names may be different to the potentially
+    pretty-printed test names that ``ctest`` uses.
+
   ``NO_PRETTY_TYPES``
     By default, the type index of type-parameterized tests is replaced by the
     actual type name in the CTest test name.  If this behavior is undesirable
@@ -411,7 +421,7 @@
     ""
     "NO_PRETTY_TYPES;NO_PRETTY_VALUES"
     "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;DISCOVERY_TIMEOUT;XML_OUTPUT_DIR;DISCOVERY_MODE"
-    "EXTRA_ARGS;PROPERTIES"
+    "EXTRA_ARGS;PROPERTIES;TEST_FILTER"
     ${ARGN}
   )
 
@@ -475,6 +485,7 @@
               -D "TEST_PROPERTIES=${_PROPERTIES}"
               -D "TEST_PREFIX=${_TEST_PREFIX}"
               -D "TEST_SUFFIX=${_TEST_SUFFIX}"
+              -D "TEST_FILTER=${_TEST_FILTER}"
               -D "NO_PRETTY_TYPES=${_NO_PRETTY_TYPES}"
               -D "NO_PRETTY_VALUES=${_NO_PRETTY_VALUES}"
               -D "TEST_LIST=${_TEST_LIST}"
@@ -505,7 +516,8 @@
     string(CONCAT ctest_include_content
       "if(EXISTS \"$<TARGET_FILE:${TARGET}>\")"                                    "\n"
       "  if(NOT EXISTS \"${ctest_tests_file}\" OR"                                 "\n"
-      "     NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\")" "\n"
+      "     NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n"
+      "     NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n"
       "    include(\"${_GOOGLETEST_DISCOVER_TESTS_SCRIPT}\")"                      "\n"
       "    gtest_discover_tests_impl("                                             "\n"
       "      TEST_EXECUTABLE"        " [==[" "$<TARGET_FILE:${TARGET}>"   "]==]"   "\n"
@@ -515,6 +527,7 @@
       "      TEST_PROPERTIES"        " [==[" "${_PROPERTIES}"             "]==]"   "\n"
       "      TEST_PREFIX"            " [==[" "${_TEST_PREFIX}"            "]==]"   "\n"
       "      TEST_SUFFIX"            " [==[" "${_TEST_SUFFIX}"            "]==]"   "\n"
+      "      TEST_FILTER"            " [==[" "${_TEST_FILTER}"            "]==]"   "\n"
       "      NO_PRETTY_TYPES"        " [==[" "${_NO_PRETTY_TYPES}"        "]==]"   "\n"
       "      NO_PRETTY_VALUES"       " [==[" "${_NO_PRETTY_VALUES}"       "]==]"   "\n"
       "      TEST_LIST"              " [==[" "${_TEST_LIST}"              "]==]"   "\n"
diff --git a/Modules/GoogleTestAddTests.cmake b/Modules/GoogleTestAddTests.cmake
index 0f79c9a..6b3bf34 100644
--- a/Modules/GoogleTestAddTests.cmake
+++ b/Modules/GoogleTestAddTests.cmake
@@ -44,7 +44,7 @@
   cmake_parse_arguments(
     ""
     ""
-    "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR"
+    "NO_PRETTY_TYPES;NO_PRETTY_VALUES;TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_PREFIX;TEST_SUFFIX;TEST_LIST;CTEST_FILE;TEST_DISCOVERY_TIMEOUT;TEST_XML_OUTPUT_DIR;TEST_FILTER"
     "TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR"
     ${ARGN}
   )
@@ -58,6 +58,12 @@
   set(tests)
   set(tests_buffer)
 
+  if(_TEST_FILTER)
+    set(filter "--gtest_filter=${_TEST_FILTER}")
+  else()
+    set(filter)
+  endif()
+
   # Run test executable to get list of available tests
   if(NOT EXISTS "${_TEST_EXECUTABLE}")
     message(FATAL_ERROR
@@ -66,7 +72,7 @@
     )
   endif()
   execute_process(
-    COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests
+    COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" --gtest_list_tests ${filter}
     WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
     TIMEOUT ${_TEST_DISCOVERY_TIMEOUT}
     OUTPUT_VARIABLE output
@@ -178,6 +184,7 @@
     TEST_WORKING_DIR ${TEST_WORKING_DIR}
     TEST_PREFIX ${TEST_PREFIX}
     TEST_SUFFIX ${TEST_SUFFIX}
+    TEST_FILTER ${TEST_FILTER}
     TEST_LIST ${TEST_LIST}
     CTEST_FILE ${CTEST_FILE}
     TEST_DISCOVERY_TIMEOUT ${TEST_DISCOVERY_TIMEOUT}
diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake
index a267278..d5e5fd2 100644
--- a/Modules/InstallRequiredSystemLibraries.cmake
+++ b/Modules/InstallRequiredSystemLibraries.cmake
@@ -73,6 +73,8 @@
 foreach(LANG IN ITEMS C CXX Fortran)
   if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "Intel")
     if(NOT _IRSL_HAVE_Intel)
+      # The oneAPI icx/ifx compilers are under ${os}/bin.
+      # The classic icc/icpc/icl/ifort compilers may be under ${os}/bin/intel64.
       get_filename_component(_Intel_basedir "${CMAKE_${LANG}_COMPILER}" PATH)
       if(CMAKE_SIZEOF_VOID_P EQUAL 8)
         set(_Intel_archdir intel64)
@@ -80,19 +82,37 @@
         set(_Intel_archdir ia32)
       endif()
       set(_Intel_compiler_ver ${CMAKE_${LANG}_COMPILER_VERSION})
-      if(WIN32 AND EXISTS "${_Intel_basedir}/../redist/${_Intel_archdir}_win/compiler")
-        get_filename_component(_Intel_redistdir "${_Intel_basedir}/../redist/${_Intel_archdir}_win/compiler" ABSOLUTE)
-      elseif(WIN32)
-        get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../redist/${_Intel_archdir}/compiler" ABSOLUTE)
+      if(WIN32)
+        set(_Intel_possible_redistdirs
+          "${_Intel_basedir}/../redist/${_Intel_archdir}_win/compiler"
+          "${_Intel_basedir}/../redist/${_Intel_archdir}/compiler"
+          "${_Intel_basedir}/../../redist/${_Intel_archdir}_win/compiler"
+          "${_Intel_basedir}/../../redist/${_Intel_archdir}/compiler"
+          )
       elseif(APPLE)
-        get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../compiler/lib" ABSOLUTE)
+        set(_Intel_possible_redistdirs
+          "${_Intel_basedir}/../../compiler/lib"
+          )
       else()
-        if(EXISTS "${_Intel_basedir}/../lib/${_Intel_archdir}_lin")
-          get_filename_component(_Intel_redistdir "${_Intel_basedir}/../lib/${_Intel_archdir}" ABSOLUTE)
-        else()
-          get_filename_component(_Intel_redistdir "${_Intel_basedir}/../../compiler/lib/${_Intel_archdir}_lin" ABSOLUTE)
-        endif()
+        set(_Intel_possible_redistdirs
+          "${_Intel_basedir}/../lib/${_Intel_archdir}"
+          "${_Intel_basedir}/../compiler/lib/${_Intel_archdir}_lin"
+          "${_Intel_basedir}/../../compiler/lib/${_Intel_archdir}_lin"
+          )
       endif()
+
+      set(_Intel_redistdir NOT-FOUND)
+      foreach(dir IN LISTS _Intel_possible_redistdirs)
+        if(EXISTS "${dir}")
+          set(_Intel_redistdir "${dir}")
+          break()
+        endif()
+      endforeach()
+      # Fall back to last dir
+      if(NOT _Intel_redistdir)
+        list(POP_BACK _Intel_possible_redistdirs _Intel_redistdir)
+      endif()
+      unset(_Intel_possible_redistdirs)
       set(_IRSL_HAVE_Intel TRUE)
     endif()
   elseif("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "MSVC")
diff --git a/Modules/Internal/CPack/CPackDeb.cmake b/Modules/Internal/CPack/CPackDeb.cmake
index 967ad7b..c115f00 100644
--- a/Modules/Internal/CPack/CPackDeb.cmake
+++ b/Modules/Internal/CPack/CPackDeb.cmake
@@ -662,10 +662,12 @@
 
   # add ldconfig call in default postrm and postint
   set(CPACK_ADD_LDCONFIG_CALL 0)
+  # all files in CPACK_DEB_SHARED_OBJECT_FILES have dot at the beginning
+  set(_LDCONF_DEFAULTS "./lib" "./usr/lib")
   foreach(_FILE IN LISTS CPACK_DEB_SHARED_OBJECT_FILES)
     get_filename_component(_DIR ${_FILE} DIRECTORY)
-    # all files in CPACK_DEB_SHARED_OBJECT_FILES have dot at the beginning
-    if(_DIR STREQUAL "./lib" OR _DIR STREQUAL "./usr/lib")
+    get_filename_component(_PARENT_DIR ${_DIR} DIRECTORY)
+    if(_DIR IN_LIST _LDCONF_DEFAULTS OR _PARENT_DIR IN_LIST _LDCONF_DEFAULTS)
       set(CPACK_ADD_LDCONFIG_CALL 1)
     endif()
   endforeach()
diff --git a/Modules/Internal/CPack/CPackRPM.cmake b/Modules/Internal/CPack/CPackRPM.cmake
index cbd748b..c72bf6d 100644
--- a/Modules/Internal/CPack/CPackRPM.cmake
+++ b/Modules/Internal/CPack/CPackRPM.cmake
@@ -1160,6 +1160,16 @@
     set(TMP_RPM_SPEC_INSTALL_POST "%define __spec_install_post ${CPACK_RPM_SPEC_INSTALL_POST}")
   endif()
 
+  # CPACK_RPM_REQUIRES_EXCLUDE_FROM
+  # May be defined to keep the dependency generator from
+  # scanning specific files or directories for deps.
+  if(CPACK_RPM_REQUIRES_EXCLUDE_FROM)
+    if(CPACK_RPM_PACKAGE_DEBUG)
+      message("CPackRPM:Debug: User defined CPACK_RPM_REQUIRES_EXCLUDE_FROM = ${CPACK_RPM_REQUIRES_EXCLUDE_FROM}")
+    endif()
+    set(TMP_RPM_REQUIRES_EXCLUDE_FROM "%global __requires_exclude_from ${CPACK_RPM_REQUIRES_EXCLUDE_FROM}")
+  endif()
+
   # CPACK_RPM_POST_INSTALL_SCRIPT_FILE (or CPACK_RPM_<COMPONENT>_POST_INSTALL_SCRIPT_FILE)
   # CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE (or CPACK_RPM_<COMPONENT>_POST_UNINSTALL_SCRIPT_FILE)
   # CPACK_RPM_POST_TRANS_SCRIPT_FILE (or CPACK_RPM_<COMPONENT>_POST_TRANS_SCRIPT_FILE)
@@ -1648,6 +1658,7 @@
 \@FILE_NAME_DEFINE\@
 %define _unpackaged_files_terminate_build 0
 \@TMP_RPM_SPEC_INSTALL_POST\@
+\@TMP_RPM_REQUIRES_EXCLUDE_FROM\@
 \@CPACK_RPM_SPEC_MORE_DEFINE\@
 \@CPACK_RPM_COMPRESSION_TYPE_TMP\@
 
@@ -1782,6 +1793,7 @@
 \@FILE_NAME_DEFINE\@
 %define _unpackaged_files_terminate_build 0
 \@TMP_RPM_SPEC_INSTALL_POST\@
+\@TMP_RPM_REQUIRES_EXCLUDE_FROM\@
 \@CPACK_RPM_SPEC_MORE_DEFINE\@
 \@CPACK_RPM_COMPRESSION_TYPE_TMP\@
 
diff --git a/Modules/Internal/CPack/NSIS.template.in b/Modules/Internal/CPack/NSIS.template.in
index 0a9c487..8a0c972 100644
--- a/Modules/Internal/CPack/NSIS.template.in
+++ b/Modules/Internal/CPack/NSIS.template.in
@@ -540,7 +540,7 @@
   @CPACK_NSIS_INSTALLER_WELCOME_TITLE_3LINES_CODE@
   !insertmacro MUI_PAGE_WELCOME
 
-  !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@"
+  @CPACK_NSIS_LICENSE_PAGE@
   Page custom InstallOptionsPage
   !insertmacro MUI_PAGE_DIRECTORY
 
@@ -564,21 +564,27 @@
 ;Languages
 
   !insertmacro MUI_LANGUAGE "English" ;first language is the default language
+  !insertmacro MUI_LANGUAGE "Afrikaans"
   !insertmacro MUI_LANGUAGE "Albanian"
   !insertmacro MUI_LANGUAGE "Arabic"
+  !insertmacro MUI_LANGUAGE "Asturian"
   !insertmacro MUI_LANGUAGE "Basque"
   !insertmacro MUI_LANGUAGE "Belarusian"
   !insertmacro MUI_LANGUAGE "Bosnian"
   !insertmacro MUI_LANGUAGE "Breton"
   !insertmacro MUI_LANGUAGE "Bulgarian"
+  !insertmacro MUI_LANGUAGE "Catalan"
+  !insertmacro MUI_LANGUAGE "Corsican"
   !insertmacro MUI_LANGUAGE "Croatian"
   !insertmacro MUI_LANGUAGE "Czech"
   !insertmacro MUI_LANGUAGE "Danish"
   !insertmacro MUI_LANGUAGE "Dutch"
+  !insertmacro MUI_LANGUAGE "Esperanto"
   !insertmacro MUI_LANGUAGE "Estonian"
   !insertmacro MUI_LANGUAGE "Farsi"
   !insertmacro MUI_LANGUAGE "Finnish"
   !insertmacro MUI_LANGUAGE "French"
+  !insertmacro MUI_LANGUAGE "Galician"
   !insertmacro MUI_LANGUAGE "German"
   !insertmacro MUI_LANGUAGE "Greek"
   !insertmacro MUI_LANGUAGE "Hebrew"
@@ -597,22 +603,29 @@
   !insertmacro MUI_LANGUAGE "Malay"
   !insertmacro MUI_LANGUAGE "Mongolian"
   !insertmacro MUI_LANGUAGE "Norwegian"
+  !insertmacro MUI_LANGUAGE "NorwegianNynorsk"
+  !insertmacro MUI_LANGUAGE "Pashto"
   !insertmacro MUI_LANGUAGE "Polish"
   !insertmacro MUI_LANGUAGE "Portuguese"
   !insertmacro MUI_LANGUAGE "PortugueseBR"
   !insertmacro MUI_LANGUAGE "Romanian"
   !insertmacro MUI_LANGUAGE "Russian"
+  !insertmacro MUI_LANGUAGE "ScotsGaelic"
   !insertmacro MUI_LANGUAGE "Serbian"
   !insertmacro MUI_LANGUAGE "SerbianLatin"
   !insertmacro MUI_LANGUAGE "SimpChinese"
   !insertmacro MUI_LANGUAGE "Slovak"
   !insertmacro MUI_LANGUAGE "Slovenian"
   !insertmacro MUI_LANGUAGE "Spanish"
+  !insertmacro MUI_LANGUAGE "SpanishInternational"
   !insertmacro MUI_LANGUAGE "Swedish"
+  !insertmacro MUI_LANGUAGE "Tatar"
   !insertmacro MUI_LANGUAGE "Thai"
   !insertmacro MUI_LANGUAGE "TradChinese"
   !insertmacro MUI_LANGUAGE "Turkish"
   !insertmacro MUI_LANGUAGE "Ukrainian"
+  !insertmacro MUI_LANGUAGE "Uzbek"
+  !insertmacro MUI_LANGUAGE "Vietnamese"
   !insertmacro MUI_LANGUAGE "Welsh"
 
 ;--------------------------------
@@ -661,7 +674,7 @@
   Push "@CPACK_PACKAGE_VENDOR@"
   Call ConditionalAddToRegisty
   Push "UninstallString"
-  Push "$INSTDIR\@CPACK_NSIS_UNINSTALL_NAME@.exe"
+  Push "$\"$INSTDIR\@CPACK_NSIS_UNINSTALL_NAME@.exe$\""
   Call ConditionalAddToRegisty
   Push "NoRepair"
   Push "1"
diff --git a/Modules/Internal/OSRelease/010-TryOldCentOS.cmake b/Modules/Internal/OSRelease/010-TryOldCentOS.cmake
new file mode 100644
index 0000000..ff35897
--- /dev/null
+++ b/Modules/Internal/OSRelease/010-TryOldCentOS.cmake
@@ -0,0 +1,41 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Author: Alex Turbov
+
+if(NOT EXISTS "${CMAKE_SYSROOT}/etc/centos-release")
+  return()
+endif()
+
+# Get the first string only
+file(
+    STRINGS "${CMAKE_SYSROOT}/etc/centos-release" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
+    LIMIT_COUNT 1
+  )
+
+#
+# Example:
+#   CentOS release 6.10 (Final)
+#
+if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "CentOS release ([0-9\.]+) .*")
+
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME CentOS)
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT}")
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID centos)
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE rhel)
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_MATCH_1})
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_MATCH_1})
+
+  list(
+      APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
+    )
+
+endif()
+
+unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)
diff --git a/Modules/Internal/OSRelease/020-TryDebianVersion.cmake b/Modules/Internal/OSRelease/020-TryDebianVersion.cmake
new file mode 100644
index 0000000..8ebe19a
--- /dev/null
+++ b/Modules/Internal/OSRelease/020-TryDebianVersion.cmake
@@ -0,0 +1,38 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Author: Alex Turbov
+
+if(NOT EXISTS "${CMAKE_SYSROOT}/etc/debian_version")
+  return()
+endif()
+
+# Get the first string only
+file(
+    STRINGS "${CMAKE_SYSROOT}/etc/debian_version" CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
+    LIMIT_COUNT 1
+  )
+
+#
+# Example:
+#   6.0.10          # Old debian
+#   wheezy/sid      # Ubuntu
+#
+if(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT MATCHES "[0-9]+(\.[0-9]+)*")
+
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME Debian)
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID debian)
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION ${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT})
+  set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID ${CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT})
+
+  list(
+      APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
+      CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
+    )
+
+endif()
+
+unset(CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT)
diff --git a/Modules/Platform/Android-Common.cmake b/Modules/Platform/Android-Common.cmake
index 39da933..f1deaca 100644
--- a/Modules/Platform/Android-Common.cmake
+++ b/Modules/Platform/Android-Common.cmake
@@ -56,7 +56,7 @@
         if(_ANDROID_STL_EXCEPTIONS OR _ANDROID_STL_RTTI)
           string(APPEND CMAKE_${lang}_STANDARD_LIBRARIES " -lc++abi")
           if(CMAKE_SYSTEM_VERSION LESS 21)
-            list(APPEND CMAKE_${lang}_STANDARD_LIBRARIES "-landroid_support")
+            string(APPEND CMAKE_${lang}_STANDARD_LIBRARIES " -landroid_support")
           endif()
         endif()
       endmacro()
diff --git a/Modules/Platform/Android-Determine.cmake b/Modules/Platform/Android-Determine.cmake
index 7b67f83..a4e9574 100644
--- a/Modules/Platform/Android-Determine.cmake
+++ b/Modules/Platform/Android-Determine.cmake
@@ -52,14 +52,16 @@
     endif()
     configure_file(${CMAKE_ROOT}/Modules/Platform/Android/VCXProjInspect.vcxproj.in
       ${CMAKE_PLATFORM_INFO_DIR}/VCXProjInspect.vcxproj @ONLY)
+    cmake_host_system_information(RESULT _msbuild QUERY VS_MSBUILD_COMMAND) # undocumented query
     execute_process(
-      COMMAND "${CMAKE_VS_MSBUILD_COMMAND}" "VCXProjInspect.vcxproj"
+      COMMAND "${_msbuild}" "VCXProjInspect.vcxproj"
         "/p:Configuration=Debug" "/p:Platform=${vcx_platform}"
       WORKING_DIRECTORY ${CMAKE_PLATFORM_INFO_DIR}
       OUTPUT_VARIABLE VCXPROJ_INSPECT_OUTPUT
       ERROR_VARIABLE VCXPROJ_INSPECT_OUTPUT
       RESULT_VARIABLE VCXPROJ_INSPECT_RESULT
       )
+    unset(_msbuild)
     if(NOT CMAKE_SYSROOT AND VCXPROJ_INSPECT_OUTPUT MATCHES "CMAKE_SYSROOT=([^%\r\n]+)[\r\n]")
       # Strip VS diagnostic output from the end of the line.
       string(REGEX REPLACE " \\(TaskId:[0-9]*\\)$" "" _sysroot "${CMAKE_MATCH_1}")
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index 373be76..1c32018 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -38,6 +38,8 @@
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ")
   set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP)
 
+  set(CMAKE_${lang}_LINKER_MANIFEST_FLAG " -Xlinker /MANIFESTINPUT:")
+
   if("${CMAKE_${lang}_SIMULATE_VERSION}" MATCHES "^([0-9]+)\\.([0-9]+)")
     math(EXPR MSVC_VERSION "${CMAKE_MATCH_1}*100 + ${CMAKE_MATCH_2}")
   endif()
@@ -66,10 +68,10 @@
   set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")
   set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES>")
+    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>")
   set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY})
   set(CMAKE_${lang}_LINK_EXECUTABLE
-    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES>")
+    "<CMAKE_${lang}_COMPILER> -fuse-ld=lld-link -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR> ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>")
 
   set(CMAKE_${lang}_CREATE_WIN32_EXE "-Xlinker /subsystem:windows")
   set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Xlinker /subsystem:console")
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index a04882f..51dc146 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -8,6 +8,8 @@
 endif()
 set(__WINDOWS_GNU 1)
 
+set(MINGW 1)
+
 set(CMAKE_IMPORT_LIBRARY_PREFIX "lib")
 set(CMAKE_SHARED_LIBRARY_PREFIX "lib")
 set(CMAKE_SHARED_MODULE_PREFIX  "lib")
@@ -19,16 +21,12 @@
 set(CMAKE_SHARED_MODULE_SUFFIX  ".dll")
 set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
 
-if(MSYS OR MINGW)
-  set(CMAKE_EXTRA_LINK_EXTENSIONS ".lib") # MinGW can also link to a MS .lib
-endif()
+set(CMAKE_EXTRA_LINK_EXTENSIONS ".lib") # MinGW can also link to a MS .lib
 
-if(MINGW)
-  set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
-  set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a" ".lib")
-  set(CMAKE_C_STANDARD_LIBRARIES_INIT "-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32")
-  set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}")
-endif()
+set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a" ".lib")
+set(CMAKE_C_STANDARD_LIBRARIES_INIT "-lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32")
+set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}")
 
 set(CMAKE_DL_LIBS "")
 set(CMAKE_LIBRARY_PATH_FLAG "-L")
@@ -48,21 +46,19 @@
 
 macro(__windows_compiler_gnu lang)
 
-  if(MSYS OR MINGW)
-    # Create archiving rules to support large object file lists for static libraries.
-    set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>")
-    set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")
-    set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
+  # Create archiving rules to support large object file lists for static libraries.
+  set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> qc <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> q <TARGET> <LINK_FLAGS> <OBJECTS>")
+  set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> <TARGET>")
 
-    # Initialize C link type selection flags.  These flags are used when
-    # building a shared library, shared module, or executable that links
-    # to other libraries to select whether to use the static or shared
-    # versions of the libraries.
-    foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
-      set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS "-Wl,-Bstatic")
-      set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic")
-    endforeach()
-  endif()
+  # Initialize C link type selection flags.  These flags are used when
+  # building a shared library, shared module, or executable that links
+  # to other libraries to select whether to use the static or shared
+  # versions of the libraries.
+  foreach(type SHARED_LIBRARY SHARED_MODULE EXE)
+    set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS "-Wl,-Bstatic")
+    set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS "-Wl,-Bdynamic")
+  endforeach()
 
   # No -fPIC on Windows
   set(CMAKE_${lang}_COMPILE_OPTIONS_PIC "")
diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake
index b48c33c..35b1704 100644
--- a/Modules/UseSWIG.cmake
+++ b/Modules/UseSWIG.cmake
@@ -192,12 +192,17 @@
   If set to ``TRUE``, implicit dependencies are generated by the ``swig`` tool
   itself. This property is only meaningful for
   :ref:`Makefile <Makefile Generators>`,
-  :ref:`Ninja <Ninja Generators>` and :generator:`Xcode` generators. Default
-  value is ``FALSE``.
+  :ref:`Ninja <Ninja Generators>`, :generator:`Xcode`, and
+  :ref:`Visual Studio <Visual Studio Generators>`
+  (:generator:`Visual Studio 11 2012` and above) generators. Default value is
+  ``FALSE``.
 
   .. versionadded:: 3.21
     Added the support of :generator:`Xcode` generator.
 
+  .. versionadded:: 3.22
+    Added the support of :ref:`Visual Studio Generators`.
+
 ``SWIG_MODULE_NAME``
   Specify the actual import name of the module in the target language.
   This is required if it cannot be scanned automatically from source
@@ -342,8 +347,10 @@
   If set to ``TRUE``, implicit dependencies are generated by the ``swig`` tool
   itself. This variable is only meaningful for
   :ref:`Makefile <Makefile Generators>`,
-  :ref:`Ninja <Ninja Generators>` and :generator:`Xcode` generators. Default
-  value is ``FALSE``.
+  :ref:`Ninja <Ninja Generators>`, :generator:`Xcode`, and
+  :ref:`Visual Studio <Visual Studio Generators>`
+  (:generator:`Visual Studio 11 2012` and above) generators. Default value is
+  ``FALSE``.
 
   Source file property ``USE_SWIG_DEPENDENCIES``, if not defined, will be
   initialized with the value of this variable.
@@ -351,6 +358,9 @@
   .. versionadded:: 3.21
     Added the support of :generator:`Xcode` generator.
 
+  .. versionadded:: 3.22
+    Added the support of :ref:`Visual Studio Generators`.
+
 #]=======================================================================]
 
 cmake_policy(PUSH)
@@ -515,7 +525,7 @@
   endif()
 
   set (use_swig_dependencies ${SWIG_USE_SWIG_DEPENDENCIES})
-  if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode")
+  if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])")
     get_property(use_swig_dependencies_set SOURCE "${infile}" PROPERTY USE_SWIG_DEPENDENCIES SET)
     if (use_swig_dependencies_set)
       get_property(use_swig_dependencies SOURCE "${infile}" PROPERTY USE_SWIG_DEPENDENCIES)
@@ -840,8 +850,9 @@
     set(SWIG_SOURCE_FILE_EXTENSIONS ".i")
   endif()
 
-  if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode")
-    # For Makefiles and Ninja generators, use SWIG generated dependencies
+  if (CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])")
+    # For Makefiles, Ninja, Xcode and Visual Studio generators,
+    # use SWIG generated dependencies if requested
     if (NOT DEFINED SWIG_USE_SWIG_DEPENDENCIES)
         set (SWIG_USE_SWIG_DEPENDENCIES OFF)
     endif()
diff --git a/Modules/VTKCompatibility.cmake b/Modules/VTKCompatibility.cmake
index 1b0815e..4ee7643 100644
--- a/Modules/VTKCompatibility.cmake
+++ b/Modules/VTKCompatibility.cmake
@@ -1,6 +1,10 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
+# Not needed for "modern" VTK.
+if (EXISTS "${VTK_SOURCE_DIR}/CMake/vtkModule.cmake")
+  return ()
+endif ()
 
 if(APPLE)
   set(CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY}")
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 9a18184..c8498a9 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -10,36 +10,6 @@
 endif()
 
 include(CheckIncludeFile)
-# Check if we can build support for ELF parsing.
-if(WIN32)
-  set(HAVE_ELF_H 0)
-elseif(CMAKE_CXX_PLATFORM_ID MATCHES "OpenBSD")
-  CHECK_INCLUDE_FILES("stdint.h;elf_abi.h" HAVE_ELF_H)
-else()
-  CHECK_INCLUDE_FILE("elf.h" HAVE_ELF_H)
-endif()
-if(HAVE_ELF_H)
-  set(CMake_USE_ELF_PARSER 1)
-elseif(HAIKU)
-  # On Haiku, we need to include elf32.h from the private headers
-  set(CMake_HAIKU_INCLUDE_DIRS
-    /boot/system/develop/headers/private/system
-    /boot/system/develop/headers/private/system/arch/x86
-    )
-
-  set(CMAKE_REQUIRED_INCLUDES ${CMake_HAIKU_INCLUDE_DIRS})
-  CHECK_INCLUDE_FILE("elf32.h" HAVE_ELF32_H)
-  unset(CMAKE_REQUIRED_INCLUDES)
-
-  if(HAVE_ELF32_H)
-    set(CMake_USE_ELF_PARSER 1)
-  else()
-    unset(CMake_HAIKU_INCLUDE_DIRS)
-    set(CMake_USE_ELF_PARSER)
-  endif()
-else()
-  set(CMake_USE_ELF_PARSER)
-endif()
 
 if(NOT CMake_DEFAULT_RECURSION_LIMIT)
   if(DEFINED ENV{DASHBOARD_TEST_FROM_CTEST})
@@ -111,11 +81,6 @@
   ${CMake_HAIKU_INCLUDE_DIRS}
   )
 
-# Check if we can build the ELF parser.
-if(CMake_USE_ELF_PARSER)
-  set(ELF_SRCS cmELF.h cmELF.cxx)
-endif()
-
 # Check if we can build the Mach-O parser.
 if(CMake_USE_MACH_PARSER)
   set(MACH_SRCS cmMachO.h cmMachO.cxx)
@@ -245,7 +210,8 @@
   cmDocumentationSection.cxx
   cmDynamicLoader.cxx
   cmDynamicLoader.h
-  ${ELF_SRCS}
+  cmELF.h
+  cmELF.cxx
   cmExprParserHelper.cxx
   cmExportBuildAndroidMKGenerator.h
   cmExportBuildAndroidMKGenerator.cxx
@@ -415,6 +381,8 @@
   cmProcessOutput.h
   cmProcessTools.cxx
   cmProcessTools.h
+  cmValue.cxx
+  cmValue.h
   cmProperty.h
   cmPropertyDefinition.cxx
   cmPropertyDefinition.h
@@ -834,7 +802,7 @@
 endif ()
 
 # Watcom support
-if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
+if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
   set_property(SOURCE cmake.cxx APPEND PROPERTY COMPILE_DEFINITIONS CMAKE_USE_WMAKE)
   list(APPEND SRCS
     cmGlobalWatcomWMakeGenerator.cxx
@@ -994,6 +962,7 @@
   CTest/cmCTestSubmitHandler.cxx
   CTest/cmCTestTestCommand.cxx
   CTest/cmCTestTestHandler.cxx
+  CTest/cmCTestTestMeasurementXMLParser.cxx
   CTest/cmCTestUpdateCommand.cxx
   CTest/cmCTestUpdateHandler.cxx
   CTest/cmCTestUploadCommand.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 4097e83..b592abf 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
-set(CMake_VERSION_MINOR 21)
-set(CMake_VERSION_PATCH 6)
+set(CMake_VERSION_MINOR 22)
+set(CMake_VERSION_PATCH 3)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CPack/IFW/cmCPackIFWCommon.cxx b/Source/CPack/IFW/cmCPackIFWCommon.cxx
index 87ebbfe..f6b8a8a 100644
--- a/Source/CPack/IFW/cmCPackIFWCommon.cxx
+++ b/Source/CPack/IFW/cmCPackIFWCommon.cxx
@@ -21,7 +21,7 @@
 {
 }
 
-const char* cmCPackIFWCommon::GetOption(const std::string& op) const
+cmValue cmCPackIFWCommon::GetOption(const std::string& op) const
 {
   return this->Generator ? this->Generator->cmCPackGenerator::GetOption(op)
                          : nullptr;
@@ -51,7 +51,7 @@
   }
 
   return cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->Generator->FrameworkVersion.data(), version);
+    cmSystemTools::OP_LESS, this->Generator->FrameworkVersion, version);
 }
 
 bool cmCPackIFWCommon::IsVersionGreater(const char* version) const
@@ -61,8 +61,7 @@
   }
 
   return cmSystemTools::VersionCompare(
-    cmSystemTools::OP_GREATER, this->Generator->FrameworkVersion.data(),
-    version);
+    cmSystemTools::OP_GREATER, this->Generator->FrameworkVersion, version);
 }
 
 bool cmCPackIFWCommon::IsVersionEqual(const char* version) const
@@ -72,8 +71,7 @@
   }
 
   return cmSystemTools::VersionCompare(
-    cmSystemTools::OP_EQUAL, this->Generator->FrameworkVersion.data(),
-    version);
+    cmSystemTools::OP_EQUAL, this->Generator->FrameworkVersion, version);
 }
 
 void cmCPackIFWCommon::ExpandListArgument(
diff --git a/Source/CPack/IFW/cmCPackIFWCommon.h b/Source/CPack/IFW/cmCPackIFWCommon.h
index 42deda4..f2e6b88 100644
--- a/Source/CPack/IFW/cmCPackIFWCommon.h
+++ b/Source/CPack/IFW/cmCPackIFWCommon.h
@@ -7,6 +7,8 @@
 #include <map>
 #include <string>
 
+#include "cmValue.h"
+
 class cmCPackIFWGenerator;
 class cmXMLWriter;
 
@@ -26,7 +28,7 @@
 public:
   // Internal implementation
 
-  const char* GetOption(const std::string& op) const;
+  cmValue GetOption(const std::string& op) const;
   bool IsOn(const std::string& op) const;
   bool IsSetToOff(const std::string& op) const;
   bool IsSetToEmpty(const std::string& op) const;
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.cxx b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
index 2806c61..b375ba6 100644
--- a/Source/CPack/IFW/cmCPackIFWGenerator.cxx
+++ b/Source/CPack/IFW/cmCPackIFWGenerator.cxx
@@ -16,6 +16,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCPackIFWGenerator::cmCPackIFWGenerator()
 {
@@ -248,9 +249,9 @@
     tmpPref += "packages/" + this->GetRootPackageName() + "/data";
   }
 
-  this->SetOption("CPACK_IFW_PACKAGING_INSTALL_PREFIX", tmpPref.c_str());
+  this->SetOption("CPACK_IFW_PACKAGING_INSTALL_PREFIX", tmpPref);
 
-  return this->GetOption("CPACK_IFW_PACKAGING_INSTALL_PREFIX");
+  return this->GetOption("CPACK_IFW_PACKAGING_INSTALL_PREFIX")->c_str();
 }
 
 const char* cmCPackIFWGenerator::GetOutputExtension()
@@ -273,11 +274,11 @@
 
   // Look 'binarycreator' executable (needs)
 
-  const char* BinCreatorStr = this->GetOption(BinCreatorOpt);
+  cmValue BinCreatorStr = this->GetOption(BinCreatorOpt);
   if (!BinCreatorStr || cmIsNOTFOUND(BinCreatorStr)) {
     this->BinCreator.clear();
   } else {
-    this->BinCreator = BinCreatorStr;
+    this->BinCreator = *BinCreatorStr;
   }
 
   if (this->BinCreator.empty()) {
@@ -290,16 +291,16 @@
 
   // Look 'repogen' executable (optional)
 
-  const char* RepoGenStr = this->GetOption(RepoGenOpt);
-  if (!RepoGenStr || cmIsNOTFOUND(RepoGenStr)) {
+  cmValue repoGen = this->GetOption(RepoGenOpt);
+  if (!repoGen || cmIsNOTFOUND(repoGen)) {
     this->RepoGen.clear();
   } else {
-    this->RepoGen = RepoGenStr;
+    this->RepoGen = *repoGen;
   }
 
   // Framework version
-  if (const char* FrameworkVersionSrt = this->GetOption(FrameworkVersionOpt)) {
-    this->FrameworkVersion = FrameworkVersionSrt;
+  if (cmValue frameworkVersion = this->GetOption(FrameworkVersionOpt)) {
+    this->FrameworkVersion = *frameworkVersion;
   } else {
     this->FrameworkVersion = "1.9.9";
   }
@@ -312,14 +313,13 @@
 
   // Additional packages dirs
   this->PkgsDirsVector.clear();
-  if (const char* dirs = this->GetOption("CPACK_IFW_PACKAGES_DIRECTORIES")) {
+  if (cmValue dirs = this->GetOption("CPACK_IFW_PACKAGES_DIRECTORIES")) {
     cmExpandList(dirs, this->PkgsDirsVector);
   }
 
   // Additional repositories dirs
   this->RepoDirsVector.clear();
-  if (const char* dirs =
-        this->GetOption("CPACK_IFW_REPOSITORIES_DIRECTORIES")) {
+  if (cmValue dirs = this->GetOption("CPACK_IFW_REPOSITORIES_DIRECTORIES")) {
     cmExpandList(dirs, this->RepoDirsVector);
   }
 
@@ -330,22 +330,22 @@
   // Repository
   this->Repository.Generator = this;
   this->Repository.Name = "Unspecified";
-  if (const char* site = this->GetOption("CPACK_DOWNLOAD_SITE")) {
-    this->Repository.Url = site;
+  if (cmValue site = this->GetOption("CPACK_DOWNLOAD_SITE")) {
+    this->Repository.Url = *site;
     this->Installer.RemoteRepositories.push_back(&this->Repository);
   }
 
   // Repositories
-  if (const char* RepoAllStr = this->GetOption("CPACK_IFW_REPOSITORIES_ALL")) {
+  if (cmValue RepoAllStr = this->GetOption("CPACK_IFW_REPOSITORIES_ALL")) {
     std::vector<std::string> RepoAllVector = cmExpandedList(RepoAllStr);
     for (std::string const& r : RepoAllVector) {
       this->GetRepository(r);
     }
   }
 
-  if (const char* ifwDownloadAll = this->GetOption("CPACK_IFW_DOWNLOAD_ALL")) {
+  if (cmValue ifwDownloadAll = this->GetOption("CPACK_IFW_DOWNLOAD_ALL")) {
     this->OnlineOnly = cmIsOn(ifwDownloadAll);
-  } else if (const char* cpackDownloadAll =
+  } else if (cmValue cpackDownloadAll =
                this->GetOption("CPACK_DOWNLOAD_ALL")) {
     this->OnlineOnly = cmIsOn(cpackDownloadAll);
   } else {
@@ -374,9 +374,9 @@
   }
 
   // Output extension
-  if (const char* optOutExt =
+  if (cmValue optOutExt =
         this->GetOption("CPACK_IFW_PACKAGE_FILE_EXTENSION")) {
-    this->OutputExtension = optOutExt;
+    this->OutputExtension = *optOutExt;
   } else if (sysName == "Darwin") {
     this->OutputExtension = ".dmg";
   } else {
@@ -508,21 +508,20 @@
 {
   // Default value
   std::string name = "root";
-  if (const char* optIFW_PACKAGE_GROUP =
+  if (cmValue optIFW_PACKAGE_GROUP =
         this->GetOption("CPACK_IFW_PACKAGE_GROUP")) {
     // Configure from root group
     cmCPackIFWPackage package;
     package.Generator = this;
     package.ConfigureFromGroup(optIFW_PACKAGE_GROUP);
     name = package.Name;
-  } else if (const char* optIFW_PACKAGE_NAME =
+  } else if (cmValue optIFW_PACKAGE_NAME =
                this->GetOption("CPACK_IFW_PACKAGE_NAME")) {
     // Configure from root package name
-    name = optIFW_PACKAGE_NAME;
-  } else if (const char* optPACKAGE_NAME =
-               this->GetOption("CPACK_PACKAGE_NAME")) {
+    name = *optIFW_PACKAGE_NAME;
+  } else if (cmValue optPACKAGE_NAME = this->GetOption("CPACK_PACKAGE_NAME")) {
     // Configure from package name
-    name = optPACKAGE_NAME;
+    name = *optPACKAGE_NAME;
   }
   return name;
 }
@@ -537,10 +536,10 @@
   if (cmCPackIFWPackage* package = this->GetGroupPackage(group)) {
     return package->Name;
   }
-  const char* option =
+  cmValue option =
     this->GetOption("CPACK_IFW_COMPONENT_GROUP_" +
                     cmsys::SystemTools::UpperCase(group->Name) + "_NAME");
-  name = option ? option : group->Name;
+  name = option ? *option : group->Name;
   if (group->ParentGroup) {
     cmCPackIFWPackage* package = this->GetGroupPackage(group->ParentGroup);
     bool dot = !this->ResolveDuplicateNames;
@@ -563,8 +562,8 @@
   }
   std::string prefix = "CPACK_IFW_COMPONENT_" +
     cmsys::SystemTools::UpperCase(component->Name) + "_";
-  const char* option = this->GetOption(prefix + "NAME");
-  name = option ? option : component->Name;
+  cmValue option = this->GetOption(prefix + "NAME");
+  name = option ? *option : component->Name;
   if (component->Group) {
     cmCPackIFWPackage* package = this->GetGroupPackage(component->Group);
     if ((this->componentPackageMethod ==
diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.cxx b/Source/CPack/IFW/cmCPackIFWInstaller.cxx
index bf8b457..7ee6300 100644
--- a/Source/CPack/IFW/cmCPackIFWInstaller.cxx
+++ b/Source/CPack/IFW/cmCPackIFWInstaller.cxx
@@ -14,6 +14,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLParser.h"
 #include "cmXMLWriter.h"
 
@@ -33,61 +34,60 @@
 void cmCPackIFWInstaller::ConfigureFromOptions()
 {
   // Name;
-  if (const char* optIFW_PACKAGE_NAME =
+  if (cmValue optIFW_PACKAGE_NAME =
         this->GetOption("CPACK_IFW_PACKAGE_NAME")) {
-    this->Name = optIFW_PACKAGE_NAME;
-  } else if (const char* optPACKAGE_NAME =
-               this->GetOption("CPACK_PACKAGE_NAME")) {
-    this->Name = optPACKAGE_NAME;
+    this->Name = *optIFW_PACKAGE_NAME;
+  } else if (cmValue optPACKAGE_NAME = this->GetOption("CPACK_PACKAGE_NAME")) {
+    this->Name = *optPACKAGE_NAME;
   } else {
     this->Name = "Your package";
   }
 
   // Title;
-  if (const char* optIFW_PACKAGE_TITLE =
+  if (cmValue optIFW_PACKAGE_TITLE =
         this->GetOption("CPACK_IFW_PACKAGE_TITLE")) {
-    this->Title = optIFW_PACKAGE_TITLE;
-  } else if (const char* optPACKAGE_DESCRIPTION_SUMMARY =
+    this->Title = *optIFW_PACKAGE_TITLE;
+  } else if (cmValue optPACKAGE_DESCRIPTION_SUMMARY =
                this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) {
-    this->Title = optPACKAGE_DESCRIPTION_SUMMARY;
+    this->Title = *optPACKAGE_DESCRIPTION_SUMMARY;
   } else {
     this->Title = "Your package description";
   }
 
   // Version;
-  if (const char* option = this->GetOption("CPACK_PACKAGE_VERSION")) {
-    this->Version = option;
+  if (cmValue option = this->GetOption("CPACK_PACKAGE_VERSION")) {
+    this->Version = *option;
   } else {
     this->Version = "1.0.0";
   }
 
   // Publisher
-  if (const char* optIFW_PACKAGE_PUBLISHER =
+  if (cmValue optIFW_PACKAGE_PUBLISHER =
         this->GetOption("CPACK_IFW_PACKAGE_PUBLISHER")) {
-    this->Publisher = optIFW_PACKAGE_PUBLISHER;
-  } else if (const char* optPACKAGE_VENDOR =
+    this->Publisher = *optIFW_PACKAGE_PUBLISHER;
+  } else if (cmValue optPACKAGE_VENDOR =
                this->GetOption("CPACK_PACKAGE_VENDOR")) {
-    this->Publisher = optPACKAGE_VENDOR;
+    this->Publisher = *optPACKAGE_VENDOR;
   }
 
   // ProductUrl
-  if (const char* option = this->GetOption("CPACK_IFW_PRODUCT_URL")) {
-    this->ProductUrl = option;
+  if (cmValue option = this->GetOption("CPACK_IFW_PRODUCT_URL")) {
+    this->ProductUrl = *option;
   }
 
   // ApplicationIcon
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_ICON")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_ICON")) {
     if (cmSystemTools::FileExists(option)) {
-      this->InstallerApplicationIcon = option;
+      this->InstallerApplicationIcon = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_ICON", option);
     }
   }
 
   // WindowIcon
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_WINDOW_ICON")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_WINDOW_ICON")) {
     if (cmSystemTools::FileExists(option)) {
-      this->InstallerWindowIcon = option;
+      this->InstallerWindowIcon = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_WINDOW_ICON", option);
     }
@@ -103,45 +103,45 @@
   }
 
   // Logo
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_LOGO")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_LOGO")) {
     if (cmSystemTools::FileExists(option)) {
-      this->Logo = option;
+      this->Logo = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_LOGO", option);
     }
   }
 
   // Watermark
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_WATERMARK")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_WATERMARK")) {
     if (cmSystemTools::FileExists(option)) {
-      this->Watermark = option;
+      this->Watermark = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_WATERMARK", option);
     }
   }
 
   // Banner
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_BANNER")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_BANNER")) {
     if (cmSystemTools::FileExists(option)) {
-      this->Banner = option;
+      this->Banner = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_BANNER", option);
     }
   }
 
   // Background
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_BACKGROUND")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_BACKGROUND")) {
     if (cmSystemTools::FileExists(option)) {
-      this->Background = option;
+      this->Background = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_BACKGROUND", option);
     }
   }
 
   // WizardStyle
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_WIZARD_STYLE")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_WIZARD_STYLE")) {
     // Setting the user value in any case
-    this->WizardStyle = option;
+    this->WizardStyle = *option;
     // Check known values
     if (this->WizardStyle != "Modern" && this->WizardStyle != "Aero" &&
         this->WizardStyle != "Mac" && this->WizardStyle != "Classic") {
@@ -154,28 +154,28 @@
   }
 
   // StyleSheet
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_STYLE_SHEET")) {
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_STYLE_SHEET")) {
     if (cmSystemTools::FileExists(option)) {
-      this->StyleSheet = option;
+      this->StyleSheet = *option;
     } else {
       this->printSkippedOptionWarning("CPACK_IFW_PACKAGE_STYLE_SHEET", option);
     }
   }
 
   // WizardDefaultWidth
-  if (const char* option =
+  if (cmValue option =
         this->GetOption("CPACK_IFW_PACKAGE_WIZARD_DEFAULT_WIDTH")) {
-    this->WizardDefaultWidth = option;
+    this->WizardDefaultWidth = *option;
   }
 
   // WizardDefaultHeight
-  if (const char* option =
+  if (cmValue option =
         this->GetOption("CPACK_IFW_PACKAGE_WIZARD_DEFAULT_HEIGHT")) {
-    this->WizardDefaultHeight = option;
+    this->WizardDefaultHeight = *option;
   }
 
   // WizardShowPageList
-  if (const char* option =
+  if (cmValue option =
         this->GetOption("CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST")) {
     if (!this->IsVersionLess("4.0")) {
       if (this->IsSetToOff("CPACK_IFW_PACKAGE_WIZARD_SHOW_PAGE_LIST")) {
@@ -204,23 +204,23 @@
   }
 
   // TitleColor
-  if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_TITLE_COLOR")) {
-    this->TitleColor = option;
+  if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_TITLE_COLOR")) {
+    this->TitleColor = *option;
   }
 
   // Start menu
-  if (const char* optIFW_START_MENU_DIR =
+  if (cmValue optIFW_START_MENU_DIR =
         this->GetOption("CPACK_IFW_PACKAGE_START_MENU_DIRECTORY")) {
-    this->StartMenuDir = optIFW_START_MENU_DIR;
+    this->StartMenuDir = *optIFW_START_MENU_DIR;
   } else {
     this->StartMenuDir = this->Name;
   }
 
   // Default target directory for installation
-  if (const char* optIFW_TARGET_DIRECTORY =
+  if (cmValue optIFW_TARGET_DIRECTORY =
         this->GetOption("CPACK_IFW_TARGET_DIRECTORY")) {
-    this->TargetDir = optIFW_TARGET_DIRECTORY;
-  } else if (const char* optPACKAGE_INSTALL_DIRECTORY =
+    this->TargetDir = *optIFW_TARGET_DIRECTORY;
+  } else if (cmValue optPACKAGE_INSTALL_DIRECTORY =
                this->GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) {
     this->TargetDir =
       cmStrCat("@ApplicationsDir@/", optPACKAGE_INSTALL_DIRECTORY);
@@ -229,21 +229,20 @@
   }
 
   // Default target directory for installation with administrator rights
-  if (const char* option =
-        this->GetOption("CPACK_IFW_ADMIN_TARGET_DIRECTORY")) {
-    this->AdminTargetDir = option;
+  if (cmValue option = this->GetOption("CPACK_IFW_ADMIN_TARGET_DIRECTORY")) {
+    this->AdminTargetDir = *option;
   }
 
   // Maintenance tool
-  if (const char* optIFW_MAINTENANCE_TOOL =
+  if (cmValue optIFW_MAINTENANCE_TOOL =
         this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME")) {
-    this->MaintenanceToolName = optIFW_MAINTENANCE_TOOL;
+    this->MaintenanceToolName = *optIFW_MAINTENANCE_TOOL;
   }
 
   // Maintenance tool ini file
-  if (const char* optIFW_MAINTENANCE_TOOL_INI =
+  if (cmValue optIFW_MAINTENANCE_TOOL_INI =
         this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE")) {
-    this->MaintenanceToolIniFile = optIFW_MAINTENANCE_TOOL_INI;
+    this->MaintenanceToolIniFile = *optIFW_MAINTENANCE_TOOL_INI;
   }
 
   // Allow non-ASCII characters
@@ -265,13 +264,13 @@
   }
 
   // Control script
-  if (const char* optIFW_CONTROL_SCRIPT =
+  if (cmValue optIFW_CONTROL_SCRIPT =
         this->GetOption("CPACK_IFW_PACKAGE_CONTROL_SCRIPT")) {
-    this->ControlScript = optIFW_CONTROL_SCRIPT;
+    this->ControlScript = *optIFW_CONTROL_SCRIPT;
   }
 
   // Resources
-  if (const char* optIFW_PACKAGE_RESOURCES =
+  if (cmValue optIFW_PACKAGE_RESOURCES =
         this->GetOption("CPACK_IFW_PACKAGE_RESOURCES")) {
     this->Resources.clear();
     cmExpandList(optIFW_PACKAGE_RESOURCES, this->Resources);
@@ -541,7 +540,7 @@
     package.Generator = this->Generator;
     package.Installer = this;
     // Check package group
-    if (const char* option = this->GetOption("CPACK_IFW_PACKAGE_GROUP")) {
+    if (cmValue option = this->GetOption("CPACK_IFW_PACKAGE_GROUP")) {
       package.ConfigureFromGroup(option);
       std::string forcedOption = "CPACK_IFW_COMPONENT_GROUP_" +
         cmsys::SystemTools::UpperCase(option) + "_FORCED_INSTALLATION";
diff --git a/Source/CPack/IFW/cmCPackIFWPackage.cxx b/Source/CPack/IFW/cmCPackIFWPackage.cxx
index 1429c46..c2109c9 100644
--- a/Source/CPack/IFW/cmCPackIFWPackage.cxx
+++ b/Source/CPack/IFW/cmCPackIFWPackage.cxx
@@ -18,6 +18,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 
 //---------------------------------------------------------- CompareStruct ---
@@ -124,10 +125,10 @@
   if (!component) {
     return "";
   }
-  const char* option =
+  cmValue option =
     this->GetOption("CPACK_IFW_COMPONENT_" +
                     cmsys::SystemTools::UpperCase(component->Name) + "_NAME");
-  return option ? option : component->Name;
+  return option ? *option : component->Name;
 }
 
 void cmCPackIFWPackage::DefaultConfiguration()
@@ -159,23 +160,22 @@
   this->Name = this->Generator->GetRootPackageName();
 
   // Display name
-  if (const char* option = this->GetOption("CPACK_PACKAGE_NAME")) {
-    this->DisplayName[""] = option;
+  if (cmValue option = this->GetOption("CPACK_PACKAGE_NAME")) {
+    this->DisplayName[""] = *option;
   } else {
     this->DisplayName[""] = "Your package";
   }
 
   // Description
-  if (const char* option =
-        this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) {
-    this->Description[""] = option;
+  if (cmValue option = this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) {
+    this->Description[""] = *option;
   } else {
     this->Description[""] = "Your package description";
   }
 
   // Version
-  if (const char* option = this->GetOption("CPACK_PACKAGE_VERSION")) {
-    this->Version = option;
+  if (cmValue option = this->GetOption("CPACK_PACKAGE_VERSION")) {
+    this->Version = *option;
   } else {
     this->Version = "1.0.0";
   }
@@ -204,22 +204,22 @@
   this->Description[""] = component->Description;
 
   // Version
-  if (const char* optVERSION = this->GetOption(prefix + "VERSION")) {
-    this->Version = optVERSION;
-  } else if (const char* optPACKAGE_VERSION =
+  if (cmValue optVERSION = this->GetOption(prefix + "VERSION")) {
+    this->Version = *optVERSION;
+  } else if (cmValue optPACKAGE_VERSION =
                this->GetOption("CPACK_PACKAGE_VERSION")) {
-    this->Version = optPACKAGE_VERSION;
+    this->Version = *optPACKAGE_VERSION;
   } else {
     this->Version = "1.0.0";
   }
 
   // Script
-  if (const char* option = this->GetOption(prefix + "SCRIPT")) {
-    this->Script = option;
+  if (cmValue option = this->GetOption(prefix + "SCRIPT")) {
+    this->Script = *option;
   }
 
   // User interfaces
-  if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) {
+  if (cmValue option = this->GetOption(prefix + "USER_INTERFACES")) {
     this->UserInterfaces.clear();
     cmExpandList(option, this->UserInterfaces);
   }
@@ -232,7 +232,7 @@
   }
 
   // Licenses
-  if (const char* option = this->GetOption(prefix + "LICENSES")) {
+  if (cmValue option = this->GetOption(prefix + "LICENSES")) {
     this->Licenses.clear();
     cmExpandList(option, this->Licenses);
     if (this->Licenses.size() % 2 != 0) {
@@ -246,8 +246,8 @@
   }
 
   // Priority
-  if (const char* option = this->GetOption(prefix + "PRIORITY")) {
-    this->SortingPriority = option;
+  if (cmValue option = this->GetOption(prefix + "PRIORITY")) {
+    this->SortingPriority = *option;
     cmCPackIFWLogger(
       WARNING,
       "The \"PRIORITY\" option is set "
@@ -289,28 +289,28 @@
   this->Description[""] = group->Description;
 
   // Version
-  if (const char* optVERSION = this->GetOption(prefix + "VERSION")) {
-    this->Version = optVERSION;
-  } else if (const char* optPACKAGE_VERSION =
+  if (cmValue optVERSION = this->GetOption(prefix + "VERSION")) {
+    this->Version = *optVERSION;
+  } else if (cmValue optPACKAGE_VERSION =
                this->GetOption("CPACK_PACKAGE_VERSION")) {
-    this->Version = optPACKAGE_VERSION;
+    this->Version = *optPACKAGE_VERSION;
   } else {
     this->Version = "1.0.0";
   }
 
   // Script
-  if (const char* option = this->GetOption(prefix + "SCRIPT")) {
-    this->Script = option;
+  if (cmValue option = this->GetOption(prefix + "SCRIPT")) {
+    this->Script = *option;
   }
 
   // User interfaces
-  if (const char* option = this->GetOption(prefix + "USER_INTERFACES")) {
+  if (cmValue option = this->GetOption(prefix + "USER_INTERFACES")) {
     this->UserInterfaces.clear();
     cmExpandList(option, this->UserInterfaces);
   }
 
   // Licenses
-  if (const char* option = this->GetOption(prefix + "LICENSES")) {
+  if (cmValue option = this->GetOption(prefix + "LICENSES")) {
     this->Licenses.clear();
     cmExpandList(option, this->Licenses);
     if (this->Licenses.size() % 2 != 0) {
@@ -324,8 +324,8 @@
   }
 
   // Priority
-  if (const char* option = this->GetOption(prefix + "PRIORITY")) {
-    this->SortingPriority = option;
+  if (cmValue option = this->GetOption(prefix + "PRIORITY")) {
+    this->SortingPriority = *option;
     cmCPackIFWLogger(
       WARNING,
       "The \"PRIORITY\" option is set "
@@ -346,14 +346,14 @@
   std::string prefix =
     "CPACK_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(groupName) + "_";
 
-  if (const char* option = this->GetOption(prefix + "DISPLAY_NAME")) {
-    group.DisplayName = option;
+  if (cmValue option = this->GetOption(prefix + "DISPLAY_NAME")) {
+    group.DisplayName = *option;
   } else {
     group.DisplayName = group.Name;
   }
 
-  if (const char* option = this->GetOption(prefix + "DESCRIPTION")) {
-    group.Description = option;
+  if (cmValue option = this->GetOption(prefix + "DESCRIPTION")) {
+    group.Description = *option;
   }
   group.IsBold = this->IsOn(prefix + "BOLD_TITLE");
   group.IsExpandedByDefault = this->IsOn(prefix + "EXPANDED");
@@ -381,7 +381,7 @@
   option = prefix + "DISPLAY_NAME";
   if (this->IsSetToEmpty(option)) {
     this->DisplayName.clear();
-  } else if (const char* value = this->GetOption(option)) {
+  } else if (cmValue value = this->GetOption(option)) {
     cmCPackIFWPackage::ExpandListArgument(value, this->DisplayName);
   }
 
@@ -389,7 +389,7 @@
   option = prefix + "DESCRIPTION";
   if (this->IsSetToEmpty(option)) {
     this->Description.clear();
-  } else if (const char* value = this->GetOption(option)) {
+  } else if (cmValue value = this->GetOption(option)) {
     cmCPackIFWPackage::ExpandListArgument(value, this->Description);
   }
 
@@ -397,31 +397,31 @@
   option = prefix + "RELEASE_DATE";
   if (this->IsSetToEmpty(option)) {
     this->ReleaseDate.clear();
-  } else if (const char* value = this->GetOption(option)) {
-    this->ReleaseDate = value;
+  } else if (cmValue value = this->GetOption(option)) {
+    this->ReleaseDate = *value;
   }
 
   // Sorting priority
   option = prefix + "SORTING_PRIORITY";
   if (this->IsSetToEmpty(option)) {
     this->SortingPriority.clear();
-  } else if (const char* value = this->GetOption(option)) {
-    this->SortingPriority = value;
+  } else if (cmValue value = this->GetOption(option)) {
+    this->SortingPriority = *value;
   }
 
   // Update text
   option = prefix + "UPDATE_TEXT";
   if (this->IsSetToEmpty(option)) {
     this->UpdateText.clear();
-  } else if (const char* value = this->GetOption(option)) {
-    this->UpdateText = value;
+  } else if (cmValue value = this->GetOption(option)) {
+    this->UpdateText = *value;
   }
 
   // Translations
   option = prefix + "TRANSLATIONS";
   if (this->IsSetToEmpty(option)) {
     this->Translations.clear();
-  } else if (const char* value = this->GetOption(option)) {
+  } else if (cmValue value = this->GetOption(option)) {
     this->Translations.clear();
     cmExpandList(value, this->Translations);
   }
@@ -429,11 +429,11 @@
   // QtIFW dependencies
   std::vector<std::string> deps;
   option = prefix + "DEPENDS";
-  if (const char* value = this->GetOption(option)) {
+  if (cmValue value = this->GetOption(option)) {
     cmExpandList(value, deps);
   }
   option = prefix + "DEPENDENCIES";
-  if (const char* value = this->GetOption(option)) {
+  if (cmValue value = this->GetOption(option)) {
     cmExpandList(value, deps);
   }
   for (std::string const& d : deps) {
@@ -454,7 +454,7 @@
   option = prefix + "AUTO_DEPEND_ON";
   if (this->IsSetToEmpty(option)) {
     this->AlienAutoDependOn.clear();
-  } else if (const char* value = this->GetOption(option)) {
+  } else if (cmValue value = this->GetOption(option)) {
     std::vector<std::string> depsOn = cmExpandedList(value);
     for (std::string const& d : depsOn) {
       DependenceStruct dep(d);
@@ -483,7 +483,7 @@
   option = prefix + "DEFAULT";
   if (this->IsSetToEmpty(option)) {
     this->Default.clear();
-  } else if (const char* value = this->GetOption(option)) {
+  } else if (cmValue value = this->GetOption(option)) {
     std::string lowerValue = cmsys::SystemTools::LowerCase(value);
     if (lowerValue == "true") {
       this->Default = "true";
@@ -492,7 +492,7 @@
     } else if (lowerValue == "script") {
       this->Default = "script";
     } else {
-      this->Default = value;
+      this->Default = *value;
     }
   }
 
@@ -510,7 +510,7 @@
   option = prefix + "REPLACES";
   if (this->IsSetToEmpty(option)) {
     this->Replaces.clear();
-  } else if (const char* value = this->GetOption(option)) {
+  } else if (cmValue value = this->GetOption(option)) {
     this->Replaces.clear();
     cmExpandList(value, this->Replaces);
   }
diff --git a/Source/CPack/IFW/cmCPackIFWRepository.cxx b/Source/CPack/IFW/cmCPackIFWRepository.cxx
index 7ec2256..f25d2d2 100644
--- a/Source/CPack/IFW/cmCPackIFWRepository.cxx
+++ b/Source/CPack/IFW/cmCPackIFWRepository.cxx
@@ -7,6 +7,7 @@
 #include "cmCPackIFWGenerator.h"
 #include "cmGeneratedFileStream.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLParser.h"
 #include "cmXMLWriter.h"
 
@@ -55,22 +56,22 @@
   }
 
   // Url
-  if (const char* url = this->GetOption(prefix + "URL")) {
-    this->Url = url;
+  if (cmValue url = this->GetOption(prefix + "URL")) {
+    this->Url = *url;
   } else {
     this->Url.clear();
   }
 
   // Old url
-  if (const char* oldUrl = this->GetOption(prefix + "OLD_URL")) {
-    this->OldUrl = oldUrl;
+  if (cmValue oldUrl = this->GetOption(prefix + "OLD_URL")) {
+    this->OldUrl = *oldUrl;
   } else {
     this->OldUrl.clear();
   }
 
   // New url
-  if (const char* newUrl = this->GetOption(prefix + "NEW_URL")) {
-    this->NewUrl = newUrl;
+  if (cmValue newUrl = this->GetOption(prefix + "NEW_URL")) {
+    this->NewUrl = *newUrl;
   } else {
     this->NewUrl.clear();
   }
@@ -83,22 +84,22 @@
   }
 
   // Username
-  if (const char* username = this->GetOption(prefix + "USERNAME")) {
-    this->Username = username;
+  if (cmValue username = this->GetOption(prefix + "USERNAME")) {
+    this->Username = *username;
   } else {
     this->Username.clear();
   }
 
   // Password
-  if (const char* password = this->GetOption(prefix + "PASSWORD")) {
-    this->Password = password;
+  if (cmValue password = this->GetOption(prefix + "PASSWORD")) {
+    this->Password = *password;
   } else {
     this->Password.clear();
   }
 
   // DisplayName
-  if (const char* displayName = this->GetOption(prefix + "DISPLAY_NAME")) {
-    this->DisplayName = displayName;
+  if (cmValue displayName = this->GetOption(prefix + "DISPLAY_NAME")) {
+    this->DisplayName = *displayName;
   } else {
     this->DisplayName.clear();
   }
diff --git a/Source/CPack/OSXScriptLauncher.cxx b/Source/CPack/OSXScriptLauncher.cxx
index bdaf779..b7140ab 100644
--- a/Source/CPack/OSXScriptLauncher.cxx
+++ b/Source/CPack/OSXScriptLauncher.cxx
@@ -118,5 +118,5 @@
 
   cmsysProcess_Delete(cp);
 
-  return 0;
+  return !result;
 }
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index 8b3644f..d03239b 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -21,6 +21,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmUuid.h"
+#include "cmValue.h"
 #include "cmWIXDirectoriesSourceWriter.h"
 #include "cmWIXFeaturesSourceWriter.h"
 #include "cmWIXFilesSourceWriter.h"
@@ -125,7 +126,7 @@
     command << " -ext " << QuotePath(ext);
   }
 
-  const char* const cultures = GetOption("CPACK_WIX_CULTURES");
+  cmValue const cultures = GetOption("CPACK_WIX_CULTURES");
   if (cultures) {
     command << " -cultures:" << cultures;
   }
@@ -156,18 +157,18 @@
     return false;
   }
 
-  if (GetOption("CPACK_WIX_PRODUCT_GUID") == 0) {
+  if (!GetOption("CPACK_WIX_PRODUCT_GUID")) {
     std::string guid = GenerateGUID();
-    SetOption("CPACK_WIX_PRODUCT_GUID", guid.c_str());
+    SetOption("CPACK_WIX_PRODUCT_GUID", guid);
 
     cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                   "CPACK_WIX_PRODUCT_GUID implicitly set to " << guid << " . "
                                                               << std::endl);
   }
 
-  if (GetOption("CPACK_WIX_UPGRADE_GUID") == 0) {
+  if (!GetOption("CPACK_WIX_UPGRADE_GUID")) {
     std::string guid = GenerateGUID();
-    SetOption("CPACK_WIX_UPGRADE_GUID", guid.c_str());
+    SetOption("CPACK_WIX_UPGRADE_GUID", guid);
 
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_WIX_UPGRADE_GUID implicitly set to "
@@ -182,36 +183,36 @@
     return false;
   }
 
-  if (GetOption("CPACK_WIX_LICENSE_RTF") == 0) {
+  if (!GetOption("CPACK_WIX_LICENSE_RTF")) {
     std::string licenseFilename = this->CPackTopLevel + "/License.rtf";
-    SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str());
+    SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename);
 
     if (!CreateLicenseFile()) {
       return false;
     }
   }
 
-  if (GetOption("CPACK_PACKAGE_VENDOR") == 0) {
+  if (!GetOption("CPACK_PACKAGE_VENDOR")) {
     std::string defaultVendor = "Humanity";
-    SetOption("CPACK_PACKAGE_VENDOR", defaultVendor.c_str());
+    SetOption("CPACK_PACKAGE_VENDOR", defaultVendor);
 
     cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                   "CPACK_PACKAGE_VENDOR implicitly set to "
                     << defaultVendor << " . " << std::endl);
   }
 
-  if (GetOption("CPACK_WIX_UI_REF") == 0) {
+  if (!GetOption("CPACK_WIX_UI_REF")) {
     std::string defaultRef = "WixUI_InstallDir";
 
     if (!this->Components.empty()) {
       defaultRef = "WixUI_FeatureTree";
     }
 
-    SetOption("CPACK_WIX_UI_REF", defaultRef.c_str());
+    SetOption("CPACK_WIX_UI_REF", defaultRef);
   }
 
-  const char* packageContact = GetOption("CPACK_PACKAGE_CONTACT");
-  if (packageContact != 0 && GetOption("CPACK_WIX_PROPERTY_ARPCONTACT") == 0) {
+  cmValue packageContact = GetOption("CPACK_PACKAGE_CONTACT");
+  if (packageContact && !GetOption("CPACK_WIX_PROPERTY_ARPCONTACT")) {
     SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact);
   }
 
@@ -223,7 +224,7 @@
   CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions);
   CollectXmlNamespaces("CPACK_WIX_CUSTOM_XMLNS", this->CustomXmlNamespaces);
 
-  const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
+  cmValue patchFilePath = GetOption("CPACK_WIX_PATCH_FILE");
   if (patchFilePath) {
     std::vector<std::string> patchFilePaths = cmExpandedList(patchFilePath);
 
@@ -295,7 +296,7 @@
 
 void cmCPackWIXGenerator::AppendUserSuppliedExtraSources()
 {
-  const char* cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES");
+  cmValue cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES");
   if (!cpackWixExtraSources)
     return;
 
@@ -304,7 +305,7 @@
 
 void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream)
 {
-  const char* cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS");
+  cmValue cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS");
   if (!cpackWixExtraObjects)
     return;
 
@@ -355,7 +356,7 @@
   for (std::string const& name : options) {
     if (cmHasPrefix(name, prefix)) {
       std::string id = name.substr(prefix.length());
-      std::string value = GetOption(name.c_str());
+      std::string value = GetOption(name);
 
       includeFile.BeginElement("Property");
       includeFile.AddAttribute("Id", id);
@@ -364,7 +365,7 @@
     }
   }
 
-  if (GetOption("CPACK_WIX_PROPERTY_ARPINSTALLLOCATION") == 0) {
+  if (!GetOption("CPACK_WIX_PROPERTY_ARPINSTALLLOCATION")) {
     includeFile.BeginElement("Property");
     includeFile.AddAttribute("Id", "INSTALL_ROOT");
     includeFile.AddAttribute("Secure", "yes");
@@ -405,7 +406,7 @@
                                          std::string const& name,
                                          DefinitionType type)
 {
-  const char* value = GetOption(name.c_str());
+  cmValue value = GetOption(name);
   if (value) {
     if (type == DefinitionType::PATH) {
       AddDefinition(source, name, CMakeToWixPath(value));
@@ -485,17 +486,17 @@
   }
 
   std::string featureTitle = cpackPackageName;
-  if (const char* title = GetOption("CPACK_WIX_ROOT_FEATURE_TITLE")) {
-    featureTitle = title;
+  if (cmValue title = GetOption("CPACK_WIX_ROOT_FEATURE_TITLE")) {
+    featureTitle = *title;
   }
   featureDefinitions.AddAttribute("Title", featureTitle);
-  if (const char* desc = GetOption("CPACK_WIX_ROOT_FEATURE_DESCRIPTION")) {
+  if (cmValue desc = GetOption("CPACK_WIX_ROOT_FEATURE_DESCRIPTION")) {
     featureDefinitions.AddAttribute("Description", desc);
   }
   featureDefinitions.AddAttribute("Level", "1");
   this->Patch->ApplyFragment("#PRODUCTFEATURE", featureDefinitions);
 
-  const char* package = GetOption("CPACK_WIX_CMAKE_PACKAGE_REGISTRY");
+  cmValue package = GetOption("CPACK_WIX_CMAKE_PACKAGE_REGISTRY");
   if (package) {
     featureDefinitions.CreateCMakePackageRegistryEntry(
       package, GetOption("CPACK_WIX_UPGRADE_GUID"));
@@ -540,10 +541,9 @@
   }
 
   bool emitUninstallShortcut = true;
-  const char* cpackWixProgramMenuFolder =
+  cmValue cpackWixProgramMenuFolder =
     GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
-  if (cpackWixProgramMenuFolder &&
-      cm::string_view(cpackWixProgramMenuFolder) == ".") {
+  if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == ".") {
     emitUninstallShortcut = false;
   } else if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) ==
              emittedShortcutTypes.end()) {
@@ -595,9 +595,9 @@
 
   std::string result = "ProgramFiles<64>Folder";
 
-  const char* rootFolderId = GetOption("CPACK_WIX_ROOT_FOLDER_ID");
+  cmValue rootFolderId = GetOption("CPACK_WIX_ROOT_FOLDER_ID");
   if (rootFolderId) {
-    result = rootFolderId;
+    result = *rootFolderId;
   }
 
   if (GetArchitecture() == "x86") {
@@ -612,8 +612,8 @@
 bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate()
 {
   std::string wixTemplate = FindTemplate("WIX.template.in");
-  if (GetOption("CPACK_WIX_TEMPLATE") != 0) {
-    wixTemplate = GetOption("CPACK_WIX_TEMPLATE");
+  if (cmValue wixtpl = GetOption("CPACK_WIX_TEMPLATE")) {
+    wixTemplate = *wixtpl;
   }
 
   if (wixTemplate.empty()) {
@@ -669,7 +669,7 @@
   featureDefinitions.AddAttribute("Id", featureId);
 
   std::vector<std::string> cpackPackageExecutablesList;
-  const char* cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES");
+  cmValue cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES");
   if (cpackPackageExecutables) {
     cmExpandList(cpackPackageExecutables, cpackPackageExecutablesList);
     if (cpackPackageExecutablesList.size() % 2 != 0) {
@@ -683,8 +683,7 @@
   }
 
   std::vector<std::string> cpackPackageDesktopLinksList;
-  const char* cpackPackageDesktopLinks =
-    GetOption("CPACK_CREATE_DESKTOP_LINKS");
+  cmValue cpackPackageDesktopLinks = GetOption("CPACK_CREATE_DESKTOP_LINKS");
   if (cpackPackageDesktopLinks) {
     cmExpandList(cpackPackageDesktopLinks, cpackPackageDesktopLinksList);
   }
@@ -743,10 +742,9 @@
   std::string directoryId;
   switch (type) {
     case cmWIXShortcuts::START_MENU: {
-      const char* cpackWixProgramMenuFolder =
+      cmValue cpackWixProgramMenuFolder =
         GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
-      if (cpackWixProgramMenuFolder &&
-          cm::string_view(cpackWixProgramMenuFolder) == ".") {
+      if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == ".") {
         directoryId = "ProgramMenuFolder";
       } else {
         directoryId = "PROGRAM_MENU_FOLDER";
@@ -805,10 +803,9 @@
                           fileDefinitions);
 
   if (type == cmWIXShortcuts::START_MENU) {
-    const char* cpackWixProgramMenuFolder =
+    cmValue cpackWixProgramMenuFolder =
       GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
-    if (cpackWixProgramMenuFolder &&
-        cm::string_view(cpackWixProgramMenuFolder) != ".") {
+    if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder != ".") {
       fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" +
                                        idSuffix);
     }
@@ -973,9 +970,9 @@
 bool cmCPackWIXGenerator::RequireOption(std::string const& name,
                                         std::string& value) const
 {
-  const char* tmp = GetOption(name.c_str());
+  cmValue tmp = GetOption(name);
   if (tmp) {
-    value = tmp;
+    value = *tmp;
 
     return true;
   } else {
@@ -1146,7 +1143,7 @@
 void cmCPackWIXGenerator::CollectExtensions(std::string const& variableName,
                                             extension_set_t& extensions)
 {
-  const char* variableContent = GetOption(variableName.c_str());
+  cmValue variableContent = GetOption(variableName);
   if (!variableContent)
     return;
 
@@ -1157,7 +1154,7 @@
 void cmCPackWIXGenerator::CollectXmlNamespaces(std::string const& variableName,
                                                xmlns_map_t& namespaces)
 {
-  const char* variableContent = GetOption(variableName.c_str());
+  cmValue variableContent = GetOption(variableName);
   if (!variableContent) {
     return;
   }
@@ -1180,13 +1177,13 @@
     oss << " xmlns:" << ns.first << "=\""
         << cmWIXSourceWriter::EscapeAttributeValue(ns.second) << '"';
   }
-  SetOption("CPACK_WIX_CUSTOM_XMLNS_EXPANDED", oss.str().c_str());
+  SetOption("CPACK_WIX_CUSTOM_XMLNS_EXPANDED", oss.str());
 }
 
 void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName,
                                          std::ostream& stream)
 {
-  const char* variableContent = GetOption(variableName.c_str());
+  cmValue variableContent = GetOption(variableName);
   if (!variableContent)
     return;
 
diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx
index d9234e6..56e8463 100644
--- a/Source/CPack/cmCPackArchiveGenerator.cxx
+++ b/Source/CPack/cmCPackArchiveGenerator.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackArchiveGenerator.h"
 
-#include <cstdlib>
 #include <cstring>
 #include <map>
 #include <ostream>
@@ -15,6 +14,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmWorkingDirectory.h"
 
 cmCPackGenerator* cmCPackArchiveGenerator::Create7ZGenerator()
@@ -77,7 +77,7 @@
 
   if (this->IsSet("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME")) {
     packageFileName +=
-      this->GetOption("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME");
+      *this->GetOption("CPACK_ARCHIVE_" + componentUpper + "_FILE_NAME");
   } else if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
     packageFileName += this->GetComponentPackageFileName(
       this->GetOption("CPACK_ARCHIVE_FILE_NAME"), component, isGroupName);
@@ -118,11 +118,11 @@
   if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
     filePrefix = cmStrCat(this->GetOption("CPACK_PACKAGE_FILE_NAME"), '/');
   }
-  const char* installPrefix =
-    this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
-  if (installPrefix && installPrefix[0] == '/' && installPrefix[1] != 0) {
+  cmValue installPrefix = this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
+  if (installPrefix && installPrefix->size() > 1 &&
+      (*installPrefix)[0] == '/') {
     // add to file prefix and remove the leading '/'
-    filePrefix += installPrefix + 1;
+    filePrefix += installPrefix->substr(1);
     filePrefix += "/";
   }
   for (std::string const& file : component->Files) {
@@ -257,9 +257,9 @@
   this->packageFileNames[0] += "/";
 
   if (this->IsSet("CPACK_ARCHIVE_FILE_NAME")) {
-    this->packageFileNames[0] += this->GetOption("CPACK_ARCHIVE_FILE_NAME");
+    this->packageFileNames[0] += *this->GetOption("CPACK_ARCHIVE_FILE_NAME");
   } else {
-    this->packageFileNames[0] += this->GetOption("CPACK_PACKAGE_FILE_NAME");
+    this->packageFileNames[0] += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
   }
 
   this->packageFileNames[0] += this->GetOutputExtension();
@@ -345,9 +345,9 @@
 
   // CPACK_ARCHIVE_THREADS overrides CPACK_THREADS
   if (this->IsSet("CPACK_ARCHIVE_THREADS")) {
-    threads = std::atoi(this->GetOption("CPACK_ARCHIVE_THREADS"));
+    threads = std::stoi(this->GetOption("CPACK_ARCHIVE_THREADS"));
   } else if (this->IsSet("CPACK_THREADS")) {
-    threads = std::atoi(this->GetOption("CPACK_THREADS"));
+    threads = std::stoi(this->GetOption("CPACK_THREADS"));
   }
 
   return threads;
diff --git a/Source/CPack/cmCPackBundleGenerator.cxx b/Source/CPack/cmCPackBundleGenerator.cxx
index 4d5f43f..b3d425a 100644
--- a/Source/CPack/cmCPackBundleGenerator.cxx
+++ b/Source/CPack/cmCPackBundleGenerator.cxx
@@ -8,6 +8,7 @@
 #include "cmCPackLog.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCPackBundleGenerator::cmCPackBundleGenerator() = default;
 
@@ -15,8 +16,8 @@
 
 int cmCPackBundleGenerator::InitializeInternal()
 {
-  const char* name = this->GetOption("CPACK_BUNDLE_NAME");
-  if (nullptr == name) {
+  cmValue name = this->GetOption("CPACK_BUNDLE_NAME");
+  if (!name) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPACK_BUNDLE_NAME must be set to use the Bundle generator."
                     << std::endl);
@@ -33,7 +34,7 @@
                     "Cannot locate codesign command" << std::endl);
       return 0;
     }
-    this->SetOptionIfNotSet("CPACK_COMMAND_CODESIGN", codesign_path.c_str());
+    this->SetOptionIfNotSet("CPACK_COMMAND_CODESIGN", codesign_path);
   }
 
   return this->Superclass::InitializeInternal();
@@ -51,30 +52,24 @@
 {
 
   // Get required arguments ...
-  const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME")
-    ? this->GetOption("CPACK_BUNDLE_NAME")
-    : "";
-  if (cpack_bundle_name.empty()) {
+  cmValue cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME");
+  if (cpack_bundle_name->empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPACK_BUNDLE_NAME must be set." << std::endl);
 
     return 0;
   }
 
-  const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST")
-    ? this->GetOption("CPACK_BUNDLE_PLIST")
-    : "";
-  if (cpack_bundle_plist.empty()) {
+  cmValue cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST");
+  if (cpack_bundle_plist->empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPACK_BUNDLE_PLIST must be set." << std::endl);
 
     return 0;
   }
 
-  const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON")
-    ? this->GetOption("CPACK_BUNDLE_ICON")
-    : "";
-  if (cpack_bundle_icon.empty()) {
+  cmValue cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON");
+  if (cpack_bundle_icon->empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPACK_BUNDLE_ICON must be set." << std::endl);
 
@@ -82,10 +77,8 @@
   }
 
   // Get optional arguments ...
-  const std::string cpack_bundle_startup_command =
-    this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND")
-    ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND")
-    : "";
+  cmValue cpack_bundle_startup_command =
+    this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND");
 
   // The staging directory contains everything that will end-up inside the
   // final disk image ...
@@ -138,7 +131,7 @@
 
   // Optionally a user-provided startup command (could be an
   // executable or a script) ...
-  if (!cpack_bundle_startup_command.empty()) {
+  if (!cpack_bundle_startup_command->empty()) {
     std::ostringstream command_source;
     command_source << cpack_bundle_startup_command;
 
@@ -180,13 +173,11 @@
 
 int cmCPackBundleGenerator::SignBundle(const std::string& src_dir)
 {
-  const std::string cpack_apple_cert_app =
-    this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP")
-    ? this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP")
-    : "";
+  cmValue cpack_apple_cert_app =
+    this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP");
 
   // codesign the application.
-  if (!cpack_apple_cert_app.empty()) {
+  if (!cpack_apple_cert_app->empty()) {
     std::string output;
     std::string bundle_path;
     bundle_path =
@@ -195,13 +186,10 @@
     // A list of additional files to sign, ie. frameworks and plugins.
     const std::string sign_parameter =
       this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
-      ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
+      ? *this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER")
       : "--deep -f";
 
-    const std::string sign_files =
-      this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES")
-      ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES")
-      : "";
+    cmValue sign_files = this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES");
 
     std::vector<std::string> relFiles = cmExpandedList(sign_files);
 
diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
index b5abd5a..484db00 100644
--- a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
+++ b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
@@ -9,6 +9,7 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmCPackCygwinBinaryGenerator::cmCPackCygwinBinaryGenerator()
@@ -59,14 +60,15 @@
 const char* cmCPackCygwinBinaryGenerator::GetOutputExtension()
 {
   this->OutputExtension = "-";
-  const char* patchNumber = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
+  cmValue patchNumber = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
   if (!patchNumber) {
-    patchNumber = "1";
+    this->OutputExtension += "1";
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_CYGWIN_PATCH_NUMBER not specified using 1"
                     << std::endl);
+  } else {
+    this->OutputExtension += patchNumber;
   }
-  this->OutputExtension += patchNumber;
   this->OutputExtension += ".tar.bz2";
   return this->OutputExtension.c_str();
 }
diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.cxx b/Source/CPack/cmCPackCygwinSourceGenerator.cxx
index 64a88eb..59df380 100644
--- a/Source/CPack/cmCPackCygwinSourceGenerator.cxx
+++ b/Source/CPack/cmCPackCygwinSourceGenerator.cxx
@@ -9,6 +9,7 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 // Includes needed for implementation of RenameFile.  This is not in
@@ -94,14 +95,15 @@
   }
   std::string outerTarFile =
     cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), '-');
-  const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
+  cmValue patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
   if (!patch) {
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_CYGWIN_PATCH_NUMBER"
                     << " not specified, defaulting to 1\n");
-    patch = "1";
+    outerTarFile += "1";
+  } else {
+    outerTarFile += patch;
   }
-  outerTarFile += patch;
   outerTarFile += "-src.tar.bz2";
   std::string tmpDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
   std::string buildScript =
@@ -145,14 +147,15 @@
 const char* cmCPackCygwinSourceGenerator::GetOutputExtension()
 {
   this->OutputExtension = "-";
-  const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
+  cmValue patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
   if (!patch) {
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_CYGWIN_PATCH_NUMBER"
                     << " not specified, defaulting to 1\n");
-    patch = "1";
+    this->OutputExtension += "1";
+  } else {
+    this->OutputExtension += patch;
   }
-  this->OutputExtension += patch;
   this->OutputExtension += "-src.tar.bz2";
   return this->OutputExtension.c_str();
 }
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx
index a1ebbdf..d7aa287 100644
--- a/Source/CPack/cmCPackDebGenerator.cxx
+++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -2,11 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCPackDebGenerator.h"
 
-#include <cstdlib>
+#include <algorithm>
 #include <cstring>
 #include <map>
 #include <ostream>
 #include <set>
+#include <stdexcept>
 #include <utility>
 
 #include "cmsys/Glob.hxx"
@@ -21,6 +22,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 
@@ -29,12 +31,12 @@
 public:
   DebGenerator(cmCPackLog* logger, std::string outputName, std::string workDir,
                std::string topLevelDir, std::string temporaryDir,
-               const char* debianCompressionType, const char* numThreads,
-               const char* debianArchiveType,
+               cmValue debianCompressionType, cmValue numThreads,
+               cmValue debianArchiveType,
                std::map<std::string, std::string> controlValues,
                bool genShLibs, std::string shLibsFilename, bool genPostInst,
                std::string postInst, bool genPostRm, std::string postRm,
-               const char* controlExtra, bool permissionStrctPolicy,
+               cmValue controlExtra, bool permissionStrctPolicy,
                std::vector<std::string> packageFiles);
 
   bool generate() const;
@@ -53,8 +55,8 @@
   std::string CompressionSuffix;
   const std::string TopLevelDir;
   const std::string TemporaryDir;
-  const char* DebianArchiveType;
-  int NumThreads;
+  const std::string DebianArchiveType;
+  long NumThreads;
   const std::map<std::string, std::string> ControlValues;
   const bool GenShLibs;
   const std::string ShLibsFilename;
@@ -62,7 +64,7 @@
   const std::string PostInst;
   const bool GenPostRm;
   const std::string PostRm;
-  const char* ControlExtra;
+  cmValue ControlExtra;
   const bool PermissionStrictPolicy;
   const std::vector<std::string> PackageFiles;
   cmArchiveWrite::Compress TarCompressionType;
@@ -71,18 +73,17 @@
 DebGenerator::DebGenerator(
   cmCPackLog* logger, std::string outputName, std::string workDir,
   std::string topLevelDir, std::string temporaryDir,
-  const char* debianCompressionType, const char* numThreads,
-  const char* debianArchiveType,
+  cmValue debCompressionType, cmValue numThreads, cmValue debianArchiveType,
   std::map<std::string, std::string> controlValues, bool genShLibs,
   std::string shLibsFilename, bool genPostInst, std::string postInst,
-  bool genPostRm, std::string postRm, const char* controlExtra,
+  bool genPostRm, std::string postRm, cmValue controlExtra,
   bool permissionStrictPolicy, std::vector<std::string> packageFiles)
   : Logger(logger)
   , OutputName(std::move(outputName))
   , WorkDir(std::move(workDir))
   , TopLevelDir(std::move(topLevelDir))
   , TemporaryDir(std::move(temporaryDir))
-  , DebianArchiveType(debianArchiveType ? debianArchiveType : "gnutar")
+  , DebianArchiveType(debianArchiveType ? *debianArchiveType : "gnutar")
   , ControlValues(std::move(controlValues))
   , GenShLibs(genShLibs)
   , ShLibsFilename(std::move(shLibsFilename))
@@ -94,23 +95,27 @@
   , PermissionStrictPolicy(permissionStrictPolicy)
   , PackageFiles(std::move(packageFiles))
 {
-  if (!debianCompressionType) {
-    debianCompressionType = "gzip";
+  std::string debianCompressionType = "gzip";
+  if (debCompressionType) {
+    debianCompressionType = *debCompressionType;
   }
 
-  if (!strcmp(debianCompressionType, "lzma")) {
+  if (debianCompressionType == "lzma") {
     this->CompressionSuffix = ".lzma";
     this->TarCompressionType = cmArchiveWrite::CompressLZMA;
-  } else if (!strcmp(debianCompressionType, "xz")) {
+  } else if (debianCompressionType == "xz") {
     this->CompressionSuffix = ".xz";
     this->TarCompressionType = cmArchiveWrite::CompressXZ;
-  } else if (!strcmp(debianCompressionType, "bzip2")) {
+  } else if (debianCompressionType == "bzip2") {
     this->CompressionSuffix = ".bz2";
     this->TarCompressionType = cmArchiveWrite::CompressBZip2;
-  } else if (!strcmp(debianCompressionType, "gzip")) {
+  } else if (debianCompressionType == "gzip") {
     this->CompressionSuffix = ".gz";
     this->TarCompressionType = cmArchiveWrite::CompressGZip;
-  } else if (!strcmp(debianCompressionType, "none")) {
+  } else if (debianCompressionType == "zstd") {
+    this->CompressionSuffix = ".zst";
+    this->TarCompressionType = cmArchiveWrite::CompressZstd;
+  } else if (debianCompressionType == "none") {
     this->CompressionSuffix.clear();
     this->TarCompressionType = cmArchiveWrite::CompressNone;
   } else {
@@ -119,16 +124,15 @@
                     << debianCompressionType << std::endl);
   }
 
-  if (numThreads == nullptr) {
-    numThreads = "1";
-  }
-
-  char* endptr;
-  this->NumThreads = static_cast<int>(strtol(numThreads, &endptr, 10));
-  if (numThreads != endptr && *endptr != '\0') {
-    cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Unrecognized number of threads: " << numThreads
-                                                     << std::endl);
+  if (numThreads != nullptr) {
+    if (!cmStrToLong(numThreads, &this->NumThreads)) {
+      this->NumThreads = 1;
+      cmCPackLogger(cmCPackLog::LOG_ERROR,
+                    "Unrecognized number of threads: " << numThreads
+                                                       << std::endl);
+    }
+  } else {
+    this->NumThreads = 1;
   }
 }
 
@@ -187,8 +191,15 @@
     return false;
   }
   cmArchiveWrite data_tar(fileStream_data_tar, this->TarCompressionType,
-                          this->DebianArchiveType, 0, this->NumThreads);
-  data_tar.Open();
+                          this->DebianArchiveType, 0,
+                          static_cast<int>(this->NumThreads));
+  if (!data_tar.Open()) {
+    cmCPackLogger(cmCPackLog::LOG_ERROR,
+                  "Error opening the archive \""
+                    << filename_data_tar
+                    << "\", ERROR = " << data_tar.GetError() << std::endl);
+    return false;
+  }
 
   // uid/gid should be the one of the root user, and this root user has
   // always uid/gid equal to 0.
@@ -248,11 +259,15 @@
     // do not recurse because the loop will do it
     if (!data_tar.Add(file, topLevelLength, ".", false)) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "Problem adding file to tar:"
-                      << std::endl
-                      << "#top level directory: " << this->WorkDir << std::endl
-                      << "#file: " << file << std::endl
-                      << "#error:" << data_tar.GetError() << std::endl);
+                    "Problem adding file to tar:\n"
+                    "#top level directory: "
+                      << this->WorkDir
+                      << "\n"
+                         "#file: "
+                      << file
+                      << "\n"
+                         "#error:"
+                      << data_tar.GetError() << std::endl);
       return false;
     }
   }
@@ -309,7 +324,13 @@
   cmArchiveWrite control_tar(fileStream_control_tar,
                              cmArchiveWrite::CompressGZip,
                              this->DebianArchiveType);
-  control_tar.Open();
+  if (!control_tar.Open()) {
+    cmCPackLogger(cmCPackLog::LOG_ERROR,
+                  "Error opening the archive \""
+                    << filename_control_tar
+                    << "\", ERROR = " << control_tar.GetError() << std::endl);
+    return false;
+  }
 
   // sets permissions and uid/gid for the files
   control_tar.SetUIDAndGID(0u, 0u);
@@ -334,11 +355,13 @@
       !control_tar.Add(this->WorkDir + "/control", this->WorkDir.length(),
                        ".")) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Error adding file to tar:"
-                    << std::endl
-                    << "#top level directory: " << this->WorkDir << std::endl
-                    << "#file: \"control\" or \"md5sums\"" << std::endl
-                    << "#error:" << control_tar.GetError() << std::endl);
+                  "Error adding file to tar:\n"
+                  "#top level directory: "
+                    << this->WorkDir
+                    << "\n"
+                       "#file: \"control\" or \"md5sums\"\n"
+                       "#error:"
+                    << control_tar.GetError() << std::endl);
     return false;
   }
 
@@ -346,11 +369,13 @@
   if (this->GenShLibs) {
     if (!control_tar.Add(this->ShLibsFilename, this->WorkDir.length(), ".")) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "Error adding file to tar:"
-                      << std::endl
-                      << "#top level directory: " << this->WorkDir << std::endl
-                      << "#file: \"shlibs\"" << std::endl
-                      << "#error:" << control_tar.GetError() << std::endl);
+                    "Error adding file to tar:\n"
+                    "#top level directory: "
+                      << this->WorkDir
+                      << "\n"
+                         "#file: \"shlibs\"\n"
+                         "#error:"
+                      << control_tar.GetError() << std::endl);
       return false;
     }
   }
@@ -360,11 +385,13 @@
     control_tar.SetPermissions(permission755);
     if (!control_tar.Add(this->PostInst, this->WorkDir.length(), ".")) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "Error adding file to tar:"
-                      << std::endl
-                      << "#top level directory: " << this->WorkDir << std::endl
-                      << "#file: \"postinst\"" << std::endl
-                      << "#error:" << control_tar.GetError() << std::endl);
+                    "Error adding file to tar:\n"
+                    "#top level directory: "
+                      << this->WorkDir
+                      << "\n"
+                         "#file: \"postinst\"\n"
+                         "#error:"
+                      << control_tar.GetError() << std::endl);
       return false;
     }
     control_tar.SetPermissions(permission644);
@@ -374,11 +401,13 @@
     control_tar.SetPermissions(permission755);
     if (!control_tar.Add(this->PostRm, this->WorkDir.length(), ".")) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "Error adding file to tar:"
-                      << std::endl
-                      << "#top level directory: " << this->WorkDir << std::endl
-                      << "#file: \"postinst\"" << std::endl
-                      << "#error:" << control_tar.GetError() << std::endl);
+                    "Error adding file to tar:\n"
+                    "#top level directory: "
+                      << this->WorkDir
+                      << "\n"
+                         "#file: \"postinst\"\n"
+                         "#error:"
+                      << control_tar.GetError() << std::endl);
       return false;
     }
     control_tar.SetPermissions(permission644);
@@ -412,11 +441,12 @@
       // if we can copy the file, it means it does exist, let's add it:
       if (!cmsys::SystemTools::FileExists(i)) {
         cmCPackLogger(cmCPackLog::LOG_WARNING,
-                      "Adding file to tar:" << std::endl
-                                            << "#top level directory: "
-                                            << this->WorkDir << std::endl
-                                            << "#missing file: " << i
-                                            << std::endl);
+                      "Adding file to tar:\n"
+                      "#top level directory: "
+                        << this->WorkDir
+                        << "\n"
+                           "#missing file: "
+                        << i << std::endl);
       }
 
       if (cmsys::SystemTools::CopyFileIfDifferent(i, localcopy)) {
@@ -440,7 +470,13 @@
   cmGeneratedFileStream debStream;
   debStream.Open(outputPath, false, true);
   cmArchiveWrite deb(debStream, cmArchiveWrite::CompressNone, "arbsd");
-  deb.Open();
+  if (!deb.Open()) {
+    cmCPackLogger(cmCPackLog::LOG_ERROR,
+                  "Error opening the archive \""
+                    << outputPath << "\", ERROR = " << deb.GetError()
+                    << std::endl);
+    return false;
+  }
 
   // uid/gid should be the one of the root user, and this root user has
   // always uid/gid equal to 0.
@@ -451,17 +487,37 @@
       !deb.Add(tlDir + "control.tar.gz", tlDir.length()) ||
       !deb.Add(tlDir + "data.tar" + this->CompressionSuffix, tlDir.length())) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Error creating debian package:"
-                    << std::endl
-                    << "#top level directory: " << this->TopLevelDir
-                    << std::endl
-                    << "#file: " << this->OutputName << std::endl
-                    << "#error:" << deb.GetError() << std::endl);
+                  "Error creating debian package:\n"
+                  "#top level directory: "
+                    << this->TopLevelDir
+                    << "\n"
+                       "#file: "
+                    << this->OutputName
+                    << "\n"
+                       "#error:"
+                    << deb.GetError() << std::endl);
     return false;
   }
   return true;
 }
 
+std::vector<std::string> findFilesIn(const std::string& path)
+{
+  cmsys::Glob gl;
+  std::string findExpr = path + "/*";
+  gl.RecurseOn();
+  gl.SetRecurseListDirs(true);
+  gl.SetRecurseThroughSymlinks(false);
+  if (!gl.FindFiles(findExpr)) {
+    throw std::runtime_error(
+      "Cannot find any files in the installed directory");
+  }
+  std::vector<std::string> files{ gl.GetFiles() };
+  // Sort files so that they have a reproducible order
+  std::sort(files.begin(), files.end());
+  return files;
+}
+
 } // end anonymous namespace
 
 cmCPackDebGenerator::cmCPackDebGenerator() = default;
@@ -480,128 +536,71 @@
 int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel,
                                         std::string const& packageName)
 {
-  int retval = 1;
   // Begin the archive for this pack
   std::string localToplevel(initialTopLevel);
   std::string packageFileName(
     cmSystemTools::GetParentDirectory(this->toplevel));
-  std::string outputFileName(
-    std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + "-" +
-    packageName + this->GetOutputExtension());
+  std::string outputFileName(*this->GetOption("CPACK_PACKAGE_FILE_NAME") +
+                             "-" + packageName + this->GetOutputExtension());
 
   localToplevel += "/" + packageName;
   /* replace the TEMP DIRECTORY with the component one */
-  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
+  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel);
   packageFileName += "/" + outputFileName;
   /* replace proposed CPACK_OUTPUT_FILE_NAME */
-  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
+  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName);
   /* replace the TEMPORARY package file name */
-  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
-                  packageFileName.c_str());
+  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName);
   // Tell CPackDeb.cmake the name of the component GROUP.
-  this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName.c_str());
+  this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName);
   // Tell CPackDeb.cmake the path where the component is.
   std::string component_path = cmStrCat('/', packageName);
-  this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
-                  component_path.c_str());
+  this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", component_path);
   if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Error while execution CPackDeb.cmake" << std::endl);
-    retval = 0;
-    return retval;
+    return 0;
   }
 
-  { // Isolate globbing of binaries vs. dbgsyms
-    cmsys::Glob gl;
-    std::string findExpr(this->GetOption("GEN_WDIR"));
-    findExpr += "/*";
-    gl.RecurseOn();
-    gl.SetRecurseListDirs(true);
-    gl.SetRecurseThroughSymlinks(false);
-    if (!gl.FindFiles(findExpr)) {
-      cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "Cannot find any files in the installed directory"
-                      << std::endl);
-      return 0;
-    }
-    this->packageFiles = gl.GetFiles();
-  }
-
-  int res = this->createDeb();
-  if (res != 1) {
-    retval = 0;
-  }
-  // add the generated package to package file names list
-  packageFileName = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
-                             this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"));
-  this->packageFileNames.push_back(std::move(packageFileName));
-
-  if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE") &&
-      this->GetOption("GEN_DBGSYMDIR")) {
-    cmsys::Glob gl;
-    std::string findExpr(this->GetOption("GEN_DBGSYMDIR"));
-    findExpr += "/*";
-    gl.RecurseOn();
-    gl.SetRecurseListDirs(true);
-    gl.SetRecurseThroughSymlinks(false);
-    if (!gl.FindFiles(findExpr)) {
-      cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "Cannot find any files in the installed directory"
-                      << std::endl);
-      return 0;
-    }
-    this->packageFiles = gl.GetFiles();
-
-    res = this->createDbgsymDDeb();
-    if (res != 1) {
-      retval = 0;
-    }
-    // add the generated package to package file names list
-    packageFileName =
-      cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
-               this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"));
-    this->packageFileNames.push_back(std::move(packageFileName));
-  }
-
-  return retval;
+  return this->createDebPackages();
 }
 
 int cmCPackDebGenerator::PackageComponents(bool ignoreGroup)
 {
-  int retval = 1;
-  /* Reset package file name list it will be populated during the
-   * component packaging run*/
+  // Reset package file name list it will be populated during the
+  // component packaging run
   this->packageFileNames.clear();
   std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
 
+  int retval = 1;
   // The default behavior is to have one package by component group
   // unless CPACK_COMPONENTS_IGNORE_GROUP is specified.
-  if (!ignoreGroup) {
-    for (auto const& compG : this->ComponentGroups) {
-      cmCPackLogger(cmCPackLog::LOG_VERBOSE,
-                    "Packaging component group: " << compG.first << std::endl);
-      // Begin the archive for this group
-      retval &= this->PackageOnePack(initialTopLevel, compG.first);
-    }
-    // Handle Orphan components (components not belonging to any groups)
+  if (ignoreGroup) {
+    // CPACK_COMPONENTS_IGNORE_GROUPS is set
+    // We build 1 package per component
     for (auto const& comp : this->Components) {
-      // Does the component belong to a group?
-      if (comp.second.Group == nullptr) {
-        cmCPackLogger(
-          cmCPackLog::LOG_VERBOSE,
-          "Component <"
-            << comp.second.Name
-            << "> does not belong to any group, package it separately."
-            << std::endl);
-        // Begin the archive for this orphan component
-        retval &= this->PackageOnePack(initialTopLevel, comp.first);
-      }
+      retval &= this->PackageOnePack(initialTopLevel, comp.first);
     }
+    return retval;
   }
-  // CPACK_COMPONENTS_IGNORE_GROUPS is set
-  // We build 1 package per component
-  else {
-    for (auto const& comp : this->Components) {
+
+  for (auto const& compG : this->ComponentGroups) {
+    cmCPackLogger(cmCPackLog::LOG_VERBOSE,
+                  "Packaging component group: " << compG.first << std::endl);
+    // Begin the archive for this group
+    retval &= this->PackageOnePack(initialTopLevel, compG.first);
+  }
+  // Handle Orphan components (components not belonging to any groups)
+  for (auto const& comp : this->Components) {
+    // Does the component belong to a group?
+    if (comp.second.Group == nullptr) {
+      cmCPackLogger(
+        cmCPackLog::LOG_VERBOSE,
+        "Component <"
+          << comp.second.Name
+          << "> does not belong to any group, package it separately."
+          << std::endl);
+      // Begin the archive for this orphan component
       retval &= this->PackageOnePack(initialTopLevel, comp.first);
     }
   }
@@ -612,7 +611,6 @@
 int cmCPackDebGenerator::PackageComponentsAllInOne(
   const std::string& compInstDirName)
 {
-  int retval = 1;
   /* Reset package file name list it will be populated during the
    * component packaging run*/
   this->packageFileNames.clear();
@@ -627,9 +625,8 @@
   std::string localToplevel(initialTopLevel);
   std::string packageFileName(
     cmSystemTools::GetParentDirectory(this->toplevel));
-  std::string outputFileName(
-    std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) +
-    this->GetOutputExtension());
+  std::string outputFileName(*this->GetOption("CPACK_PACKAGE_FILE_NAME") +
+                             this->GetOutputExtension());
   // all GROUP in one vs all COMPONENT in one
   // if must be here otherwise non component paths have a trailing / while
   // components don't
@@ -638,50 +635,25 @@
   }
 
   /* replace the TEMP DIRECTORY with the component one */
-  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
+  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel);
   packageFileName += "/" + outputFileName;
   /* replace proposed CPACK_OUTPUT_FILE_NAME */
-  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
+  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName);
   /* replace the TEMPORARY package file name */
-  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
-                  packageFileName.c_str());
+  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName);
 
   if (!compInstDirName.empty()) {
     // Tell CPackDeb.cmake the path where the component is.
     std::string component_path = cmStrCat('/', compInstDirName);
-    this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH",
-                    component_path.c_str());
+    this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", component_path);
   }
   if (!this->ReadListFile("Internal/CPack/CPackDeb.cmake")) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Error while execution CPackDeb.cmake" << std::endl);
-    retval = 0;
-    return retval;
-  }
-
-  cmsys::Glob gl;
-  std::string findExpr(this->GetOption("GEN_WDIR"));
-  findExpr += "/*";
-  gl.RecurseOn();
-  gl.SetRecurseListDirs(true);
-  gl.SetRecurseThroughSymlinks(false);
-  if (!gl.FindFiles(findExpr)) {
-    cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Cannot find any files in the installed directory"
-                    << std::endl);
     return 0;
   }
-  this->packageFiles = gl.GetFiles();
 
-  int res = this->createDeb();
-  if (res != 1) {
-    retval = 0;
-  }
-  // add the generated package to package file names list
-  packageFileName = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
-                             this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"));
-  this->packageFileNames.push_back(std::move(packageFileName));
-  return retval;
+  return this->createDebPackages();
 }
 
 int cmCPackDebGenerator::PackageFiles()
@@ -705,7 +677,40 @@
   return this->PackageComponentsAllInOne("");
 }
 
-int cmCPackDebGenerator::createDeb()
+bool cmCPackDebGenerator::createDebPackages()
+{
+  auto make_package = [this](const std::string& path,
+                             const char* const output_var,
+                             bool (cmCPackDebGenerator::*creator)()) -> bool {
+    try {
+      this->packageFiles = findFilesIn(path);
+    } catch (const std::runtime_error& ex) {
+      cmCPackLogger(cmCPackLog::LOG_ERROR, ex.what() << std::endl);
+      return false;
+    }
+
+    if ((this->*creator)()) {
+      // add the generated package to package file names list
+      this->packageFileNames.emplace_back(
+        cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), '/',
+                 this->GetOption(output_var)));
+      return true;
+    }
+    return false;
+  };
+  bool retval =
+    make_package(this->GetOption("GEN_WDIR"), "GEN_CPACK_OUTPUT_FILE_NAME",
+                 &cmCPackDebGenerator::createDeb);
+  cmValue dbgsymdir_path = this->GetOption("GEN_DBGSYMDIR");
+  if (this->IsOn("GEN_CPACK_DEBIAN_DEBUGINFO_PACKAGE") && dbgsymdir_path) {
+    retval = make_package(dbgsymdir_path, "GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME",
+                          &cmCPackDebGenerator::createDbgsymDDeb) &&
+      retval;
+  }
+  return int(retval);
+}
+
+bool cmCPackDebGenerator::createDeb()
 {
   std::map<std::string, std::string> controlValues;
 
@@ -713,78 +718,77 @@
   controlValues["Package"] = cmsys::SystemTools::LowerCase(
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
   controlValues["Version"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
   controlValues["Section"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION");
   controlValues["Priority"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY");
   controlValues["Architecture"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
   controlValues["Maintainer"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
   controlValues["Description"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION");
 
-  const char* debian_pkg_source =
+  cmValue debian_pkg_source =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
   if (cmNonempty(debian_pkg_source)) {
-    controlValues["Source"] = debian_pkg_source;
+    controlValues["Source"] = *debian_pkg_source;
   }
-  const char* debian_pkg_dep =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS");
+  cmValue debian_pkg_dep = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS");
   if (cmNonempty(debian_pkg_dep)) {
-    controlValues["Depends"] = debian_pkg_dep;
+    controlValues["Depends"] = *debian_pkg_dep;
   }
-  const char* debian_pkg_rec =
+  cmValue debian_pkg_rec =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS");
   if (cmNonempty(debian_pkg_rec)) {
-    controlValues["Recommends"] = debian_pkg_rec;
+    controlValues["Recommends"] = *debian_pkg_rec;
   }
-  const char* debian_pkg_sug =
+  cmValue debian_pkg_sug =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS");
   if (cmNonempty(debian_pkg_sug)) {
-    controlValues["Suggests"] = debian_pkg_sug;
+    controlValues["Suggests"] = *debian_pkg_sug;
   }
-  const char* debian_pkg_url =
+  cmValue debian_pkg_url =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE");
   if (cmNonempty(debian_pkg_url)) {
-    controlValues["Homepage"] = debian_pkg_url;
+    controlValues["Homepage"] = *debian_pkg_url;
   }
-  const char* debian_pkg_predep =
+  cmValue debian_pkg_predep =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS");
   if (cmNonempty(debian_pkg_predep)) {
-    controlValues["Pre-Depends"] = debian_pkg_predep;
+    controlValues["Pre-Depends"] = *debian_pkg_predep;
   }
-  const char* debian_pkg_enhances =
+  cmValue debian_pkg_enhances =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES");
   if (cmNonempty(debian_pkg_enhances)) {
-    controlValues["Enhances"] = debian_pkg_enhances;
+    controlValues["Enhances"] = *debian_pkg_enhances;
   }
-  const char* debian_pkg_breaks =
+  cmValue debian_pkg_breaks =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS");
   if (cmNonempty(debian_pkg_breaks)) {
-    controlValues["Breaks"] = debian_pkg_breaks;
+    controlValues["Breaks"] = *debian_pkg_breaks;
   }
-  const char* debian_pkg_conflicts =
+  cmValue debian_pkg_conflicts =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS");
   if (cmNonempty(debian_pkg_conflicts)) {
-    controlValues["Conflicts"] = debian_pkg_conflicts;
+    controlValues["Conflicts"] = *debian_pkg_conflicts;
   }
-  const char* debian_pkg_provides =
+  cmValue debian_pkg_provides =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES");
   if (cmNonempty(debian_pkg_provides)) {
-    controlValues["Provides"] = debian_pkg_provides;
+    controlValues["Provides"] = *debian_pkg_provides;
   }
-  const char* debian_pkg_replaces =
+  cmValue debian_pkg_replaces =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES");
   if (cmNonempty(debian_pkg_replaces)) {
-    controlValues["Replaces"] = debian_pkg_replaces;
+    controlValues["Replaces"] = *debian_pkg_replaces;
   }
 
   const std::string strGenWDIR(this->GetOption("GEN_WDIR"));
   const std::string shlibsfilename = strGenWDIR + "/shlibs";
 
-  const char* debian_pkg_shlibs =
+  cmValue debian_pkg_shlibs =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SHLIBS");
   const bool gen_shibs = this->IsOn("CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS") &&
     cmNonempty(debian_pkg_shlibs);
@@ -829,13 +833,10 @@
     this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"),
     this->packageFiles);
 
-  if (!gen.generate()) {
-    return 0;
-  }
-  return 1;
+  return gen.generate();
 }
 
-int cmCPackDebGenerator::createDbgsymDDeb()
+bool cmCPackDebGenerator::createDbgsymDDeb()
 {
   // Packages containing debug symbols follow the same structure as .debs
   // but have different metadata and content.
@@ -844,38 +845,38 @@
   // debian policy enforce lower case for package name
   std::string packageNameLower = cmsys::SystemTools::LowerCase(
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME"));
-  const char* debian_pkg_version =
+  cmValue debian_pkg_version =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION");
 
   controlValues["Package"] = packageNameLower + "-dbgsym";
   controlValues["Package-Type"] = "ddeb";
-  controlValues["Version"] = debian_pkg_version;
+  controlValues["Version"] = *debian_pkg_version;
   controlValues["Auto-Built-Package"] = "debug-symbols";
-  controlValues["Depends"] = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME") +
-    std::string(" (= ") + debian_pkg_version + ")";
+  controlValues["Depends"] =
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME") + std::string(" (= ") +
+    *debian_pkg_version + ")";
   controlValues["Section"] = "debug";
   controlValues["Priority"] = "optional";
   controlValues["Architecture"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE");
   controlValues["Maintainer"] =
-    this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
+    *this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER");
   controlValues["Description"] =
     std::string("debug symbols for ") + packageNameLower;
 
-  const char* debian_pkg_source =
+  cmValue debian_pkg_source =
     this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE");
   if (cmNonempty(debian_pkg_source)) {
-    controlValues["Source"] = debian_pkg_source;
+    controlValues["Source"] = *debian_pkg_source;
   }
-  const char* debian_build_ids = this->GetOption("GEN_BUILD_IDS");
+  cmValue debian_build_ids = this->GetOption("GEN_BUILD_IDS");
   if (cmNonempty(debian_build_ids)) {
-    controlValues["Build-Ids"] = debian_build_ids;
+    controlValues["Build-Ids"] = *debian_build_ids;
   }
 
   DebGenerator gen(
     this->Logger, this->GetOption("GEN_CPACK_DBGSYM_OUTPUT_FILE_NAME"),
     this->GetOption("GEN_DBGSYMDIR"),
-
     this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
     this->GetOption("CPACK_TEMPORARY_DIRECTORY"),
     this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"),
@@ -885,10 +886,7 @@
     this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"),
     this->packageFiles);
 
-  if (!gen.generate()) {
-    return 0;
-  }
-  return 1;
+  return gen.generate();
 }
 
 bool cmCPackDebGenerator::SupportsComponentInstallation() const
@@ -911,7 +909,7 @@
   std::string groupVar =
     "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP";
   if (nullptr != this->GetOption(groupVar)) {
-    return std::string(this->GetOption(groupVar));
+    return *this->GetOption(groupVar);
   }
   return componentName;
 }
diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h
index ee8f39a..61a6616 100644
--- a/Source/CPack/cmCPackDebGenerator.h
+++ b/Source/CPack/cmCPackDebGenerator.h
@@ -63,8 +63,9 @@
     const std::string& componentName) override;
 
 private:
-  int createDeb();
-  int createDbgsymDDeb();
+  bool createDebPackages();
+  bool createDeb();
+  bool createDbgsymDDeb();
 
   std::vector<std::string> packageFiles;
 };
diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx
index b71c969..9385a5a 100644
--- a/Source/CPack/cmCPackDragNDropGenerator.cxx
+++ b/Source/CPack/cmCPackDragNDropGenerator.cxx
@@ -20,6 +20,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 
 #ifdef HAVE_CoreServices
@@ -76,7 +77,7 @@
                   "Cannot locate hdiutil command" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str());
+  this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path);
 
   const std::string setfile_path =
     cmSystemTools::FindProgram("SetFile", paths, false);
@@ -85,7 +86,7 @@
                   "Cannot locate SetFile command" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str());
+  this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path);
 
   const std::string rez_path = cmSystemTools::FindProgram("Rez", paths, false);
   if (rez_path.empty()) {
@@ -93,7 +94,7 @@
                   "Cannot locate Rez command" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path.c_str());
+  this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path);
 
   if (this->IsSet("CPACK_DMG_SLA_DIR")) {
     slaDirectory = this->GetOption("CPACK_DMG_SLA_DIR");
@@ -260,48 +261,35 @@
                                          const std::string& output_file)
 {
   // Get optional arguments ...
-  const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON")
-    ? this->GetOption("CPACK_PACKAGE_ICON")
-    : "";
+  cmValue cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON");
 
   const std::string cpack_dmg_volume_name =
     this->GetOption("CPACK_DMG_VOLUME_NAME")
-    ? this->GetOption("CPACK_DMG_VOLUME_NAME")
-    : this->GetOption("CPACK_PACKAGE_FILE_NAME");
+    ? *this->GetOption("CPACK_DMG_VOLUME_NAME")
+    : *this->GetOption("CPACK_PACKAGE_FILE_NAME");
 
   const std::string cpack_dmg_format = this->GetOption("CPACK_DMG_FORMAT")
-    ? this->GetOption("CPACK_DMG_FORMAT")
+    ? *this->GetOption("CPACK_DMG_FORMAT")
     : "UDZO";
 
   const std::string cpack_dmg_filesystem =
     this->GetOption("CPACK_DMG_FILESYSTEM")
-    ? this->GetOption("CPACK_DMG_FILESYSTEM")
+    ? *this->GetOption("CPACK_DMG_FILESYSTEM")
     : "HFS+";
 
   // Get optional arguments ...
   std::string cpack_license_file =
-    this->GetOption("CPACK_RESOURCE_FILE_LICENSE")
-    ? this->GetOption("CPACK_RESOURCE_FILE_LICENSE")
-    : "";
+    *this->GetOption("CPACK_RESOURCE_FILE_LICENSE");
 
-  const std::string cpack_dmg_background_image =
-    this->GetOption("CPACK_DMG_BACKGROUND_IMAGE")
-    ? this->GetOption("CPACK_DMG_BACKGROUND_IMAGE")
-    : "";
+  cmValue cpack_dmg_background_image =
+    this->GetOption("CPACK_DMG_BACKGROUND_IMAGE");
 
-  const std::string cpack_dmg_ds_store = this->GetOption("CPACK_DMG_DS_STORE")
-    ? this->GetOption("CPACK_DMG_DS_STORE")
-    : "";
+  cmValue cpack_dmg_ds_store = this->GetOption("CPACK_DMG_DS_STORE");
 
-  const std::string cpack_dmg_languages =
-    this->GetOption("CPACK_DMG_SLA_LANGUAGES")
-    ? this->GetOption("CPACK_DMG_SLA_LANGUAGES")
-    : "";
+  cmValue cpack_dmg_languages = this->GetOption("CPACK_DMG_SLA_LANGUAGES");
 
-  const std::string cpack_dmg_ds_store_setup_script =
-    this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT")
-    ? this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT")
-    : "";
+  cmValue cpack_dmg_ds_store_setup_script =
+    this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT");
 
   const bool cpack_dmg_disable_applications_symlink =
     this->IsOn("CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK");
@@ -332,7 +320,7 @@
   }
 
   // Optionally add a custom volume icon ...
-  if (!cpack_package_icon.empty()) {
+  if (!cpack_package_icon->empty()) {
     std::ostringstream package_icon_source;
     package_icon_source << cpack_package_icon;
 
@@ -351,7 +339,7 @@
 
   // Optionally add a custom .DS_Store file
   // (e.g. for setting background/layout) ...
-  if (!cpack_dmg_ds_store.empty()) {
+  if (!cpack_dmg_ds_store->empty()) {
     std::ostringstream package_settings_source;
     package_settings_source << cpack_dmg_ds_store;
 
@@ -372,7 +360,7 @@
   // Optionally add a custom background image ...
   // Make sure the background file type is the same as the custom image
   // and that the file is hidden so it doesn't show up.
-  if (!cpack_dmg_background_image.empty()) {
+  if (!cpack_dmg_background_image->empty()) {
     const std::string extension =
       cmSystemTools::GetFilenameLastExtension(cpack_dmg_background_image);
     std::ostringstream package_background_source;
@@ -394,7 +382,7 @@
   }
 
   bool remount_image =
-    !cpack_package_icon.empty() || !cpack_dmg_ds_store_setup_script.empty();
+    !cpack_package_icon->empty() || !cpack_dmg_ds_store_setup_script->empty();
 
   std::string temp_image_format = "UDZO";
 
@@ -471,7 +459,7 @@
     }
 
     // Optionally set the custom icon flag for the image ...
-    if (!had_error && !cpack_package_icon.empty()) {
+    if (!had_error && !cpack_package_icon->empty()) {
       std::string error;
       std::ostringstream setfile_command;
       setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
@@ -490,7 +478,7 @@
 
     // Optionally we can execute a custom apple script to generate
     // the .DS_Store for the volume folder ...
-    if (!had_error && !cpack_dmg_ds_store_setup_script.empty()) {
+    if (!had_error && !cpack_dmg_ds_store_setup_script->empty()) {
       std::ostringstream setup_script_command;
       setup_script_command << "osascript"
                            << " \"" << cpack_dmg_ds_store_setup_script << "\""
@@ -718,7 +706,7 @@
     // the current COMPONENT belongs to.
     std::string groupVar =
       "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP";
-    const char* _groupName = GetOption(groupVar);
+    cmValue _groupName = this->GetOption(groupVar);
     if (_groupName) {
       std::string groupName = _groupName;
 
diff --git a/Source/CPack/cmCPackExternalGenerator.cxx b/Source/CPack/cmCPackExternalGenerator.cxx
index e3521a0..157ee1d 100644
--- a/Source/CPack/cmCPackExternalGenerator.cxx
+++ b/Source/CPack/cmCPackExternalGenerator.cxx
@@ -18,6 +18,7 @@
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 int cmCPackExternalGenerator::InitializeInternal()
 {
@@ -60,7 +61,7 @@
     return 0;
   }
 
-  const char* packageScript = this->GetOption("CPACK_EXTERNAL_PACKAGE_SCRIPT");
+  cmValue packageScript = this->GetOption("CPACK_EXTERNAL_PACKAGE_SCRIPT");
   if (cmNonempty(packageScript)) {
     if (!cmSystemTools::FileIsFullPath(packageScript)) {
       cmCPackLogger(
@@ -76,10 +77,9 @@
       return 0;
     }
 
-    const char* builtPackagesStr =
-      this->GetOption("CPACK_EXTERNAL_BUILT_PACKAGES");
-    if (builtPackagesStr) {
-      cmExpandList(builtPackagesStr, this->packageFileNames, false);
+    cmValue builtPackages = this->GetOption("CPACK_EXTERNAL_BUILT_PACKAGES");
+    if (builtPackages) {
+      cmExpandList(builtPackages, this->packageFileNames, false);
     }
   }
 
@@ -181,43 +181,42 @@
     return 0;
   }
 
-  const char* packageName = this->Parent->GetOption("CPACK_PACKAGE_NAME");
+  cmValue packageName = this->Parent->GetOption("CPACK_PACKAGE_NAME");
   if (packageName) {
-    root["packageName"] = packageName;
+    root["packageName"] = *packageName;
   }
 
-  const char* packageVersion =
-    this->Parent->GetOption("CPACK_PACKAGE_VERSION");
+  cmValue packageVersion = this->Parent->GetOption("CPACK_PACKAGE_VERSION");
   if (packageVersion) {
-    root["packageVersion"] = packageVersion;
+    root["packageVersion"] = *packageVersion;
   }
 
-  const char* packageDescriptionFile =
+  cmValue packageDescriptionFile =
     this->Parent->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE");
   if (packageDescriptionFile) {
-    root["packageDescriptionFile"] = packageDescriptionFile;
+    root["packageDescriptionFile"] = *packageDescriptionFile;
   }
 
-  const char* packageDescriptionSummary =
+  cmValue packageDescriptionSummary =
     this->Parent->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY");
   if (packageDescriptionSummary) {
-    root["packageDescriptionSummary"] = packageDescriptionSummary;
+    root["packageDescriptionSummary"] = *packageDescriptionSummary;
   }
 
-  const char* buildConfigCstr = this->Parent->GetOption("CPACK_BUILD_CONFIG");
+  cmValue buildConfigCstr = this->Parent->GetOption("CPACK_BUILD_CONFIG");
   if (buildConfigCstr) {
-    root["buildConfig"] = buildConfigCstr;
+    root["buildConfig"] = *buildConfigCstr;
   }
 
-  const char* defaultDirectoryPermissions =
+  cmValue defaultDirectoryPermissions =
     this->Parent->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
   if (cmNonempty(defaultDirectoryPermissions)) {
-    root["defaultDirectoryPermissions"] = defaultDirectoryPermissions;
+    root["defaultDirectoryPermissions"] = *defaultDirectoryPermissions;
   }
   if (cmIsInternallyOn(this->Parent->GetOption("CPACK_SET_DESTDIR"))) {
     root["setDestdir"] = true;
     root["packagingInstallPrefix"] =
-      this->Parent->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
+      *this->Parent->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
   } else {
     root["setDestdir"] = false;
   }
diff --git a/Source/CPack/cmCPackFreeBSDGenerator.cxx b/Source/CPack/cmCPackFreeBSDGenerator.cxx
index b673006..30b6b0d 100644
--- a/Source/CPack/cmCPackFreeBSDGenerator.cxx
+++ b/Source/CPack/cmCPackFreeBSDGenerator.cxx
@@ -203,11 +203,11 @@
 // basically a wrapper that handles the NULL-ptr return from GetOption().
 std::string cmCPackFreeBSDGenerator::var_lookup(const char* var_name)
 {
-  const char* pv = this->GetOption(var_name);
+  cmValue pv = this->GetOption(var_name);
   if (!pv) {
     return std::string();
   }
-  return pv;
+  return *pv;
 }
 
 // Produce UCL in the given @p manifest file for the common
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index 00e274d..2f700b4 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -20,11 +20,11 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLSafe.h"
@@ -78,14 +78,14 @@
 
   std::string tempDirectory =
     cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/_CPack_Packages/");
-  const char* toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG");
+  cmValue toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG");
   if (toplevelTag) {
-    tempDirectory += toplevelTag;
+    tempDirectory += *toplevelTag;
     tempDirectory += "/";
   }
-  tempDirectory += this->GetOption("CPACK_GENERATOR");
+  tempDirectory += *this->GetOption("CPACK_GENERATOR");
   std::string topDirectory = tempDirectory;
-  const char* pfname = this->GetOption("CPACK_PACKAGE_FILE_NAME");
+  cmValue pfname = this->GetOption("CPACK_PACKAGE_FILE_NAME");
   if (!pfname) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPACK_PACKAGE_FILE_NAME not specified" << std::endl);
@@ -99,7 +99,7 @@
     return 0;
   }
   outName += this->GetOutputExtension();
-  const char* pdir = this->GetOption("CPACK_PACKAGE_DIRECTORY");
+  cmValue pdir = this->GetOption("CPACK_PACKAGE_DIRECTORY");
   if (!pdir) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPACK_PACKAGE_DIRECTORY not specified" << std::endl);
@@ -107,25 +107,23 @@
   }
 
   std::string destFile = pdir;
-  this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", destFile.c_str());
+  this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", destFile);
   destFile += "/" + outName;
   std::string outFile = topDirectory + "/" + outName;
-  this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory.c_str());
-  this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str());
-  this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName.c_str());
-  this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", destFile.c_str());
-  this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
-                          outFile.c_str());
+  this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory);
+  this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory);
+  this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName);
+  this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", destFile);
+  this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", outFile);
   this->SetOptionIfNotSet("CPACK_INSTALL_DIRECTORY", this->GetInstallPath());
   this->SetOptionIfNotSet(
     "CPACK_NATIVE_INSTALL_DIRECTORY",
-    cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
-  this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY",
-                          tempDirectory.c_str());
+    cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()));
+  this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", tempDirectory);
 
   cmCPackLogger(cmCPackLog::LOG_DEBUG,
                 "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl);
-  const char* descFileName = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE");
+  cmValue descFileName = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE");
   if (descFileName && !this->GetOption("CPACK_PACKAGE_DESCRIPTION")) {
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
                   "Look for: " << descFileName << std::endl);
@@ -135,7 +133,7 @@
                       << descFileName << "]" << std::endl);
       return 0;
     }
-    cmsys::ifstream ifs(descFileName);
+    cmsys::ifstream ifs(descFileName->c_str());
     if (!ifs) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "Cannot open description file name: " << descFileName
@@ -150,10 +148,10 @@
     while (ifs && cmSystemTools::GetLineFromStream(ifs, line)) {
       ostr << cmXMLSafe(line) << std::endl;
     }
-    this->SetOption("CPACK_PACKAGE_DESCRIPTION", ostr.str().c_str());
-    const char* defFileName =
+    this->SetOption("CPACK_PACKAGE_DESCRIPTION", ostr.str());
+    cmValue defFileName =
       this->GetOption("CPACK_DEFAULT_PACKAGE_DESCRIPTION_FILE");
-    if (defFileName && !strcmp(defFileName, descFileName)) {
+    if (defFileName && (defFileName == descFileName)) {
       this->SetOption("CPACK_USED_DEFAULT_PACKAGE_DESCRIPTION_FILE", "ON");
     }
   }
@@ -165,9 +163,9 @@
         << std::endl);
     return 0;
   }
-  const char* algoSignature = this->GetOption("CPACK_PACKAGE_CHECKSUM");
+  cmValue algoSignature = this->GetOption("CPACK_PACKAGE_CHECKSUM");
   if (algoSignature) {
-    if (!cmCryptoHash::New(algoSignature)) {
+    if (!cmCryptoHash::New(*algoSignature)) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "Cannot recognize algorithm: " << algoSignature
                                                    << std::endl);
@@ -215,7 +213,7 @@
   // prepare default created directory permissions
   mode_t default_dir_mode_v = 0;
   mode_t* default_dir_mode = nullptr;
-  const char* default_dir_install_permissions =
+  cmValue default_dir_install_permissions =
     this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
   if (cmNonempty(default_dir_install_permissions)) {
     std::vector<std::string> items =
@@ -266,7 +264,7 @@
   }
 
   // Run pre-build actions
-  const char* preBuildScripts = this->GetOption("CPACK_PRE_BUILD_SCRIPTS");
+  cmValue preBuildScripts = this->GetOption("CPACK_PRE_BUILD_SCRIPTS");
   if (preBuildScripts) {
     const auto scripts = cmExpandedList(preBuildScripts, false);
     for (const auto& script : scripts) {
@@ -293,7 +291,7 @@
   bool setDestDir, const std::string& tempInstallDirectory)
 {
   (void)setDestDir;
-  const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
+  cmValue installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
   if (cmNonempty(installCommands)) {
     std::string tempInstallDirectoryEnv =
       cmStrCat("CMAKE_INSTALL_PREFIX=", tempInstallDirectory);
@@ -333,7 +331,7 @@
   (void)setDestDir;
   (void)tempInstallDirectory;
   std::vector<cmsys::RegularExpression> ignoreFilesRegex;
-  const char* cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES");
+  cmValue cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES");
   if (cpackIgnoreFiles) {
     std::vector<std::string> ignoreFilesRegexString =
       cmExpandedList(cpackIgnoreFiles);
@@ -343,8 +341,7 @@
       ignoreFilesRegex.emplace_back(ifr);
     }
   }
-  const char* installDirectories =
-    this->GetOption("CPACK_INSTALLED_DIRECTORIES");
+  cmValue installDirectories = this->GetOption("CPACK_INSTALLED_DIRECTORIES");
   if (cmNonempty(installDirectories)) {
     std::vector<std::string> installDirectoriesVector =
       cmExpandedList(installDirectories);
@@ -472,9 +469,9 @@
 int cmCPackGenerator::InstallProjectViaInstallScript(
   bool setDestDir, const std::string& tempInstallDirectory)
 {
-  const char* cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPTS");
+  cmValue cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPTS");
   {
-    const char* const cmakeScript = this->GetOption("CPACK_INSTALL_SCRIPT");
+    cmValue const cmakeScript = this->GetOption("CPACK_INSTALL_SCRIPT");
     if (cmakeScript && cmakeScripts) {
       cmCPackLogger(
         cmCPackLog::LOG_WARNING,
@@ -485,7 +482,7 @@
       cmakeScripts = cmakeScript;
     }
   }
-  if (cmakeScripts && *cmakeScripts) {
+  if (cmakeScripts && !cmakeScripts->empty()) {
     cmCPackLogger(cmCPackLog::LOG_OUTPUT,
                   "- Install scripts: " << cmakeScripts << std::endl);
     std::vector<std::string> cmakeScriptsVector = cmExpandedList(cmakeScripts);
@@ -502,9 +499,9 @@
 
         std::string dir;
         if (this->GetOption("CPACK_INSTALL_PREFIX")) {
-          dir += this->GetOption("CPACK_INSTALL_PREFIX");
+          dir += *this->GetOption("CPACK_INSTALL_PREFIX");
         }
-        this->SetOption("CMAKE_INSTALL_PREFIX", dir.c_str());
+        this->SetOption("CMAKE_INSTALL_PREFIX", dir);
         cmCPackLogger(
           cmCPackLog::LOG_DEBUG,
           "- Using DESTDIR + CPACK_INSTALL_PREFIX... (this->SetOption)"
@@ -513,7 +510,7 @@
                       "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
                                                             << std::endl);
       } else {
-        this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory.c_str());
+        this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
 
         cmCPackLogger(cmCPackLog::LOG_DEBUG,
                       "- Using non-DESTDIR install... (this->SetOption)"
@@ -524,9 +521,9 @@
       }
 
       this->SetOptionIfNotSet("CMAKE_CURRENT_BINARY_DIR",
-                              tempInstallDirectory.c_str());
+                              tempInstallDirectory);
       this->SetOptionIfNotSet("CMAKE_CURRENT_SOURCE_DIR",
-                              tempInstallDirectory.c_str());
+                              tempInstallDirectory);
       bool res = this->MakefileMap->ReadListFile(installScript);
       if (cmSystemTools::GetErrorOccuredFlag() || !res) {
         return 0;
@@ -540,8 +537,8 @@
   bool setDestDir, const std::string& baseTempInstallDirectory,
   const mode_t* default_dir_mode)
 {
-  const char* cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
-  const char* cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR");
+  cmValue cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
+  cmValue cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR");
   std::string absoluteDestFiles;
   if (cmNonempty(cmakeProjects)) {
     if (!cmakeGenerator) {
@@ -595,7 +592,7 @@
         // Determine the installation types for this project (if provided).
         std::string installTypesVar = "CPACK_" +
           cmSystemTools::UpperCase(project.Component) + "_INSTALL_TYPES";
-        const char* installTypes = this->GetOption(installTypesVar);
+        cmValue installTypes = this->GetOption(installTypesVar);
         if (cmNonempty(installTypes)) {
           std::vector<std::string> installTypesVector =
             cmExpandedList(installTypes);
@@ -608,7 +605,7 @@
         // Determine the set of components that will be used in this project
         std::string componentsVar =
           "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(project.Component);
-        const char* components = this->GetOption(componentsVar);
+        cmValue components = this->GetOption(componentsVar);
         if (cmNonempty(components)) {
           cmExpandList(components, componentsVector);
           for (std::string const& comp : componentsVector) {
@@ -625,12 +622,7 @@
       std::vector<std::string> buildConfigs;
 
       // Try get configuration names given via `-C` CLI option
-      {
-        const char* const buildConfigCstr =
-          this->GetOption("CPACK_BUILD_CONFIG");
-        auto buildConfig = buildConfigCstr ? buildConfigCstr : std::string{};
-        cmExpandList(buildConfig, buildConfigs);
-      }
+      cmExpandList(this->GetOption("CPACK_BUILD_CONFIG"), buildConfigs);
 
       // Remove duplicates
       std::sort(buildConfigs.begin(), buildConfigs.end());
@@ -682,8 +674,7 @@
       this->CMakeProjects.emplace_back(std::move(project));
     }
   }
-  this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES",
-                  absoluteDestFiles.c_str());
+  this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES", absoluteDestFiles);
   return 1;
 }
 
@@ -767,11 +758,11 @@
     tempInstallDirectory += this->GetComponentInstallDirNameSuffix(component);
     if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) {
       tempInstallDirectory += "/";
-      tempInstallDirectory += this->GetOption("CPACK_PACKAGE_FILE_NAME");
+      tempInstallDirectory += *this->GetOption("CPACK_PACKAGE_FILE_NAME");
     }
   }
 
-  const char* default_dir_inst_permissions =
+  cmValue default_dir_inst_permissions =
     this->GetOption("CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
   if (cmNonempty(default_dir_inst_permissions)) {
     mf.AddDefinition("CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS",
@@ -800,7 +791,7 @@
     }
     std::string dir;
     if (this->GetOption("CPACK_INSTALL_PREFIX")) {
-      dir += this->GetOption("CPACK_INSTALL_PREFIX");
+      dir += *this->GetOption("CPACK_INSTALL_PREFIX");
     }
     mf.AddDefinition("CMAKE_INSTALL_PREFIX", dir);
 
@@ -924,7 +915,7 @@
   // forward definition of CMAKE_ABSOLUTE_DESTINATION_FILES
   // to CPack (may be used by generators like CPack RPM or DEB)
   // in order to transparently handle ABSOLUTE PATH
-  if (cmProp def = mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) {
+  if (cmValue def = mf.GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) {
     mf.AddDefinition("CPACK_ABSOLUTE_DESTINATION_FILES", *def);
   }
 
@@ -958,7 +949,7 @@
     }
   }
 
-  if (cmProp d = mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) {
+  if (cmValue d = mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) {
     if (!absoluteDestFiles.empty()) {
       absoluteDestFiles += ";";
     }
@@ -975,11 +966,10 @@
         std::string absoluteDestFilesListComponent =
           cmStrCat(this->GetOption(absoluteDestFileComponent), ';', *d);
         this->SetOption(absoluteDestFileComponent,
-                        absoluteDestFilesListComponent.c_str());
+                        absoluteDestFilesListComponent);
       } else {
-        this->SetOption(
-          absoluteDestFileComponent,
-          cmToCStr(mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")));
+        this->SetOption(absoluteDestFileComponent,
+                        mf.GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"));
       }
     }
   }
@@ -999,17 +989,29 @@
   return retval;
 }
 
-void cmCPackGenerator::SetOptionIfNotSet(const std::string& op,
-                                         const char* value)
+template <typename ValueType>
+void cmCPackGenerator::StoreOptionIfNotSet(const std::string& op,
+                                           ValueType value)
 {
-  cmProp def = this->MakefileMap->GetDefinition(op);
+  cmValue def = this->MakefileMap->GetDefinition(op);
   if (cmNonempty(def)) {
     return;
   }
-  this->SetOption(op, value);
+  this->StoreOption(op, value);
 }
 
-void cmCPackGenerator::SetOption(const std::string& op, const char* value)
+void cmCPackGenerator::SetOptionIfNotSet(const std::string& op,
+                                         const char* value)
+{
+  this->StoreOptionIfNotSet(op, value);
+}
+void cmCPackGenerator::SetOptionIfNotSet(const std::string& op, cmValue value)
+{
+  this->StoreOptionIfNotSet(op, value);
+}
+
+template <typename ValueType>
+void cmCPackGenerator::StoreOption(const std::string& op, ValueType value)
 {
   if (!value) {
     this->MakefileMap->RemoveDefinition(op);
@@ -1021,6 +1023,15 @@
   this->MakefileMap->AddDefinition(op, value);
 }
 
+void cmCPackGenerator::SetOption(const std::string& op, const char* value)
+{
+  this->StoreOption(op, value);
+}
+void cmCPackGenerator::SetOption(const std::string& op, cmValue value)
+{
+  this->StoreOption(op, value);
+}
+
 int cmCPackGenerator::DoPackage()
 {
   cmCPackLogger(cmCPackLog::LOG_OUTPUT,
@@ -1038,8 +1049,7 @@
   }
 
   if (cmIsOn(this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY"))) {
-    const char* toplevelDirectory =
-      this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+    cmValue toplevelDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
     if (cmSystemTools::FileExists(toplevelDirectory)) {
       cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                     "Remove toplevel directory: " << toplevelDirectory
@@ -1060,9 +1070,9 @@
   }
   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Done install project " << std::endl);
 
-  const char* tempPackageFileName =
+  cmValue tempPackageFileName =
     this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME");
-  const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+  cmValue tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
 
   cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
   cmsys::Glob gl;
@@ -1079,7 +1089,7 @@
   cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl);
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "Package files to: "
-                  << (tempPackageFileName ? tempPackageFileName : "(NULL)")
+                  << (tempPackageFileName ? *tempPackageFileName : "(NULL)")
                   << std::endl);
   if (cmSystemTools::FileExists(tempPackageFileName)) {
     cmCPackLogger(cmCPackLog::LOG_VERBOSE,
@@ -1099,9 +1109,8 @@
    * may update this during PackageFiles.
    * (either putting several names or updating the provided one)
    */
-  this->packageFileNames.emplace_back(tempPackageFileName ? tempPackageFileName
-                                                          : "");
-  this->toplevel = tempDirectory;
+  this->packageFileNames.emplace_back(tempPackageFileName);
+  this->toplevel = *tempDirectory;
   { // scope that enables package generators to run internal scripts with
     // latest CMake policies enabled
     cmMakefile::ScopePushPop pp{ this->MakefileMap };
@@ -1115,7 +1124,7 @@
     }
   }
   // Run post-build actions
-  const char* postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS");
+  cmValue postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS");
   if (postBuildScripts) {
     this->MakefileMap->AddDefinition("CPACK_PACKAGE_FILES",
                                      cmJoin(this->packageFileNames, ";"));
@@ -1135,8 +1144,8 @@
   }
 
   /* Prepare checksum algorithm*/
-  const char* algo = this->GetOption("CPACK_PACKAGE_CHECKSUM");
-  std::unique_ptr<cmCryptoHash> crypto = cmCryptoHash::New(algo ? algo : "");
+  cmValue algo = this->GetOption("CPACK_PACKAGE_CHECKSUM");
+  std::unique_ptr<cmCryptoHash> crypto = cmCryptoHash::New(*algo);
 
   /*
    * Copy the generated packages to final destination
@@ -1151,19 +1160,19 @@
   for (std::string const& pkgFileName : this->packageFileNames) {
     std::string tmpPF(this->GetOption("CPACK_OUTPUT_FILE_PREFIX"));
     std::string filename(cmSystemTools::GetFilenameName(pkgFileName));
-    tempPackageFileName = pkgFileName.c_str();
+    tempPackageFileName = cmValue(pkgFileName);
     tmpPF += "/" + filename;
     const char* packageFileName = tmpPF.c_str();
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
                   "Copy final package(s): "
-                    << (tempPackageFileName ? tempPackageFileName : "(NULL)")
+                    << (tempPackageFileName ? *tempPackageFileName : "(NULL)")
                     << " to " << (packageFileName ? packageFileName : "(NULL)")
                     << std::endl);
     if (!cmSystemTools::CopyFileIfDifferent(pkgFileName, tmpPF)) {
       cmCPackLogger(
         cmCPackLog::LOG_ERROR,
         "Problem copying the package: "
-          << (tempPackageFileName ? tempPackageFileName : "(NULL)") << " to "
+          << (tempPackageFileName ? *tempPackageFileName : "(NULL)") << " to "
           << (packageFileName ? packageFileName : "(NULL)") << std::endl);
       return 0;
     }
@@ -1198,9 +1207,9 @@
   this->MakefileMap = mf;
   this->Name = name;
   // set the running generator name
-  this->SetOption("CPACK_GENERATOR", this->Name.c_str());
+  this->SetOption("CPACK_GENERATOR", this->Name);
   // Load the project specific config file
-  const char* config = this->GetOption("CPACK_PROJECT_CONFIG_FILE");
+  cmValue config = this->GetOption("CPACK_PROJECT_CONFIG_FILE");
   if (config) {
     mf->ReadListFile(config);
   }
@@ -1234,7 +1243,7 @@
 
 bool cmCPackGenerator::IsSetToOff(const std::string& op) const
 {
-  cmProp ret = this->MakefileMap->GetDefinition(op);
+  cmValue ret = this->MakefileMap->GetDefinition(op);
   if (cmNonempty(ret)) {
     return cmIsOff(*ret);
   }
@@ -1243,22 +1252,21 @@
 
 bool cmCPackGenerator::IsSetToEmpty(const std::string& op) const
 {
-  cmProp ret = this->MakefileMap->GetDefinition(op);
+  cmValue ret = this->MakefileMap->GetDefinition(op);
   if (ret) {
     return ret->empty();
   }
   return false;
 }
 
-const char* cmCPackGenerator::GetOption(const std::string& op) const
+cmValue cmCPackGenerator::GetOption(const std::string& op) const
 {
-  cmProp ret = this->MakefileMap->GetDefinition(op);
+  cmValue ret = this->MakefileMap->GetDefinition(op);
   if (!ret) {
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
                   "Warning, GetOption return NULL for: " << op << std::endl);
-    return nullptr;
   }
-  return ret->c_str();
+  return ret;
 }
 
 std::vector<std::string> cmCPackGenerator::GetOptions() const
@@ -1311,7 +1319,7 @@
                   << this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX") << "'"
                   << std::endl);
 
-  return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX");
+  return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX")->c_str();
 }
 
 std::string cmCPackGenerator::FindTemplate(const char* name)
@@ -1391,12 +1399,8 @@
     method = ONE_PACKAGE_PER_GROUP;
   }
 
-  std::string groupingType;
-
   // Second way to specify grouping
-  if (nullptr != this->GetOption("CPACK_COMPONENTS_GROUPING")) {
-    groupingType = this->GetOption("CPACK_COMPONENTS_GROUPING");
-  }
+  std::string groupingType = *this->GetOption("CPACK_COMPONENTS_GROUPING");
 
   if (!groupingType.empty()) {
     cmCPackLogger(cmCPackLog::LOG_VERBOSE,
@@ -1477,18 +1481,18 @@
     if (isGroupName) {
       std::string groupDispVar = "CPACK_COMPONENT_GROUP_" +
         cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME";
-      const char* groupDispName = this->GetOption(groupDispVar);
+      cmValue groupDispName = this->GetOption(groupDispVar);
       if (groupDispName) {
-        suffix = "-" + std::string(groupDispName);
+        suffix = "-" + *groupDispName;
       }
     }
     /* the [single] component case */
     else {
       std::string dispVar = "CPACK_COMPONENT_" +
         cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME";
-      const char* dispName = this->GetOption(dispVar);
+      cmValue dispName = this->GetOption(dispVar);
       if (dispName) {
-        suffix = "-" + std::string(dispName);
+        suffix = "-" + *dispName;
       }
     }
   }
@@ -1531,9 +1535,9 @@
       "CPACK_INSTALL_TYPE_" + cmsys::SystemTools::UpperCase(name);
     installType->Name = name;
 
-    const char* displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
+    cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
     if (cmNonempty(displayName)) {
-      installType->DisplayName = displayName;
+      installType->DisplayName = *displayName;
     } else {
       installType->DisplayName = installType->Name;
     }
@@ -1553,9 +1557,9 @@
     std::string macroPrefix =
       "CPACK_COMPONENT_" + cmsys::SystemTools::UpperCase(name);
     component->Name = name;
-    const char* displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
+    cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
     if (cmNonempty(displayName)) {
-      component->DisplayName = displayName;
+      component->DisplayName = *displayName;
     } else {
       component->DisplayName = component->Name;
     }
@@ -1565,17 +1569,17 @@
     component->IsDownloaded = this->IsOn(macroPrefix + "_DOWNLOADED") ||
       cmIsOn(this->GetOption("CPACK_DOWNLOAD_ALL"));
 
-    const char* archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE");
+    cmValue archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE");
     if (cmNonempty(archiveFile)) {
-      component->ArchiveFile = archiveFile;
+      component->ArchiveFile = *archiveFile;
     }
 
-    const char* plist = this->GetOption(macroPrefix + "_PLIST");
+    cmValue plist = this->GetOption(macroPrefix + "_PLIST");
     if (cmNonempty(plist)) {
-      component->Plist = plist;
+      component->Plist = *plist;
     }
 
-    const char* groupName = this->GetOption(macroPrefix + "_GROUP");
+    cmValue groupName = this->GetOption(macroPrefix + "_GROUP");
     if (cmNonempty(groupName)) {
       component->Group = this->GetComponentGroup(projectName, groupName);
       component->Group->Components.push_back(component);
@@ -1583,13 +1587,13 @@
       component->Group = nullptr;
     }
 
-    const char* description = this->GetOption(macroPrefix + "_DESCRIPTION");
+    cmValue description = this->GetOption(macroPrefix + "_DESCRIPTION");
     if (cmNonempty(description)) {
-      component->Description = description;
+      component->Description = *description;
     }
 
     // Determine the installation types.
-    const char* installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES");
+    cmValue installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES");
     if (cmNonempty(installTypes)) {
       std::vector<std::string> installTypesVector =
         cmExpandedList(installTypes);
@@ -1600,7 +1604,7 @@
     }
 
     // Determine the component dependencies.
-    const char* depends = this->GetOption(macroPrefix + "_DEPENDS");
+    cmValue depends = this->GetOption(macroPrefix + "_DEPENDS");
     if (cmNonempty(depends)) {
       std::vector<std::string> dependsVector = cmExpandedList(depends);
       for (std::string const& depend : dependsVector) {
@@ -1624,21 +1628,20 @@
   if (!hasGroup) {
     // Define the group
     group->Name = name;
-    const char* displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
+    cmValue displayName = this->GetOption(macroPrefix + "_DISPLAY_NAME");
     if (cmNonempty(displayName)) {
-      group->DisplayName = displayName;
+      group->DisplayName = *displayName;
     } else {
       group->DisplayName = group->Name;
     }
 
-    const char* description = this->GetOption(macroPrefix + "_DESCRIPTION");
+    cmValue description = this->GetOption(macroPrefix + "_DESCRIPTION");
     if (cmNonempty(description)) {
-      group->Description = description;
+      group->Description = *description;
     }
     group->IsBold = this->IsOn(macroPrefix + "_BOLD_TITLE");
     group->IsExpandedByDefault = this->IsOn(macroPrefix + "_EXPANDED");
-    const char* parentGroupName =
-      this->GetOption(macroPrefix + "_PARENT_GROUP");
+    cmValue parentGroupName = this->GetOption(macroPrefix + "_PARENT_GROUP");
     if (cmNonempty(parentGroupName)) {
       group->ParentGroup =
         this->GetComponentGroup(projectName, parentGroupName);
diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h
index 2512d42..65156ab 100644
--- a/Source/CPack/cmCPackGenerator.h
+++ b/Source/CPack/cmCPackGenerator.h
@@ -13,6 +13,7 @@
 
 #include "cmCPackComponentGroup.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 class cmCPackLog;
 class cmGlobalGenerator;
@@ -84,8 +85,18 @@
 
   //! Set and get the options
   void SetOption(const std::string& op, const char* value);
+  void SetOption(const std::string& op, const std::string& value)
+  {
+    this->SetOption(op, cmValue(value));
+  }
+  void SetOption(const std::string& op, cmValue value);
   void SetOptionIfNotSet(const std::string& op, const char* value);
-  const char* GetOption(const std::string& op) const;
+  void SetOptionIfNotSet(const std::string& op, const std::string& value)
+  {
+    this->SetOptionIfNotSet(op, cmValue(value));
+  }
+  void SetOptionIfNotSet(const std::string& op, cmValue value);
+  cmValue GetOption(const std::string& op) const;
   std::vector<std::string> GetOptions() const;
   bool IsSet(const std::string& name) const;
   bool IsOn(const std::string& name) const;
@@ -323,6 +334,12 @@
   bool TraceExpand;
 
   cmMakefile* MakefileMap;
+
+private:
+  template <typename ValueType>
+  void StoreOption(const std::string& op, ValueType value);
+  template <typename ValueType>
+  void StoreOptionIfNotSet(const std::string& op, ValueType value);
 };
 
 #define cmCPackTypeMacro(klass, superclass)                                   \
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index 6bd0d1b..ecc5e08 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -21,6 +21,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 /* NSIS uses different command line syntax on Windows and others */
 #ifdef _WIN32
@@ -84,7 +85,7 @@
   }
   cmCPackLogger(cmCPackLog::LOG_DEBUG,
                 "Uninstall Files: " << str.str() << std::endl);
-  this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str());
+  this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str());
   std::vector<std::string> dirs;
   this->GetListOfSubdirectories(this->toplevel.c_str(), dirs);
   std::ostringstream dstr;
@@ -120,7 +121,7 @@
   }
   cmCPackLogger(cmCPackLog::LOG_DEBUG,
                 "Uninstall Dirs: " << dstr.str() << std::endl);
-  this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES", dstr.str().c_str());
+  this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES", dstr.str());
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "Configure file: " << nsisInFileName << " to " << nsisFileName
@@ -142,15 +143,15 @@
   }
   std::string installerHeaderImage;
   if (this->IsSet("CPACK_NSIS_MUI_HEADERIMAGE")) {
-    installerHeaderImage = this->GetOption("CPACK_NSIS_MUI_HEADERIMAGE");
+    installerHeaderImage = *this->GetOption("CPACK_NSIS_MUI_HEADERIMAGE");
   } else if (this->IsSet("CPACK_PACKAGE_ICON")) {
-    installerHeaderImage = this->GetOption("CPACK_PACKAGE_ICON");
+    installerHeaderImage = *this->GetOption("CPACK_PACKAGE_ICON");
   }
   if (!installerHeaderImage.empty()) {
     std::string installerIconCode = cmStrCat(
       "!define MUI_HEADERIMAGE_BITMAP \"", installerHeaderImage, "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
-                            installerIconCode.c_str());
+                            installerIconCode);
   }
 
   if (this->IsSet("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP")) {
@@ -158,7 +159,7 @@
       "!define MUI_WELCOMEFINISHPAGE_BITMAP \"",
       this->GetOption("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_WELCOMEFINISH_CODE",
-                            installerBitmapCode.c_str());
+                            installerBitmapCode);
   }
 
   if (this->IsSet("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP")) {
@@ -166,7 +167,7 @@
       "!define MUI_UNWELCOMEFINISHPAGE_BITMAP \"",
       this->GetOption("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_UNWELCOMEFINISH_CODE",
-                            installerBitmapCode.c_str());
+                            installerBitmapCode);
   }
 
   if (this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN")) {
@@ -175,7 +176,7 @@
                this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY"), '\\',
                this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN"), "\"\n");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE",
-                            installerRunCode.c_str());
+                            installerRunCode);
   }
 
   if (this->IsSet("CPACK_NSIS_WELCOME_TITLE")) {
@@ -183,7 +184,7 @@
       cmStrCat("!define MUI_WELCOMEPAGE_TITLE \"",
                this->GetOption("CPACK_NSIS_WELCOME_TITLE"), "\"");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_WELCOME_TITLE_CODE",
-                            welcomeTitleCode.c_str());
+                            welcomeTitleCode);
   }
 
   if (this->IsSet("CPACK_NSIS_WELCOME_TITLE_3LINES")) {
@@ -196,7 +197,7 @@
       cmStrCat("!define MUI_FINISHPAGE_TITLE \"",
                this->GetOption("CPACK_NSIS_FINISH_TITLE"), "\"");
     this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_FINISH_TITLE_CODE",
-                            finishTitleCode.c_str());
+                            finishTitleCode);
   }
 
   if (this->IsSet("CPACK_NSIS_FINISH_TITLE_3LINES")) {
@@ -231,8 +232,14 @@
     std::string brandingTextCode =
       cmStrCat("BrandingText /TRIM", brandingTextPosition, " \"",
                this->GetOption("CPACK_NSIS_BRANDING_TEXT"), "\"\n");
-    this->SetOptionIfNotSet("CPACK_NSIS_BRANDING_TEXT_CODE",
-                            brandingTextCode.c_str());
+    this->SetOptionIfNotSet("CPACK_NSIS_BRANDING_TEXT_CODE", brandingTextCode);
+  }
+
+  if (!this->IsSet("CPACK_NSIS_IGNORE_LICENSE_PAGE")) {
+    std::string licenceCode =
+      cmStrCat("!insertmacro MUI_PAGE_LICENSE \"",
+               this->GetOption("CPACK_RESOURCE_FILE_LICENSE"), "\"\n");
+    this->SetOptionIfNotSet("CPACK_NSIS_LICENSE_PAGE", licenceCode);
   }
 
   // Setup all of the component sections
@@ -327,7 +334,7 @@
         componentDescriptions + groupDescriptions +
         "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
       this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
-                              componentDescriptions.c_str());
+                              componentDescriptions);
     }
 
     if (anyDownloadedComponents) {
@@ -337,18 +344,15 @@
       }
     }
 
-    this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES",
-                            installTypesCode.c_str());
+    this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", installTypesCode);
     this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS",
                             "!insertmacro MUI_PAGE_COMPONENTS");
     this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", "");
-    this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS",
-                            componentCode.c_str());
-    this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST",
-                            sectionList.c_str());
+    this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", componentCode);
+    this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", sectionList);
     this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS",
-                            selectedVarsList.c_str());
-    this->SetOption("CPACK_NSIS_DEFINES", defines.c_str());
+                            selectedVarsList);
+    this->SetOption("CPACK_NSIS_DEFINES", defines);
   }
 
   this->ConfigureFile(nsisInInstallOptions, nsisInstallOptions);
@@ -472,8 +476,8 @@
   cmsys::RegularExpression versionRexCVS("v(.*)\\.cvs");
   if (!resS || retVal ||
       (!versionRex.find(output) && !versionRexCVS.find(output))) {
-    const char* topDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
-    std::string tmpFile = cmStrCat(topDir ? topDir : ".", "/NSISOutput.log");
+    cmValue topDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+    std::string tmpFile = cmStrCat(topDir ? *topDir : ".", "/NSISOutput.log");
     cmGeneratedFileStream ofs(tmpFile);
     ofs << "# Run command: " << nsisCmd << std::endl
         << "# Output:" << std::endl
@@ -487,12 +491,12 @@
   }
   if (versionRex.find(output)) {
     double nsisVersion = atof(versionRex.match(1).c_str());
-    double minNSISVersion = 3.0;
+    double minNSISVersion = 3.03;
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
                   "NSIS Version: " << nsisVersion << std::endl);
     if (nsisVersion < minNSISVersion) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
-                    "CPack requires NSIS Version 3.0 or greater.  "
+                    "CPack requires NSIS Version 3.03 or greater. "
                     "NSIS found on the system was: "
                       << nsisVersion << std::endl);
       return 0;
@@ -503,13 +507,13 @@
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
                   "NSIS Version: CVS " << versionRexCVS.match(1) << std::endl);
   }
-  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str());
+  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath);
   this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin");
-  const char* cpackPackageExecutables =
+  cmValue cpackPackageExecutables =
     this->GetOption("CPACK_PACKAGE_EXECUTABLES");
-  const char* cpackPackageDeskTopLinks =
+  cmValue cpackPackageDeskTopLinks =
     this->GetOption("CPACK_CREATE_DESKTOP_LINKS");
-  const char* cpackNsisExecutablesDirectory =
+  cmValue cpackNsisExecutablesDirectory =
     this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY");
   std::vector<std::string> cpackPackageDesktopLinksVector;
   if (cpackPackageDeskTopLinks) {
@@ -571,8 +575,8 @@
   }
 
   this->CreateMenuLinks(str, deleteStr);
-  this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str());
-  this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS", deleteStr.str().c_str());
+  this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str());
+  this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS", deleteStr.str());
 
   this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma");
 
@@ -582,7 +586,7 @@
 void cmCPackNSISGenerator::CreateMenuLinks(std::ostream& str,
                                            std::ostream& deleteStr)
 {
-  const char* cpackMenuLinks = this->GetOption("CPACK_NSIS_MENU_LINKS");
+  cmValue cpackMenuLinks = this->GetOption("CPACK_NSIS_MENU_LINKS");
   if (!cpackMenuLinks) {
     return;
   }
@@ -721,11 +725,10 @@
     }
 
     // Create the directory for the upload area
-    const char* userUploadDirectory =
-      this->GetOption("CPACK_UPLOAD_DIRECTORY");
+    cmValue userUploadDirectory = this->GetOption("CPACK_UPLOAD_DIRECTORY");
     std::string uploadDirectory;
     if (cmNonempty(userUploadDirectory)) {
-      uploadDirectory = userUploadDirectory;
+      uploadDirectory = *userUploadDirectory;
     } else {
       uploadDirectory =
         cmStrCat(this->GetOption("CPACK_PACKAGE_DIRECTORY"), "/CPackUploads");
@@ -961,9 +964,9 @@
 std::string cmCPackNSISGenerator::CustomComponentInstallDirectory(
   cm::string_view componentName)
 {
-  const char* outputDir = this->GetOption(
+  cmValue outputDir = this->GetOption(
     cmStrCat("CPACK_NSIS_", componentName, "_INSTALL_DIRECTORY"));
-  return outputDir ? outputDir : "$INSTDIR";
+  return outputDir ? *outputDir : "$INSTDIR";
 }
 
 std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
diff --git a/Source/CPack/cmCPackNuGetGenerator.cxx b/Source/CPack/cmCPackNuGetGenerator.cxx
index 98dc890..5de8179 100644
--- a/Source/CPack/cmCPackNuGetGenerator.cxx
+++ b/Source/CPack/cmCPackNuGetGenerator.cxx
@@ -14,6 +14,7 @@
 #include "cmCPackLog.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 bool cmCPackNuGetGenerator::SupportsComponentInstallation() const
 {
@@ -81,10 +82,10 @@
                      std::back_inserter(components),
                      [](cmCPackComponent const* comp) { return comp->Name; });
       this->SetOption("CPACK_NUGET_" + compGUp + "_GROUP_COMPONENTS",
-                      cmJoin(components, ";").c_str());
+                      cmJoin(components, ";"));
     }
     if (!groups.empty()) {
-      this->SetOption("CPACK_NUGET_GROUPS", cmJoin(groups, ";").c_str());
+      this->SetOption("CPACK_NUGET_GROUPS", cmJoin(groups, ";"));
     }
 
     // Handle Orphan components (components not belonging to any groups)
@@ -102,8 +103,7 @@
       }
     }
     if (!components.empty()) {
-      this->SetOption("CPACK_NUGET_COMPONENTS",
-                      cmJoin(components, ";").c_str());
+      this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";"));
     }
 
   } else {
@@ -114,13 +114,13 @@
                    [](std::pair<std::string, cmCPackComponent> const& comp) {
                      return comp.first;
                    });
-    this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";").c_str());
+    this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";"));
   }
 }
 
 void cmCPackNuGetGenerator::AddGeneratedPackageNames()
 {
-  const char* const files_list = this->GetOption("GEN_CPACK_OUTPUT_FILES");
+  cmValue const files_list = this->GetOption("GEN_CPACK_OUTPUT_FILES");
   if (!files_list) {
     cmCPackLogger(
       cmCPackLog::LOG_ERROR,
@@ -129,7 +129,7 @@
     return;
   }
   // add the generated packages to package file names list
-  std::string fileNames{ files_list };
+  const std::string& fileNames = *files_list;
   const char sep = ';';
   std::string::size_type pos1 = 0;
   std::string::size_type pos2 = fileNames.find(sep, pos1 + 1);
diff --git a/Source/CPack/cmCPackOSXX11Generator.cxx b/Source/CPack/cmCPackOSXX11Generator.cxx
index 5de4a6f..7bf1dc7 100644
--- a/Source/CPack/cmCPackOSXX11Generator.cxx
+++ b/Source/CPack/cmCPackOSXX11Generator.cxx
@@ -12,6 +12,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCPackOSXX11Generator::cmCPackOSXX11Generator() = default;
 
@@ -22,7 +23,7 @@
   // TODO: Use toplevel ?
   //       It is used! Is this an obsolete comment?
 
-  const char* cpackPackageExecutables =
+  cmValue cpackPackageExecutables =
     this->GetOption("CPACK_PACKAGE_EXECUTABLES");
   if (cpackPackageExecutables) {
     cmCPackLogger(cmCPackLog::LOG_DEBUG,
@@ -45,8 +46,7 @@
          it != cpackPackageExecutablesVector.end(); ++it) {
       std::string cpackExecutableName = *it;
       ++it;
-      this->SetOptionIfNotSet("CPACK_EXECUTABLE_NAME",
-                              cpackExecutableName.c_str());
+      this->SetOptionIfNotSet("CPACK_EXECUTABLE_NAME", cpackExecutableName);
     }
   }
 
@@ -70,7 +70,7 @@
   const char* scrDir = scriptDirectory.c_str();
   const char* contDir = contentsDirectory.c_str();
   const char* rsrcFile = resourceFileName.c_str();
-  const char* iconFile = this->GetOption("CPACK_PACKAGE_ICON");
+  cmValue iconFile = this->GetOption("CPACK_PACKAGE_ICON");
   if (iconFile) {
     std::string iconFileName = cmsys::SystemTools::GetFilenameName(iconFile);
     if (!cmSystemTools::FileExists(iconFile)) {
@@ -83,7 +83,7 @@
     }
     std::string destFileName = resourcesDirectory + "/" + iconFileName;
     this->ConfigureFile(iconFile, destFileName, true);
-    this->SetOptionIfNotSet("CPACK_APPLE_GUI_ICON", iconFileName.c_str());
+    this->SetOptionIfNotSet("CPACK_APPLE_GUI_ICON", iconFileName);
   }
 
   std::string applicationsLinkName = diskImageDirectory + "/Applications";
@@ -103,9 +103,9 @@
                                    true) ||
       !this->CopyResourcePlistFile("OSXScriptLauncher.rsrc", dir, rsrcFile,
                                    true) ||
-      !this->CopyResourcePlistFile("OSXScriptLauncher", appdir,
-                                   this->GetOption("CPACK_PACKAGE_FILE_NAME"),
-                                   true)) {
+      !this->CopyResourcePlistFile(
+        "OSXScriptLauncher", appdir,
+        this->GetOption("CPACK_PACKAGE_FILE_NAME").GetCStr(), true)) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Problem copying the resource files" << std::endl);
     return 0;
@@ -190,8 +190,7 @@
                   "Cannot find hdiutil compiler" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
-                          pkgPath.c_str());
+  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", pkgPath);
 
   return this->Superclass::InitializeInternal();
 }
diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx
index ac3d64d..91adf32 100644
--- a/Source/CPack/cmCPackPKGGenerator.cxx
+++ b/Source/CPack/cmCPackPKGGenerator.cxx
@@ -9,6 +9,7 @@
 #include "cmCPackLog.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 
 cmCPackPKGGenerator::cmCPackPKGGenerator()
@@ -56,7 +57,7 @@
   std::string opt = (themeName == nullptr)
     ? cmStrCat("CPACK_", genName, "_BACKGROUND")
     : cmStrCat("CPACK_", genName, "_BACKGROUND_", paramSuffix);
-  const char* bgFileName = this->GetOption(opt);
+  cmValue bgFileName = this->GetOption(opt);
   if (bgFileName == nullptr) {
     return;
   }
@@ -78,7 +79,7 @@
 
   xout.Attribute("file", bgFileName);
 
-  const char* param = this->GetOption(cmStrCat(opt, "_ALIGNMENT"));
+  cmValue param = this->GetOption(cmStrCat(opt, "_ALIGNMENT"));
   if (param != nullptr) {
     xout.Attribute("alignment", param);
   }
@@ -166,7 +167,7 @@
   // Dark Aqua
   this->CreateBackground("darkAqua", metapackageFile, genName, xout);
 
-  this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
+  this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str());
 
   // Create the distribution.dist file in the metapackage to turn it
   // into a distribution package.
@@ -315,7 +316,7 @@
 {
   std::string uname = cmSystemTools::UpperCase(name);
   std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
-  const char* inFileName = this->GetOption(cpackVar);
+  cmValue inFileName = this->GetOption(cpackVar);
   if (!inFileName) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "CPack option: " << cpackVar.c_str()
@@ -347,11 +348,10 @@
 
   // Set this so that distribution.dist gets the right name (without
   // the path).
-  this->SetOption("CPACK_RESOURCE_FILE_" + uname + "_NOPATH",
-                  (name + ext).c_str());
+  this->SetOption("CPACK_RESOURCE_FILE_" + uname + "_NOPATH", (name + ext));
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
-                "Configure file: " << (inFileName ? inFileName : "(NULL)")
+                "Configure file: " << (inFileName ? *inFileName : "(NULL)")
                                    << " to " << destFileName << std::endl);
   this->ConfigureFile(inFileName, destFileName);
   return true;
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx
index f51ea42..a8cf1fa 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.cxx
+++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx
@@ -18,6 +18,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 
 static inline unsigned int getVersion(unsigned int major, unsigned int minor)
@@ -79,9 +80,9 @@
     resDir += "/en.lproj";
   }
 
-  const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
-  const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
-  const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
+  cmValue preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT");
+  cmValue postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT");
+  cmValue postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT");
 
   if (this->Components.empty()) {
     // Create directory structure
@@ -167,10 +168,9 @@
 
     // Create the directory where downloaded component packages will
     // be placed.
-    const char* userUploadDirectory =
-      this->GetOption("CPACK_UPLOAD_DIRECTORY");
+    cmValue userUploadDirectory = this->GetOption("CPACK_UPLOAD_DIRECTORY");
     std::string uploadDirectory;
-    if (userUploadDirectory && *userUploadDirectory) {
+    if (userUploadDirectory && !userUploadDirectory->empty()) {
       uploadDirectory = userUploadDirectory;
     } else {
       uploadDirectory =
@@ -352,8 +352,8 @@
                      "/PackageMaker.app/Contents/MacOS");
 
   std::string pkgPath;
-  const char* inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
-  if (inst_program && *inst_program) {
+  cmValue inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM");
+  if (inst_program && !inst_program->empty()) {
     pkgPath = inst_program;
   } else {
     pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false);
@@ -362,7 +362,7 @@
                     "Cannot find PackageMaker compiler" << std::endl);
       return 0;
     }
-    this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str());
+    this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath);
   }
 
   // Get path to the real PackageMaker, not a symlink:
@@ -427,11 +427,12 @@
   // Determine the package compatibility version. If it wasn't
   // specified by the user, we define it based on which features the
   // user requested.
-  const char* packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
-  if (packageCompat && *packageCompat) {
+  cmValue packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION");
+  if (packageCompat && !packageCompat->empty()) {
     unsigned int majorVersion = 10;
     unsigned int minorVersion = 5;
-    int res = sscanf(packageCompat, "%u.%u", &majorVersion, &minorVersion);
+    int res =
+      sscanf(packageCompat->c_str(), "%u.%u", &majorVersion, &minorVersion);
     if (res == 2) {
       this->PackageCompatibilityVersion =
         getVersion(majorVersion, minorVersion);
@@ -454,8 +455,7 @@
                   "Cannot find hdiutil compiler" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE",
-                          pkgPath.c_str());
+  this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", pkgPath);
 
   return this->Superclass::InitializeInternal();
 }
@@ -543,8 +543,7 @@
 
     // Create the Info.plist file for this component
     std::string moduleVersionSuffix = cmStrCat('.', component.Name);
-    this->SetOption("CPACK_MODULE_VERSION_SUFFIX",
-                    moduleVersionSuffix.c_str());
+    this->SetOption("CPACK_MODULE_VERSION_SUFFIX", moduleVersionSuffix);
     std::string infoFileName = cmStrCat(component.Name, "-Info.plist");
     if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str())) {
       return false;
diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx
index a3e55de..f55b8de 100644
--- a/Source/CPack/cmCPackProductBuildGenerator.cxx
+++ b/Source/CPack/cmCPackProductBuildGenerator.cxx
@@ -12,6 +12,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCPackProductBuildGenerator::cmCPackProductBuildGenerator()
 {
@@ -87,11 +88,11 @@
   std::string version = this->GetOption("CPACK_PACKAGE_VERSION");
   std::string productbuild = this->GetOption("CPACK_COMMAND_PRODUCTBUILD");
   std::string identityName;
-  if (const char* n = this->GetOption("CPACK_PRODUCTBUILD_IDENTITY_NAME")) {
+  if (cmValue n = this->GetOption("CPACK_PRODUCTBUILD_IDENTITY_NAME")) {
     identityName = n;
   }
   std::string keychainPath;
-  if (const char* p = this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH")) {
+  if (cmValue p = this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH")) {
     keychainPath = p;
   }
 
@@ -122,7 +123,7 @@
                   "Cannot find pkgbuild executable" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_COMMAND_PKGBUILD", program.c_str());
+  this->SetOptionIfNotSet("CPACK_COMMAND_PKGBUILD", program);
 
   program = cmSystemTools::FindProgram("productbuild", no_paths, false);
   if (program.empty()) {
@@ -130,7 +131,7 @@
                   "Cannot find productbuild executable" << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_COMMAND_PRODUCTBUILD", program.c_str());
+  this->SetOptionIfNotSet("CPACK_COMMAND_PRODUCTBUILD", program);
 
   return this->Superclass::InitializeInternal();
 }
@@ -173,8 +174,8 @@
 
   const char* comp_name = component ? component->Name.c_str() : nullptr;
 
-  const char* preflight = this->GetComponentScript("PREFLIGHT", comp_name);
-  const char* postflight = this->GetComponentScript("POSTFLIGHT", comp_name);
+  cmValue preflight = this->GetComponentScript("PREFLIGHT", comp_name);
+  cmValue postflight = this->GetComponentScript("POSTFLIGHT", comp_name);
 
   std::string resDir = packageFileDir;
   if (component) {
@@ -213,11 +214,11 @@
   std::string version = this->GetOption("CPACK_PACKAGE_VERSION");
   std::string pkgbuild = this->GetOption("CPACK_COMMAND_PKGBUILD");
   std::string identityName;
-  if (const char* n = this->GetOption("CPACK_PKGBUILD_IDENTITY_NAME")) {
+  if (cmValue n = this->GetOption("CPACK_PKGBUILD_IDENTITY_NAME")) {
     identityName = n;
   }
   std::string keychainPath;
-  if (const char* p = this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH")) {
+  if (cmValue p = this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH")) {
     keychainPath = p;
   }
 
@@ -239,7 +240,7 @@
   return RunProductBuild(pkgCmd.str());
 }
 
-const char* cmCPackProductBuildGenerator::GetComponentScript(
+cmValue cmCPackProductBuildGenerator::GetComponentScript(
   const char* script, const char* component_name)
 {
   std::string scriptname = std::string("CPACK_") + script + "_";
diff --git a/Source/CPack/cmCPackProductBuildGenerator.h b/Source/CPack/cmCPackProductBuildGenerator.h
index 462e2fc..31cfafa 100644
--- a/Source/CPack/cmCPackProductBuildGenerator.h
+++ b/Source/CPack/cmCPackProductBuildGenerator.h
@@ -8,6 +8,7 @@
 
 #include "cmCPackGenerator.h"
 #include "cmCPackPKGGenerator.h"
+#include "cmValue.h"
 
 class cmCPackComponent;
 
@@ -45,6 +46,5 @@
                                 const std::string& packageDir,
                                 const cmCPackComponent* component);
 
-  const char* GetComponentScript(const char* script,
-                                 const char* script_component);
+  cmValue GetComponentScript(const char* script, const char* script_component);
 };
diff --git a/Source/CPack/cmCPackRPMGenerator.cxx b/Source/CPack/cmCPackRPMGenerator.cxx
index c3f6d59..9e50700 100644
--- a/Source/CPack/cmCPackRPMGenerator.cxx
+++ b/Source/CPack/cmCPackRPMGenerator.cxx
@@ -14,6 +14,7 @@
 #include "cmCPackLog.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCPackRPMGenerator::cmCPackRPMGenerator() = default;
 
@@ -32,13 +33,13 @@
   if (this->GetOption("CPACK_PACKAGE_NAME")) {
     std::string packageName = this->GetOption("CPACK_PACKAGE_NAME");
     std::replace(packageName.begin(), packageName.end(), ' ', '-');
-    this->SetOption("CPACK_PACKAGE_NAME", packageName.c_str());
+    this->SetOption("CPACK_PACKAGE_NAME", packageName);
   }
   /* same for CPACK_PACKAGE_FILE_NAME */
   if (this->GetOption("CPACK_PACKAGE_FILE_NAME")) {
     std::string packageName = this->GetOption("CPACK_PACKAGE_FILE_NAME");
     std::replace(packageName.begin(), packageName.end(), ' ', '-');
-    this->SetOption("CPACK_PACKAGE_FILE_NAME", packageName.c_str());
+    this->SetOption("CPACK_PACKAGE_FILE_NAME", packageName);
   }
   return this->Superclass::InitializeInternal();
 }
@@ -73,19 +74,17 @@
 
   localToplevel += "/" + packageName;
   /* replace the TEMP DIRECTORY with the component one */
-  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
+  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel);
   packageFileName += "/" + outputFileName;
   /* replace proposed CPACK_OUTPUT_FILE_NAME */
-  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
+  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName);
   /* replace the TEMPORARY package file name */
-  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
-                  packageFileName.c_str());
+  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName);
   // Tell CPackRPM.cmake the name of the component NAME.
-  this->SetOption("CPACK_RPM_PACKAGE_COMPONENT", packageName.c_str());
+  this->SetOption("CPACK_RPM_PACKAGE_COMPONENT", packageName);
   // Tell CPackRPM.cmake the path where the component is.
   std::string component_path = cmStrCat('/', packageName);
-  this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH",
-                  component_path.c_str());
+  this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", component_path);
   if (!this->ReadListFile("Internal/CPack/CPackRPM.cmake")) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Error while execution CPackRPM.cmake" << std::endl);
@@ -103,7 +102,7 @@
   this->packageFileNames.clear();
   std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY"));
 
-  const char* mainComponent = this->GetOption("CPACK_RPM_MAIN_COMPONENT");
+  cmValue mainComponent = this->GetOption("CPACK_RPM_MAIN_COMPONENT");
 
   if (this->IsOn("CPACK_RPM_DEBUGINFO_SINGLE_PACKAGE") &&
       !this->IsOn("CPACK_RPM_DEBUGINFO_PACKAGE")) {
@@ -364,19 +363,17 @@
   localToplevel += "/" + compInstDirName;
 
   /* replace the TEMP DIRECTORY with the component one */
-  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str());
+  this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel);
   packageFileName += "/" + outputFileName;
   /* replace proposed CPACK_OUTPUT_FILE_NAME */
-  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str());
+  this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName);
   /* replace the TEMPORARY package file name */
-  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME",
-                  packageFileName.c_str());
+  this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", packageFileName);
 
   if (!compInstDirName.empty()) {
     // Tell CPackRPM.cmake the path where the component is.
     std::string component_path = cmStrCat('/', compInstDirName);
-    this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH",
-                    component_path.c_str());
+    this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", component_path);
   }
 
   if (this->ReadListFile("Internal/CPack/CPackRPM.cmake")) {
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx
index ad0a3e2..1340fb5 100644
--- a/Source/CPack/cmCPackSTGZGenerator.cxx
+++ b/Source/CPack/cmCPackSTGZGenerator.cxx
@@ -15,6 +15,7 @@
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCPackSTGZGenerator::cmCPackSTGZGenerator()
   : cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr", ".sh")
@@ -33,7 +34,7 @@
                   "Cannot find template file: " << inFile << std::endl);
     return 0;
   }
-  this->SetOptionIfNotSet("CPACK_STGZ_HEADER_FILE", inFile.c_str());
+  this->SetOptionIfNotSet("CPACK_STGZ_HEADER_FILE", inFile);
   this->SetOptionIfNotSet("CPACK_AT_SIGN", "@");
 
   return this->Superclass::InitializeInternal();
@@ -78,8 +79,7 @@
   while (cmSystemTools::GetLineFromStream(ilfs, line)) {
     licenseText += line + "\n";
   }
-  this->SetOptionIfNotSet("CPACK_RESOURCE_FILE_LICENSE_CONTENT",
-                          licenseText.c_str());
+  this->SetOptionIfNotSet("CPACK_RESOURCE_FILE_LICENSE_CONTENT", licenseText);
 
   const char headerLengthTag[] = "###CPACK_HEADER_LENGTH###";
 
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx
index a778939..54fd358 100644
--- a/Source/CPack/cpack.cxx
+++ b/Source/CPack/cpack.cxx
@@ -22,11 +22,11 @@
 #include "cmDocumentationFormatter.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -326,11 +326,11 @@
       cmSystemTools::CollapseFullPath(cpackProjectDirectory);
     globalMF.AddDefinition("CPACK_PACKAGE_DIRECTORY", cpackProjectDirectory);
 
-    cmProp cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH");
+    cmValue cpackModulesPath = globalMF.GetDefinition("CPACK_MODULE_PATH");
     if (cpackModulesPath) {
       globalMF.AddDefinition("CMAKE_MODULE_PATH", *cpackModulesPath);
     }
-    cmProp genList = globalMF.GetDefinition("CPACK_GENERATOR");
+    cmValue genList = globalMF.GetDefinition("CPACK_GENERATOR");
     if (!genList) {
       cmCPack_Log(&log, cmCPackLog::LOG_ERROR,
                   "CPack generator not specified" << std::endl);
@@ -408,20 +408,20 @@
             parsed = 0;
           }
           if (parsed) {
-            cmProp projName = mf->GetDefinition("CPACK_PACKAGE_NAME");
+            cmValue projName = mf->GetDefinition("CPACK_PACKAGE_NAME");
             cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
                         "Use generator: " << cpackGenerator->GetNameOfClass()
                                           << std::endl);
             cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE,
                         "For project: " << *projName << std::endl);
 
-            cmProp projVersion = mf->GetDefinition("CPACK_PACKAGE_VERSION");
+            cmValue projVersion = mf->GetDefinition("CPACK_PACKAGE_VERSION");
             if (!projVersion) {
-              cmProp projVersionMajor =
+              cmValue projVersionMajor =
                 mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR");
-              cmProp projVersionMinor =
+              cmValue projVersionMinor =
                 mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR");
-              cmProp projVersionPatch =
+              cmValue projVersionPatch =
                 mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH");
               std::ostringstream ostr;
               ostr << *projVersionMajor << "." << *projVersionMinor << "."
diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx
index 483c316..6e7c9e1 100644
--- a/Source/CTest/cmCTestBuildCommand.cxx
+++ b/Source/CTest/cmCTestBuildCommand.cxx
@@ -11,9 +11,9 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 class cmExecutionStatus;
@@ -39,13 +39,13 @@
 
   this->Handler = handler;
 
-  cmProp ctestBuildCommand =
+  cmValue ctestBuildCommand =
     this->Makefile->GetDefinition("CTEST_BUILD_COMMAND");
   if (cmNonempty(ctestBuildCommand)) {
     this->CTest->SetCTestConfiguration("MakeCommand", *ctestBuildCommand,
                                        this->Quiet);
   } else {
-    cmProp cmakeGeneratorName =
+    cmValue cmakeGeneratorName =
       this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR");
 
     // Build configuration is determined by: CONFIGURATION argument,
@@ -53,7 +53,7 @@
     // CTEST_CONFIGURATION_TYPE script variable, or ctest -C command
     // line argument... in that order.
     //
-    cmProp ctestBuildConfiguration =
+    cmValue ctestBuildConfiguration =
       this->Makefile->GetDefinition("CTEST_BUILD_CONFIGURATION");
     std::string cmakeBuildConfiguration = cmNonempty(this->Configuration)
       ? this->Configuration
@@ -119,13 +119,13 @@
     }
   }
 
-  if (cmProp useLaunchers =
+  if (cmValue useLaunchers =
         this->Makefile->GetDefinition("CTEST_USE_LAUNCHERS")) {
     this->CTest->SetCTestConfiguration("UseLaunchers", *useLaunchers,
                                        this->Quiet);
   }
 
-  if (cmProp labelsForSubprojects =
+  if (cmValue labelsForSubprojects =
         this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
     this->CTest->SetCTestConfiguration("LabelsForSubprojects",
                                        *labelsForSubprojects, this->Quiet);
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index 03caa63..f9c4a8e 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -19,10 +19,10 @@
 #include "cmGeneratedFileStream.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 
 static const char* cmCTestErrorMatches[] = {
@@ -249,11 +249,11 @@
   }
 
   // Record the user-specified custom warning rules.
-  if (cmProp customWarningMatchers =
+  if (cmValue customWarningMatchers =
         mf->GetDefinition("CTEST_CUSTOM_WARNING_MATCH")) {
     cmExpandList(*customWarningMatchers, this->ReallyCustomWarningMatches);
   }
-  if (cmProp customWarningExceptions =
+  if (cmValue customWarningExceptions =
         mf->GetDefinition("CTEST_CUSTOM_WARNING_EXCEPTION")) {
     cmExpandList(*customWarningExceptions,
                  this->ReallyCustomWarningExceptions);
diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx
index db9923e..fd20398 100644
--- a/Source/CTest/cmCTestConfigureCommand.cxx
+++ b/Source/CTest/cmCTestConfigureCommand.cxx
@@ -12,9 +12,9 @@
 #include "cmCTestConfigureHandler.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 void cmCTestConfigureCommand::BindArguments()
@@ -39,14 +39,14 @@
     return nullptr;
   }
 
-  cmProp ctestConfigureCommand =
+  cmValue ctestConfigureCommand =
     this->Makefile->GetDefinition("CTEST_CONFIGURE_COMMAND");
 
   if (cmNonempty(ctestConfigureCommand)) {
     this->CTest->SetCTestConfiguration("ConfigureCommand",
                                        *ctestConfigureCommand, this->Quiet);
   } else {
-    cmProp cmakeGeneratorName =
+    cmValue cmakeGeneratorName =
       this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR");
     if (cmNonempty(cmakeGeneratorName)) {
       const std::string& source_dir =
@@ -106,7 +106,7 @@
       cmakeConfigureCommand += *cmakeGeneratorName;
       cmakeConfigureCommand += "\"";
 
-      cmProp cmakeGeneratorPlatform =
+      cmValue cmakeGeneratorPlatform =
         this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR_PLATFORM");
       if (cmNonempty(cmakeGeneratorPlatform)) {
         cmakeConfigureCommand += " \"-A";
@@ -114,7 +114,7 @@
         cmakeConfigureCommand += "\"";
       }
 
-      cmProp cmakeGeneratorToolset =
+      cmValue cmakeGeneratorToolset =
         this->Makefile->GetDefinition("CTEST_CMAKE_GENERATOR_TOOLSET");
       if (cmNonempty(cmakeGeneratorToolset)) {
         cmakeConfigureCommand += " \"-T";
@@ -137,7 +137,7 @@
     }
   }
 
-  if (cmProp labelsForSubprojects =
+  if (cmValue labelsForSubprojects =
         this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
     this->CTest->SetCTestConfiguration("LabelsForSubprojects",
                                        *labelsForSubprojects, this->Quiet);
diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx
index 568b091..d85edcc 100644
--- a/Source/CTest/cmCTestGIT.cxx
+++ b/Source/CTest/cmCTestGIT.cxx
@@ -18,6 +18,7 @@
 #include "cmProcessTools.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
                                       unsigned int minor, unsigned int fix)
diff --git a/Source/CTest/cmCTestGenericHandler.cxx b/Source/CTest/cmCTestGenericHandler.cxx
index 48cc0e4..9800192 100644
--- a/Source/CTest/cmCTestGenericHandler.cxx
+++ b/Source/CTest/cmCTestGenericHandler.cxx
@@ -21,11 +21,12 @@
 
 cmCTestGenericHandler::~cmCTestGenericHandler() = default;
 
+namespace {
 /* Modify the given `map`, setting key `op` to `value` if `value`
  * is non-null, otherwise removing key `op` (if it exists).
  */
-static void SetMapValue(cmCTestGenericHandler::t_StringToString& map,
-                        const std::string& op, const char* value)
+void SetMapValue(cmCTestGenericHandler::t_StringToString& map,
+                 const std::string& op, const char* value)
 {
   if (!value) {
     map.erase(op);
@@ -34,11 +35,26 @@
 
   map[op] = value;
 }
+void SetMapValue(cmCTestGenericHandler::t_StringToString& map,
+                 const std::string& op, cmValue value)
+{
+  if (!value) {
+    map.erase(op);
+    return;
+  }
+
+  map[op] = *value;
+}
+}
 
 void cmCTestGenericHandler::SetOption(const std::string& op, const char* value)
 {
   SetMapValue(this->Options, op, value);
 }
+void cmCTestGenericHandler::SetOption(const std::string& op, cmValue value)
+{
+  SetMapValue(this->Options, op, value);
+}
 
 void cmCTestGenericHandler::SetPersistentOption(const std::string& op,
                                                 const char* value)
@@ -46,6 +62,12 @@
   this->SetOption(op, value);
   SetMapValue(this->PersistentOptions, op, value);
 }
+void cmCTestGenericHandler::SetPersistentOption(const std::string& op,
+                                                cmValue value)
+{
+  this->SetOption(op, value);
+  SetMapValue(this->PersistentOptions, op, value);
+}
 
 void cmCTestGenericHandler::AddMultiOption(const std::string& op,
                                            const std::string& value)
@@ -72,13 +94,13 @@
   this->MultiOptions = this->PersistentMultiOptions;
 }
 
-const char* cmCTestGenericHandler::GetOption(const std::string& op)
+cmValue cmCTestGenericHandler::GetOption(const std::string& op)
 {
   auto remit = this->Options.find(op);
   if (remit == this->Options.end()) {
     return nullptr;
   }
-  return remit->second.c_str();
+  return cmValue(remit->second);
 }
 
 std::vector<std::string> cmCTestGenericHandler::GetMultiOption(
diff --git a/Source/CTest/cmCTestGenericHandler.h b/Source/CTest/cmCTestGenericHandler.h
index e846fd9..b4b0ad8 100644
--- a/Source/CTest/cmCTestGenericHandler.h
+++ b/Source/CTest/cmCTestGenericHandler.h
@@ -12,6 +12,7 @@
 
 #include "cmCTest.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 class cmGeneratedFileStream;
 class cmMakefile;
@@ -86,8 +87,18 @@
    * as a multi-value will return nullptr.
    */
   void SetPersistentOption(const std::string& op, const char* value);
+  void SetPersistentOption(const std::string& op, const std::string& value)
+  {
+    this->SetPersistentOption(op, cmValue(value));
+  }
+  void SetPersistentOption(const std::string& op, cmValue value);
   void SetOption(const std::string& op, const char* value);
-  const char* GetOption(const std::string& op);
+  void SetOption(const std::string& op, const std::string& value)
+  {
+    this->SetOption(op, cmValue(value));
+  }
+  void SetOption(const std::string& op, cmValue value);
+  cmValue GetOption(const std::string& op);
 
   /**
    * Multi-Options collect one or more values from flags; passing
diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx
index 731932e..ea8feaa 100644
--- a/Source/CTest/cmCTestHandlerCommand.cxx
+++ b/Source/CTest/cmCTestHandlerCommand.cxx
@@ -14,9 +14,9 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmWorkingDirectory.h"
 
 namespace {
@@ -126,7 +126,7 @@
   // CTEST_CONFIGURATION_TYPE script variable if it is defined.
   // The current script value trumps the -C argument on the command
   // line.
-  cmProp ctestConfigType =
+  cmValue ctestConfigType =
     this->Makefile->GetDefinition("CTEST_CONFIGURATION_TYPE");
   if (ctestConfigType) {
     this->CTest->SetConfigType(*ctestConfigType);
@@ -161,7 +161,7 @@
       this->Quiet);
   }
 
-  if (cmProp changeId = this->Makefile->GetDefinition("CTEST_CHANGE_ID")) {
+  if (cmValue changeId = this->Makefile->GetDefinition("CTEST_CHANGE_ID")) {
     this->CTest->SetCTestConfiguration("ChangeId", *changeId, this->Quiet);
   }
 
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index 125d003..6bb8e79 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -305,7 +305,7 @@
   return this->DefectCount;
 }
 
-void cmCTestMemCheckHandler::GenerateDartOutput(cmXMLWriter& xml)
+void cmCTestMemCheckHandler::GenerateCTestXML(cmXMLWriter& xml)
 {
   if (!this->CTest->GetProduceXML()) {
     return;
diff --git a/Source/CTest/cmCTestMemCheckHandler.h b/Source/CTest/cmCTestMemCheckHandler.h
index b200c43..a63a24d 100644
--- a/Source/CTest/cmCTestMemCheckHandler.h
+++ b/Source/CTest/cmCTestMemCheckHandler.h
@@ -119,9 +119,9 @@
   bool InitializeMemoryChecking();
 
   /**
-   * Generate the Dart compatible output
+   * Generate CTest DynamicAnalysis.xml files
    */
-  void GenerateDartOutput(cmXMLWriter& xml) override;
+  void GenerateCTestXML(cmXMLWriter& xml) override;
 
   std::vector<std::string> CustomPreMemCheck;
   std::vector<std::string> CustomPostMemCheck;
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 86a8e00..d90c4a6 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -1026,6 +1026,11 @@
     properties.append(DumpCTestProperty(
       "ENVIRONMENT", DumpToJsonArray(testProperties.Environment)));
   }
+  if (!testProperties.EnvironmentModification.empty()) {
+    properties.append(DumpCTestProperty(
+      "ENVIRONMENT_MODIFICATION",
+      DumpToJsonArray(testProperties.EnvironmentModification)));
+  }
   if (!testProperties.ErrorRegularExpressions.empty()) {
     properties.append(DumpCTestProperty(
       "FAIL_REGULAR_EXPRESSION",
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index a892113..9d2cef6 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -2,17 +2,22 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestRunTest.h"
 
+#include <algorithm>
 #include <chrono>
 #include <cstddef> // IWYU pragma: keep
 #include <cstdint>
 #include <cstdio>
 #include <cstring>
+#include <functional>
 #include <iomanip>
 #include <ratio>
 #include <sstream>
 #include <utility>
 
 #include <cm/memory>
+#include <cm/optional>
+#include <cm/string_view>
+#include <cmext/string_view>
 
 #include "cmsys/RegularExpression.hxx"
 
@@ -44,7 +49,9 @@
   // Check for special CTest XML tags in this line of output.
   // If any are found, this line is excluded from ProcessOutput.
   if (!line.empty() && line.find("<CTest") != std::string::npos) {
+    bool ctest_tag_found = false;
     if (this->TestHandler->CustomCompletionStatusRegex.find(line)) {
+      ctest_tag_found = true;
       this->TestResult.CustomCompletionStatus =
         this->TestHandler->CustomCompletionStatusRegex.match(1);
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
@@ -52,6 +59,20 @@
                                   << "Test Details changed to '"
                                   << this->TestResult.CustomCompletionStatus
                                   << "'" << std::endl);
+    } else if (this->TestHandler->CustomLabelRegex.find(line)) {
+      ctest_tag_found = true;
+      auto label = this->TestHandler->CustomLabelRegex.match(1);
+      auto& labels = this->TestProperties->Labels;
+      if (std::find(labels.begin(), labels.end(), label) == labels.end()) {
+        labels.push_back(label);
+        std::sort(labels.begin(), labels.end());
+        cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                   this->GetIndex()
+                     << ": "
+                     << "Test Label added: '" << label << "'" << std::endl);
+      }
+    }
+    if (ctest_tag_found) {
       return;
     }
   }
@@ -245,7 +266,7 @@
     *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
   }
 
-  this->DartProcessing();
+  this->ParseOutputForMeasurements();
 
   // if this is doing MemCheck then all the output needs to be put into
   // Output since that is what is parsed by cmCTestMemCheckHandler
@@ -623,6 +644,7 @@
 
   return this->ForkProcess(timeout, this->TestProperties->ExplicitTimeout,
                            &this->TestProperties->Environment,
+                           &this->TestProperties->EnvironmentModification,
                            &this->TestProperties->Affinity);
 }
 
@@ -679,28 +701,45 @@
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                this->Index << ":  " << env << std::endl);
   }
+  if (!this->TestProperties->EnvironmentModification.empty()) {
+    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+               this->Index << ": "
+                           << "Environment variable modifications: "
+                           << std::endl);
+  }
+  for (std::string const& envmod :
+       this->TestProperties->EnvironmentModification) {
+    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+               this->Index << ":  " << envmod << std::endl);
+  }
 }
 
-void cmCTestRunTest::DartProcessing()
+void cmCTestRunTest::ParseOutputForMeasurements()
 {
   if (!this->ProcessOutput.empty() &&
-      this->ProcessOutput.find("<DartMeasurement") != std::string::npos) {
-    if (this->TestHandler->DartStuff.find(this->ProcessOutput)) {
-      this->TestResult.DartString = this->TestHandler->DartStuff.match(1);
+      (this->ProcessOutput.find("<DartMeasurement") != std::string::npos ||
+       this->ProcessOutput.find("<CTestMeasurement") != std::string::npos)) {
+    if (this->TestHandler->AllTestMeasurementsRegex.find(
+          this->ProcessOutput)) {
+      this->TestResult.TestMeasurementsOutput =
+        this->TestHandler->AllTestMeasurementsRegex.match(1);
       // keep searching and replacing until none are left
-      while (this->TestHandler->DartStuff1.find(this->ProcessOutput)) {
+      while (this->TestHandler->SingleTestMeasurementRegex.find(
+        this->ProcessOutput)) {
         // replace the exact match for the string
         cmSystemTools::ReplaceString(
-          this->ProcessOutput, this->TestHandler->DartStuff1.match(1).c_str(),
-          "");
+          this->ProcessOutput,
+          this->TestHandler->SingleTestMeasurementRegex.match(1).c_str(), "");
       }
     }
   }
 }
 
-bool cmCTestRunTest::ForkProcess(cmDuration testTimeOut, bool explicitTimeout,
-                                 std::vector<std::string>* environment,
-                                 std::vector<size_t>* affinity)
+bool cmCTestRunTest::ForkProcess(
+  cmDuration testTimeOut, bool explicitTimeout,
+  std::vector<std::string>* environment,
+  std::vector<std::string>* environment_modification,
+  std::vector<size_t>* affinity)
 {
   this->TestProcess->SetId(this->Index);
   this->TestProcess->SetWorkingDirectory(this->TestProperties->Directory);
@@ -743,12 +782,145 @@
 
   std::ostringstream envMeasurement;
   if (environment && !environment->empty()) {
+    // Environment modification works on the assumption that the environment is
+    // actually modified here. If another strategy is used, there will need to
+    // be updates below in `apply_diff`.
     cmSystemTools::AppendEnv(*environment);
     for (auto const& var : *environment) {
       envMeasurement << var << std::endl;
     }
   }
 
+  if (environment_modification && !environment_modification->empty()) {
+    std::map<std::string, cm::optional<std::string>> env_application;
+
+#ifdef _WIN32
+    char path_sep = ';';
+#else
+    char path_sep = ':';
+#endif
+
+    auto apply_diff =
+      [&env_application](const std::string& name,
+                         std::function<void(std::string&)> const& apply) {
+        cm::optional<std::string> old_value = env_application[name];
+        std::string output;
+        if (old_value) {
+          output = *old_value;
+        } else {
+          // This only works because the environment is actually modified above
+          // (`AppendEnv`). If CTest ever just creates an environment block
+          // directly, that block will need to be queried for the subprocess'
+          // value instead.
+          const char* curval = cmSystemTools::GetEnv(name);
+          if (curval) {
+            output = curval;
+          }
+        }
+        apply(output);
+        env_application[name] = output;
+      };
+
+    bool err_occurred = false;
+
+    for (auto const& envmod : *environment_modification) {
+      // Split on `=`
+      auto const eq_loc = envmod.find_first_of('=');
+      if (eq_loc == std::string::npos) {
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "Error: Missing `=` after the variable name in: "
+                     << envmod << std::endl);
+        err_occurred = true;
+        continue;
+      }
+      auto const name = envmod.substr(0, eq_loc);
+
+      // Split value on `:`
+      auto const op_value_start = eq_loc + 1;
+      auto const colon_loc = envmod.find_first_of(':', op_value_start);
+      if (colon_loc == std::string::npos) {
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "Error: Missing `:` after the operation in: " << envmod
+                                                                 << std::endl);
+        err_occurred = true;
+        continue;
+      }
+      auto const op =
+        envmod.substr(op_value_start, colon_loc - op_value_start);
+
+      auto const value_start = colon_loc + 1;
+      auto const value = envmod.substr(value_start);
+
+      // Determine what to do with the operation.
+      if (op == "reset"_s) {
+        auto entry = env_application.find(name);
+        if (entry != env_application.end()) {
+          env_application.erase(entry);
+        }
+      } else if (op == "set"_s) {
+        env_application[name] = value;
+      } else if (op == "unset"_s) {
+        env_application[name] = {};
+      } else if (op == "string_append"_s) {
+        apply_diff(name, [&value](std::string& output) { output += value; });
+      } else if (op == "string_prepend"_s) {
+        apply_diff(name,
+                   [&value](std::string& output) { output.insert(0, value); });
+      } else if (op == "path_list_append"_s) {
+        apply_diff(name, [&value, path_sep](std::string& output) {
+          if (!output.empty()) {
+            output += path_sep;
+          }
+          output += value;
+        });
+      } else if (op == "path_list_prepend"_s) {
+        apply_diff(name, [&value, path_sep](std::string& output) {
+          if (!output.empty()) {
+            output.insert(output.begin(), path_sep);
+          }
+          output.insert(0, value);
+        });
+      } else if (op == "cmake_list_append"_s) {
+        apply_diff(name, [&value](std::string& output) {
+          if (!output.empty()) {
+            output += ';';
+          }
+          output += value;
+        });
+      } else if (op == "cmake_list_prepend"_s) {
+        apply_diff(name, [&value](std::string& output) {
+          if (!output.empty()) {
+            output.insert(output.begin(), ';');
+          }
+          output.insert(0, value);
+        });
+      } else {
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "Error: Unrecognized environment manipulation argument: "
+                     << op << std::endl);
+        err_occurred = true;
+        continue;
+      }
+    }
+
+    if (err_occurred) {
+      return false;
+    }
+
+    for (auto const& env_apply : env_application) {
+      if (env_apply.second) {
+        auto const env_update =
+          cmStrCat(env_apply.first, '=', *env_apply.second);
+        cmSystemTools::PutEnv(env_update);
+        envMeasurement << env_update << std::endl;
+      } else {
+        cmSystemTools::UnsetEnv(env_apply.first.c_str());
+        // Signify that this variable is being actively unset
+        envMeasurement << "#" << env_apply.first << "=" << std::endl;
+      }
+    }
+  }
+
   if (this->UseAllocatedResources) {
     std::vector<std::string> envLog;
     this->SetupResourcesEnvironment(&envLog);
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index 863ac1b..2082156 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -109,10 +109,11 @@
 
 private:
   bool NeedsToRepeat();
-  void DartProcessing();
+  void ParseOutputForMeasurements();
   void ExeNotFound(std::string exe);
   bool ForkProcess(cmDuration testTimeOut, bool explicitTimeout,
                    std::vector<std::string>* environment,
+                   std::vector<std::string>* environment_modification,
                    std::vector<size_t>* affinity);
   void WriteLogOutputTop(size_t completed, size_t total);
   // Run post processing of the process output for MemCheck
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index d2cad39..f685f66 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -34,12 +34,12 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 #ifdef _WIN32
@@ -372,8 +372,8 @@
 int cmCTestScriptHandler::ExtractVariables()
 {
   // Temporary variables
-  cmProp minInterval;
-  cmProp contDuration;
+  cmValue minInterval;
+  cmValue contDuration;
 
   this->SourceDir =
     this->Makefile->GetSafeDefinition("CTEST_SOURCE_DIRECTORY");
@@ -412,7 +412,7 @@
   int i;
   for (i = 1; i < 10; ++i) {
     sprintf(updateVar, "CTEST_EXTRA_UPDATES_%i", i);
-    cmProp updateVal = this->Makefile->GetDefinition(updateVar);
+    cmValue updateVal = this->Makefile->GetDefinition(updateVar);
     if (updateVal) {
       if (this->UpdateCmd.empty()) {
         cmSystemTools::Error(
@@ -930,7 +930,7 @@
     return cmCTest::MaxDuration();
   }
 
-  cmProp timelimitS = this->Makefile->GetDefinition("CTEST_TIME_LIMIT");
+  cmValue timelimitS = this->Makefile->GetDefinition("CTEST_TIME_LIMIT");
 
   if (!timelimitS) {
     return cmCTest::MaxDuration();
diff --git a/Source/CTest/cmCTestStartCommand.cxx b/Source/CTest/cmCTestStartCommand.cxx
index 53e1b2f..84d12d7 100644
--- a/Source/CTest/cmCTestStartCommand.cxx
+++ b/Source/CTest/cmCTestStartCommand.cxx
@@ -9,8 +9,8 @@
 #include "cmCTestVC.h"
 #include "cmGeneratedFileStream.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 class cmExecutionStatus;
 
@@ -30,8 +30,8 @@
 
   size_t cnt = 0;
   const char* smodel = nullptr;
-  const std::string* src_dir = nullptr;
-  const std::string* bld_dir = nullptr;
+  cmValue src_dir;
+  cmValue bld_dir;
 
   while (cnt < args.size()) {
     if (args[cnt] == "GROUP" || args[cnt] == "TRACK") {
@@ -55,10 +55,10 @@
       smodel = args[cnt].c_str();
       cnt++;
     } else if (!src_dir) {
-      src_dir = &args[cnt];
+      src_dir = cmValue(args[cnt]);
       cnt++;
     } else if (!bld_dir) {
-      bld_dir = &args[cnt];
+      bld_dir = cmValue(args[cnt]);
       cnt++;
     } else {
       this->SetError("Too many arguments");
@@ -162,7 +162,7 @@
                                           std::string const& sourceDir)
 {
   // Use the user-provided command to create the source tree.
-  cmProp initialCheckoutCommand =
+  cmValue initialCheckoutCommand =
     this->Makefile->GetDefinition("CTEST_CHECKOUT_COMMAND");
   if (!initialCheckoutCommand) {
     initialCheckoutCommand =
diff --git a/Source/CTest/cmCTestSubmitCommand.cxx b/Source/CTest/cmCTestSubmitCommand.cxx
index bdba0e5..c4f87e9 100644
--- a/Source/CTest/cmCTestSubmitCommand.cxx
+++ b/Source/CTest/cmCTestSubmitCommand.cxx
@@ -16,10 +16,10 @@
 #include "cmCommand.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 class cmExecutionStatus;
 
@@ -36,8 +36,8 @@
 
 cmCTestGenericHandler* cmCTestSubmitCommand::InitializeHandler()
 {
-  const std::string* submitURL = !this->SubmitURL.empty()
-    ? &this->SubmitURL
+  cmValue submitURL = !this->SubmitURL.empty()
+    ? cmValue(this->SubmitURL)
     : this->Makefile->GetDefinition("CTEST_SUBMIT_URL");
 
   if (submitURL) {
@@ -59,14 +59,14 @@
   this->CTest->SetCTestConfigurationFromCMakeVariable(
     this->Makefile, "CurlOptions", "CTEST_CURL_OPTIONS", this->Quiet);
 
-  cmProp notesFilesVariable =
+  cmValue notesFilesVariable =
     this->Makefile->GetDefinition("CTEST_NOTES_FILES");
   if (notesFilesVariable) {
     std::vector<std::string> notesFiles = cmExpandedList(*notesFilesVariable);
     this->CTest->GenerateNotesFile(notesFiles);
   }
 
-  cmProp extraFilesVariable =
+  cmValue extraFilesVariable =
     this->Makefile->GetDefinition("CTEST_EXTRA_SUBMIT_FILES");
   if (extraFilesVariable) {
     std::vector<std::string> extraFiles = cmExpandedList(*extraFilesVariable);
@@ -119,15 +119,15 @@
     handler->SetHttpHeaders(this->HttpHeaders);
   }
 
-  handler->SetOption("RetryDelay", this->RetryDelay.c_str());
-  handler->SetOption("RetryCount", this->RetryCount.c_str());
+  handler->SetOption("RetryDelay", this->RetryDelay);
+  handler->SetOption("RetryCount", this->RetryCount);
   handler->SetOption("InternalTest", this->InternalTest ? "ON" : "OFF");
 
   handler->SetQuiet(this->Quiet);
 
   if (this->CDashUpload) {
-    handler->SetOption("CDashUploadFile", this->CDashUploadFile.c_str());
-    handler->SetOption("CDashUploadType", this->CDashUploadType.c_str());
+    handler->SetOption("CDashUploadFile", this->CDashUploadFile);
+    handler->SetOption("CDashUploadType", this->CDashUploadType);
   }
   return handler;
 }
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index 5b54573..b99bb79 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -21,10 +21,10 @@
 #include "cmCurl.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLParser.h"
 #include "cmake.h"
 
@@ -261,7 +261,7 @@
         cmCTestScriptHandler* ch = this->CTest->GetScriptHandler();
         cmake* cm = ch->GetCMake();
         if (cm) {
-          cmProp subproject = cm->GetState()->GetGlobalProperty("SubProject");
+          cmValue subproject = cm->GetState()->GetGlobalProperty("SubProject");
           if (subproject) {
             upload_as += "&subproject=";
             upload_as += ctest_curl.Escape(*subproject);
@@ -357,12 +357,8 @@
       // If curl failed for any reason, or checksum fails, wait and retry
       //
       if (res != CURLE_OK || this->HasErrors) {
-        std::string retryDelay = this->GetOption("RetryDelay") == nullptr
-          ? ""
-          : this->GetOption("RetryDelay");
-        std::string retryCount = this->GetOption("RetryCount") == nullptr
-          ? ""
-          : this->GetOption("RetryCount");
+        std::string retryDelay = *this->GetOption("RetryDelay");
+        std::string retryCount = *this->GetOption("RetryCount");
 
         auto delay = cmDuration(
           retryDelay.empty()
@@ -522,12 +518,8 @@
   bool internalTest = cmIsOn(this->GetOption("InternalTest"));
 
   // Get RETRY_COUNT and RETRY_DELAY values if they were set.
-  std::string retryDelayString = this->GetOption("RetryDelay") == nullptr
-    ? ""
-    : this->GetOption("RetryDelay");
-  std::string retryCountString = this->GetOption("RetryCount") == nullptr
-    ? ""
-    : this->GetOption("RetryCount");
+  std::string retryDelayString = *this->GetOption("RetryDelay");
+  std::string retryCountString = *this->GetOption("RetryCount");
   auto retryDelay = std::chrono::seconds(0);
   if (!retryDelayString.empty()) {
     unsigned long retryDelayValue = 0;
@@ -556,7 +548,7 @@
   // a "&subproject=subprojectname" to the first POST.
   cmCTestScriptHandler* ch = this->CTest->GetScriptHandler();
   cmake* cm = ch->GetCMake();
-  cmProp subproject = cm->GetState()->GetGlobalProperty("SubProject");
+  cmValue subproject = cm->GetState()->GetGlobalProperty("SubProject");
   // TODO: Encode values for a URL instead of trusting caller.
   std::ostringstream str;
   if (subproject) {
@@ -716,8 +708,8 @@
 
 int cmCTestSubmitHandler::ProcessHandler()
 {
-  const char* cdashUploadFile = this->GetOption("CDashUploadFile");
-  const char* cdashUploadType = this->GetOption("CDashUploadType");
+  cmValue cdashUploadFile = this->GetOption("CDashUploadFile");
+  cmValue cdashUploadType = this->GetOption("CDashUploadType");
   if (cdashUploadFile && cdashUploadType) {
     return this->HandleCDashUploadFile(cdashUploadFile, cdashUploadType);
   }
@@ -804,6 +796,7 @@
     }
   }
   this->CTest->AddIfExists(cmCTest::PartMemCheck, "DynamicAnalysis.xml");
+  this->CTest->AddIfExists(cmCTest::PartMemCheck, "DynamicAnalysis-Test.xml");
   this->CTest->AddIfExists(cmCTest::PartMemCheck, "Purify.xml");
   this->CTest->AddIfExists(cmCTest::PartNotes, "Notes.xml");
   this->CTest->AddIfExists(cmCTest::PartUpload, "Upload.xml");
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index 67f4986..5488388 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -12,8 +12,8 @@
 #include "cmCTestTestHandler.h"
 #include "cmDuration.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 void cmCTestTestCommand::BindArguments()
 {
@@ -40,7 +40,7 @@
 
 cmCTestGenericHandler* cmCTestTestCommand::InitializeHandler()
 {
-  cmProp ctestTimeout = this->Makefile->GetDefinition("CTEST_TEST_TIMEOUT");
+  cmValue ctestTimeout = this->Makefile->GetDefinition("CTEST_TEST_TIMEOUT");
 
   cmDuration timeout;
   if (ctestTimeout) {
@@ -54,7 +54,7 @@
   }
   this->CTest->SetTimeOut(timeout);
 
-  cmProp resourceSpecFile =
+  cmValue resourceSpecFile =
     this->Makefile->GetDefinition("CTEST_RESOURCE_SPEC_FILE");
   if (this->ResourceSpecFile.empty() && resourceSpecFile) {
     this->ResourceSpecFile = *resourceSpecFile;
@@ -64,13 +64,13 @@
   if (!this->Start.empty() || !this->End.empty() || !this->Stride.empty()) {
     handler->SetOption(
       "TestsToRunInformation",
-      cmStrCat(this->Start, ',', this->End, ',', this->Stride).c_str());
+      cmStrCat(this->Start, ',', this->End, ',', this->Stride));
   }
   if (!this->Exclude.empty()) {
-    handler->SetOption("ExcludeRegularExpression", this->Exclude.c_str());
+    handler->SetOption("ExcludeRegularExpression", this->Exclude);
   }
   if (!this->Include.empty()) {
-    handler->SetOption("IncludeRegularExpression", this->Include.c_str());
+    handler->SetOption("IncludeRegularExpression", this->Include);
   }
   if (!this->ExcludeLabel.empty()) {
     handler->AddMultiOption("ExcludeLabelRegularExpression",
@@ -81,30 +81,30 @@
   }
   if (!this->ExcludeFixture.empty()) {
     handler->SetOption("ExcludeFixtureRegularExpression",
-                       this->ExcludeFixture.c_str());
+                       this->ExcludeFixture);
   }
   if (!this->ExcludeFixtureSetup.empty()) {
     handler->SetOption("ExcludeFixtureSetupRegularExpression",
-                       this->ExcludeFixtureSetup.c_str());
+                       this->ExcludeFixtureSetup);
   }
   if (!this->ExcludeFixtureCleanup.empty()) {
     handler->SetOption("ExcludeFixtureCleanupRegularExpression",
-                       this->ExcludeFixtureCleanup.c_str());
+                       this->ExcludeFixtureCleanup);
   }
   if (this->StopOnFailure) {
     handler->SetOption("StopOnFailure", "ON");
   }
   if (!this->ParallelLevel.empty()) {
-    handler->SetOption("ParallelLevel", this->ParallelLevel.c_str());
+    handler->SetOption("ParallelLevel", this->ParallelLevel);
   }
   if (!this->Repeat.empty()) {
-    handler->SetOption("Repeat", this->Repeat.c_str());
+    handler->SetOption("Repeat", this->Repeat);
   }
   if (!this->ScheduleRandom.empty()) {
-    handler->SetOption("ScheduleRandom", this->ScheduleRandom.c_str());
+    handler->SetOption("ScheduleRandom", this->ScheduleRandom);
   }
   if (!this->ResourceSpecFile.empty()) {
-    handler->SetOption("ResourceSpecFile", this->ResourceSpecFile.c_str());
+    handler->SetOption("ResourceSpecFile", this->ResourceSpecFile);
   }
   if (!this->StopTime.empty()) {
     this->CTest->SetStopTime(this->StopTime);
@@ -114,7 +114,7 @@
   // or CTEST_TEST_LOAD script variable, or ctest --test-load
   // command line argument... in that order.
   unsigned long testLoad;
-  cmProp ctestTestLoad = this->Makefile->GetDefinition("CTEST_TEST_LOAD");
+  cmValue ctestTestLoad = this->Makefile->GetDefinition("CTEST_TEST_LOAD");
   if (!this->TestLoad.empty()) {
     if (!cmStrToULong(this->TestLoad, &testLoad)) {
       testLoad = 0;
@@ -134,7 +134,7 @@
   }
   handler->SetTestLoad(testLoad);
 
-  if (cmProp labelsForSubprojects =
+  if (cmValue labelsForSubprojects =
         this->Makefile->GetDefinition("CTEST_LABELS_FOR_SUBPROJECTS")) {
     this->CTest->SetCTestConfiguration("LabelsForSubprojects",
                                        *labelsForSubprojects, this->Quiet);
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 730ec0f..6e97a83 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -32,17 +32,18 @@
 #include "cmCTest.h"
 #include "cmCTestMultiProcessHandler.h"
 #include "cmCTestResourceGroupsLexerHelper.h"
+#include "cmCTestTestMeasurementXMLParser.h"
 #include "cmDuration.h"
 #include "cmExecutionStatus.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
+#include "cmValue.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
@@ -303,15 +304,24 @@
   // Support for JUnit XML output.
   this->JUnitXMLFileName = "";
 
-  // regex to detect <DartMeasurement>...</DartMeasurement>
-  this->DartStuff.compile("(<DartMeasurement.*/DartMeasurement[a-zA-Z]*>)");
-  // regex to detect each individual <DartMeasurement>...</DartMeasurement>
-  this->DartStuff1.compile(
-    "(<DartMeasurement[^<]*</DartMeasurement[a-zA-Z]*>)");
+  // Regular expressions to scan test output for custom measurements.
 
-  // regex to detect <CTestDetails>...</CTestDetails>
+  // Capture the whole section of test output from the first opening
+  // <(CTest|Dart)Measurement*> tag to the last </(CTest|Dart)Measurement*>
+  // closing tag.
+  this->AllTestMeasurementsRegex.compile(
+    "(<(CTest|Dart)Measurement.*/(CTest|Dart)Measurement[a-zA-Z]*>)");
+
+  // Capture a single <(CTest|Dart)Measurement*> XML element.
+  this->SingleTestMeasurementRegex.compile(
+    "(<(CTest|Dart)Measurement[^<]*</(CTest|Dart)Measurement[a-zA-Z]*>)");
+
+  // Capture content from <CTestDetails>...</CTestDetails>
   this->CustomCompletionStatusRegex.compile(
     "<CTestDetails>(.*)</CTestDetails>");
+
+  // Capture content from <CTestLabel>...</CTestLabel>
+  this->CustomLabelRegex.compile("<CTestLabel>(.*)</CTestLabel>");
 }
 
 void cmCTestTestHandler::Initialize()
@@ -511,7 +521,7 @@
   if (cmIsOn(this->GetOption("ScheduleRandom"))) {
     this->CTest->SetScheduleType("Random");
   }
-  if (const char* repeat = this->GetOption("Repeat")) {
+  if (cmValue repeat = this->GetOption("Repeat")) {
     cmsys::RegularExpression repeatRegex(
       "^(UNTIL_FAIL|UNTIL_PASS|AFTER_TIMEOUT):([0-9]+)$");
     if (repeatRegex.find(repeat)) {
@@ -536,7 +546,7 @@
     }
   }
   if (this->GetOption("ParallelLevel")) {
-    this->CTest->SetParallelLevel(atoi(this->GetOption("ParallelLevel")));
+    this->CTest->SetParallelLevel(std::stoi(this->GetOption("ParallelLevel")));
   }
 
   if (this->GetOption("StopOnFailure")) {
@@ -547,7 +557,7 @@
                this->IncludeLabelRegularExpressions);
   BuildLabelRE(this->GetMultiOption("ExcludeLabelRegularExpression"),
                this->ExcludeLabelRegularExpressions);
-  const char* val = this->GetOption("IncludeRegularExpression");
+  cmValue val = this->GetOption("IncludeRegularExpression");
   if (val) {
     this->UseIncludeRegExp();
     this->SetIncludeRegExp(val);
@@ -559,19 +569,19 @@
   }
   val = this->GetOption("ExcludeFixtureRegularExpression");
   if (val) {
-    this->ExcludeFixtureRegExp = val;
+    this->ExcludeFixtureRegExp = *val;
   }
   val = this->GetOption("ExcludeFixtureSetupRegularExpression");
   if (val) {
-    this->ExcludeFixtureSetupRegExp = val;
+    this->ExcludeFixtureSetupRegExp = *val;
   }
   val = this->GetOption("ExcludeFixtureCleanupRegularExpression");
   if (val) {
-    this->ExcludeFixtureCleanupRegExp = val;
+    this->ExcludeFixtureCleanupRegExp = *val;
   }
   val = this->GetOption("ResourceSpecFile");
   if (val) {
-    this->ResourceSpecFile = val;
+    this->ResourceSpecFile = *val;
   }
   this->SetRerunFailed(cmIsOn(this->GetOption("RerunFailed")));
 
@@ -692,7 +702,22 @@
       return false;
     }
     cmXMLWriter xml(xmlfile);
-    this->GenerateDartOutput(xml);
+    this->GenerateCTestXML(xml);
+  }
+
+  if (this->MemCheck) {
+    cmGeneratedFileStream xmlfile;
+    if (!this->StartResultingXML(cmCTest::PartTest, "DynamicAnalysis-Test",
+                                 xmlfile)) {
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "Cannot create testing XML file" << std::endl);
+      this->LogFile = nullptr;
+      return false;
+    }
+    cmXMLWriter xml(xmlfile);
+    // Explicitly call this class' `GenerateCTestXML` method to make `Test.xml`
+    // as well.
+    this->cmCTestTestHandler::GenerateCTestXML(xml);
   }
 
   return true;
@@ -1400,7 +1425,7 @@
 {
 }
 
-void cmCTestTestHandler::GenerateDartOutput(cmXMLWriter& xml)
+void cmCTestTestHandler::GenerateCTestXML(cmXMLWriter& xml)
 {
   if (!this->CTest->GetProduceXML()) {
     return;
@@ -1436,7 +1461,7 @@
         xml.Element("Value", result.ReturnValue);
         xml.EndElement(); // NamedMeasurement
       }
-      this->GenerateRegressionImages(xml, result.DartString);
+      this->RecordCustomTestMeasurements(xml, result.TestMeasurementsOutput);
       xml.StartElement("NamedMeasurement");
       xml.Attribute("type", "numeric/double");
       xml.Attribute("name", "Execution Time");
@@ -1804,7 +1829,7 @@
     // SEND_ERROR or FATAL_ERROR in CTestTestfile or TEST_INCLUDE_FILES
     return false;
   }
-  cmProp specFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE");
+  cmValue specFile = mf.GetDefinition("CTEST_RESOURCE_SPEC_FILE");
   if (this->ResourceSpecFile.empty() && specFile) {
     this->ResourceSpecFile = *specFile;
   }
@@ -1976,124 +2001,48 @@
   }
 }
 
-// Just for convenience
-#define SPACE_REGEX "[ \t\r\n]"
-void cmCTestTestHandler::GenerateRegressionImages(cmXMLWriter& xml,
-                                                  const std::string& dart)
+void cmCTestTestHandler::RecordCustomTestMeasurements(cmXMLWriter& xml,
+                                                      std::string content)
 {
-  cmsys::RegularExpression twoattributes(
-    "<DartMeasurement" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*>([^<]*)</DartMeasurement>");
-  cmsys::RegularExpression threeattributes(
-    "<DartMeasurement" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*>([^<]*)</DartMeasurement>");
-  cmsys::RegularExpression fourattributes(
-    "<DartMeasurement" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*>([^<]*)</DartMeasurement>");
-  cmsys::RegularExpression cdatastart(
-    "<DartMeasurement" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*>" SPACE_REGEX "*<!\\[CDATA\\[");
-  cmsys::RegularExpression cdataend("]]>" SPACE_REGEX "*</DartMeasurement>");
-  cmsys::RegularExpression measurementfile(
-    "<DartMeasurementFile" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*(name|type|encoding|compression)=\"([^\"]*)\"" SPACE_REGEX
-    "*>([^<]*)</DartMeasurementFile>");
+  while (this->SingleTestMeasurementRegex.find(content)) {
+    // Extract regex match from content and parse it as an XML element.
+    auto measurement_str = this->SingleTestMeasurementRegex.match(1);
+    auto parser = cmCTestTestMeasurementXMLParser();
+    parser.Parse(measurement_str.c_str());
 
-  bool done = false;
-  std::string cxml = dart;
-  while (!done) {
-    if (twoattributes.find(cxml)) {
+    if (parser.ElementName == "CTestMeasurement" ||
+        parser.ElementName == "DartMeasurement") {
       xml.StartElement("NamedMeasurement");
-      xml.Attribute(twoattributes.match(1).c_str(), twoattributes.match(2));
-      xml.Attribute(twoattributes.match(3).c_str(), twoattributes.match(4));
-      xml.Element("Value", twoattributes.match(5));
+      xml.Attribute("type", parser.MeasurementType);
+      xml.Attribute("name", parser.MeasurementName);
+      xml.Element("Value", parser.CharacterData);
       xml.EndElement();
-      cxml.erase(twoattributes.start(),
-                 twoattributes.end() - twoattributes.start());
-    } else if (threeattributes.find(cxml)) {
-      xml.StartElement("NamedMeasurement");
-      xml.Attribute(threeattributes.match(1).c_str(),
-                    threeattributes.match(2));
-      xml.Attribute(threeattributes.match(3).c_str(),
-                    threeattributes.match(4));
-      xml.Attribute(threeattributes.match(5).c_str(),
-                    threeattributes.match(6));
-      xml.Element("Value", twoattributes.match(7));
-      xml.EndElement();
-      cxml.erase(threeattributes.start(),
-                 threeattributes.end() - threeattributes.start());
-    } else if (fourattributes.find(cxml)) {
-      xml.StartElement("NamedMeasurement");
-      xml.Attribute(fourattributes.match(1).c_str(), fourattributes.match(2));
-      xml.Attribute(fourattributes.match(3).c_str(), fourattributes.match(4));
-      xml.Attribute(fourattributes.match(5).c_str(), fourattributes.match(6));
-      xml.Attribute(fourattributes.match(7).c_str(), fourattributes.match(8));
-      xml.Element("Value", twoattributes.match(9));
-      xml.EndElement();
-      cxml.erase(fourattributes.start(),
-                 fourattributes.end() - fourattributes.start());
-    } else if (cdatastart.find(cxml) && cdataend.find(cxml)) {
-      xml.StartElement("NamedMeasurement");
-      xml.Attribute(cdatastart.match(1).c_str(), cdatastart.match(2));
-      xml.Attribute(cdatastart.match(3).c_str(), cdatastart.match(4));
-      xml.StartElement("Value");
-      xml.CData(
-        cxml.substr(cdatastart.end(), cdataend.start() - cdatastart.end()));
-      xml.EndElement(); // Value
-      xml.EndElement(); // NamedMeasurement
-      cxml.erase(cdatastart.start(), cdataend.end() - cdatastart.start());
-    } else if (measurementfile.find(cxml)) {
-      const std::string& filename =
-        cmCTest::CleanString(measurementfile.match(5));
-      if (cmSystemTools::FileExists(filename)) {
+    } else if (parser.ElementName == "CTestMeasurementFile" ||
+               parser.ElementName == "DartMeasurementFile") {
+      const std::string& filename = cmCTest::CleanString(parser.CharacterData);
+      if (!cmSystemTools::FileExists(filename)) {
+        xml.StartElement("NamedMeasurement");
+        xml.Attribute("name", parser.MeasurementName);
+        xml.Attribute("text", "text/string");
+        xml.Element("Value", "File " + filename + " not found");
+        xml.EndElement();
+        cmCTestOptionalLog(
+          this->CTest, HANDLER_OUTPUT,
+          "File \"" << filename << "\" not found." << std::endl, this->Quiet);
+      } else {
         long len = cmSystemTools::FileLength(filename);
-        std::string k1 = measurementfile.match(1);
-        std::string v1 = measurementfile.match(2);
-        std::string k2 = measurementfile.match(3);
-        std::string v2 = measurementfile.match(4);
         if (len == 0) {
-          if (cmSystemTools::LowerCase(k1) == "type") {
-            v1 = "text/string";
-          }
-          if (cmSystemTools::LowerCase(k2) == "type") {
-            v2 = "text/string";
-          }
-
           xml.StartElement("NamedMeasurement");
-          xml.Attribute(k1.c_str(), v1);
-          xml.Attribute(k2.c_str(), v2);
+          xml.Attribute("name", parser.MeasurementName);
+          xml.Attribute("type", "text/string");
           xml.Attribute("encoding", "none");
           xml.Element("Value", "Image " + filename + " is empty");
           xml.EndElement();
         } else {
-          std::string type;
-          std::string name;
-          if (cmSystemTools::LowerCase(k1) == "type") {
-            type = v1;
-          } else if (cmSystemTools::LowerCase(k2) == "type") {
-            type = v2;
-          }
-          if (cmSystemTools::LowerCase(k1) == "name") {
-            name = v1;
-          } else if (cmSystemTools::LowerCase(k2) == "name") {
-            name = v2;
-          }
-          if (type == "file") {
+          if (parser.MeasurementType == "file") {
             // Treat this measurement like an "ATTACHED_FILE" when the type
             // is explicitly "file" (not an image).
-            this->AttachFile(xml, filename, name);
+            this->AttachFile(xml, filename, parser.MeasurementName);
           } else {
             cmsys::ifstream ifs(filename.c_str(),
                                 std::ios::in
@@ -2110,10 +2059,8 @@
                                              encoded_buffer.get(), 1);
 
             xml.StartElement("NamedMeasurement");
-            xml.Attribute(measurementfile.match(1).c_str(),
-                          measurementfile.match(2));
-            xml.Attribute(measurementfile.match(3).c_str(),
-                          measurementfile.match(4));
+            xml.Attribute("name", parser.MeasurementName);
+            xml.Attribute("type", parser.MeasurementType);
             xml.Attribute("encoding", "base64");
             std::ostringstream ostr;
             for (size_t cc = 0; cc < rlen; cc++) {
@@ -2126,48 +2073,34 @@
             xml.EndElement(); // NamedMeasurement
           }
         }
-      } else {
-        int idx = 4;
-        if (measurementfile.match(1) == "name") {
-          idx = 2;
-        }
-        xml.StartElement("NamedMeasurement");
-        xml.Attribute("name", measurementfile.match(idx));
-        xml.Attribute("text", "text/string");
-        xml.Element("Value", "File " + filename + " not found");
-        xml.EndElement();
-        cmCTestOptionalLog(
-          this->CTest, HANDLER_OUTPUT,
-          "File \"" << filename << "\" not found." << std::endl, this->Quiet);
       }
-      cxml.erase(measurementfile.start(),
-                 measurementfile.end() - measurementfile.start());
-    } else {
-      done = true;
     }
+
+    // Remove this element from content.
+    cmSystemTools::ReplaceString(content, measurement_str.c_str(), "");
   }
 }
 
-void cmCTestTestHandler::SetIncludeRegExp(const char* arg)
+void cmCTestTestHandler::SetIncludeRegExp(const std::string& arg)
 {
   this->IncludeRegExp = arg;
 }
 
-void cmCTestTestHandler::SetExcludeRegExp(const char* arg)
+void cmCTestTestHandler::SetExcludeRegExp(const std::string& arg)
 {
   this->ExcludeRegExp = arg;
 }
 
-void cmCTestTestHandler::SetTestsToRunInformation(const char* in)
+void cmCTestTestHandler::SetTestsToRunInformation(cmValue in)
 {
   if (!in) {
     return;
   }
-  this->TestsToRunString = in;
+  this->TestsToRunString = *in;
   // if the argument is a file, then read it and use the contents as the
   // string
   if (cmSystemTools::FileExists(in)) {
-    cmsys::ifstream fin(in);
+    cmsys::ifstream fin(in->c_str());
     unsigned long filelen = cmSystemTools::FileLength(in);
     auto buff = cm::make_unique<char[]>(filelen + 1);
     fin.getline(buff.get(), filelen);
@@ -2247,7 +2180,7 @@
 
             // Ensure we have complete triples otherwise the data is corrupt.
             if (triples.size() % 3 == 0) {
-              cmState state;
+              cmState state(cmState::Unknown);
               rt.Backtrace = cmListFileBacktrace(state.CreateBaseSnapshot());
 
               // the first entry represents the top of the trace so we need to
@@ -2327,6 +2260,8 @@
             cmExpandList(val, rt.Depends);
           } else if (key == "ENVIRONMENT"_s) {
             cmExpandList(val, rt.Environment);
+          } else if (key == "ENVIRONMENT_MODIFICATION"_s) {
+            cmExpandList(val, rt.EnvironmentModification);
           } else if (key == "LABELS"_s) {
             std::vector<std::string> Labels = cmExpandedList(val);
             rt.Labels.insert(rt.Labels.end(), Labels.begin(), Labels.end());
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index bd51738..3ac05e7 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -22,6 +22,7 @@
 #include "cmCTestResourceSpec.h"
 #include "cmDuration.h"
 #include "cmListFileCache.h"
+#include "cmValue.h"
 
 class cmMakefile;
 class cmXMLWriter;
@@ -65,8 +66,8 @@
   /// them on
   void UseIncludeRegExp();
   void UseExcludeRegExp();
-  void SetIncludeRegExp(const char*);
-  void SetExcludeRegExp(const char*);
+  void SetIncludeRegExp(const std::string&);
+  void SetExcludeRegExp(const std::string&);
 
   void SetMaxIndex(int n) { this->MaxIndex = n; }
   int GetMaxIndex() { return this->MaxIndex; }
@@ -81,7 +82,7 @@
   }
 
   //! pass the -I argument down
-  void SetTestsToRunInformation(const char*);
+  void SetTestsToRunInformation(cmValue);
 
   cmCTestTestHandler();
 
@@ -151,6 +152,7 @@
     // return code of test which will mark test as "not run"
     int SkipReturnCode;
     std::vector<std::string> Environment;
+    std::vector<std::string> EnvironmentModification;
     std::vector<std::string> Labels;
     std::set<std::string> LockedResources;
     std::set<std::string> FixturesSetup;
@@ -177,7 +179,7 @@
     std::string CompletionStatus;
     std::string CustomCompletionStatus;
     std::string Output;
-    std::string DartString;
+    std::string TestMeasurementsOutput;
     int TestCount;
     cmCTestTestProperties* Properties;
   };
@@ -276,9 +278,9 @@
 
 private:
   /**
-   * Generate the Dart compatible output
+   * Write test results in CTest's Test.xml format
    */
-  virtual void GenerateDartOutput(cmXMLWriter& xml);
+  virtual void GenerateCTestXML(cmXMLWriter& xml);
 
   /**
    * Write test results in JUnit XML format
@@ -348,8 +350,7 @@
   cmCTestResourceSpec ResourceSpec;
   std::string ResourceSpecFile;
 
-  void GenerateRegressionImages(cmXMLWriter& xml, const std::string& dart);
-  cmsys::RegularExpression DartStuff1;
+  void RecordCustomTestMeasurements(cmXMLWriter& xml, std::string content);
   void CheckLabelFilter(cmCTestTestProperties& it);
   void CheckLabelFilterExclude(cmCTestTestProperties& it);
   void CheckLabelFilterInclude(cmCTestTestProperties& it);
@@ -358,8 +359,10 @@
   bool UseUnion;
   ListOfTests TestList;
   size_t TotalNumberOfTests;
-  cmsys::RegularExpression DartStuff;
+  cmsys::RegularExpression AllTestMeasurementsRegex;
+  cmsys::RegularExpression SingleTestMeasurementRegex;
   cmsys::RegularExpression CustomCompletionStatusRegex;
+  cmsys::RegularExpression CustomLabelRegex;
 
   std::ostream* LogFile;
 
diff --git a/Source/CTest/cmCTestTestMeasurementXMLParser.cxx b/Source/CTest/cmCTestTestMeasurementXMLParser.cxx
new file mode 100644
index 0000000..636be24
--- /dev/null
+++ b/Source/CTest/cmCTestTestMeasurementXMLParser.cxx
@@ -0,0 +1,26 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmCTestTestMeasurementXMLParser.h"
+
+#include <cstring>
+
+void cmCTestTestMeasurementXMLParser::StartElement(const std::string& name,
+                                                   const char** attributes)
+{
+  this->CharacterData.clear();
+  this->ElementName = name;
+  for (const char** attr = attributes; *attr; attr += 2) {
+    if (strcmp(attr[0], "name") == 0) {
+      this->MeasurementName = attr[1];
+    } else if (strcmp(attr[0], "type") == 0) {
+      this->MeasurementType = attr[1];
+    }
+  }
+}
+
+void cmCTestTestMeasurementXMLParser::CharacterDataHandler(const char* data,
+                                                           int length)
+{
+  this->CharacterData.append(data, length);
+}
diff --git a/Source/CTest/cmCTestTestMeasurementXMLParser.h b/Source/CTest/cmCTestTestMeasurementXMLParser.h
new file mode 100644
index 0000000..b2c3eb3
--- /dev/null
+++ b/Source/CTest/cmCTestTestMeasurementXMLParser.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include <string>
+
+#include "cmXMLParser.h"
+
+class cmCTestTestMeasurementXMLParser : public cmXMLParser
+{
+public:
+  cmCTestTestMeasurementXMLParser() {}
+  std::string CharacterData;
+  std::string ElementName;
+  std::string MeasurementName;
+  std::string MeasurementType;
+
+protected:
+  void StartElement(const std::string& name, const char** atts) override;
+  void EndElement(const std::string& /*name*/) override {}
+  void CharacterDataHandler(const char* data, int length) override;
+};
diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx
index 797cb01..6655bf7 100644
--- a/Source/CTest/cmCTestUpdateCommand.cxx
+++ b/Source/CTest/cmCTestUpdateCommand.cxx
@@ -79,7 +79,7 @@
     this->SetError("source directory not specified. Please use SOURCE tag");
     return nullptr;
   }
-  handler->SetOption("SourceDirectory", source_dir.c_str());
+  handler->SetOption("SourceDirectory", source_dir);
   handler->SetQuiet(this->Quiet);
   return handler;
 }
diff --git a/Source/CTest/cmCTestUpdateHandler.cxx b/Source/CTest/cmCTestUpdateHandler.cxx
index 57cc024..d448c76 100644
--- a/Source/CTest/cmCTestUpdateHandler.cxx
+++ b/Source/CTest/cmCTestUpdateHandler.cxx
@@ -19,6 +19,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmXMLWriter.h"
 
@@ -108,7 +109,7 @@
   static_cast<void>(fixLocale);
 
   // Get source dir
-  const char* sourceDirectory = this->GetOption("SourceDirectory");
+  cmValue sourceDirectory = this->GetOption("SourceDirectory");
   if (!sourceDirectory) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Cannot find SourceDirectory  key in the DartConfiguration.tcl"
@@ -257,7 +258,7 @@
   return updated && loadedMods ? numUpdated : -1;
 }
 
-int cmCTestUpdateHandler::DetectVCS(const char* dir)
+int cmCTestUpdateHandler::DetectVCS(const std::string& dir)
 {
   std::string sourceDirectory = dir;
   cmCTestOptionalLog(this->CTest, DEBUG,
diff --git a/Source/CTest/cmCTestUpdateHandler.h b/Source/CTest/cmCTestUpdateHandler.h
index 25bbb2f..70269ea 100644
--- a/Source/CTest/cmCTestUpdateHandler.h
+++ b/Source/CTest/cmCTestUpdateHandler.h
@@ -59,6 +59,6 @@
   std::string UpdateCommand;
   int UpdateType;
 
-  int DetectVCS(const char* dir);
+  int DetectVCS(const std::string& dir);
   bool SelectVCS();
 };
diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx
index 452d714..423b506 100644
--- a/Source/CTest/cmCTestVC.cxx
+++ b/Source/CTest/cmCTestVC.cxx
@@ -10,8 +10,8 @@
 #include "cmsys/Process.h"
 
 #include "cmCTest.h"
-#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 
 cmCTestVC::cmCTestVC(cmCTest* ct, std::ostream& log)
diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
index 35f09fd..e9ec09e 100644
--- a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
+++ b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
@@ -15,11 +15,11 @@
 #include "cmCursesPathWidget.h"
 #include "cmCursesStringWidget.h"
 #include "cmCursesWidget.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmCursesCacheEntryComposite::cmCursesCacheEntryComposite(
   const std::string& key, int labelwidth, int entrywidth)
@@ -49,7 +49,7 @@
     this->IsNewLabel = cm::make_unique<cmCursesLabelWidget>(1, 1, 1, 1, " ");
   }
 
-  cmProp value = state->GetCacheEntryValue(key);
+  cmValue value = state->GetCacheEntryValue(key);
   assert(value);
   switch (state->GetCacheEntryType(key)) {
     case cmStateEnums::BOOL: {
@@ -72,7 +72,7 @@
       break;
     }
     case cmStateEnums::STRING: {
-      cmProp stringsProp = state->GetCacheEntryProperty(key, "STRINGS");
+      cmValue stringsProp = state->GetCacheEntryProperty(key, "STRINGS");
       if (stringsProp) {
         auto ow =
           cm::make_unique<cmCursesOptionsWidget>(this->EntryWidth, 1, 1, 1);
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx
index 069c02e..b28c5b7 100644
--- a/Source/CursesDialog/cmCursesMainForm.cxx
+++ b/Source/CursesDialog/cmCursesMainForm.cxx
@@ -17,11 +17,11 @@
 #include "cmCursesStandardIncludes.h"
 #include "cmCursesStringWidget.h"
 #include "cmCursesWidget.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -162,7 +162,7 @@
     // If normal mode, count only non-advanced entries
     this->NumberOfVisibleEntries = 0;
     for (cmCursesCacheEntryComposite& entry : this->Entries) {
-      cmProp existingValue =
+      cmValue existingValue =
         this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
       bool advanced =
         this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
@@ -183,7 +183,7 @@
 
   // Assign fields
   for (cmCursesCacheEntryComposite& entry : this->Entries) {
-    cmProp existingValue =
+    cmValue existingValue =
       this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
     bool advanced =
       this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
@@ -242,7 +242,7 @@
     // If normal, display only non-advanced entries
     this->NumberOfVisibleEntries = 0;
     for (cmCursesCacheEntryComposite& entry : this->Entries) {
-      cmProp existingValue =
+      cmValue existingValue =
         this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
       bool advanced =
         this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
@@ -260,7 +260,7 @@
     bool isNewPage;
     int i = 0;
     for (cmCursesCacheEntryComposite& entry : this->Entries) {
-      cmProp existingValue =
+      cmValue existingValue =
         this->CMakeInstance->GetState()->GetCacheEntryValue(entry.GetValue());
       bool advanced =
         this->CMakeInstance->GetState()->GetCacheEntryPropertyAsBool(
@@ -406,9 +406,9 @@
     // Get the help string of the current entry
     // and add it to the help string
     auto* cmakeState = this->CMakeInstance->GetState();
-    cmProp existingValue = cmakeState->GetCacheEntryValue(labelValue);
+    cmValue existingValue = cmakeState->GetCacheEntryValue(labelValue);
     if (existingValue) {
-      cmProp help =
+      cmValue help =
         cmakeState->GetCacheEntryProperty(labelValue, "HELPSTRING");
       if (help) {
         bar += *help;
@@ -618,7 +618,7 @@
 {
   for (cmCursesCacheEntryComposite& entry : this->Entries) {
     const std::string& cacheKey = entry.Key;
-    cmProp existingValue =
+    cmValue existingValue =
       this->CMakeInstance->GetState()->GetCacheEntryValue(cacheKey);
     if (existingValue) {
       std::string oldValue = *existingValue;
@@ -804,9 +804,9 @@
         cmCursesWidget* lbl = reinterpret_cast<cmCursesWidget*>(
           field_userptr(this->Fields[findex - 2]));
         const char* curField = lbl->GetValue();
-        cmProp helpString = nullptr;
+        cmValue helpString = nullptr;
 
-        cmProp existingValue =
+        cmValue existingValue =
           this->CMakeInstance->GetState()->GetCacheEntryValue(curField);
         if (existingValue) {
           helpString = this->CMakeInstance->GetState()->GetCacheEntryProperty(
diff --git a/Source/LexerParser/cmFortranParser.cxx b/Source/LexerParser/cmFortranParser.cxx
index 3f3ddde..50e9752 100644
--- a/Source/LexerParser/cmFortranParser.cxx
+++ b/Source/LexerParser/cmFortranParser.cxx
@@ -600,12 +600,12 @@
 static const yytype_uint8 yyrline[] =
 {
        0,   101,   101,   101,   104,   108,   113,   122,   128,   135,
-     140,   144,   149,   157,   162,   167,   172,   177,   182,   187,
-     192,   197,   201,   205,   209,   213,   214,   219,   219,   219,
-     220,   220,   221,   221,   222,   222,   223,   223,   224,   224,
-     225,   225,   226,   226,   227,   227,   228,   228,   231,   232,
-     233,   234,   235,   236,   237,   238,   239,   240,   241,   242,
-     243,   244,   245,   246,   247
+     140,   144,   149,   161,   166,   171,   176,   181,   186,   191,
+     196,   201,   205,   209,   213,   217,   218,   223,   223,   223,
+     224,   224,   225,   225,   226,   226,   227,   227,   228,   228,
+     229,   229,   230,   230,   231,   231,   232,   232,   235,   236,
+     237,   238,   239,   240,   241,   242,   243,   244,   245,   246,
+     247,   248,   249,   250,   251
 };
 #endif
 
@@ -1747,142 +1747,146 @@
       cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
       cmFortranParser_RuleUse(parser, (yyvsp[-2].string));
     }
+    if (cmsysString_strcasecmp((yyvsp[-4].string), "intrinsic") == 0) {
+      cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
+      cmFortranParser_RuleUseIntrinsic(parser, (yyvsp[-2].string));
+    }
     free((yyvsp[-4].string));
     free((yyvsp[-2].string));
   }
-#line 1754 "cmFortranParser.cxx"
+#line 1758 "cmFortranParser.cxx"
     break;
 
   case 13: /* stmt: INCLUDE STRING other EOSTMT  */
-#line 157 "cmFortranParser.y"
+#line 161 "cmFortranParser.y"
                               {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1764 "cmFortranParser.cxx"
+#line 1768 "cmFortranParser.cxx"
     break;
 
   case 14: /* stmt: CPP_LINE_DIRECTIVE STRING other EOSTMT  */
-#line 162 "cmFortranParser.y"
+#line 166 "cmFortranParser.y"
                                          {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleLineDirective(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1774 "cmFortranParser.cxx"
+#line 1778 "cmFortranParser.cxx"
     break;
 
   case 15: /* stmt: CPP_INCLUDE_ANGLE other EOSTMT  */
-#line 167 "cmFortranParser.y"
+#line 171 "cmFortranParser.y"
                                  {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1784 "cmFortranParser.cxx"
+#line 1788 "cmFortranParser.cxx"
     break;
 
   case 16: /* stmt: include STRING other EOSTMT  */
-#line 172 "cmFortranParser.y"
+#line 176 "cmFortranParser.y"
                               {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleInclude(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1794 "cmFortranParser.cxx"
+#line 1798 "cmFortranParser.cxx"
     break;
 
   case 17: /* stmt: define WORD other EOSTMT  */
-#line 177 "cmFortranParser.y"
+#line 181 "cmFortranParser.y"
                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleDefine(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1804 "cmFortranParser.cxx"
+#line 1808 "cmFortranParser.cxx"
     break;
 
   case 18: /* stmt: undef WORD other EOSTMT  */
-#line 182 "cmFortranParser.y"
+#line 186 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleUndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1814 "cmFortranParser.cxx"
+#line 1818 "cmFortranParser.cxx"
     break;
 
   case 19: /* stmt: ifdef WORD other EOSTMT  */
-#line 187 "cmFortranParser.y"
+#line 191 "cmFortranParser.y"
                           {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfdef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1824 "cmFortranParser.cxx"
+#line 1828 "cmFortranParser.cxx"
     break;
 
   case 20: /* stmt: ifndef WORD other EOSTMT  */
-#line 192 "cmFortranParser.y"
+#line 196 "cmFortranParser.y"
                            {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIfndef(parser, (yyvsp[-2].string));
     free((yyvsp[-2].string));
   }
-#line 1834 "cmFortranParser.cxx"
+#line 1838 "cmFortranParser.cxx"
     break;
 
   case 21: /* stmt: if other EOSTMT  */
-#line 197 "cmFortranParser.y"
+#line 201 "cmFortranParser.y"
                   {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleIf(parser);
   }
-#line 1843 "cmFortranParser.cxx"
+#line 1847 "cmFortranParser.cxx"
     break;
 
   case 22: /* stmt: elif other EOSTMT  */
-#line 201 "cmFortranParser.y"
+#line 205 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElif(parser);
   }
-#line 1852 "cmFortranParser.cxx"
+#line 1856 "cmFortranParser.cxx"
     break;
 
   case 23: /* stmt: else other EOSTMT  */
-#line 205 "cmFortranParser.y"
+#line 209 "cmFortranParser.y"
                     {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleElse(parser);
   }
-#line 1861 "cmFortranParser.cxx"
+#line 1865 "cmFortranParser.cxx"
     break;
 
   case 24: /* stmt: endif other EOSTMT  */
-#line 209 "cmFortranParser.y"
+#line 213 "cmFortranParser.y"
                      {
     cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
     cmFortranParser_RuleEndif(parser);
   }
-#line 1870 "cmFortranParser.cxx"
+#line 1874 "cmFortranParser.cxx"
     break;
 
   case 48: /* misc_code: WORD  */
-#line 231 "cmFortranParser.y"
+#line 235 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1876 "cmFortranParser.cxx"
+#line 1880 "cmFortranParser.cxx"
     break;
 
   case 55: /* misc_code: STRING  */
-#line 238 "cmFortranParser.y"
+#line 242 "cmFortranParser.y"
                       { free ((yyvsp[0].string)); }
-#line 1882 "cmFortranParser.cxx"
+#line 1886 "cmFortranParser.cxx"
     break;
 
 
-#line 1886 "cmFortranParser.cxx"
+#line 1890 "cmFortranParser.cxx"
 
       default: break;
     }
@@ -2107,6 +2111,6 @@
   return yyresult;
 }
 
-#line 250 "cmFortranParser.y"
+#line 254 "cmFortranParser.y"
 
 /* End of grammar */
diff --git a/Source/LexerParser/cmFortranParser.y b/Source/LexerParser/cmFortranParser.y
index a3e1c24..8ef1903 100644
--- a/Source/LexerParser/cmFortranParser.y
+++ b/Source/LexerParser/cmFortranParser.y
@@ -151,6 +151,10 @@
       cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
       cmFortranParser_RuleUse(parser, $5);
     }
+    if (cmsysString_strcasecmp($3, "intrinsic") == 0) {
+      cmFortranParser* parser = cmFortran_yyget_extra(yyscanner);
+      cmFortranParser_RuleUseIntrinsic(parser, $5);
+    }
     free($3);
     free($5);
   }
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index e6faef4..8ab8656 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -122,26 +122,26 @@
 
     QCMakePropertyList props = this->properties();
     emit this->propertiesChanged(props);
-    cmProp homeDir = state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY");
+    cmValue homeDir = state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY");
     if (homeDir) {
       setSourceDirectory(QString::fromLocal8Bit(homeDir->c_str()));
     }
-    cmProp gen = state->GetCacheEntryValue("CMAKE_GENERATOR");
+    cmValue gen = state->GetCacheEntryValue("CMAKE_GENERATOR");
     if (gen) {
-      const std::string* extraGen =
+      cmValue extraGen =
         state->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
       std::string curGen =
-        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
-          *gen, extraGen ? *extraGen : "");
+        cmExternalMakefileProjectGenerator::CreateFullGeneratorName(*gen,
+                                                                    *extraGen);
       this->setGenerator(QString::fromLocal8Bit(curGen.c_str()));
     }
 
-    cmProp platform = state->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
+    cmValue platform = state->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
     if (platform) {
       this->setPlatform(QString::fromLocal8Bit(platform->c_str()));
     }
 
-    cmProp toolset = state->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET");
+    cmValue toolset = state->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET");
     if (toolset) {
       this->setToolset(QString::fromLocal8Bit(toolset->c_str()));
     }
@@ -396,11 +396,11 @@
       continue;
     }
 
-    cmProp cachedValue = state->GetCacheEntryValue(key);
+    cmValue cachedValue = state->GetCacheEntryValue(key);
 
     QCMakeProperty prop;
     prop.Key = QString::fromLocal8Bit(key.c_str());
-    if (cmProp hs = state->GetCacheEntryProperty(key, "HELPSTRING")) {
+    if (cmValue hs = state->GetCacheEntryProperty(key, "HELPSTRING")) {
       prop.Help = QString::fromLocal8Bit(hs->c_str());
     }
     prop.Value = QString::fromLocal8Bit(cachedValue->c_str());
@@ -414,7 +414,7 @@
       prop.Type = QCMakeProperty::FILEPATH;
     } else if (t == cmStateEnums::STRING) {
       prop.Type = QCMakeProperty::STRING;
-      cmProp stringsProperty = state->GetCacheEntryProperty(key, "STRINGS");
+      cmValue stringsProperty = state->GetCacheEntryProperty(key, "STRINGS");
       if (stringsProperty) {
         prop.Strings =
           QString::fromLocal8Bit(stringsProperty->c_str()).split(";");
@@ -550,17 +550,14 @@
     }
 
     QCMakePreset preset;
-    preset.name = std::move(QString::fromLocal8Bit(p.Name.data()));
-    preset.displayName =
-      std::move(QString::fromLocal8Bit(p.DisplayName.data()));
-    preset.description =
-      std::move(QString::fromLocal8Bit(p.Description.data()));
-    preset.generator = std::move(QString::fromLocal8Bit(p.Generator.data()));
-    preset.architecture =
-      std::move(QString::fromLocal8Bit(p.Architecture.data()));
+    preset.name = QString::fromLocal8Bit(p.Name.data());
+    preset.displayName = QString::fromLocal8Bit(p.DisplayName.data());
+    preset.description = QString::fromLocal8Bit(p.Description.data());
+    preset.generator = QString::fromLocal8Bit(p.Generator.data());
+    preset.architecture = QString::fromLocal8Bit(p.Architecture.data());
     preset.setArchitecture = !p.ArchitectureStrategy ||
       p.ArchitectureStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set;
-    preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data()));
+    preset.toolset = QString::fromLocal8Bit(p.Toolset.data());
     preset.setToolset = !p.ToolsetStrategy ||
       p.ToolsetStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set;
     preset.enabled = it.Expanded && it.Expanded->ConditionResult &&
diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx
index 92e04e4..a5d1f6a 100644
--- a/Source/cmAddLibraryCommand.cxx
+++ b/Source/cmAddLibraryCommand.cxx
@@ -12,6 +12,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 bool cmAddLibraryCommand(std::vector<std::string> const& args,
                          cmExecutionStatus& status)
diff --git a/Source/cmAddTestCommand.cxx b/Source/cmAddTestCommand.cxx
index 205c1c7..a0d5732 100644
--- a/Source/cmAddTestCommand.cxx
+++ b/Source/cmAddTestCommand.cxx
@@ -140,7 +140,7 @@
   test->SetOldStyle(false);
   test->SetCommand(command);
   if (!working_directory.empty()) {
-    test->SetProperty("WORKING_DIRECTORY", working_directory.c_str());
+    test->SetProperty("WORKING_DIRECTORY", working_directory);
   }
   test->SetCommandExpandLists(command_expand_lists);
   mf.AddTestGenerator(cm::make_unique<cmTestGenerator>(test, configurations));
diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h
index c192e2a..a1830f9 100644
--- a/Source/cmAlgorithms.h
+++ b/Source/cmAlgorithms.h
@@ -60,6 +60,10 @@
 using cmBacktraceRange =
   cmRange<std::vector<cmListFileBacktrace>::const_iterator>;
 
+template <typename T>
+class BT;
+using cmBTStringRange = cmRange<std::vector<BT<std::string>>::const_iterator>;
+
 template <typename Range>
 typename Range::const_iterator cmRemoveN(Range& r, size_t n)
 {
@@ -133,7 +137,13 @@
 }
 
 template <typename Range>
-typename Range::const_iterator cmRemoveDuplicates(Range& r)
+typename Range::iterator cmRemoveDuplicates(Range& r)
+{
+  return cmRemoveDuplicates(r.begin(), r.end());
+}
+
+template <typename Range>
+typename Range::const_iterator cmRemoveDuplicates(Range const& r)
 {
   return cmRemoveDuplicates(r.begin(), r.end());
 }
diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx
index 54b2998..9e0d80c 100644
--- a/Source/cmArchiveWrite.cxx
+++ b/Source/cmArchiveWrite.cxx
@@ -250,6 +250,9 @@
 
 bool cmArchiveWrite::Open()
 {
+  if (!this->Error.empty()) {
+    return false;
+  }
   if (archive_write_open(
         this->Archive, this, nullptr,
         reinterpret_cast<archive_write_callback*>(&Callback::Write),
diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx
index 99707a3..a69c00d 100644
--- a/Source/cmBinUtilsLinuxELFLinker.cxx
+++ b/Source/cmBinUtilsLinuxELFLinker.cxx
@@ -11,6 +11,7 @@
 #include <cmsys/RegularExpression.hxx>
 
 #include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+#include "cmELF.h"
 #include "cmLDConfigLDConfigTool.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -18,10 +19,6 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#ifdef CMake_USE_ELF_PARSER
-#  include "cmELF.h"
-#endif
-
 static std::string ReplaceOrigin(const std::string& rpath,
                                  const std::string& origin)
 {
@@ -91,7 +88,6 @@
 {
   std::vector<std::string> parentRpaths;
 
-#ifdef CMake_USE_ELF_PARSER
   cmELF elf(file.c_str());
   if (!elf) {
     return false;
@@ -106,7 +102,6 @@
       this->Machine = elf.GetMachine();
     }
   }
-#endif
 
   return this->ScanDependencies(file, parentRpaths);
 }
@@ -175,15 +170,11 @@
 namespace {
 bool FileHasArchitecture(const char* filename, std::uint16_t machine)
 {
-#ifdef CMake_USE_ELF_PARSER
   cmELF elf(filename);
   if (!elf) {
     return false;
   }
   return machine == 0 || machine == elf.GetMachine();
-#else
-  return true;
-#endif
 }
 }
 
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx
index 47f77d8..c064377 100644
--- a/Source/cmBinUtilsMacOSMachOLinker.cxx
+++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -5,6 +5,8 @@
 
 #include <sstream>
 #include <string>
+#include <type_traits>
+#include <utility>
 #include <vector>
 
 #include <cm/memory>
@@ -52,6 +54,26 @@
   return true;
 }
 
+auto cmBinUtilsMacOSMachOLinker::GetFileInfo(std::string const& file)
+  -> const FileInfo*
+{
+  // Memoize processed rpaths and library dependencies to reduce the number
+  // of calls to otool, especially in the case of heavily recursive libraries
+  auto iter = ScannedFileInfo.find(file);
+  if (iter != ScannedFileInfo.end()) {
+    return &iter->second;
+  }
+
+  FileInfo file_info;
+  if (!this->Tool->GetFileInfo(file, file_info.libs, file_info.rpaths)) {
+    // Call to otool failed
+    return nullptr;
+  }
+
+  auto iter_inserted = ScannedFileInfo.insert({ file, std::move(file_info) });
+  return &iter_inserted.first->second;
+}
+
 bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
   std::string const& file, cmStateEnums::TargetType type)
 {
@@ -65,12 +87,12 @@
   if (!executableFile.empty()) {
     executablePath = cmSystemTools::GetFilenamePath(executableFile);
   }
-  std::vector<std::string> libs;
-  std::vector<std::string> rpaths;
-  if (!this->Tool->GetFileInfo(file, libs, rpaths)) {
+  const FileInfo* file_info = this->GetFileInfo(file);
+  if (file_info == nullptr) {
     return false;
   }
-  return this->ScanDependencies(file, libs, rpaths, executablePath);
+  return this->ScanDependencies(file, file_info->libs, file_info->rpaths,
+                                executablePath);
 }
 
 bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
@@ -98,14 +120,16 @@
             !IsMissingSystemDylib(path)) {
           auto filename = cmSystemTools::GetFilenameName(path);
           bool unique;
-          std::vector<std::string> libs;
-          std::vector<std::string> depRpaths;
-          if (!this->Tool->GetFileInfo(path, libs, depRpaths)) {
+          const FileInfo* dep_file_info = this->GetFileInfo(path);
+          if (dep_file_info == nullptr) {
             return false;
           }
-          this->Archive->AddResolvedPath(filename, path, unique, depRpaths);
+
+          this->Archive->AddResolvedPath(filename, path, unique,
+                                         dep_file_info->rpaths);
           if (unique &&
-              !this->ScanDependencies(path, libs, depRpaths, executablePath)) {
+              !this->ScanDependencies(path, dep_file_info->libs,
+                                      dep_file_info->rpaths, executablePath)) {
             return false;
           }
         }
diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h
index eae23cc..ac1177b 100644
--- a/Source/cmBinUtilsMacOSMachOLinker.h
+++ b/Source/cmBinUtilsMacOSMachOLinker.h
@@ -5,6 +5,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "cmBinUtilsLinker.h"
@@ -24,7 +25,16 @@
                         cmStateEnums::TargetType type) override;
 
 private:
+  struct FileInfo
+  {
+    std::vector<std::string> libs;
+    std::vector<std::string> rpaths;
+  };
+
   std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool;
+  std::unordered_map<std::string, FileInfo> ScannedFileInfo;
+
+  const FileInfo* GetFileInfo(std::string const& file);
 
   bool ScanDependencies(std::string const& file,
                         std::vector<std::string> const& libs,
diff --git a/Source/cmBuildCommand.cxx b/Source/cmBuildCommand.cxx
index 56b080a..415a124 100644
--- a/Source/cmBuildCommand.cxx
+++ b/Source/cmBuildCommand.cxx
@@ -6,10 +6,10 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 
@@ -102,7 +102,7 @@
   cmMakefile& mf = status.GetMakefile();
 
   std::string const& define = args[0];
-  cmProp cacheValue = mf.GetDefinition(define);
+  cmValue cacheValue = mf.GetDefinition(define);
 
   std::string configType;
   if (!cmSystemTools::GetEnv("CMAKE_CONFIG_TYPE", configType) ||
diff --git a/Source/cmBuildNameCommand.cxx b/Source/cmBuildNameCommand.cxx
index f9b8f8f..20d6089 100644
--- a/Source/cmBuildNameCommand.cxx
+++ b/Source/cmBuildNameCommand.cxx
@@ -8,9 +8,9 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 bool cmBuildNameCommand(std::vector<std::string> const& args,
                         cmExecutionStatus& status)
@@ -20,7 +20,7 @@
     return false;
   }
   cmMakefile& mf = status.GetMakefile();
-  cmProp cacheValue = mf.GetDefinition(args[0]);
+  cmValue cacheValue = mf.GetDefinition(args[0]);
   if (cacheValue) {
     // do we need to correct the value?
     cmsys::RegularExpression reg("[()/]");
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 0550568..3922c56 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -2,214 +2,517 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCMakeHostSystemInformationCommand.h"
 
-#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <initializer_list>
+#include <map>
+#include <string>
+#include <type_traits>
+#include <utility>
 
+#include <cm/optional>
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmsys/FStream.hxx"
+#include "cmsys/Glob.hxx"
 #include "cmsys/SystemInformation.hxx"
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 #  include "cmAlgorithms.h"
 #  include "cmGlobalGenerator.h"
+#  include "cmGlobalVisualStudio10Generator.h"
 #  include "cmGlobalVisualStudioVersionedGenerator.h"
-#  include "cmSystemTools.h"
 #  include "cmVSSetupHelper.h"
 #  define HAVE_VS_SETUP_HELPER
 #endif
 
 namespace {
-bool GetValue(cmExecutionStatus& status, cmsys::SystemInformation& info,
-              std::string const& key, std::string& value);
-std::string ValueToString(size_t value);
-std::string ValueToString(const char* value);
-std::string ValueToString(std::string const& value);
-}
+std::string const DELIM[2] = { {}, ";" };
 
-// cmCMakeHostSystemInformation
-bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
-                                         cmExecutionStatus& status)
-{
-  size_t current_index = 0;
-
-  if (args.size() < (current_index + 2) || args[current_index] != "RESULT") {
-    status.SetError("missing RESULT specification.");
-    return false;
-  }
-
-  std::string const& variable = args[current_index + 1];
-  current_index += 2;
-
-  if (args.size() < (current_index + 2) || args[current_index] != "QUERY") {
-    status.SetError("missing QUERY specification");
-    return false;
-  }
-
-  cmsys::SystemInformation info;
-  info.RunCPUCheck();
-  info.RunOSCheck();
-  info.RunMemoryCheck();
-
-  std::string result_list;
-  for (size_t i = current_index + 1; i < args.size(); ++i) {
-    std::string const& key = args[i];
-    if (i != current_index + 1) {
-      result_list += ";";
-    }
-    std::string value;
-    if (!GetValue(status, info, key, value)) {
-      return false;
-    }
-    result_list += value;
-  }
-
-  status.GetMakefile().AddDefinition(variable, result_list);
-
-  return true;
-}
-
-namespace {
-
-bool GetValue(cmExecutionStatus& status, cmsys::SystemInformation& info,
-              std::string const& key, std::string& value)
-{
-  if (key == "NUMBER_OF_LOGICAL_CORES") {
-    value = ValueToString(info.GetNumberOfLogicalCPU());
-  } else if (key == "NUMBER_OF_PHYSICAL_CORES") {
-    value = ValueToString(info.GetNumberOfPhysicalCPU());
-  } else if (key == "HOSTNAME") {
-    value = ValueToString(info.GetHostname());
-  } else if (key == "FQDN") {
-    value = ValueToString(info.GetFullyQualifiedDomainName());
-  } else if (key == "TOTAL_VIRTUAL_MEMORY") {
-    value = ValueToString(info.GetTotalVirtualMemory());
-  } else if (key == "AVAILABLE_VIRTUAL_MEMORY") {
-    value = ValueToString(info.GetAvailableVirtualMemory());
-  } else if (key == "TOTAL_PHYSICAL_MEMORY") {
-    value = ValueToString(info.GetTotalPhysicalMemory());
-  } else if (key == "AVAILABLE_PHYSICAL_MEMORY") {
-    value = ValueToString(info.GetAvailablePhysicalMemory());
-  } else if (key == "IS_64BIT") {
-    value = ValueToString(info.Is64Bits());
-  } else if (key == "HAS_FPU") {
-    value = ValueToString(
-      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_FPU));
-  } else if (key == "HAS_MMX") {
-    value = ValueToString(
-      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_MMX));
-  } else if (key == "HAS_MMX_PLUS") {
-    value = ValueToString(info.DoesCPUSupportFeature(
-      cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS));
-  } else if (key == "HAS_SSE") {
-    value = ValueToString(
-      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE));
-  } else if (key == "HAS_SSE2") {
-    value = ValueToString(
-      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE2));
-  } else if (key == "HAS_SSE_FP") {
-    value = ValueToString(info.DoesCPUSupportFeature(
-      cmsys::SystemInformation::CPU_FEATURE_SSE_FP));
-  } else if (key == "HAS_SSE_MMX") {
-    value = ValueToString(info.DoesCPUSupportFeature(
-      cmsys::SystemInformation::CPU_FEATURE_SSE_MMX));
-  } else if (key == "HAS_AMD_3DNOW") {
-    value = ValueToString(info.DoesCPUSupportFeature(
-      cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW));
-  } else if (key == "HAS_AMD_3DNOW_PLUS") {
-    value = ValueToString(info.DoesCPUSupportFeature(
-      cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS));
-  } else if (key == "HAS_IA64") {
-    value = ValueToString(
-      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_IA64));
-  } else if (key == "HAS_SERIAL_NUMBER") {
-    value = ValueToString(info.DoesCPUSupportFeature(
-      cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER));
-  } else if (key == "PROCESSOR_NAME") {
-    value = ValueToString(info.GetExtendedProcessorName());
-  } else if (key == "PROCESSOR_DESCRIPTION") {
-    value = info.GetCPUDescription();
-  } else if (key == "PROCESSOR_SERIAL_NUMBER") {
-    value = ValueToString(info.GetProcessorSerialNumber());
-  } else if (key == "OS_NAME") {
-    value = ValueToString(info.GetOSName());
-  } else if (key == "OS_RELEASE") {
-    value = ValueToString(info.GetOSRelease());
-  } else if (key == "OS_VERSION") {
-    value = ValueToString(info.GetOSVersion());
-  } else if (key == "OS_PLATFORM") {
-    value = ValueToString(info.GetOSPlatform());
-#ifdef HAVE_VS_SETUP_HELPER
-  } else if (key == "VS_15_DIR") {
-    // If generating for the VS 15 IDE, use the same instance.
-    cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
-    if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 15 ")) {
-      cmGlobalVisualStudioVersionedGenerator* vs15gen =
-        static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
-      if (vs15gen->GetVSInstance(value)) {
-        return true;
-      }
-    }
-
-    // Otherwise, find a VS 15 instance ourselves.
-    cmVSSetupAPIHelper vsSetupAPIHelper(15);
-    if (vsSetupAPIHelper.GetVSInstanceInfo(value)) {
-      cmSystemTools::ConvertToUnixSlashes(value);
-    }
-  } else if (key == "VS_16_DIR") {
-    // If generating for the VS 16 IDE, use the same instance.
-    cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
-    if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 16 ")) {
-      cmGlobalVisualStudioVersionedGenerator* vs16gen =
-        static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
-      if (vs16gen->GetVSInstance(value)) {
-        return true;
-      }
-    }
-
-    // Otherwise, find a VS 16 instance ourselves.
-    cmVSSetupAPIHelper vsSetupAPIHelper(16);
-    if (vsSetupAPIHelper.GetVSInstanceInfo(value)) {
-      cmSystemTools::ConvertToUnixSlashes(value);
-    }
-  } else if (key == "VS_17_DIR") {
-    // If generating for the VS 17 IDE, use the same instance.
-    cmGlobalGenerator* gg = status.GetMakefile().GetGlobalGenerator();
-    if (cmHasLiteralPrefix(gg->GetName(), "Visual Studio 17 ")) {
-      cmGlobalVisualStudioVersionedGenerator* vs17gen =
-        static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
-      if (vs17gen->GetVSInstance(value)) {
-        return true;
-      }
-    }
-
-    // Otherwise, find a VS 17 instance ourselves.
-    cmVSSetupAPIHelper vsSetupAPIHelper(17);
-    if (vsSetupAPIHelper.GetVSInstanceInfo(value)) {
-      cmSystemTools::ConvertToUnixSlashes(value);
-    }
-#endif
-  } else {
-    std::string e = "does not recognize <key> " + key;
-    status.SetError(e);
-    return false;
-  }
-
-  return true;
-}
-
-std::string ValueToString(size_t value)
+// BEGIN Private functions
+std::string ValueToString(std::size_t const value)
 {
   return std::to_string(value);
 }
 
-std::string ValueToString(const char* value)
+std::string ValueToString(const char* const value)
 {
-  std::string safe_string = value ? value : "";
-  return safe_string;
+  return value ? value : std::string{};
 }
 
 std::string ValueToString(std::string const& value)
 {
   return value;
 }
+
+cm::optional<std::string> GetValue(cmsys::SystemInformation& info,
+                                   std::string const& key)
+{
+  if (key == "NUMBER_OF_LOGICAL_CORES"_s) {
+    return ValueToString(info.GetNumberOfLogicalCPU());
+  }
+  if (key == "NUMBER_OF_PHYSICAL_CORES"_s) {
+    return ValueToString(info.GetNumberOfPhysicalCPU());
+  }
+  if (key == "HOSTNAME"_s) {
+    return ValueToString(info.GetHostname());
+  }
+  if (key == "FQDN"_s) {
+    return ValueToString(info.GetFullyQualifiedDomainName());
+  }
+  if (key == "TOTAL_VIRTUAL_MEMORY"_s) {
+    return ValueToString(info.GetTotalVirtualMemory());
+  }
+  if (key == "AVAILABLE_VIRTUAL_MEMORY"_s) {
+    return ValueToString(info.GetAvailableVirtualMemory());
+  }
+  if (key == "TOTAL_PHYSICAL_MEMORY"_s) {
+    return ValueToString(info.GetTotalPhysicalMemory());
+  }
+  if (key == "AVAILABLE_PHYSICAL_MEMORY"_s) {
+    return ValueToString(info.GetAvailablePhysicalMemory());
+  }
+  if (key == "IS_64BIT"_s) {
+    return ValueToString(info.Is64Bits());
+  }
+  if (key == "HAS_FPU"_s) {
+    return ValueToString(
+      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_FPU));
+  }
+  if (key == "HAS_MMX"_s) {
+    return ValueToString(
+      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_MMX));
+  }
+  if (key == "HAS_MMX_PLUS"_s) {
+    return ValueToString(info.DoesCPUSupportFeature(
+      cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS));
+  }
+  if (key == "HAS_SSE"_s) {
+    return ValueToString(
+      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE));
+  }
+  if (key == "HAS_SSE2"_s) {
+    return ValueToString(
+      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE2));
+  }
+  if (key == "HAS_SSE_FP"_s) {
+    return ValueToString(info.DoesCPUSupportFeature(
+      cmsys::SystemInformation::CPU_FEATURE_SSE_FP));
+  }
+  if (key == "HAS_SSE_MMX"_s) {
+    return ValueToString(info.DoesCPUSupportFeature(
+      cmsys::SystemInformation::CPU_FEATURE_SSE_MMX));
+  }
+  if (key == "HAS_AMD_3DNOW"_s) {
+    return ValueToString(info.DoesCPUSupportFeature(
+      cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW));
+  }
+  if (key == "HAS_AMD_3DNOW_PLUS"_s) {
+    return ValueToString(info.DoesCPUSupportFeature(
+      cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS));
+  }
+  if (key == "HAS_IA64"_s) {
+    return ValueToString(
+      info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_IA64));
+  }
+  if (key == "HAS_SERIAL_NUMBER"_s) {
+    return ValueToString(info.DoesCPUSupportFeature(
+      cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER));
+  }
+  if (key == "PROCESSOR_NAME"_s) {
+    return ValueToString(info.GetExtendedProcessorName());
+  }
+  if (key == "PROCESSOR_DESCRIPTION"_s) {
+    return info.GetCPUDescription();
+  }
+  if (key == "PROCESSOR_SERIAL_NUMBER"_s) {
+    return ValueToString(info.GetProcessorSerialNumber());
+  }
+  if (key == "OS_NAME"_s) {
+    return ValueToString(info.GetOSName());
+  }
+  if (key == "OS_RELEASE"_s) {
+    return ValueToString(info.GetOSRelease());
+  }
+  if (key == "OS_VERSION"_s) {
+    return ValueToString(info.GetOSVersion());
+  }
+  if (key == "OS_PLATFORM"_s) {
+    return ValueToString(info.GetOSPlatform());
+  }
+  return {};
+}
+
+cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine(
+  std::string const& line)
+{
+  std::string key;
+  std::string value;
+
+  char prev = 0;
+  enum ParserState
+  {
+    PARSE_KEY_1ST,
+    PARSE_KEY,
+    FOUND_EQ,
+    PARSE_SINGLE_QUOTE_VALUE,
+    PARSE_DBL_QUOTE_VALUE,
+    PARSE_VALUE,
+    IGNORE_REST
+  } state = PARSE_KEY_1ST;
+
+  for (auto ch : line) {
+    switch (state) {
+      case PARSE_KEY_1ST:
+        if (std::isalpha(ch) || ch == '_') {
+          key += ch;
+          state = PARSE_KEY;
+        } else if (!std::isspace(ch)) {
+          state = IGNORE_REST;
+        }
+        break;
+
+      case PARSE_KEY:
+        if (ch == '=') {
+          state = FOUND_EQ;
+        } else if (std::isalnum(ch) || ch == '_') {
+          key += ch;
+        } else {
+          state = IGNORE_REST;
+        }
+        break;
+
+      case FOUND_EQ:
+        switch (ch) {
+          case '\'':
+            state = PARSE_SINGLE_QUOTE_VALUE;
+            break;
+          case '"':
+            state = PARSE_DBL_QUOTE_VALUE;
+            break;
+          case '#':
+          case '\\':
+            state = IGNORE_REST;
+            break;
+          default:
+            value += ch;
+            state = PARSE_VALUE;
+        }
+        break;
+
+      case PARSE_SINGLE_QUOTE_VALUE:
+        if (ch == '\'') {
+          if (prev != '\\') {
+            state = IGNORE_REST;
+          } else {
+            assert(!value.empty());
+            value[value.size() - 1] = ch;
+          }
+        } else {
+          value += ch;
+        }
+        break;
+
+      case PARSE_DBL_QUOTE_VALUE:
+        if (ch == '"') {
+          if (prev != '\\') {
+            state = IGNORE_REST;
+          } else {
+            assert(!value.empty());
+            value[value.size() - 1] = ch;
+          }
+        } else {
+          value += ch;
+        }
+        break;
+
+      case PARSE_VALUE:
+        if (ch == '#' || std::isspace(ch)) {
+          state = IGNORE_REST;
+        } else {
+          value += ch;
+        }
+        break;
+
+      default:
+        // Unexpected os-release parser state!
+        state = IGNORE_REST;
+        break;
+    }
+
+    if (state == IGNORE_REST) {
+      break;
+    }
+    prev = ch;
+  }
+  if (!(key.empty() || value.empty())) {
+    return std::make_pair(key, value);
+  }
+  return {};
+}
+
+std::map<std::string, std::string> GetOSReleaseVariables(
+  cmExecutionStatus& status)
+{
+  auto& makefile = status.GetMakefile();
+  const auto& sysroot = makefile.GetSafeDefinition("CMAKE_SYSROOT");
+
+  std::map<std::string, std::string> data;
+  // Based on
+  // https://www.freedesktop.org/software/systemd/man/os-release.html
+  for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) {
+    const auto& filename = cmStrCat(sysroot, name);
+    if (cmSystemTools::FileExists(filename)) {
+      cmsys::ifstream fin(filename.c_str());
+      for (std::string line; !std::getline(fin, line).fail();) {
+        auto kv = ParseOSReleaseLine(line);
+        if (kv.has_value()) {
+          data.emplace(kv.value());
+        }
+      }
+      break;
+    }
+  }
+  // Got smth?
+  if (!data.empty()) {
+    return data;
+  }
+
+  // Ugh, it could be some pre-os-release distro.
+  // Lets try some fallback getters.
+  // See also:
+  //  - http://linuxmafia.com/faq/Admin/release-files.html
+
+  // 1. CMake provided
+  cmsys::Glob gl;
+  std::vector<std::string> scripts;
+  auto const findExpr = cmStrCat(cmSystemTools::GetCMakeRoot(),
+                                 "/Modules/Internal/OSRelease/*.cmake");
+  if (gl.FindFiles(findExpr)) {
+    scripts = gl.GetFiles();
+  }
+
+  // 2. User provided (append to the CMake prvided)
+  makefile.GetDefExpandList("CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS", scripts);
+
+  // Filter out files that are not in format `NNN-name.cmake`
+  auto checkName = [](std::string const& filepath) -> bool {
+    auto const& filename = cmSystemTools::GetFilenameName(filepath);
+    // NOTE Minimum filename length expected:
+    //   NNN-<at-least-one-char-name>.cmake  --> 11
+    return (filename.size() < 11) || !std::isdigit(filename[0]) ||
+      !std::isdigit(filename[1]) || !std::isdigit(filename[2]) ||
+      filename[3] != '-';
+  };
+  scripts.erase(std::remove_if(scripts.begin(), scripts.end(), checkName),
+                scripts.end());
+
+  // Make sure scripts are running in desired order
+  std::sort(scripts.begin(), scripts.end(),
+            [](std::string const& lhs, std::string const& rhs) -> bool {
+              long lhs_order;
+              cmStrToLong(cmSystemTools::GetFilenameName(lhs).substr(0u, 3u),
+                          &lhs_order);
+              long rhs_order;
+              cmStrToLong(cmSystemTools::GetFilenameName(rhs).substr(0u, 3u),
+                          &rhs_order);
+              return lhs_order < rhs_order;
+            });
+
+  // Name of the variable to put the results
+  auto const result_variable = "CMAKE_GET_OS_RELEASE_FALLBACK_RESULT"_s;
+
+  for (auto const& script : scripts) {
+    // Unset the result variable
+    makefile.RemoveDefinition(result_variable.data());
+
+    // include FATAL_ERROR and ERROR in the return status
+    if (!makefile.ReadListFile(script) ||
+        cmSystemTools::GetErrorOccuredFlag()) {
+      // Ok, no worries... go try the next script.
+      continue;
+    }
+
+    std::vector<std::string> variables;
+    if (!makefile.GetDefExpandList(result_variable.data(), variables)) {
+      // Heh, this script didn't found anything... go try the next one.
+      continue;
+    }
+
+    for (auto const& variable : variables) {
+      auto value = makefile.GetSafeDefinition(variable);
+      makefile.RemoveDefinition(variable);
+
+      if (!cmHasPrefix(variable, cmStrCat(result_variable, '_'))) {
+        // Ignore unknown variable set by the script
+        continue;
+      }
+
+      auto key = variable.substr(result_variable.size() + 1,
+                                 variable.size() - result_variable.size() - 1);
+      data.emplace(std::move(key), std::move(value));
+    }
+
+    // Try 'till some script can get anything
+    if (!data.empty()) {
+      data.emplace("USED_FALLBACK_SCRIPT", script);
+      break;
+    }
+  }
+
+  makefile.RemoveDefinition(result_variable.data());
+
+  return data;
+}
+
+cm::optional<std::string> GetValue(cmExecutionStatus& status,
+                                   std::string const& key,
+                                   std::string const& variable)
+{
+  const auto prefix = "DISTRIB_"_s;
+  if (!cmHasPrefix(key, prefix)) {
+    return {};
+  }
+
+  static const std::map<std::string, std::string> s_os_release =
+    GetOSReleaseVariables(status);
+
+  auto& makefile = status.GetMakefile();
+
+  const std::string subkey =
+    key.substr(prefix.size(), key.size() - prefix.size());
+  if (subkey == "INFO"_s) {
+    std::string vars;
+    for (const auto& kv : s_os_release) {
+      auto cmake_var_name = cmStrCat(variable, '_', kv.first);
+      vars += DELIM[!vars.empty()] + cmake_var_name;
+      makefile.AddDefinition(cmake_var_name, kv.second);
+    }
+    return cm::optional<std::string>(std::move(vars));
+  }
+
+  // Query individual variable
+  const auto it = s_os_release.find(subkey);
+  if (it != s_os_release.cend()) {
+    return it->second;
+  }
+
+  // NOTE Empty string means requested variable not set
+  return std::string{};
+}
+
+#ifdef HAVE_VS_SETUP_HELPER
+cm::optional<std::string> GetValue(cmExecutionStatus& status,
+                                   std::string const& key)
+{
+  auto* const gg = status.GetMakefile().GetGlobalGenerator();
+  for (auto vs : { 15, 16, 17 }) {
+    if (key == cmStrCat("VS_"_s, vs, "_DIR"_s)) {
+      std::string value;
+      // If generating for the VS nn IDE, use the same instance.
+
+      if (cmHasPrefix(gg->GetName(), cmStrCat("Visual Studio "_s, vs, ' '))) {
+        cmGlobalVisualStudioVersionedGenerator* vsNNgen =
+          static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
+        if (vsNNgen->GetVSInstance(value)) {
+          return value;
+        }
+      }
+
+      // Otherwise, find a VS nn instance ourselves.
+      cmVSSetupAPIHelper vsSetupAPIHelper(vs);
+      if (vsSetupAPIHelper.GetVSInstanceInfo(value)) {
+        cmSystemTools::ConvertToUnixSlashes(value);
+      }
+      return value;
+    }
+  }
+
+  if (key == "VS_MSBUILD_COMMAND"_s && gg->IsVisualStudioAtLeast10()) {
+    cmGlobalVisualStudio10Generator* vs10gen =
+      static_cast<cmGlobalVisualStudio10Generator*>(gg);
+    return vs10gen->FindMSBuildCommandEarly(&status.GetMakefile());
+  }
+
+  return {};
+}
+#endif
+
+cm::optional<std::string> GetValueChained()
+{
+  return {};
+}
+
+template <typename GetterFn, typename... Next>
+cm::optional<std::string> GetValueChained(GetterFn current, Next... chain)
+{
+  auto value = current();
+  if (value.has_value()) {
+    return value;
+  }
+  return GetValueChained(chain...);
+}
+// END Private functions
+} // anonymous namespace
+
+// cmCMakeHostSystemInformation
+bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
+                                         cmExecutionStatus& status)
+{
+  std::size_t current_index = 0;
+
+  if (args.size() < (current_index + 2) || args[current_index] != "RESULT"_s) {
+    status.SetError("missing RESULT specification.");
+    return false;
+  }
+
+  auto const& variable = args[current_index + 1];
+  current_index += 2;
+
+  if (args.size() < (current_index + 2) || args[current_index] != "QUERY"_s) {
+    status.SetError("missing QUERY specification");
+    return false;
+  }
+
+  static cmsys::SystemInformation info;
+  static auto initialized = false;
+  if (!initialized) {
+    info.RunCPUCheck();
+    info.RunOSCheck();
+    info.RunMemoryCheck();
+    initialized = true;
+  }
+
+  std::string result_list;
+  for (auto i = current_index + 1; i < args.size(); ++i) {
+    result_list += DELIM[!result_list.empty()];
+
+    auto const& key = args[i];
+    // clang-format off
+    auto value =
+      GetValueChained(
+          [&]() { return GetValue(info, key); }
+        , [&]() { return GetValue(status, key, variable); }
+#ifdef HAVE_VS_SETUP_HELPER
+        , [&]() { return GetValue(status, key); }
+#endif
+        );
+    // clang-format on
+    if (!value) {
+      status.SetError("does not recognize <key> " + key);
+      return false;
+    }
+    result_list += value.value();
+  }
+
+  status.GetMakefile().AddDefinition(variable, result_list);
+
+  return true;
 }
diff --git a/Source/cmCMakePathCommand.cxx b/Source/cmCMakePathCommand.cxx
index 9a5fa7b..bf94c2d 100644
--- a/Source/cmCMakePathCommand.cxx
+++ b/Source/cmCMakePathCommand.cxx
@@ -18,11 +18,11 @@
 #include "cmCMakePath.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 // Helper classes for argument parsing
@@ -150,7 +150,7 @@
 bool getInputPath(const std::string& arg, cmExecutionStatus& status,
                   std::string& path)
 {
-  cmProp def = status.GetMakefile().GetDefinition(arg);
+  cmValue def = status.GetMakefile().GetDefinition(arg);
   if (!def) {
     status.SetError("undefined variable for input path.");
     return false;
diff --git a/Source/cmCMakePolicyCommand.cxx b/Source/cmCMakePolicyCommand.cxx
index 1f99043..ebf639b 100644
--- a/Source/cmCMakePolicyCommand.cxx
+++ b/Source/cmCMakePolicyCommand.cxx
@@ -9,6 +9,7 @@
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 namespace {
 bool HandleSetMode(std::vector<std::string> const& args,
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index ace7382..e460031 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -140,7 +140,7 @@
 const char* CCONV cmGetDefinition(void* arg, const char* def)
 {
   cmMakefile* mf = static_cast<cmMakefile*>(arg);
-  return cmToCStr(mf->GetDefinition(def));
+  return mf->GetDefinition(def).GetCStr();
 }
 
 int CCONV cmIsOn(void* arg, const char* name)
@@ -173,7 +173,7 @@
       std::string(tgt) + " for directory " + std::string(d));
     return;
   }
-  t->InsertLinkDirectory(d, mf->GetBacktrace());
+  t->InsertLinkDirectory(BT<std::string>(d, mf->GetBacktrace()));
 }
 
 void CCONV cmAddExecutable(void* arg, const char* exename, int numSrcs,
@@ -592,12 +592,12 @@
 {
   cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
   if (cmSourceFile* rsf = sf->RealSourceFile) {
-    return cmToCStr(rsf->GetProperty(prop));
+    return rsf->GetProperty(prop).GetCStr();
   }
   if (!strcmp(prop, "LOCATION")) {
     return sf->FullPath.c_str();
   }
-  return cmToCStr(sf->Properties.GetPropertyValue(prop));
+  return sf->Properties.GetPropertyValue(prop).GetCStr();
 }
 
 int CCONV cmSourceFileGetPropertyAsBool(void* arg, const char* prop)
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 7c469c8..dfd2b6c 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -56,12 +56,12 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
 #include "cmXMLWriter.h"
@@ -1457,11 +1457,11 @@
     return;
   }
   // This code should go when cdash is changed to use labels only
-  cmProp subproject = cm->GetState()->GetGlobalProperty("SubProject");
+  cmValue subproject = cm->GetState()->GetGlobalProperty("SubProject");
   if (subproject) {
     xml.StartElement("Subproject");
     xml.Attribute("name", *subproject);
-    cmProp labels =
+    cmValue labels =
       ch->GetCMake()->GetState()->GetGlobalProperty("SubProjectLabels");
     if (labels) {
       xml.StartElement("Labels");
@@ -1475,7 +1475,7 @@
   }
 
   // This code should stay when cdash only does label based sub-projects
-  cmProp label = cm->GetState()->GetGlobalProperty("Label");
+  cmValue label = cm->GetState()->GetGlobalProperty("Label");
   if (label) {
     xml.StartElement("Labels");
     xml.Element("Label", *label);
@@ -2425,7 +2425,7 @@
         case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum::
           Extra:
           this->Impl->ExtraVerbose = true;
-          // intentional fallthrough
+          CM_FALLTHROUGH;
         case cmCMakePresetsFile::TestPreset::OutputOptions::VerbosityEnum::
           Verbose:
           this->Impl->Verbose = true;
@@ -3056,7 +3056,7 @@
 void cmCTest::PopulateCustomVector(cmMakefile* mf, const std::string& def,
                                    std::vector<std::string>& vec)
 {
-  cmProp dval = mf->GetDefinition(def);
+  cmValue dval = mf->GetDefinition(def);
   if (!dval) {
     return;
   }
@@ -3073,7 +3073,7 @@
 void cmCTest::PopulateCustomInteger(cmMakefile* mf, const std::string& def,
                                     int& val)
 {
-  cmProp dval = mf->GetDefinition(def);
+  cmValue dval = mf->GetDefinition(def);
   if (!dval) {
     return;
   }
@@ -3407,7 +3407,7 @@
   cmMakefile* mf, const char* dconfig, const std::string& cmake_var,
   bool suppress)
 {
-  cmProp ctvar = mf->GetDefinition(cmake_var);
+  cmValue ctvar = mf->GetDefinition(cmake_var);
   if (!ctvar) {
     return false;
   }
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index 1a950df..b9d1f66 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -118,13 +118,13 @@
   }
   this->CacheMajorVersion = 0;
   this->CacheMinorVersion = 0;
-  if (cmProp cmajor =
+  if (cmValue cmajor =
         this->GetInitializedCacheValue("CMAKE_CACHE_MAJOR_VERSION")) {
     unsigned int v = 0;
     if (sscanf(cmajor->c_str(), "%u", &v) == 1) {
       this->CacheMajorVersion = v;
     }
-    if (cmProp cminor =
+    if (cmValue cminor =
           this->GetInitializedCacheValue("CMAKE_CACHE_MINOR_VERSION")) {
       if (sscanf(cminor->c_str(), "%u", &v) == 1) {
         this->CacheMinorVersion = v;
@@ -144,7 +144,7 @@
   }
   // check to make sure the cache directory has not
   // been moved
-  cmProp oldDir = this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR");
+  cmValue oldDir = this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR");
   if (internal && oldDir) {
     std::string currentcwd = path;
     std::string oldcwd = *oldDir;
@@ -152,7 +152,7 @@
     currentcwd += "/CMakeCache.txt";
     oldcwd += "/CMakeCache.txt";
     if (!cmSystemTools::SameFile(oldcwd, currentcwd)) {
-      cmProp dir = this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR");
+      cmValue dir = this->GetInitializedCacheValue("CMAKE_CACHEFILE_DIR");
       std::ostringstream message;
       message << "The current CMakeCache.txt directory " << currentcwd
               << " is different than the directory " << (dir ? *dir : "")
@@ -203,7 +203,7 @@
                                           cmMessenger* messenger) const
 {
   for (const char* p : cmCacheManager::PersistentProperties) {
-    if (cmProp value = e.GetProperty(p)) {
+    if (cmValue value = e.GetProperty(p)) {
       std::string helpstring =
         cmStrCat(p, " property for variable: ", entryKey);
       cmCacheManager::OutputHelpString(os, helpstring);
@@ -232,17 +232,17 @@
   // before writing the cache, update the version numbers
   // to the
   this->AddCacheEntry("CMAKE_CACHE_MAJOR_VERSION",
-                      std::to_string(cmVersion::GetMajorVersion()).c_str(),
+                      std::to_string(cmVersion::GetMajorVersion()),
                       "Major version of cmake used to create the "
                       "current loaded cache",
                       cmStateEnums::INTERNAL);
   this->AddCacheEntry("CMAKE_CACHE_MINOR_VERSION",
-                      std::to_string(cmVersion::GetMinorVersion()).c_str(),
+                      std::to_string(cmVersion::GetMinorVersion()),
                       "Minor version of cmake used to create the "
                       "current loaded cache",
                       cmStateEnums::INTERNAL);
   this->AddCacheEntry("CMAKE_CACHE_PATCH_VERSION",
-                      std::to_string(cmVersion::GetPatchVersion()).c_str(),
+                      std::to_string(cmVersion::GetPatchVersion()),
                       "Patch version of cmake used to create the "
                       "current loaded cache",
                       cmStateEnums::INTERNAL);
@@ -256,7 +256,7 @@
     currentcwd[0] = static_cast<char>(currentcwd[0] - 'A' + 'a');
   }
   cmSystemTools::ConvertToUnixSlashes(currentcwd);
-  this->AddCacheEntry("CMAKE_CACHEFILE_DIR", currentcwd.c_str(),
+  this->AddCacheEntry("CMAKE_CACHEFILE_DIR", currentcwd,
                       "This is the directory where this CMakeCache.txt"
                       " was created",
                       cmStateEnums::INTERNAL);
@@ -296,7 +296,7 @@
       */
     } else if (t != cmStateEnums::INTERNAL) {
       // Format is key:type=value
-      if (cmProp help = ce.GetProperty("HELPSTRING")) {
+      if (cmValue help = ce.GetProperty("HELPSTRING")) {
         cmCacheManager::OutputHelpString(fout, *help);
       } else {
         cmCacheManager::OutputHelpString(fout, "Missing description");
@@ -326,7 +326,7 @@
     this->WritePropertyEntries(fout, i.first, i.second, messenger);
     if (t == cmStateEnums::INTERNAL) {
       // Format is key:type=value
-      if (cmProp help = i.second.GetProperty("HELPSTRING")) {
+      if (cmValue help = i.second.GetProperty("HELPSTRING")) {
         cmCacheManager::OutputHelpString(fout, *help);
       }
       cmCacheManager::OutputKey(fout, i.first);
@@ -496,11 +496,11 @@
   return nullptr;
 }
 
-cmProp cmCacheManager::GetInitializedCacheValue(const std::string& key) const
+cmValue cmCacheManager::GetInitializedCacheValue(const std::string& key) const
 {
   if (const auto* entry = this->GetCacheEntry(key)) {
     if (entry->Initialized) {
-      return &entry->GetValue();
+      return cmValue(entry->GetValue());
     }
   }
   return nullptr;
@@ -521,7 +521,7 @@
          "=================================================\n";
 }
 
-void cmCacheManager::AddCacheEntry(const std::string& key, const char* value,
+void cmCacheManager::AddCacheEntry(const std::string& key, cmValue value,
                                    const char* helpString,
                                    cmStateEnums::CacheEntryType type)
 {
@@ -550,10 +550,10 @@
                   : "(This variable does not exist and should not be used)");
 }
 
-void cmCacheManager::CacheEntry::SetValue(const char* value)
+void cmCacheManager::CacheEntry::SetValue(cmValue value)
 {
   if (value) {
-    this->Value = value;
+    this->Value = *value;
     this->Initialized = true;
   } else {
     this->Value.clear();
@@ -565,13 +565,13 @@
   return this->Properties.GetKeys();
 }
 
-cmProp cmCacheManager::CacheEntry::GetProperty(const std::string& prop) const
+cmValue cmCacheManager::CacheEntry::GetProperty(const std::string& prop) const
 {
   if (prop == "TYPE") {
-    return &cmState::CacheEntryTypeToString(this->Type);
+    return cmValue(cmState::CacheEntryTypeToString(this->Type));
   }
   if (prop == "VALUE") {
-    return &this->Value;
+    return cmValue(this->Value);
   }
   return this->Properties.GetPropertyValue(prop);
 }
diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h
index 7a9a7dc..bc3fb51 100644
--- a/Source/cmCacheManager.h
+++ b/Source/cmCacheManager.h
@@ -11,9 +11,9 @@
 #include <utility>
 #include <vector>
 
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmStateTypes.h"
+#include "cmValue.h"
 
 class cmMessenger;
 
@@ -31,13 +31,13 @@
 
   public:
     const std::string& GetValue() const { return this->Value; }
-    void SetValue(const char*);
+    void SetValue(cmValue);
 
     cmStateEnums::CacheEntryType GetType() const { return this->Type; }
     void SetType(cmStateEnums::CacheEntryType ty) { this->Type = ty; }
 
     std::vector<std::string> GetPropertyList() const;
-    cmProp GetProperty(const std::string& property) const;
+    cmValue GetProperty(const std::string& property) const;
     bool GetPropertyAsBool(const std::string& property) const;
     void SetProperty(const std::string& property, const char* value);
     void SetProperty(const std::string& property, bool value);
@@ -70,12 +70,12 @@
   bool IsCacheLoaded() const { return this->CacheLoaded; }
 
   //! Get a value from the cache given a key
-  cmProp GetInitializedCacheValue(const std::string& key) const;
+  cmValue GetInitializedCacheValue(const std::string& key) const;
 
-  cmProp GetCacheEntryValue(const std::string& key) const
+  cmValue GetCacheEntryValue(const std::string& key) const
   {
     if (const auto* entry = this->GetCacheEntry(key)) {
-      return &entry->GetValue();
+      return cmValue(entry->GetValue());
     }
     return nullptr;
   }
@@ -83,7 +83,7 @@
   void SetCacheEntryValue(std::string const& key, std::string const& value)
   {
     if (auto* entry = this->GetCacheEntry(key)) {
-      entry->SetValue(value.c_str());
+      entry->SetValue(cmValue(value));
     }
   }
 
@@ -104,8 +104,8 @@
     return {};
   }
 
-  cmProp GetCacheEntryProperty(std::string const& key,
-                               std::string const& propName) const
+  cmValue GetCacheEntryProperty(std::string const& key,
+                                std::string const& propName) const
   {
     if (const auto* entry = this->GetCacheEntry(key)) {
       return entry->GetProperty(propName);
@@ -173,6 +173,18 @@
 
   //! Add an entry into the cache
   void AddCacheEntry(const std::string& key, const char* value,
+                     const char* helpString, cmStateEnums::CacheEntryType type)
+  {
+    this->AddCacheEntry(key,
+                        value ? cmValue(std::string(value)) : cmValue(nullptr),
+                        helpString, type);
+  }
+  void AddCacheEntry(const std::string& key, const std::string& value,
+                     const char* helpString, cmStateEnums::CacheEntryType type)
+  {
+    this->AddCacheEntry(key, cmValue(value), helpString, type);
+  }
+  void AddCacheEntry(const std::string& key, cmValue value,
                      const char* helpString,
                      cmStateEnums::CacheEntryType type);
 
diff --git a/Source/cmCommand.h b/Source/cmCommand.h
index 68c56d9..f5a5190 100644
--- a/Source/cmCommand.h
+++ b/Source/cmCommand.h
@@ -44,7 +44,7 @@
   cmMakefile* GetMakefile() { return this->Makefile; }
 
   void SetExecutionStatus(cmExecutionStatus* s);
-  cmExecutionStatus* GetExecutionStatus() { return this->Status; };
+  cmExecutionStatus* GetExecutionStatus() { return this->Status; }
 
   /**
    * This is called by the cmMakefile when the command is first
diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx
index deddba8..2ed04e5 100644
--- a/Source/cmCommandArgumentParserHelper.cxx
+++ b/Source/cmCommandArgumentParserHelper.cxx
@@ -14,10 +14,10 @@
 #include "cmCommandArgumentLexer.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 int cmCommandArgument_yyparse(yyscan_t yyscanner);
 //
@@ -73,7 +73,8 @@
     return "";
   }
   if (strcmp(key, "CACHE") == 0) {
-    if (cmProp c = this->Makefile->GetState()->GetInitializedCacheValue(var)) {
+    if (cmValue c =
+          this->Makefile->GetState()->GetInitializedCacheValue(var)) {
       if (this->EscapeQuotes) {
         return this->AddString(cmEscapeQuotes(*c));
       }
@@ -103,7 +104,7 @@
     }
     return this->AddString(line);
   }
-  cmProp value = this->Makefile->GetDefinition(var);
+  cmValue value = this->Makefile->GetDefinition(var);
   if (!value) {
     this->Makefile->MaybeWarnUninitialized(var, this->FileName);
     if (!this->RemoveEmpty) {
@@ -113,7 +114,7 @@
   if (this->EscapeQuotes && value) {
     return this->AddString(cmEscapeQuotes(*value));
   }
-  return this->AddString(cmToCStrSafe(value));
+  return this->AddString(value);
 }
 
 const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var)
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index 7c2e20c..8d5ce7e 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -14,12 +14,12 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
   : GeneratorTarget(gt)
@@ -39,10 +39,10 @@
   return this->ConfigNames;
 }
 
-const char* cmCommonTargetGenerator::GetFeature(const std::string& feature,
-                                                const std::string& config)
+cmValue cmCommonTargetGenerator::GetFeature(const std::string& feature,
+                                            const std::string& config)
 {
-  return this->GeneratorTarget->GetFeature(feature, config)->c_str();
+  return this->GeneratorTarget->GetFeature(feature, config);
 }
 
 void cmCommonTargetGenerator::AddModuleDefinitionFlag(
@@ -56,7 +56,7 @@
   }
 
   // TODO: Create a per-language flag variable.
-  cmProp defFileFlag =
+  cmValue defFileFlag =
     this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG");
   if (!defFileFlag) {
     return;
@@ -240,11 +240,16 @@
 
   std::vector<std::string> manifests;
   manifests.reserve(manifest_srcs.size());
+
+  std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
+  std::string const& manifestFlag =
+    this->Makefile->GetDefinition("CMAKE_" + lang + "_LINKER_MANIFEST_FLAG");
   for (cmSourceFile const* manifest_src : manifest_srcs) {
-    manifests.push_back(this->LocalCommonGenerator->ConvertToOutputFormat(
-      this->LocalCommonGenerator->MaybeRelativeToWorkDir(
-        manifest_src->GetFullPath()),
-      cmOutputConverter::SHELL));
+    manifests.push_back(manifestFlag +
+                        this->LocalCommonGenerator->ConvertToOutputFormat(
+                          this->LocalCommonGenerator->MaybeRelativeToWorkDir(
+                            manifest_src->GetFullPath()),
+                          cmOutputConverter::SHELL));
   }
 
   return cmJoin(manifests, " ");
@@ -254,7 +259,7 @@
 {
   std::string aixExports;
   if (this->GeneratorTarget->Target->IsAIX()) {
-    if (cmProp exportAll =
+    if (cmValue exportAll =
           this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) {
       if (cmIsOff(*exportAll)) {
         aixExports = "-n";
@@ -270,7 +275,7 @@
 {
   // Lookup the flag to specify the version.
   std::string fvar = cmStrCat("CMAKE_", lang, "_OSX_", name, "_VERSION_FLAG");
-  cmProp flag = this->Makefile->GetDefinition(fvar);
+  cmValue flag = this->Makefile->GetDefinition(fvar);
 
   // Skip if no such flag.
   if (!flag) {
@@ -297,7 +302,7 @@
   const std::string& config)
 {
   std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
-  cmProp launcherProp =
+  cmValue launcherProp =
     this->GeneratorTarget->GetProperty(lang + "_LINKER_LAUNCHER");
   if (cmNonempty(launcherProp)) {
     // Convert ;-delimited list to single string
diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h
index a156a41..baa36c9 100644
--- a/Source/cmCommonTargetGenerator.h
+++ b/Source/cmCommonTargetGenerator.h
@@ -8,6 +8,8 @@
 #include <string>
 #include <vector>
 
+#include "cmValue.h"
+
 class cmGeneratorTarget;
 class cmGlobalCommonGenerator;
 class cmLinkLineComputer;
@@ -28,8 +30,7 @@
 
 protected:
   // Feature query methods.
-  const char* GetFeature(const std::string& feature,
-                         const std::string& config);
+  cmValue GetFeature(const std::string& feature, const std::string& config);
 
   // Helper to add flag for windows .def file.
   void AddModuleDefinitionFlag(cmLinkLineComputer* linkLineComputer,
@@ -40,6 +41,7 @@
   cmLocalCommonGenerator* LocalCommonGenerator;
   cmGlobalCommonGenerator* GlobalCommonGenerator;
   std::vector<std::string> ConfigNames;
+  bool UseLWYU = false;
 
   void AppendFortranFormatFlags(std::string& flags,
                                 cmSourceFile const& source);
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 0b27e34..370ddff 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -18,11 +18,11 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 /*
@@ -322,7 +322,7 @@
   } else {
     // Look for an old-style <item>_LIB_DEPENDS variable.
     std::string var = cmStrCat(entry.Item.Value, "_LIB_DEPENDS");
-    if (cmProp val = this->Makefile->GetDefinition(var)) {
+    if (cmValue val = this->Makefile->GetDefinition(var)) {
       // The item dependencies are known.  Follow them.
       BFSEntry qe = { index, val->c_str() };
       this->BFSQueue.push(qe);
@@ -489,7 +489,7 @@
       // lower.
       if (!haveLLT) {
         std::string var = cmStrCat(d, "_LINK_TYPE");
-        if (cmProp val = this->Makefile->GetDefinition(var)) {
+        if (cmValue val = this->Makefile->GetDefinition(var)) {
           if (*val == "debug") {
             llt = DEBUG_LibraryType;
           } else if (*val == "optimized") {
@@ -607,7 +607,7 @@
       from = depender;
     }
   }
-  return from->ResolveLinkItem(name, cmListFileBacktrace());
+  return from->ResolveLinkItem(BT<std::string>(name));
 }
 
 void cmComputeLinkDepends::InferDependencies()
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index d15da0c..831a81f 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -20,12 +20,12 @@
 #include "cmOrderDirectories.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 //#define CM_COMPUTE_LINK_INFO_DEBUG
@@ -278,38 +278,36 @@
   // On platforms without import libraries there may be a special flag
   // to use when creating a plugin (module) that obtains symbols from
   // the program that will load it.
-  this->LoaderFlag = nullptr;
   if (!this->Target->IsDLLPlatform() &&
       this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
     std::string loader_flag_var =
       cmStrCat("CMAKE_SHARED_MODULE_LOADER_", this->LinkLanguage, "_FLAG");
-    this->LoaderFlag =
-      cmToCStr(this->Makefile->GetDefinition(loader_flag_var));
+    this->LoaderFlag = this->Makefile->GetDefinition(loader_flag_var);
   }
 
   // Get options needed to link libraries.
-  if (cmProp flag = this->Makefile->GetDefinition(
+  if (cmValue flag = this->Makefile->GetDefinition(
         "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FLAG")) {
     this->LibLinkFlag = *flag;
   } else {
     this->LibLinkFlag =
       this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FLAG");
   }
-  if (cmProp flag = this->Makefile->GetDefinition(
+  if (cmValue flag = this->Makefile->GetDefinition(
         "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_FILE_FLAG")) {
     this->LibLinkFileFlag = *flag;
   } else {
     this->LibLinkFileFlag =
       this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_FILE_FLAG");
   }
-  if (cmProp suffix = this->Makefile->GetDefinition(
+  if (cmValue suffix = this->Makefile->GetDefinition(
         "CMAKE_" + this->LinkLanguage + "_LINK_LIBRARY_SUFFIX")) {
     this->LibLinkSuffix = *suffix;
   } else {
     this->LibLinkSuffix =
       this->Makefile->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX");
   }
-  if (cmProp flag = this->Makefile->GetDefinition(
+  if (cmValue flag = this->Makefile->GetDefinition(
         "CMAKE_" + this->LinkLanguage + "_LINK_OBJECT_FILE_FLAG")) {
     this->ObjLinkFileFlag = *flag;
   } else {
@@ -528,7 +526,7 @@
 
   // Restore the target link type so the correct system runtime
   // libraries are found.
-  cmProp lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC");
+  cmValue lss = this->Target->GetProperty("LINK_SEARCH_END_STATIC");
   if (cmIsOn(lss)) {
     this->SetCurrentLinkType(LinkStatic);
   } else {
@@ -607,7 +605,7 @@
   if (runtimeLibrary.empty()) {
     return;
   }
-  if (cmProp runtimeLinkOptions = this->Makefile->GetDefinition(
+  if (cmValue runtimeLinkOptions = this->Makefile->GetDefinition(
         "CMAKE_" + lang + "_RUNTIME_LIBRARY_LINK_OPTIONS_" + runtimeLibrary)) {
     std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
     for (std::string const& i : libsVec) {
@@ -623,7 +621,7 @@
   // Add libraries for this language that are not implied by the
   // linker language.
   std::string libVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_LIBRARIES");
-  if (cmProp libs = this->Makefile->GetDefinition(libVar)) {
+  if (cmValue libs = this->Makefile->GetDefinition(libVar)) {
     std::vector<std::string> libsVec = cmExpandedList(*libs);
     for (std::string const& i : libsVec) {
       if (!cm::contains(this->ImplicitLinkLibs, i)) {
@@ -635,7 +633,7 @@
   // Add linker search paths for this language that are not
   // implied by the linker language.
   std::string dirVar = cmStrCat("CMAKE_", lang, "_IMPLICIT_LINK_DIRECTORIES");
-  if (cmProp dirs = this->Makefile->GetDefinition(dirVar)) {
+  if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) {
     std::vector<std::string> dirsVec = cmExpandedList(*dirs);
     this->OrderLinkerSearchPath->AddLanguageDirectories(dirsVec);
   }
@@ -660,8 +658,7 @@
       // This link item is an executable that may provide symbols
       // used by this target.  A special flag is needed on this
       // platform.  Add it now.
-      std::string linkItem;
-      linkItem = this->LoaderFlag;
+      std::string linkItem = this->LoaderFlag;
       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
         ? cmStateEnums::ImportLibraryArtifact
         : cmStateEnums::RuntimeBinaryArtifact;
@@ -839,8 +836,8 @@
   this->LinkTypeEnabled = false;
 
   // Lookup link type selection flags.
-  cmProp static_link_type_flag = nullptr;
-  cmProp shared_link_type_flag = nullptr;
+  cmValue static_link_type_flag = nullptr;
+  cmValue shared_link_type_flag = nullptr;
   const char* target_type_str = nullptr;
   switch (this->Target->GetType()) {
     case cmStateEnums::EXECUTABLE:
@@ -878,7 +875,7 @@
   }
 
   // Lookup the starting link type from the target (linked statically?).
-  cmProp lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC");
+  cmValue lss = this->Target->GetProperty("LINK_SEARCH_START_STATIC");
   this->StartLinkType = cmIsOn(lss) ? LinkStatic : LinkShared;
   this->CurrentLinkType = this->StartLinkType;
 }
@@ -900,13 +897,14 @@
                          LinkShared);
   this->AddLinkExtension(mf->GetSafeDefinition("CMAKE_LINK_LIBRARY_SUFFIX"),
                          LinkUnknown);
-  if (cmProp linkSuffixes = mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) {
+  if (cmValue linkSuffixes =
+        mf->GetDefinition("CMAKE_EXTRA_LINK_EXTENSIONS")) {
     std::vector<std::string> linkSuffixVec = cmExpandedList(*linkSuffixes);
     for (std::string const& i : linkSuffixVec) {
       this->AddLinkExtension(i, LinkUnknown);
     }
   }
-  if (cmProp sharedSuffixes =
+  if (cmValue sharedSuffixes =
         mf->GetDefinition("CMAKE_EXTRA_SHARED_LIBRARY_SUFFIXES")) {
     std::vector<std::string> sharedSuffixVec = cmExpandedList(*sharedSuffixes);
     for (std::string const& i : sharedSuffixVec) {
@@ -1187,6 +1185,7 @@
           this->CMP0060WarnItems.insert(item);
         }
       }
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       break;
     case cmPolicies::REQUIRED_ALWAYS:
@@ -1456,8 +1455,10 @@
                                           this->Target->GetBacktrace());
       }
     }
-    case cmPolicies::OLD:
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD: // NOLINT(bugprone-branch-clone)
       // OLD behavior does not warn.
+      break;
     case cmPolicies::NEW:
       // NEW behavior will not get here.
       break;
@@ -1496,6 +1497,7 @@
         this->CMakeInstance->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
                                           this->Target->GetBacktrace());
       }
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       // OLD behavior is to add the paths containing libraries with
       // known full paths as link directories.
@@ -1592,7 +1594,7 @@
 
   // Append library architecture to all implicit platform directories
   // and add them to the set
-  if (cmProp libraryArch =
+  if (cmValue libraryArch =
         this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
     for (std::string const& i : implicitDirVec) {
       this->ImplicitLinkDirs.insert(i + "/" + *libraryArch);
@@ -1785,13 +1787,13 @@
   }
   if (use_build_rpath || use_link_rpath) {
     std::string rootPath;
-    if (cmProp sysrootLink =
+    if (cmValue sysrootLink =
           this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
       rootPath = *sysrootLink;
     } else {
       rootPath = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
     }
-    cmProp stagePath = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX");
+    cmValue stagePath = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX");
     std::string const& installPrefix =
       this->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX");
     cmSystemTools::ConvertToUnixSlashes(rootPath);
@@ -1859,7 +1861,7 @@
         "CMAKE_" + li + "_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH";
       if (this->Makefile->IsOn(useVar)) {
         std::string dirVar = "CMAKE_" + li + "_IMPLICIT_LINK_DIRECTORIES";
-        if (cmProp dirs = this->Makefile->GetDefinition(dirVar)) {
+        if (cmValue dirs = this->Makefile->GetDefinition(dirVar)) {
           cmCLI_ExpandListUnique(*dirs, runtimeDirs, emitted);
         }
       }
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 7fe30b3..90a699e 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -14,6 +14,7 @@
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmListFileCache.h"
+#include "cmValue.h"
 
 class cmGeneratorTarget;
 class cmGlobalGenerator;
@@ -137,7 +138,7 @@
     SharedDepModeLink    // List file on link line
   };
 
-  const char* LoaderFlag;
+  cmValue LoaderFlag;
   std::string LibLinkFlag;
   std::string LibLinkFileFlag;
   std::string ObjLinkFileFlag;
diff --git a/Source/cmComputeTargetDepends.cxx b/Source/cmComputeTargetDepends.cxx
index 76712f4..ef89c8b 100644
--- a/Source/cmComputeTargetDepends.cxx
+++ b/Source/cmComputeTargetDepends.cxx
@@ -17,16 +17,15 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocationKind.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
-#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 /*
@@ -323,7 +322,7 @@
   }
   cmGeneratorTarget const* depender = this->Targets[depender_index];
   cmLinkItem const& objItem =
-    depender->ResolveLinkItem(objLib, cmListFileBacktrace());
+    depender->ResolveLinkItem(BT<std::string>(objLib));
   if (emitted.insert(objItem).second) {
     if (depender->GetType() != cmStateEnums::EXECUTABLE &&
         depender->GetType() != cmStateEnums::STATIC_LIBRARY &&
@@ -360,6 +359,7 @@
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0046) << "\n";
         issueMessage = true;
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::NEW:
@@ -367,6 +367,7 @@
       case cmPolicies::REQUIRED_ALWAYS:
         issueMessage = true;
         messageType = MessageType::FATAL_ERROR;
+        break;
     }
     if (issueMessage) {
       cmake* cm = this->GlobalGenerator->GetCMakeInstance();
@@ -469,7 +470,7 @@
         gt->GetType() != cmStateEnums::OBJECT_LIBRARY) {
       intermediateEdges = initialEdges;
     } else {
-      if (cmProp optimizeDependencies =
+      if (cmValue optimizeDependencies =
             gt->GetProperty("OPTIMIZE_DEPENDENCIES")) {
         if (cmIsOn(optimizeDependencies)) {
           this->OptimizeLinkDependencies(gt, intermediateEdges, initialEdges);
diff --git a/Source/cmConditionEvaluator.cxx b/Source/cmConditionEvaluator.cxx
index f99592c..8e479c5 100644
--- a/Source/cmConditionEvaluator.cxx
+++ b/Source/cmConditionEvaluator.cxx
@@ -2,58 +2,207 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmConditionEvaluator.h"
 
+#include <array>
 #include <cstdio>
 #include <cstdlib>
 #include <functional>
+#include <iterator>
+#include <list>
 #include <sstream>
 #include <utility>
 
+#include <cm/string_view>
 #include <cmext/algorithm>
 
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmExpandedCommandArgument.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
-class cmTest;
+namespace {
+auto const keyAND = "AND"_s;
+auto const keyCOMMAND = "COMMAND"_s;
+auto const keyDEFINED = "DEFINED"_s;
+auto const keyEQUAL = "EQUAL"_s;
+auto const keyEXISTS = "EXISTS"_s;
+auto const keyGREATER = "GREATER"_s;
+auto const keyGREATER_EQUAL = "GREATER_EQUAL"_s;
+auto const keyIN_LIST = "IN_LIST"_s;
+auto const keyIS_ABSOLUTE = "IS_ABSOLUTE"_s;
+auto const keyIS_DIRECTORY = "IS_DIRECTORY"_s;
+auto const keyIS_NEWER_THAN = "IS_NEWER_THAN"_s;
+auto const keyIS_SYMLINK = "IS_SYMLINK"_s;
+auto const keyLESS = "LESS"_s;
+auto const keyLESS_EQUAL = "LESS_EQUAL"_s;
+auto const keyMATCHES = "MATCHES"_s;
+auto const keyNOT = "NOT"_s;
+auto const keyOR = "OR"_s;
+auto const keyParenL = "("_s;
+auto const keyParenR = ")"_s;
+auto const keyPOLICY = "POLICY"_s;
+auto const keySTREQUAL = "STREQUAL"_s;
+auto const keySTRGREATER = "STRGREATER"_s;
+auto const keySTRGREATER_EQUAL = "STRGREATER_EQUAL"_s;
+auto const keySTRLESS = "STRLESS"_s;
+auto const keySTRLESS_EQUAL = "STRLESS_EQUAL"_s;
+auto const keyTARGET = "TARGET"_s;
+auto const keyTEST = "TEST"_s;
+auto const keyVERSION_EQUAL = "VERSION_EQUAL"_s;
+auto const keyVERSION_GREATER = "VERSION_GREATER"_s;
+auto const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL"_s;
+auto const keyVERSION_LESS = "VERSION_LESS"_s;
+auto const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL"_s;
 
-static std::string const keyAND = "AND";
-static std::string const keyCOMMAND = "COMMAND";
-static std::string const keyDEFINED = "DEFINED";
-static std::string const keyEQUAL = "EQUAL";
-static std::string const keyEXISTS = "EXISTS";
-static std::string const keyGREATER = "GREATER";
-static std::string const keyGREATER_EQUAL = "GREATER_EQUAL";
-static std::string const keyIN_LIST = "IN_LIST";
-static std::string const keyIS_ABSOLUTE = "IS_ABSOLUTE";
-static std::string const keyIS_DIRECTORY = "IS_DIRECTORY";
-static std::string const keyIS_NEWER_THAN = "IS_NEWER_THAN";
-static std::string const keyIS_SYMLINK = "IS_SYMLINK";
-static std::string const keyLESS = "LESS";
-static std::string const keyLESS_EQUAL = "LESS_EQUAL";
-static std::string const keyMATCHES = "MATCHES";
-static std::string const keyNOT = "NOT";
-static std::string const keyOR = "OR";
-static std::string const keyParenL = "(";
-static std::string const keyParenR = ")";
-static std::string const keyPOLICY = "POLICY";
-static std::string const keySTREQUAL = "STREQUAL";
-static std::string const keySTRGREATER = "STRGREATER";
-static std::string const keySTRGREATER_EQUAL = "STRGREATER_EQUAL";
-static std::string const keySTRLESS = "STRLESS";
-static std::string const keySTRLESS_EQUAL = "STRLESS_EQUAL";
-static std::string const keyTARGET = "TARGET";
-static std::string const keyTEST = "TEST";
-static std::string const keyVERSION_EQUAL = "VERSION_EQUAL";
-static std::string const keyVERSION_GREATER = "VERSION_GREATER";
-static std::string const keyVERSION_GREATER_EQUAL = "VERSION_GREATER_EQUAL";
-static std::string const keyVERSION_LESS = "VERSION_LESS";
-static std::string const keyVERSION_LESS_EQUAL = "VERSION_LESS_EQUAL";
+cmSystemTools::CompareOp const MATCH2CMPOP[5] = {
+  cmSystemTools::OP_LESS, cmSystemTools::OP_LESS_EQUAL,
+  cmSystemTools::OP_GREATER, cmSystemTools::OP_GREATER_EQUAL,
+  cmSystemTools::OP_EQUAL
+};
+
+// Run-Time to Compile-Time template selector
+template <template <typename> class Comp, template <typename> class... Ops>
+struct cmRt2CtSelector
+{
+  template <typename T>
+  static bool eval(int r, T lhs, T rhs)
+  {
+    switch (r) {
+      case 0:
+        return false;
+      case 1:
+        return Comp<T>()(lhs, rhs);
+      default:
+        return cmRt2CtSelector<Ops...>::eval(r - 1, lhs, rhs);
+    }
+  }
+};
+
+template <template <typename> class Comp>
+struct cmRt2CtSelector<Comp>
+{
+  template <typename T>
+  static bool eval(int r, T lhs, T rhs)
+  {
+    return r == 1 && Comp<T>()(lhs, rhs);
+  }
+};
+
+std::string bool2string(bool const value)
+{
+  return std::string(std::size_t(1), static_cast<char>('0' + int(value)));
+}
+
+bool looksLikeSpecialVariable(const std::string& var,
+                              cm::static_string_view prefix,
+                              const std::size_t varNameLen)
+{
+  // NOTE Expecting a variable name at least 1 char length:
+  // <prefix> + `{` + <varname> + `}`
+  return ((prefix.size() + 3) <= varNameLen) &&
+    cmHasPrefix(var, cmStrCat(prefix, '{')) && var[varNameLen - 1] == '}';
+}
+} // anonymous namespace
+
+#if defined(__SUNPRO_CC)
+#  define CM_INHERIT_CTOR(Class, Base, Tpl)                                   \
+    template <typename... Args>                                               \
+    Class(Args&&... args)                                                     \
+      : Base Tpl(std::forward<Args>(args)...)                                 \
+    {                                                                         \
+    }
+#else
+#  define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base
+#endif
+
+// BEGIN cmConditionEvaluator::cmArgumentList
+class cmConditionEvaluator::cmArgumentList
+  : public std::list<cmExpandedCommandArgument>
+{
+  using base_t = std::list<cmExpandedCommandArgument>;
+
+public:
+  CM_INHERIT_CTOR(cmArgumentList, list, <cmExpandedCommandArgument>);
+
+  class CurrentAndNextIter
+  {
+    friend class cmConditionEvaluator::cmArgumentList;
+
+  public:
+    base_t::iterator current;
+    base_t::iterator next;
+
+    CurrentAndNextIter advance(base_t& args)
+    {
+      this->current = std::next(this->current);
+      this->next =
+        std::next(this->current, difference_type(this->current != args.end()));
+      return *this;
+    }
+
+  private:
+    CurrentAndNextIter(base_t& args)
+      : current(args.begin())
+      , next(std::next(this->current,
+                       difference_type(this->current != args.end())))
+    {
+    }
+  };
+
+  class CurrentAndTwoMoreIter
+  {
+    friend class cmConditionEvaluator::cmArgumentList;
+
+  public:
+    base_t::iterator current;
+    base_t::iterator next;
+    base_t::iterator nextnext;
+
+    CurrentAndTwoMoreIter advance(base_t& args)
+    {
+      this->current = std::next(this->current);
+      this->next =
+        std::next(this->current, difference_type(this->current != args.end()));
+      this->nextnext =
+        std::next(this->next, difference_type(this->next != args.end()));
+      return *this;
+    }
+
+  private:
+    CurrentAndTwoMoreIter(base_t& args)
+      : current(args.begin())
+      , next(std::next(this->current,
+                       difference_type(this->current != args.end())))
+      , nextnext(
+          std::next(this->next, difference_type(this->next != args.end())))
+    {
+    }
+  };
+
+  CurrentAndNextIter make2ArgsIterator() { return *this; }
+  CurrentAndTwoMoreIter make3ArgsIterator() { return *this; }
+
+  template <typename Iter>
+  void ReduceOneArg(const bool value, Iter args)
+  {
+    *args.current = cmExpandedCommandArgument(bool2string(value), true);
+    this->erase(args.next);
+  }
+
+  void ReduceTwoArgs(const bool value, CurrentAndTwoMoreIter args)
+  {
+    *args.current = cmExpandedCommandArgument(bool2string(value), true);
+    this->erase(args.nextnext);
+    this->erase(args.next);
+  }
+};
+
+// END cmConditionEvaluator::cmArgumentList
 
 cmConditionEvaluator::cmConditionEvaluator(cmMakefile& makefile,
                                            cmListFileBacktrace bt)
@@ -99,25 +248,29 @@
   // now loop through the arguments and see if we can reduce any of them
   // we do this multiple times. Once for each level of precedence
   // parens
-  if (!this->HandleLevel0(newArgs, errorString, status)) {
-    return false;
-  }
-  // predicates
-  if (!this->HandleLevel1(newArgs, errorString, status)) {
-    return false;
-  }
-  // binary ops
-  if (!this->HandleLevel2(newArgs, errorString, status)) {
-    return false;
-  }
+  using handlerFn_t = bool (cmConditionEvaluator::*)(
+    cmArgumentList&, std::string&, MessageType&);
+  const std::array<handlerFn_t, 5> handlers = { {
+    &cmConditionEvaluator::HandleLevel0, // parenthesis
+    &cmConditionEvaluator::HandleLevel1, // predicates
+    &cmConditionEvaluator::HandleLevel2, // binary ops
+    &cmConditionEvaluator::HandleLevel3, // NOT
+    &cmConditionEvaluator::HandleLevel4  // AND OR
+  } };
+  for (auto fn : handlers) {
+    // Call the reducer 'till there is anything to reduce...
+    // (i.e., if after an iteration the size becomes smaller)
+    auto levelResult = true;
+    for (auto beginSize = newArgs.size();
+         (levelResult = (this->*fn)(newArgs, errorString, status)) &&
+         newArgs.size() < beginSize;
+         beginSize = newArgs.size()) {
+    }
 
-  // NOT
-  if (!this->HandleLevel3(newArgs, errorString, status)) {
-    return false;
-  }
-  // AND OR
-  if (!this->HandleLevel4(newArgs, errorString, status)) {
-    return false;
+    if (!levelResult) {
+      // NOTE `errorString` supposed to be set already
+      return false;
+    }
   }
 
   // now at the end there should only be one argument left
@@ -132,7 +285,7 @@
 }
 
 //=========================================================================
-cmProp cmConditionEvaluator::GetDefinitionIfUnquoted(
+cmValue cmConditionEvaluator::GetDefinitionIfUnquoted(
   cmExpandedCommandArgument const& argument) const
 {
   if ((this->Policy54Status != cmPolicies::WARN &&
@@ -141,17 +294,19 @@
     return nullptr;
   }
 
-  cmProp def = this->Makefile.GetDefinition(argument.GetValue());
+  cmValue def = this->Makefile.GetDefinition(argument.GetValue());
 
   if (def && argument.WasQuoted() &&
       this->Policy54Status == cmPolicies::WARN) {
     if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) {
       std::ostringstream e;
-      e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)) << "\n";
-      e << "Quoted variables like \"" << argument.GetValue()
-        << "\" will no longer be dereferenced "
-           "when the policy is set to NEW.  "
+      // clang-format off
+      e << (cmPolicies::GetPolicyWarning(cmPolicies::CMP0054))
+        << "\n"
+           "Quoted variables like \"" << argument.GetValue() << "\" "
+           "will no longer be dereferenced when the policy is set to NEW.  "
            "Since the policy is not set the OLD behavior will be used.";
+      // clang-format on
 
       this->Makefile.GetCMakeInstance()->IssueMessage(
         MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
@@ -162,21 +317,22 @@
 }
 
 //=========================================================================
-cmProp cmConditionEvaluator::GetVariableOrString(
+cmValue cmConditionEvaluator::GetVariableOrString(
   const cmExpandedCommandArgument& argument) const
 {
-  cmProp def = this->GetDefinitionIfUnquoted(argument);
+  cmValue def = this->GetDefinitionIfUnquoted(argument);
 
   if (!def) {
-    def = &argument.GetValue();
+    def = cmValue(argument.GetValue());
   }
 
   return def;
 }
 
 //=========================================================================
-bool cmConditionEvaluator::IsKeyword(std::string const& keyword,
-                                     cmExpandedCommandArgument& argument) const
+bool cmConditionEvaluator::IsKeyword(
+  cm::static_string_view keyword,
+  const cmExpandedCommandArgument& argument) const
 {
   if ((this->Policy54Status != cmPolicies::WARN &&
        this->Policy54Status != cmPolicies::OLD) &&
@@ -184,17 +340,20 @@
     return false;
   }
 
-  bool isKeyword = argument.GetValue() == keyword;
+  const auto isKeyword = argument.GetValue() == keyword;
 
   if (isKeyword && argument.WasQuoted() &&
       this->Policy54Status == cmPolicies::WARN) {
     if (!this->Makefile.HasCMP0054AlreadyBeenReported(this->Backtrace.Top())) {
       std::ostringstream e;
-      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054) << "\n";
-      e << "Quoted keywords like \"" << argument.GetValue()
-        << "\" will no longer be interpreted as keywords "
+      // clang-format off
+      e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0054)
+        << "\n"
+           "Quoted keywords like \"" << argument.GetValue() << "\" "
+           "will no longer be interpreted as keywords "
            "when the policy is set to NEW.  "
            "Since the policy is not set the OLD behavior will be used.";
+      // clang-format on
 
       this->Makefile.GetCMakeInstance()->IssueMessage(
         MessageType::AUTHOR_WARNING, e.str(), this->Backtrace);
@@ -208,15 +367,7 @@
 bool cmConditionEvaluator::GetBooleanValue(
   cmExpandedCommandArgument& arg) const
 {
-  // Check basic constants.
-  if (arg == "0") {
-    return false;
-  }
-  if (arg == "1") {
-    return true;
-  }
-
-  // Check named constants.
+  // Check basic and named constants.
   if (cmIsOn(arg.GetValue())) {
     return true;
   }
@@ -227,7 +378,7 @@
   // Check for numbers.
   if (!arg.empty()) {
     char* end;
-    double d = strtod(arg.GetValue().c_str(), &end);
+    const double d = std::strtod(arg.GetValue().c_str(), &end);
     if (*end == '\0') {
       // The whole string is a number.  Use C conversion to bool.
       return static_cast<bool>(d);
@@ -235,14 +386,14 @@
   }
 
   // Check definition.
-  cmProp def = this->GetDefinitionIfUnquoted(arg);
+  cmValue def = this->GetDefinitionIfUnquoted(arg);
   return !cmIsOff(def);
 }
 
 //=========================================================================
 // Boolean value behavior from CMake 2.6.4 and below.
 bool cmConditionEvaluator::GetBooleanValueOld(
-  cmExpandedCommandArgument const& arg, bool one) const
+  cmExpandedCommandArgument const& arg, bool const one) const
 {
   if (one) {
     // Old IsTrue behavior for single argument.
@@ -252,13 +403,13 @@
     if (arg == "1") {
       return true;
     }
-    cmProp def = this->GetDefinitionIfUnquoted(arg);
+    cmValue def = this->GetDefinitionIfUnquoted(arg);
     return !cmIsOff(def);
   }
   // Old GetVariableOrNumber behavior.
-  cmProp def = this->GetDefinitionIfUnquoted(arg);
-  if (!def && atoi(arg.GetValue().c_str())) {
-    def = &arg.GetValue();
+  cmValue def = this->GetDefinitionIfUnquoted(arg);
+  if (!def && std::atoi(arg.GetValue().c_str())) {
+    def = cmValue(arg.GetValue());
   }
   return !cmIsOff(def);
 }
@@ -267,7 +418,7 @@
 // returns the resulting boolean value
 bool cmConditionEvaluator::GetBooleanValueWithAutoDereference(
   cmExpandedCommandArgument& newArg, std::string& errorString,
-  MessageType& status, bool oneArg) const
+  MessageType& status, bool const oneArg) const
 {
   // Use the policy if it is set.
   if (this->Policy12Status == cmPolicies::NEW) {
@@ -278,8 +429,8 @@
   }
 
   // Check policy only if old and new results differ.
-  bool newResult = this->GetBooleanValue(newArg);
-  bool oldResult = this->GetBooleanValueOld(newArg, oneArg);
+  const auto newResult = this->GetBooleanValue(newArg);
+  const auto oldResult = this->GetBooleanValueOld(newArg, oneArg);
   if (newResult != oldResult) {
     switch (this->Policy12Status) {
       case cmPolicies::WARN:
@@ -296,6 +447,7 @@
           "\" appears in a conditional statement.  " +
           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0012);
         status = MessageType::FATAL_ERROR;
+        break;
       }
       case cmPolicies::NEW:
         break;
@@ -304,56 +456,31 @@
   return newResult;
 }
 
-//=========================================================================
-void cmConditionEvaluator::IncrementArguments(
-  cmArgumentList& newArgs, cmArgumentList::iterator& argP1,
-  cmArgumentList::iterator& argP2) const
+template <int N>
+inline int cmConditionEvaluator::matchKeysImpl(
+  const cmExpandedCommandArgument&)
 {
-  if (argP1 != newArgs.end()) {
-    argP1++;
-    argP2 = argP1;
-    if (argP1 != newArgs.end()) {
-      argP2++;
-    }
-  }
+  // Zero means "not found"
+  return 0;
 }
 
-//=========================================================================
-// helper function to reduce code duplication
-void cmConditionEvaluator::HandlePredicate(
-  bool value, int& reducible, cmArgumentList::iterator& arg,
-  cmArgumentList& newArgs, cmArgumentList::iterator& argP1,
-  cmArgumentList::iterator& argP2) const
+template <int N, typename T, typename... Keys>
+inline int cmConditionEvaluator::matchKeysImpl(
+  const cmExpandedCommandArgument& arg, T current, Keys... key)
 {
-  if (value) {
-    *arg = cmExpandedCommandArgument("1", true);
-  } else {
-    *arg = cmExpandedCommandArgument("0", true);
+  if (this->IsKeyword(current, arg)) {
+    // Stop searching as soon as smth has found
+    return N;
   }
-  newArgs.erase(argP1);
-  argP1 = arg;
-  this->IncrementArguments(newArgs, argP1, argP2);
-  reducible = 1;
+  return matchKeysImpl<N + 1>(arg, key...);
 }
 
-//=========================================================================
-// helper function to reduce code duplication
-void cmConditionEvaluator::HandleBinaryOp(bool value, int& reducible,
-                                          cmArgumentList::iterator& arg,
-                                          cmArgumentList& newArgs,
-                                          cmArgumentList::iterator& argP1,
-                                          cmArgumentList::iterator& argP2)
+template <typename... Keys>
+inline int cmConditionEvaluator::matchKeys(
+  const cmExpandedCommandArgument& arg, Keys... key)
 {
-  if (value) {
-    *arg = cmExpandedCommandArgument("1", true);
-  } else {
-    *arg = cmExpandedCommandArgument("0", true);
-  }
-  newArgs.erase(argP2);
-  newArgs.erase(argP1);
-  argP1 = arg;
-  this->IncrementArguments(newArgs, argP1, argP2);
-  reducible = 1;
+  // Get index of the matched key (1-based)
+  return matchKeysImpl<1>(arg, key...);
 }
 
 //=========================================================================
@@ -362,55 +489,35 @@
                                         std::string& errorString,
                                         MessageType& status)
 {
-  int reducible;
-  do {
-    reducible = 0;
-    auto arg = newArgs.begin();
-    while (arg != newArgs.end()) {
-      if (this->IsKeyword(keyParenL, *arg)) {
-        // search for the closing paren for this opening one
-        cmArgumentList::iterator argClose;
-        argClose = arg;
-        argClose++;
-        unsigned int depth = 1;
-        while (argClose != newArgs.end() && depth) {
-          if (this->IsKeyword(keyParenL, *argClose)) {
-            depth++;
-          }
-          if (this->IsKeyword(keyParenR, *argClose)) {
-            depth--;
-          }
-          argClose++;
-        }
-        if (depth) {
-          errorString = "mismatched parenthesis in condition";
-          status = MessageType::FATAL_ERROR;
-          return false;
-        }
-        // store the reduced args in this vector
-        std::vector<cmExpandedCommandArgument> newArgs2;
-
-        // copy to the list structure
-        auto argP1 = arg;
-        argP1++;
-        cm::append(newArgs2, argP1, argClose);
-        newArgs2.pop_back();
-        // now recursively invoke IsTrue to handle the values inside the
-        // parenthetical expression
-        bool value = this->IsTrue(newArgs2, errorString, status);
-        if (value) {
-          *arg = cmExpandedCommandArgument("1", true);
-        } else {
-          *arg = cmExpandedCommandArgument("0", true);
-        }
-        argP1 = arg;
-        argP1++;
-        // remove the now evaluated parenthetical expression
-        newArgs.erase(argP1, argClose);
+  for (auto arg = newArgs.begin(); arg != newArgs.end(); ++arg) {
+    if (this->IsKeyword(keyParenL, *arg)) {
+      // search for the closing paren for this opening one
+      auto depth = 1;
+      auto argClose = std::next(arg);
+      for (; argClose != newArgs.end() && depth; ++argClose) {
+        depth += int(this->IsKeyword(keyParenL, *argClose)) -
+          int(this->IsKeyword(keyParenR, *argClose));
       }
-      ++arg;
+      if (depth) {
+        errorString = "mismatched parenthesis in condition";
+        status = MessageType::FATAL_ERROR;
+        return false;
+      }
+
+      // store the reduced args in this vector
+      auto argOpen = std::next(arg);
+      const std::vector<cmExpandedCommandArgument> subExpr(
+        argOpen, std::prev(argClose));
+
+      // now recursively invoke IsTrue to handle the values inside the
+      // parenthetical expression
+      const auto value = this->IsTrue(subExpr, errorString, status);
+      *arg = cmExpandedCommandArgument(bool2string(value), true);
+      argOpen = std::next(arg);
+      // remove the now evaluated parenthetical expression
+      newArgs.erase(argOpen, argClose);
     }
-  } while (reducible);
+  }
   return true;
 }
 
@@ -419,96 +526,104 @@
 bool cmConditionEvaluator::HandleLevel1(cmArgumentList& newArgs, std::string&,
                                         MessageType&)
 {
-  int reducible;
-  do {
-    reducible = 0;
-    auto arg = newArgs.begin();
-    cmArgumentList::iterator argP1;
-    cmArgumentList::iterator argP2;
-    while (arg != newArgs.end()) {
-      argP1 = arg;
-      this->IncrementArguments(newArgs, argP1, argP2);
-      // does a file exist
-      if (this->IsKeyword(keyEXISTS, *arg) && argP1 != newArgs.end()) {
-        this->HandlePredicate(cmSystemTools::FileExists(argP1->GetValue()),
-                              reducible, arg, newArgs, argP1, argP2);
-      }
-      // does a directory with this name exist
-      if (this->IsKeyword(keyIS_DIRECTORY, *arg) && argP1 != newArgs.end()) {
-        this->HandlePredicate(
-          cmSystemTools::FileIsDirectory(argP1->GetValue()), reducible, arg,
-          newArgs, argP1, argP2);
-      }
-      // does a symlink with this name exist
-      if (this->IsKeyword(keyIS_SYMLINK, *arg) && argP1 != newArgs.end()) {
-        this->HandlePredicate(cmSystemTools::FileIsSymlink(argP1->GetValue()),
-                              reducible, arg, newArgs, argP1, argP2);
-      }
-      // is the given path an absolute path ?
-      if (this->IsKeyword(keyIS_ABSOLUTE, *arg) && argP1 != newArgs.end()) {
-        this->HandlePredicate(cmSystemTools::FileIsFullPath(argP1->GetValue()),
-                              reducible, arg, newArgs, argP1, argP2);
-      }
-      // does a command exist
-      if (this->IsKeyword(keyCOMMAND, *arg) && argP1 != newArgs.end()) {
-        cmState::Command command =
-          this->Makefile.GetState()->GetCommand(argP1->GetValue());
-        this->HandlePredicate(command != nullptr, reducible, arg, newArgs,
-                              argP1, argP2);
-      }
-      // does a policy exist
-      if (this->IsKeyword(keyPOLICY, *arg) && argP1 != newArgs.end()) {
-        cmPolicies::PolicyID pid;
-        this->HandlePredicate(
-          cmPolicies::GetPolicyID(argP1->GetValue().c_str(), pid), reducible,
-          arg, newArgs, argP1, argP2);
-      }
-      // does a target exist
-      if (this->IsKeyword(keyTARGET, *arg) && argP1 != newArgs.end()) {
-        this->HandlePredicate(
-          this->Makefile.FindTargetToUse(argP1->GetValue()) != nullptr,
-          reducible, arg, newArgs, argP1, argP2);
-      }
-      // does a test exist
-      if (this->Policy64Status != cmPolicies::OLD &&
-          this->Policy64Status != cmPolicies::WARN) {
-        if (this->IsKeyword(keyTEST, *arg) && argP1 != newArgs.end()) {
-          const cmTest* haveTest = this->Makefile.GetTest(argP1->GetValue());
-          this->HandlePredicate(haveTest != nullptr, reducible, arg, newArgs,
-                                argP1, argP2);
-        }
-      } else if (this->Policy64Status == cmPolicies::WARN &&
-                 this->IsKeyword(keyTEST, *arg)) {
+  const auto policy64IsOld = this->Policy64Status == cmPolicies::OLD ||
+    this->Policy64Status == cmPolicies::WARN;
+
+  for (auto args = newArgs.make2ArgsIterator(); args.current != newArgs.end();
+       args.advance(newArgs)) {
+
+    auto policyCheck = [&, this](const cmPolicies::PolicyID id,
+                                 const cmPolicies::PolicyStatus status,
+                                 const cm::static_string_view kw) {
+      if (status == cmPolicies::WARN && this->IsKeyword(kw, *args.current)) {
         std::ostringstream e;
-        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0064) << "\n";
-        e << "TEST will be interpreted as an operator "
+        e << cmPolicies::GetPolicyWarning(id) << "\n"
+          << kw
+          << " will be interpreted as an operator "
              "when the policy is set to NEW.  "
              "Since the policy is not set the OLD behavior will be used.";
 
         this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
       }
-      // is a variable defined
-      if (this->IsKeyword(keyDEFINED, *arg) && argP1 != newArgs.end()) {
-        size_t argP1len = argP1->GetValue().size();
-        bool bdef = false;
-        if (argP1len > 4 && cmHasLiteralPrefix(argP1->GetValue(), "ENV{") &&
-            argP1->GetValue().operator[](argP1len - 1) == '}') {
-          std::string env = argP1->GetValue().substr(4, argP1len - 5);
-          bdef = cmSystemTools::HasEnv(env);
-        } else if (argP1len > 6 &&
-                   cmHasLiteralPrefix(argP1->GetValue(), "CACHE{") &&
-                   argP1->GetValue().operator[](argP1len - 1) == '}') {
-          std::string cache = argP1->GetValue().substr(6, argP1len - 7);
-          bdef =
-            this->Makefile.GetState()->GetCacheEntryValue(cache) != nullptr;
-        } else {
-          bdef = this->Makefile.IsDefinitionSet(argP1->GetValue());
-        }
-        this->HandlePredicate(bdef, reducible, arg, newArgs, argP1, argP2);
-      }
-      ++arg;
+    };
+
+    // NOTE Checking policies for warnings are not require an access to the
+    // next arg. Check them first!
+    policyCheck(cmPolicies::CMP0064, this->Policy64Status, keyTEST);
+
+    // NOTE Fail fast: All the predicates below require the next arg to be
+    // valid
+    if (args.next == newArgs.end()) {
+      continue;
     }
-  } while (reducible);
+
+    // does a file exist
+    if (this->IsKeyword(keyEXISTS, *args.current)) {
+      newArgs.ReduceOneArg(cmSystemTools::FileExists(args.next->GetValue()),
+                           args);
+    }
+    // does a directory with this name exist
+    else if (this->IsKeyword(keyIS_DIRECTORY, *args.current)) {
+      newArgs.ReduceOneArg(
+        cmSystemTools::FileIsDirectory(args.next->GetValue()), args);
+    }
+    // does a symlink with this name exist
+    else if (this->IsKeyword(keyIS_SYMLINK, *args.current)) {
+      newArgs.ReduceOneArg(cmSystemTools::FileIsSymlink(args.next->GetValue()),
+                           args);
+    }
+    // is the given path an absolute path ?
+    else if (this->IsKeyword(keyIS_ABSOLUTE, *args.current)) {
+      newArgs.ReduceOneArg(
+        cmSystemTools::FileIsFullPath(args.next->GetValue()), args);
+    }
+    // does a command exist
+    else if (this->IsKeyword(keyCOMMAND, *args.current)) {
+      newArgs.ReduceOneArg(
+        bool(this->Makefile.GetState()->GetCommand(args.next->GetValue())),
+        args);
+    }
+    // does a policy exist
+    else if (this->IsKeyword(keyPOLICY, *args.current)) {
+      cmPolicies::PolicyID pid;
+      newArgs.ReduceOneArg(
+        cmPolicies::GetPolicyID(args.next->GetValue().c_str(), pid), args);
+    }
+    // does a target exist
+    else if (this->IsKeyword(keyTARGET, *args.current)) {
+      newArgs.ReduceOneArg(
+        bool(this->Makefile.FindTargetToUse(args.next->GetValue())), args);
+    }
+    // is a variable defined
+    else if (this->IsKeyword(keyDEFINED, *args.current)) {
+      const auto& var = args.next->GetValue();
+      const auto varNameLen = var.size();
+
+      auto result = false;
+      if (looksLikeSpecialVariable(var, "ENV"_s, varNameLen)) {
+        const auto env = args.next->GetValue().substr(4, varNameLen - 5);
+        result = cmSystemTools::HasEnv(env);
+      }
+
+      else if (looksLikeSpecialVariable(var, "CACHE"_s, varNameLen)) {
+        const auto cache = args.next->GetValue().substr(6, varNameLen - 7);
+        result = bool(this->Makefile.GetState()->GetCacheEntryValue(cache));
+      }
+
+      else {
+        result = this->Makefile.IsDefinitionSet(args.next->GetValue());
+      }
+      newArgs.ReduceOneArg(result, args);
+    }
+    // does a test exist
+    else if (this->IsKeyword(keyTEST, *args.current)) {
+      if (policy64IsOld) {
+        continue;
+      }
+      newArgs.ReduceOneArg(bool(this->Makefile.GetTest(args.next->GetValue())),
+                           args);
+    }
+  }
   return true;
 }
 
@@ -518,178 +633,142 @@
                                         std::string& errorString,
                                         MessageType& status)
 {
-  int reducible;
-  std::string def_buf;
-  cmProp def;
-  cmProp def2;
-  do {
-    reducible = 0;
-    auto arg = newArgs.begin();
-    cmArgumentList::iterator argP1;
-    cmArgumentList::iterator argP2;
-    while (arg != newArgs.end()) {
-      argP1 = arg;
-      this->IncrementArguments(newArgs, argP1, argP2);
-      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          this->IsKeyword(keyMATCHES, *argP1)) {
-        def = this->GetDefinitionIfUnquoted(*arg);
-        if (!def) {
-          def = &arg->GetValue();
-        } else if (cmHasLiteralPrefix(arg->GetValue(), "CMAKE_MATCH_")) {
-          // The string to match is owned by our match result variables.
-          // Move it to our own buffer before clearing them.
-          def_buf = *def;
-          def = &def_buf;
-        }
-        const std::string& rex = argP2->GetValue();
-        this->Makefile.ClearMatches();
-        cmsys::RegularExpression regEntry;
-        if (!regEntry.compile(rex)) {
-          std::ostringstream error;
-          error << "Regular expression \"" << rex << "\" cannot compile";
-          errorString = error.str();
-          status = MessageType::FATAL_ERROR;
-          return false;
-        }
-        if (regEntry.find(*def)) {
-          this->Makefile.StoreMatches(regEntry);
-          *arg = cmExpandedCommandArgument("1", true);
-        } else {
-          *arg = cmExpandedCommandArgument("0", true);
-        }
-        newArgs.erase(argP2);
-        newArgs.erase(argP1);
-        argP1 = arg;
-        this->IncrementArguments(newArgs, argP1, argP2);
-        reducible = 1;
-      }
+  for (auto args = newArgs.make3ArgsIterator(); args.current != newArgs.end();
+       args.advance(newArgs)) {
 
-      if (argP1 != newArgs.end() && this->IsKeyword(keyMATCHES, *arg)) {
-        *arg = cmExpandedCommandArgument("0", true);
-        newArgs.erase(argP1);
-        argP1 = arg;
-        this->IncrementArguments(newArgs, argP1, argP2);
-        reducible = 1;
-      }
+    int matchNo;
 
-      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          (this->IsKeyword(keyLESS, *argP1) ||
-           this->IsKeyword(keyLESS_EQUAL, *argP1) ||
-           this->IsKeyword(keyGREATER, *argP1) ||
-           this->IsKeyword(keyGREATER_EQUAL, *argP1) ||
-           this->IsKeyword(keyEQUAL, *argP1))) {
-        def = this->GetVariableOrString(*arg);
-        def2 = this->GetVariableOrString(*argP2);
-        double lhs;
-        double rhs;
-        bool result;
-        if (sscanf(def->c_str(), "%lg", &lhs) != 1 ||
-            sscanf(def2->c_str(), "%lg", &rhs) != 1) {
-          result = false;
-        } else if (*(argP1) == keyLESS) {
-          result = (lhs < rhs);
-        } else if (*(argP1) == keyLESS_EQUAL) {
-          result = (lhs <= rhs);
-        } else if (*(argP1) == keyGREATER) {
-          result = (lhs > rhs);
-        } else if (*(argP1) == keyGREATER_EQUAL) {
-          result = (lhs >= rhs);
-        } else {
-          result = (lhs == rhs);
-        }
-        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
-      }
-
-      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          (this->IsKeyword(keySTRLESS, *argP1) ||
-           this->IsKeyword(keySTRLESS_EQUAL, *argP1) ||
-           this->IsKeyword(keySTRGREATER, *argP1) ||
-           this->IsKeyword(keySTRGREATER_EQUAL, *argP1) ||
-           this->IsKeyword(keySTREQUAL, *argP1))) {
-        def = this->GetVariableOrString(*arg);
-        def2 = this->GetVariableOrString(*argP2);
-        int val = (*def).compare(*def2);
-        bool result;
-        if (*(argP1) == keySTRLESS) {
-          result = (val < 0);
-        } else if (*(argP1) == keySTRLESS_EQUAL) {
-          result = (val <= 0);
-        } else if (*(argP1) == keySTRGREATER) {
-          result = (val > 0);
-        } else if (*(argP1) == keySTRGREATER_EQUAL) {
-          result = (val >= 0);
-        } else // strequal
-        {
-          result = (val == 0);
-        }
-        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
-      }
-
-      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          (this->IsKeyword(keyVERSION_LESS, *argP1) ||
-           this->IsKeyword(keyVERSION_LESS_EQUAL, *argP1) ||
-           this->IsKeyword(keyVERSION_GREATER, *argP1) ||
-           this->IsKeyword(keyVERSION_GREATER_EQUAL, *argP1) ||
-           this->IsKeyword(keyVERSION_EQUAL, *argP1))) {
-        def = this->GetVariableOrString(*arg);
-        def2 = this->GetVariableOrString(*argP2);
-        cmSystemTools::CompareOp op;
-        if (*argP1 == keyVERSION_LESS) {
-          op = cmSystemTools::OP_LESS;
-        } else if (*argP1 == keyVERSION_LESS_EQUAL) {
-          op = cmSystemTools::OP_LESS_EQUAL;
-        } else if (*argP1 == keyVERSION_GREATER) {
-          op = cmSystemTools::OP_GREATER;
-        } else if (*argP1 == keyVERSION_GREATER_EQUAL) {
-          op = cmSystemTools::OP_GREATER_EQUAL;
-        } else { // version_equal
-          op = cmSystemTools::OP_EQUAL;
-        }
-        bool result =
-          cmSystemTools::VersionCompare(op, def->c_str(), def2->c_str());
-        this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
-      }
-
-      // is file A newer than file B
-      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          this->IsKeyword(keyIS_NEWER_THAN, *argP1)) {
-        int fileIsNewer = 0;
-        cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
-          arg->GetValue(), (argP2)->GetValue(), &fileIsNewer);
-        this->HandleBinaryOp(
-          (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), reducible, arg,
-          newArgs, argP1, argP2);
-      }
-
-      if (argP1 != newArgs.end() && argP2 != newArgs.end() &&
-          this->IsKeyword(keyIN_LIST, *argP1)) {
-        if (this->Policy57Status != cmPolicies::OLD &&
-            this->Policy57Status != cmPolicies::WARN) {
-          bool result = false;
-
-          def = this->GetVariableOrString(*arg);
-          def2 = this->Makefile.GetDefinition(argP2->GetValue());
-
-          if (def2) {
-            std::vector<std::string> list = cmExpandedList(*def2, true);
-            result = cm::contains(list, *def);
-          }
-
-          this->HandleBinaryOp(result, reducible, arg, newArgs, argP1, argP2);
-        } else if (this->Policy57Status == cmPolicies::WARN) {
-          std::ostringstream e;
-          e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0057) << "\n";
-          e << "IN_LIST will be interpreted as an operator "
-               "when the policy is set to NEW.  "
-               "Since the policy is not set the OLD behavior will be used.";
-
-          this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
-        }
-      }
-
-      ++arg;
+    // NOTE Handle special case `if(... BLAH_BLAH MATCHES)`
+    // (i.e., w/o regex to match which is possibly result of
+    // variable expansion to an empty string)
+    if (args.next != newArgs.end() &&
+        this->IsKeyword(keyMATCHES, *args.current)) {
+      newArgs.ReduceOneArg(false, args);
     }
-  } while (reducible);
+
+    // NOTE Fail fast: All the binary ops below require 2 arguments.
+    else if (args.next == newArgs.end() || args.nextnext == newArgs.end()) {
+      continue;
+    }
+
+    else if (this->IsKeyword(keyMATCHES, *args.next)) {
+      cmValue def = this->GetDefinitionIfUnquoted(*args.current);
+
+      std::string def_buf;
+      if (!def) {
+        def = cmValue(args.current->GetValue());
+      } else if (cmHasLiteralPrefix(args.current->GetValue(),
+                                    "CMAKE_MATCH_")) {
+        // The string to match is owned by our match result variables.
+        // Move it to our own buffer before clearing them.
+        def_buf = *def;
+        def = cmValue(def_buf);
+      }
+
+      this->Makefile.ClearMatches();
+
+      const auto& rex = args.nextnext->GetValue();
+      cmsys::RegularExpression regEntry;
+      if (!regEntry.compile(rex)) {
+        std::ostringstream error;
+        error << "Regular expression \"" << rex << "\" cannot compile";
+        errorString = error.str();
+        status = MessageType::FATAL_ERROR;
+        return false;
+      }
+
+      const auto match = regEntry.find(*def);
+      if (match) {
+        this->Makefile.StoreMatches(regEntry);
+      }
+      newArgs.ReduceTwoArgs(match, args);
+    }
+
+    else if ((matchNo =
+                this->matchKeys(*args.next, keyLESS, keyLESS_EQUAL, keyGREATER,
+                                keyGREATER_EQUAL, keyEQUAL))) {
+
+      cmValue ldef = this->GetVariableOrString(*args.current);
+      cmValue rdef = this->GetVariableOrString(*args.nextnext);
+
+      double lhs;
+      double rhs;
+      auto parseDoubles = [&]() {
+        return std::sscanf(ldef->c_str(), "%lg", &lhs) == 1 &&
+          std::sscanf(rdef->c_str(), "%lg", &rhs) == 1;
+      };
+      // clang-format off
+      const auto result = parseDoubles() &&
+        cmRt2CtSelector<
+            std::less, std::less_equal,
+            std::greater, std::greater_equal,
+            std::equal_to
+          >::eval(matchNo, lhs, rhs);
+      // clang-format on
+      newArgs.ReduceTwoArgs(result, args);
+    }
+
+    else if ((matchNo = this->matchKeys(*args.next, keySTRLESS,
+                                        keySTRLESS_EQUAL, keySTRGREATER,
+                                        keySTRGREATER_EQUAL, keySTREQUAL))) {
+
+      const cmValue lhs = this->GetVariableOrString(*args.current);
+      const cmValue rhs = this->GetVariableOrString(*args.nextnext);
+      const auto val = (*lhs).compare(*rhs);
+      // clang-format off
+      const auto result = cmRt2CtSelector<
+            std::less, std::less_equal,
+            std::greater, std::greater_equal,
+            std::equal_to
+          >::eval(matchNo, val, 0);
+      // clang-format on
+      newArgs.ReduceTwoArgs(result, args);
+    }
+
+    else if ((matchNo =
+                this->matchKeys(*args.next, keyVERSION_LESS,
+                                keyVERSION_LESS_EQUAL, keyVERSION_GREATER,
+                                keyVERSION_GREATER_EQUAL, keyVERSION_EQUAL))) {
+      const auto op = MATCH2CMPOP[matchNo - 1];
+      const std::string& lhs = this->GetVariableOrString(*args.current);
+      const std::string& rhs = this->GetVariableOrString(*args.nextnext);
+      const auto result = cmSystemTools::VersionCompare(op, lhs, rhs);
+      newArgs.ReduceTwoArgs(result, args);
+    }
+
+    // is file A newer than file B
+    else if (this->IsKeyword(keyIS_NEWER_THAN, *args.next)) {
+      auto fileIsNewer = 0;
+      cmsys::Status ftcStatus = cmSystemTools::FileTimeCompare(
+        args.current->GetValue(), args.nextnext->GetValue(), &fileIsNewer);
+      newArgs.ReduceTwoArgs(
+        (!ftcStatus || fileIsNewer == 1 || fileIsNewer == 0), args);
+    }
+
+    else if (this->IsKeyword(keyIN_LIST, *args.next)) {
+
+      if (this->Policy57Status != cmPolicies::OLD &&
+          this->Policy57Status != cmPolicies::WARN) {
+
+        cmValue lhs = this->GetVariableOrString(*args.current);
+        cmValue rhs = this->Makefile.GetDefinition(args.nextnext->GetValue());
+
+        newArgs.ReduceTwoArgs(
+          rhs && cm::contains(cmExpandedList(*rhs, true), *lhs), args);
+      }
+
+      else if (this->Policy57Status == cmPolicies::WARN) {
+        std::ostringstream e;
+        e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0057)
+          << "\n"
+             "IN_LIST will be interpreted as an operator "
+             "when the policy is set to NEW.  "
+             "Since the policy is not set the OLD behavior will be used.";
+
+        this->Makefile.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+      }
+    }
+  }
   return true;
 }
 
@@ -699,23 +778,14 @@
                                         std::string& errorString,
                                         MessageType& status)
 {
-  int reducible;
-  do {
-    reducible = 0;
-    auto arg = newArgs.begin();
-    cmArgumentList::iterator argP1;
-    cmArgumentList::iterator argP2;
-    while (arg != newArgs.end()) {
-      argP1 = arg;
-      this->IncrementArguments(newArgs, argP1, argP2);
-      if (argP1 != newArgs.end() && this->IsKeyword(keyNOT, *arg)) {
-        bool rhs = this->GetBooleanValueWithAutoDereference(
-          *argP1, errorString, status);
-        this->HandlePredicate(!rhs, reducible, arg, newArgs, argP1, argP2);
-      }
-      ++arg;
+  for (auto args = newArgs.make2ArgsIterator(); args.next != newArgs.end();
+       args.advance(newArgs)) {
+    if (this->IsKeyword(keyNOT, *args.current)) {
+      const auto rhs = this->GetBooleanValueWithAutoDereference(
+        *args.next, errorString, status);
+      newArgs.ReduceOneArg(!rhs, args);
     }
-  } while (reducible);
+  }
   return true;
 }
 
@@ -725,38 +795,24 @@
                                         std::string& errorString,
                                         MessageType& status)
 {
-  int reducible;
-  bool lhs;
-  bool rhs;
-  do {
-    reducible = 0;
-    auto arg = newArgs.begin();
-    cmArgumentList::iterator argP1;
-    cmArgumentList::iterator argP2;
-    while (arg != newArgs.end()) {
-      argP1 = arg;
-      this->IncrementArguments(newArgs, argP1, argP2);
-      if (argP1 != newArgs.end() && this->IsKeyword(keyAND, *argP1) &&
-          argP2 != newArgs.end()) {
-        lhs =
-          this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
-        rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
-                                                       status);
-        this->HandleBinaryOp((lhs && rhs), reducible, arg, newArgs, argP1,
-                             argP2);
-      }
+  for (auto args = newArgs.make3ArgsIterator(); args.nextnext != newArgs.end();
+       args.advance(newArgs)) {
 
-      if (argP1 != newArgs.end() && this->IsKeyword(keyOR, *argP1) &&
-          argP2 != newArgs.end()) {
-        lhs =
-          this->GetBooleanValueWithAutoDereference(*arg, errorString, status);
-        rhs = this->GetBooleanValueWithAutoDereference(*argP2, errorString,
-                                                       status);
-        this->HandleBinaryOp((lhs || rhs), reducible, arg, newArgs, argP1,
-                             argP2);
-      }
-      ++arg;
+    int matchNo;
+
+    if ((matchNo = this->matchKeys(*args.next, keyAND, keyOR))) {
+      const auto lhs = this->GetBooleanValueWithAutoDereference(
+        *args.current, errorString, status);
+      const auto rhs = this->GetBooleanValueWithAutoDereference(
+        *args.nextnext, errorString, status);
+      // clang-format off
+      const auto result =
+        cmRt2CtSelector<
+            std::logical_and, std::logical_or
+          >::eval(matchNo, lhs, rhs);
+      // clang-format on
+      newArgs.ReduceTwoArgs(result, args);
     }
-  } while (reducible);
+  }
   return true;
 }
diff --git a/Source/cmConditionEvaluator.h b/Source/cmConditionEvaluator.h
index cf00ede..37b7825 100644
--- a/Source/cmConditionEvaluator.h
+++ b/Source/cmConditionEvaluator.h
@@ -4,23 +4,22 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <list>
 #include <string>
 #include <vector>
 
-#include "cmExpandedCommandArgument.h"
+#include <cmext/string_view>
+
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
+#include "cmValue.h"
 
+class cmExpandedCommandArgument;
 class cmMakefile;
 
 class cmConditionEvaluator
 {
 public:
-  using cmArgumentList = std::list<cmExpandedCommandArgument>;
-
   cmConditionEvaluator(cmMakefile& makefile, cmListFileBacktrace bt);
 
   // this is a shared function for both If and Else to determine if the
@@ -30,14 +29,16 @@
               std::string& errorString, MessageType& status);
 
 private:
+  class cmArgumentList;
+
   // Filter the given variable definition based on policy CMP0054.
-  cmProp GetDefinitionIfUnquoted(
+  cmValue GetDefinitionIfUnquoted(
     const cmExpandedCommandArgument& argument) const;
 
-  cmProp GetVariableOrString(const cmExpandedCommandArgument& argument) const;
+  cmValue GetVariableOrString(const cmExpandedCommandArgument& argument) const;
 
-  bool IsKeyword(std::string const& keyword,
-                 cmExpandedCommandArgument& argument) const;
+  bool IsKeyword(cm::static_string_view keyword,
+                 const cmExpandedCommandArgument& argument) const;
 
   bool GetBooleanValue(cmExpandedCommandArgument& arg) const;
 
@@ -49,19 +50,14 @@
                                           MessageType& status,
                                           bool oneArg = false) const;
 
-  void IncrementArguments(cmArgumentList& newArgs,
-                          cmArgumentList::iterator& argP1,
-                          cmArgumentList::iterator& argP2) const;
+  template <int N>
+  int matchKeysImpl(const cmExpandedCommandArgument&);
 
-  void HandlePredicate(bool value, int& reducible,
-                       cmArgumentList::iterator& arg, cmArgumentList& newArgs,
-                       cmArgumentList::iterator& argP1,
-                       cmArgumentList::iterator& argP2) const;
+  template <int N, typename T, typename... Keys>
+  int matchKeysImpl(const cmExpandedCommandArgument&, T, Keys...);
 
-  void HandleBinaryOp(bool value, int& reducible,
-                      cmArgumentList::iterator& arg, cmArgumentList& newArgs,
-                      cmArgumentList::iterator& argP1,
-                      cmArgumentList::iterator& argP2);
+  template <typename... Keys>
+  int matchKeys(const cmExpandedCommandArgument&, Keys...);
 
   bool HandleLevel0(cmArgumentList& newArgs, std::string& errorString,
                     MessageType& status);
diff --git a/Source/cmConfigure.cmake.h.in b/Source/cmConfigure.cmake.h.in
index aeca6b4..6a419f6 100644
--- a/Source/cmConfigure.cmake.h.in
+++ b/Source/cmConfigure.cmake.h.in
@@ -16,7 +16,6 @@
 
 #cmakedefine HAVE_ENVIRON_NOT_REQUIRE_PROTOTYPE
 #cmakedefine HAVE_UNSETENV
-#cmakedefine CMake_USE_ELF_PARSER
 #cmakedefine CMake_USE_MACH_PARSER
 #cmakedefine CMake_USE_XCOFF_PARSER
 #define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index bf18143..971c86e 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -19,11 +19,11 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -246,7 +246,7 @@
   this->SrcFileSignature = true;
 
   cmStateEnums::TargetType targetType = cmStateEnums::EXECUTABLE;
-  cmProp tt = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE");
+  cmValue tt = this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_TARGET_TYPE");
   if (!isTryRun && cmNonempty(tt)) {
     if (*tt == cmState::GetTargetTypeName(cmStateEnums::EXECUTABLE)) {
       targetType = cmStateEnums::EXECUTABLE;
@@ -540,7 +540,7 @@
       return -1;
     }
 
-    cmProp def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
+    cmValue def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
     fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
             cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
             cmVersion::GetPatchVersion(), cmVersion::GetTweakVersion());
@@ -549,7 +549,7 @@
     }
 
     /* Set MSVC runtime library policy to match our selection.  */
-    if (cmProp msvcRuntimeLibraryDefault =
+    if (cmValue msvcRuntimeLibraryDefault =
           this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
       fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
               !msvcRuntimeLibraryDefault->empty() ? "NEW" : "OLD");
@@ -564,7 +564,7 @@
     }
 
     /* Set ARMClang cpu/arch policy to match outer project.  */
-    if (cmProp cmp0123 =
+    if (cmValue cmp0123 =
           this->Makefile->GetDefinition(kCMAKE_ARMClang_CMP0123)) {
       fprintf(fout, "cmake_policy(SET CMP0123 %s)\n",
               *cmp0123 == "NEW"_s ? "NEW" : "OLD");
@@ -582,11 +582,11 @@
       projectLangs += " " + li;
       std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
       std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
-      if (cmProp rulesOverridePath =
+      if (cmValue rulesOverridePath =
             this->Makefile->GetDefinition(rulesOverrideLang)) {
         fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
                 rulesOverridePath->c_str());
-      } else if (cmProp rulesOverridePath2 =
+      } else if (cmValue rulesOverridePath2 =
                    this->Makefile->GetDefinition(rulesOverrideBase)) {
         fprintf(fout, "set(%s \"%s\")\n", rulesOverrideBase.c_str(),
                 rulesOverridePath2->c_str());
@@ -604,9 +604,9 @@
     fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
     for (std::string const& li : testLangs) {
       std::string langFlags = "CMAKE_" + li + "_FLAGS";
-      cmProp flags = this->Makefile->GetDefinition(langFlags);
+      cmValue flags = this->Makefile->GetDefinition(langFlags);
       fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
-              cmOutputConverter::EscapeForCMake(cmToCStrSafe(flags)).c_str());
+              cmOutputConverter::EscapeForCMake(*flags).c_str());
       fprintf(fout,
               "set(CMAKE_%s_FLAGS \"${CMAKE_%s_FLAGS}"
               " ${COMPILE_DEFINITIONS}\")\n",
@@ -626,6 +626,7 @@
           /* clang-format on */
           this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
         }
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior is to do nothing.
         break;
@@ -643,10 +644,9 @@
         for (std::string const& li : testLangs) {
           std::string const langFlagsCfg =
             cmStrCat("CMAKE_", li, "_FLAGS_", cfg);
-          cmProp flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
-          fprintf(
-            fout, "set(%s %s)\n", langFlagsCfg.c_str(),
-            cmOutputConverter::EscapeForCMake(cmToCStrSafe(flagsCfg)).c_str());
+          cmValue flagsCfg = this->Makefile->GetDefinition(langFlagsCfg);
+          fprintf(fout, "set(%s %s)\n", langFlagsCfg.c_str(),
+                  cmOutputConverter::EscapeForCMake(*flagsCfg).c_str());
         }
       } break;
     }
@@ -664,6 +664,7 @@
           /* clang-format on */
           this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
         }
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior is to do nothing.
         break;
@@ -676,11 +677,10 @@
       case cmPolicies::NEW:
         // NEW behavior is to pass linker flags.
         {
-          cmProp exeLinkFlags =
+          cmValue exeLinkFlags =
             this->Makefile->GetDefinition("CMAKE_EXE_LINKER_FLAGS");
           fprintf(fout, "set(CMAKE_EXE_LINKER_FLAGS %s)\n",
-                  cmOutputConverter::EscapeForCMake(cmToCStrSafe(exeLinkFlags))
-                    .c_str());
+                  cmOutputConverter::EscapeForCMake(*exeLinkFlags).c_str());
         }
         break;
     }
@@ -762,7 +762,7 @@
       vars.insert(kCMAKE_WARN_DEPRECATED);
       vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
 
-      if (cmProp varListStr = this->Makefile->GetDefinition(
+      if (cmValue varListStr = this->Makefile->GetDefinition(
             kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
         std::vector<std::string> varList = cmExpandedList(*varListStr);
         vars.insert(varList.begin(), varList.end());
@@ -801,7 +801,7 @@
          cmLocalGenerator doesn't allow building for "the other"
          architecture only via CMAKE_OSX_ARCHITECTURES.
          */
-      if (cmProp tcArchs = this->Makefile->GetDefinition(
+      if (cmValue tcArchs = this->Makefile->GetDefinition(
             kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
         vars.erase(kCMAKE_OSX_ARCHITECTURES);
         std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
@@ -809,7 +809,7 @@
       }
 
       for (std::string const& var : vars) {
-        if (cmProp val = this->Makefile->GetDefinition(var)) {
+        if (cmValue val = this->Makefile->GetDefinition(var)) {
           std::string flag = "-D" + var + "=" + *val;
           cmakeFlags.push_back(std::move(flag));
         }
@@ -880,6 +880,7 @@
           this->Makefile->IssueMessage(
             MessageType::FATAL_ERROR,
             cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0067));
+          break;
         case cmPolicies::NEW:
           // NEW behavior is to honor the language standard variables.
           // We already initialized honorStandard to true.
@@ -954,7 +955,7 @@
   if (this->Makefile->GetState()->UseGhsMultiIDE()) {
     // Forward the GHS variables to the inner project cache.
     for (std::string const& var : ghs_platform_vars) {
-      if (cmProp val = this->Makefile->GetDefinition(var)) {
+      if (cmValue val = this->Makefile->GetDefinition(var)) {
         std::string flag = "-D" + var + "=" + "'" + *val + "'";
         cmakeFlags.push_back(std::move(flag));
       }
@@ -1096,7 +1097,7 @@
   std::vector<std::string> searchDirs;
   searchDirs.emplace_back();
 
-  cmProp config =
+  cmValue config =
     this->Makefile->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION");
   // if a config was specified try that first
   if (cmNonempty(config)) {
diff --git a/Source/cmCurl.cxx b/Source/cmCurl.cxx
index 233790e..28ee24d 100644
--- a/Source/cmCurl.cxx
+++ b/Source/cmCurl.cxx
@@ -5,9 +5,17 @@
 #if !defined(CMAKE_USE_SYSTEM_CURL) && !defined(_WIN32) &&                    \
   !defined(__APPLE__) && !defined(CURL_CA_BUNDLE) && !defined(CURL_CA_PATH)
 #  define CMAKE_FIND_CAFILE
-#  include "cmSystemTools.h"
 #endif
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+#if defined(_WIN32)
+#  include <vector>
+
+#  include <windows.h>
+
+#  include "cmsys/Encoding.hxx"
+#endif
 
 // curl versions before 7.21.5 did not provide this error code
 #if defined(LIBCURL_VERSION_NUM) && LIBCURL_VERSION_NUM < 0x071505
@@ -23,11 +31,11 @@
     }                                                                         \
   } while (false)
 
-std::string cmCurlSetCAInfo(::CURL* curl, const char* cafile)
+std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile)
 {
   std::string e;
-  if (cafile && *cafile) {
-    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile);
+  if (!cafile.empty()) {
+    ::CURLcode res = ::curl_easy_setopt(curl, CURLOPT_CAINFO, cafile.c_str());
     check_curl_result(res, "Unable to set TLS/SSL Verify CAINFO: ");
   }
 #ifdef CMAKE_FIND_CAFILE
@@ -95,3 +103,35 @@
   }
   return e;
 }
+
+std::string cmCurlFixFileURL(std::string url)
+{
+  if (!cmHasLiteralPrefix(url, "file://")) {
+    return url;
+  }
+
+  // libcurl 7.77 and below accidentally allowed spaces in URLs in some cases.
+  // One such case was file:// URLs, which CMake has long accepted as a result.
+  // Explicitly encode spaces for a URL.
+  cmSystemTools::ReplaceString(url, " ", "%20");
+
+#if defined(_WIN32)
+  // libcurl doesn't support file:// urls for unicode filenames on Windows.
+  // Convert string from UTF-8 to ACP if this is a file:// URL.
+  std::wstring wurl = cmsys::Encoding::ToWide(url);
+  if (!wurl.empty()) {
+    int mblen =
+      WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, NULL, 0, NULL, NULL);
+    if (mblen > 0) {
+      std::vector<char> chars(mblen);
+      mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0],
+                                  mblen, NULL, NULL);
+      if (mblen > 0) {
+        url = &chars[0];
+      }
+    }
+  }
+#endif
+
+  return url;
+}
diff --git a/Source/cmCurl.h b/Source/cmCurl.h
index fb716f8..b5134f4 100644
--- a/Source/cmCurl.h
+++ b/Source/cmCurl.h
@@ -8,6 +8,7 @@
 
 #include <cm3p/curl/curl.h>
 
-std::string cmCurlSetCAInfo(::CURL* curl, const char* cafile = nullptr);
+std::string cmCurlSetCAInfo(::CURL* curl, const std::string& cafile = {});
 std::string cmCurlSetNETRCOption(::CURL* curl, const std::string& netrc_level,
                                  const std::string& netrc_file);
+std::string cmCurlFixFileURL(std::string url);
diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index 5a2683b..fd0a63c 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -19,11 +19,11 @@
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTransformDepfile.h"
+#include "cmValue.h"
 
 namespace {
 std::string EvaluateSplitConfigGenex(
@@ -91,7 +91,7 @@
 
     // Record targets referenced by the genex.
     if (utils) {
-      // FIXME: What is the proper condition for a cross-dependency?
+      // Use a cross-dependency if we referenced the command config.
       bool const cross = !useOutputConfig;
       for (cmGeneratorTarget* gt : cge->GetTargets()) {
         utils->emplace(BT<std::pair<std::string, bool>>(
@@ -176,6 +176,8 @@
   cmGeneratorTarget const* target{ lg->FindGeneratorTargetToUse(
     this->Target) };
 
+  bool const distinctConfigs = this->OutputConfig != this->CommandConfig;
+
   const cmCustomCommandLines& cmdlines = this->CC->GetCommandLines();
   for (cmCustomCommandLine const& cmdline : cmdlines) {
     cmCustomCommandLine argv;
@@ -191,8 +193,10 @@
         argv.push_back(std::move(parsed_arg));
       }
 
-      // For remaining arguments, we default to the OUTPUT_CONFIG.
-      useOutputConfig = true;
+      if (distinctConfigs) {
+        // For remaining arguments, we default to the OUTPUT_CONFIG.
+        useOutputConfig = true;
+      }
     }
 
     if (!argv.empty()) {
@@ -200,7 +204,7 @@
       // collect the target to add a target-level dependency on it.
       cmGeneratorTarget* gt = this->LG->FindGeneratorTargetToUse(argv.front());
       if (gt && gt->GetType() == cmStateEnums::EXECUTABLE) {
-        // FIXME: What is the proper condition for a cross-dependency?
+        // GetArgv0Location uses the command config, so use a cross-dependency.
         bool const cross = true;
         this->Utilities.emplace(BT<std::pair<std::string, bool>>(
           { gt->GetName(), cross }, cc.GetBacktrace()));
@@ -284,7 +288,7 @@
     if (target && target->GetType() == cmStateEnums::EXECUTABLE &&
         !target->IsImported()) {
 
-      cmProp emulator_property =
+      cmValue emulator_property =
         target->GetProperty("CROSSCOMPILING_EMULATOR");
       if (!emulator_property) {
         continue;
diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx
index 4a4f87d..a5f8aab 100644
--- a/Source/cmDefinitions.cxx
+++ b/Source/cmDefinitions.cxx
@@ -34,11 +34,11 @@
   return begin->Map.emplace(key, def).first->second;
 }
 
-const std::string* cmDefinitions::Get(const std::string& key, StackIter begin,
-                                      StackIter end)
+cmValue cmDefinitions::Get(const std::string& key, StackIter begin,
+                           StackIter end)
 {
   Def const& def = cmDefinitions::GetInternal(key, begin, end, false);
-  return def.Value ? def.Value.str_if_stable() : nullptr;
+  return def.Value ? cmValue(def.Value.str_if_stable()) : nullptr;
 }
 
 void cmDefinitions::Raise(const std::string& key, StackIter begin,
diff --git a/Source/cmDefinitions.h b/Source/cmDefinitions.h
index b650aa8..22fef80 100644
--- a/Source/cmDefinitions.h
+++ b/Source/cmDefinitions.h
@@ -13,6 +13,7 @@
 
 #include "cmLinkedTree.h"
 #include "cmString.hxx"
+#include "cmValue.h"
 
 /** \class cmDefinitions
  * \brief Store a scope of variable definitions for CMake language.
@@ -28,8 +29,7 @@
 public:
   // -- Static member functions
 
-  static const std::string* Get(const std::string& key, StackIter begin,
-                                StackIter end);
+  static cmValue Get(const std::string& key, StackIter begin, StackIter end);
 
   static void Raise(const std::string& key, StackIter begin, StackIter end);
 
diff --git a/Source/cmDepends.cxx b/Source/cmDepends.cxx
index 46c7e3e..eca1abd 100644
--- a/Source/cmDepends.cxx
+++ b/Source/cmDepends.cxx
@@ -11,9 +11,9 @@
 #include "cmGeneratedFileStream.h"
 #include "cmLocalUnixMakefileGenerator3.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmDepends::cmDepends(cmLocalUnixMakefileGenerator3* lg, std::string targetDir)
   : LocalGenerator(lg)
@@ -232,7 +232,7 @@
   std::string includePathVar =
     cmStrCat("CMAKE_", lang, "_TARGET_INCLUDE_PATH");
   cmMakefile* mf = this->LocalGenerator->GetMakefile();
-  cmProp includePath = mf->GetDefinition(includePathVar);
+  cmValue includePath = mf->GetDefinition(includePathVar);
   if (includePath) {
     cmExpandList(*includePath, this->IncludePath);
   } else {
diff --git a/Source/cmDependsC.cxx b/Source/cmDependsC.cxx
index da37d45..2527809 100644
--- a/Source/cmDependsC.cxx
+++ b/Source/cmDependsC.cxx
@@ -10,9 +10,9 @@
 #include "cmGlobalUnixMakefileGenerator3.h"
 #include "cmLocalUnixMakefileGenerator3.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 #define INCLUDE_REGEX_LINE                                                    \
   "^[ \t]*[#%][ \t]*(include|import)[ \t]*[<\"]([^\">]+)([\">])"
@@ -40,12 +40,12 @@
   std::string complainRegex = "^$";
   {
     std::string scanRegexVar = cmStrCat("CMAKE_", lang, "_INCLUDE_REGEX_SCAN");
-    if (cmProp sr = mf->GetDefinition(scanRegexVar)) {
+    if (cmValue sr = mf->GetDefinition(scanRegexVar)) {
       scanRegex = *sr;
     }
     std::string complainRegexVar =
       cmStrCat("CMAKE_", lang, "_INCLUDE_REGEX_COMPLAIN");
-    if (cmProp cr = mf->GetDefinition(complainRegexVar)) {
+    if (cmValue cr = mf->GetDefinition(complainRegexVar)) {
       complainRegex = *cr;
     }
   }
diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx
index a64dc27..d5e54ae 100644
--- a/Source/cmDependsFortran.cxx
+++ b/Source/cmDependsFortran.cxx
@@ -16,9 +16,9 @@
 #include "cmLocalUnixMakefileGenerator3.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // TODO: Test compiler for the case of the mod file.  Some always
 // use lower case and some always use upper case.  I do not know if any
@@ -163,12 +163,17 @@
     mod_dir = this->LocalGenerator->GetCurrentBinaryDirectory();
   }
 
+  bool building_intrinsics =
+    !mf->GetSafeDefinition("CMAKE_Fortran_TARGET_BUILDING_INSTRINSIC_MODULES")
+       .empty();
+
   // Actually write dependencies to the streams.
   using ObjectInfoMap = cmDependsFortranInternals::ObjectInfoMap;
   ObjectInfoMap const& objInfo = this->Internal->ObjectInfo;
   for (auto const& i : objInfo) {
     if (!this->WriteDependenciesReal(i.first, i.second, mod_dir, stamp_dir,
-                                     makeDepends, internalDepends)) {
+                                     makeDepends, internalDepends,
+                                     building_intrinsics)) {
       return false;
     }
   }
@@ -307,7 +312,8 @@
                                              std::string const& mod_dir,
                                              std::string const& stamp_dir,
                                              std::ostream& makeDepends,
-                                             std::ostream& internalDepends)
+                                             std::ostream& internalDepends,
+                                             bool buildingIntrinsics)
 {
   // Get the source file for this object.
   std::string const& src = info.Source;
@@ -339,8 +345,13 @@
     makeDepends << '\n';
   }
 
+  std::set<std::string> req = info.Requires;
+  if (buildingIntrinsics) {
+    req.insert(info.Intrinsics.begin(), info.Intrinsics.end());
+  }
+
   // Write module requirements to the output stream.
-  for (std::string const& i : info.Requires) {
+  for (std::string const& i : req) {
     // Require only modules not provided in the same source.
     if (info.Provides.find(i) != info.Provides.cend()) {
       continue;
@@ -405,7 +416,7 @@
       makeDepends << "\t$(CMAKE_COMMAND) -E cmake_copy_f90_mod " << modFile
                   << ' ' << stampFileForShell;
       cmMakefile* mf = this->LocalGenerator->GetMakefile();
-      cmProp cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID");
+      cmValue cid = mf->GetDefinition("CMAKE_Fortran_COMPILER_ID");
       if (cmNonempty(cid)) {
         makeDepends << ' ' << *cid;
       }
diff --git a/Source/cmDependsFortran.h b/Source/cmDependsFortran.h
index 0d407bc..a74db91 100644
--- a/Source/cmDependsFortran.h
+++ b/Source/cmDependsFortran.h
@@ -72,7 +72,8 @@
                              std::string const& mod_dir,
                              std::string const& stamp_dir,
                              std::ostream& makeDepends,
-                             std::ostream& internalDepends);
+                             std::ostream& internalDepends,
+                             bool buildingIntrinsics);
 
   // The source file from which to start scanning.
   std::string SourceFile;
diff --git a/Source/cmELF.cxx b/Source/cmELF.cxx
index 9a474e3..1678ce8 100644
--- a/Source/cmELF.cxx
+++ b/Source/cmELF.cxx
@@ -17,41 +17,9 @@
 
 #include "cmsys/FStream.hxx"
 
-// Include the ELF format information system header.
-#if defined(__OpenBSD__)
-#  include <elf_abi.h>
-#elif defined(__HAIKU__)
-#  include <elf32.h>
-#  include <elf64.h>
-using Elf32_Ehdr = struct Elf32_Ehdr;
-using Elf32_Shdr = struct Elf32_Shdr;
-using Elf32_Sym = struct Elf32_Sym;
-using Elf32_Rel = struct Elf32_Rel;
-using Elf32_Rela = struct Elf32_Rela;
-#  define ELFMAG0 0x7F
-#  define ELFMAG1 'E'
-#  define ELFMAG2 'L'
-#  define ELFMAG3 'F'
-#  define ET_NONE 0
-#  define ET_REL 1
-#  define ET_EXEC 2
-#  define ET_DYN 3
-#  define ET_CORE 4
-#  define EM_386 3
-#  define EM_SPARC 2
-#  define EM_PPC 20
-#else
-#  include <elf.h>
-#endif
-#if defined(__sun)
-#  include <sys/link.h> // For dynamic section information
-#endif
-#ifdef _SCO_DS
-#  include <link.h> // For DT_SONAME etc.
-#endif
-#ifndef DT_RUNPATH
-#  define DT_RUNPATH 29
-#endif
+#include "cmelf/elf32.h"
+#include "cmelf/elf64.h"
+#include "cmelf/elf_common.h"
 
 // Low-level byte swapping implementation.
 template <size_t s>
@@ -145,6 +113,7 @@
   virtual std::vector<char> EncodeDynamicEntries(
     const cmELF::DynamicEntryList&) = 0;
   virtual StringEntry const* GetDynamicSectionString(unsigned int tag) = 0;
+  virtual bool IsMips() const = 0;
   virtual void PrintInfo(std::ostream& os) const = 0;
 
   // Lookup the SONAME in the DYNAMIC section.
@@ -218,7 +187,6 @@
 };
 
 // Configure the implementation template for 64-bit ELF files.
-#ifndef _SCO_DS
 struct cmELFTypes64
 {
   using ELF_Ehdr = Elf64_Ehdr;
@@ -228,7 +196,6 @@
   using tagtype = ::uint64_t;
   static const char* GetName() { return "64-bit"; }
 };
-#endif
 
 // Parser implementation template.
 template <class Types>
@@ -262,6 +229,8 @@
   // Lookup a string from the dynamic section with the given tag.
   StringEntry const* GetDynamicSectionString(unsigned int tag) override;
 
+  bool IsMips() const override { return this->ELFHeader.e_machine == EM_MIPS; }
+
   // Print information about the ELF file.
   void PrintInfo(std::ostream& os) const override
   {
@@ -345,16 +314,12 @@
         eti == ET_CORE) {
       return true;
     }
-#if defined(ET_LOOS) && defined(ET_HIOS)
     if (eti >= ET_LOOS && eti <= ET_HIOS) {
       return true;
     }
-#endif
-#if defined(ET_LOPROC) && defined(ET_HIPROC)
     if (eti >= ET_LOPROC && eti <= ET_HIPROC) {
       return true;
     }
-#endif
     return false;
   }
 
@@ -465,18 +430,14 @@
       break;
     default: {
       unsigned int eti = static_cast<unsigned int>(this->ELFHeader.e_type);
-#if defined(ET_LOOS) && defined(ET_HIOS)
       if (eti >= ET_LOOS && eti <= ET_HIOS) {
         this->ELFType = cmELF::FileTypeSpecificOS;
         break;
       }
-#endif
-#if defined(ET_LOPROC) && defined(ET_HIPROC)
       if (eti >= ET_LOPROC && eti <= ET_HIPROC) {
         this->ELFType = cmELF::FileTypeSpecificProc;
         break;
       }
-#endif
       std::ostringstream e;
       e << "Unknown ELF file type " << eti;
       this->SetErrorMessage(e.str().c_str());
@@ -681,17 +642,12 @@
 
 const long cmELF::TagRPath = DT_RPATH;
 const long cmELF::TagRunPath = DT_RUNPATH;
-
-#ifdef DT_MIPS_RLD_MAP_REL
 const long cmELF::TagMipsRldMapRel = DT_MIPS_RLD_MAP_REL;
-#else
-const long cmELF::TagMipsRldMapRel = 0;
-#endif
 
 cmELF::cmELF(const char* fname)
 {
   // Try to open the file.
-  auto fin = cm::make_unique<cmsys::ifstream>(fname);
+  auto fin = cm::make_unique<cmsys::ifstream>(fname, std::ios::binary);
 
   // Quit now if the file could not be opened.
   if (!fin || !*fin) {
@@ -736,15 +692,11 @@
     // 32-bit ELF
     this->Internal = cm::make_unique<cmELFInternalImpl<cmELFTypes32>>(
       this, std::move(fin), order);
-  }
-#ifndef _SCO_DS
-  else if (ident[EI_CLASS] == ELFCLASS64) {
+  } else if (ident[EI_CLASS] == ELFCLASS64) {
     // 64-bit ELF
     this->Internal = cm::make_unique<cmELFInternalImpl<cmELFTypes64>>(
       this, std::move(fin), order);
-  }
-#endif
-  else {
+  } else {
     this->ErrorMessage = "ELF file class is not 32-bit or 64-bit.";
     return;
   }
@@ -846,6 +798,14 @@
   return nullptr;
 }
 
+bool cmELF::IsMIPS() const
+{
+  if (this->Valid()) {
+    return this->Internal->IsMips();
+  }
+  return false;
+}
+
 void cmELF::PrintInfo(std::ostream& os) const
 {
   if (this->Valid()) {
diff --git a/Source/cmELF.h b/Source/cmELF.h
index f88ebe9..ce8bd7f 100644
--- a/Source/cmELF.h
+++ b/Source/cmELF.h
@@ -11,10 +11,6 @@
 #include <utility>
 #include <vector>
 
-#if !defined(CMake_USE_ELF_PARSER)
-#  error "This file may be included only if CMake_USE_ELF_PARSER is enabled."
-#endif
-
 class cmELFInternal;
 
 /** \class cmELF
@@ -102,6 +98,9 @@
   /** Get the RUNPATH field if any.  */
   StringEntry const* GetRunPath();
 
+  /** Returns true if the ELF file targets a MIPS CPU.  */
+  bool IsMIPS() const;
+
   /** Print human-readable information about the ELF file.  */
   void PrintInfo(std::ostream& os) const;
 
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index 1a31ae4..aa968dc 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -22,6 +22,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 class cmSourceFile;
@@ -254,7 +255,7 @@
 
 void cmExportBuildFileGenerator::HandleMissingTarget(
   std::string& link_libs, std::vector<std::string>& missingTargets,
-  cmGeneratorTarget* depender, cmGeneratorTarget* dependee)
+  cmGeneratorTarget const* depender, cmGeneratorTarget* dependee)
 {
   // The target is not in the export.
   if (!this->AppendMode) {
@@ -321,7 +322,7 @@
 }
 
 void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
-  cmGeneratorTarget* depender, cmGeneratorTarget* dependee,
+  cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
   std::vector<std::string> const& exportFiles)
 {
   std::ostringstream e;
@@ -344,7 +345,7 @@
 }
 
 std::string cmExportBuildFileGenerator::InstallNameDir(
-  cmGeneratorTarget* target, const std::string& config)
+  cmGeneratorTarget const* target, const std::string& config)
 {
   std::string install_name_dir;
 
diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h
index 264494d..244f526 100644
--- a/Source/cmExportBuildFileGenerator.h
+++ b/Source/cmExportBuildFileGenerator.h
@@ -60,11 +60,11 @@
     cmGeneratorTarget const* target) const;
   void HandleMissingTarget(std::string& link_libs,
                            std::vector<std::string>& missingTargets,
-                           cmGeneratorTarget* depender,
+                           cmGeneratorTarget const* depender,
                            cmGeneratorTarget* dependee) override;
 
-  void ComplainAboutMissingTarget(cmGeneratorTarget* depender,
-                                  cmGeneratorTarget* dependee,
+  void ComplainAboutMissingTarget(cmGeneratorTarget const* depender,
+                                  cmGeneratorTarget const* dependee,
                                   std::vector<std::string> const& namespaces);
 
   /** Fill in properties indicating built file locations.  */
@@ -73,7 +73,7 @@
                                  cmGeneratorTarget* target,
                                  ImportPropertyMap& properties);
 
-  std::string InstallNameDir(cmGeneratorTarget* target,
+  std::string InstallNameDir(cmGeneratorTarget const* target,
                              const std::string& config) override;
 
   std::pair<std::vector<std::string>, std::string> FindBuildExportInfo(
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index 352eaf2..63440a3 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -282,6 +282,7 @@
   // CMP0090 decides both the default and what variable changes it.
   switch (mf.GetPolicyStatus(cmPolicies::CMP0090)) {
     case cmPolicies::WARN:
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       // Default is to export, but can be disabled.
       if (mf.IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) {
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index dd611de..8b0f64e 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -21,13 +21,12 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
-#include "cmTargetExport.h"
+#include "cmValue.h"
 
 static std::string cmExportFileGeneratorEscape(std::string const& str)
 {
@@ -123,10 +122,10 @@
 }
 
 void cmExportFileGenerator::PopulateInterfaceProperty(
-  const std::string& propName, cmGeneratorTarget* target,
+  const std::string& propName, cmGeneratorTarget const* target,
   ImportPropertyMap& properties)
 {
-  cmProp input = target->GetProperty(propName);
+  cmValue input = target->GetProperty(propName);
   if (input) {
     properties[propName] = *input;
   }
@@ -134,11 +133,11 @@
 
 void cmExportFileGenerator::PopulateInterfaceProperty(
   const std::string& propName, const std::string& outputName,
-  cmGeneratorTarget* target,
+  cmGeneratorTarget const* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
 {
-  cmProp input = target->GetProperty(propName);
+  cmValue input = target->GetProperty(propName);
   if (input) {
     if (input->empty()) {
       // Set to empty
@@ -168,14 +167,14 @@
 }
 
 bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
-  cmGeneratorTarget* target,
+  cmGeneratorTarget const* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
 {
   if (!target->IsLinkable()) {
     return false;
   }
-  cmProp input = target->GetProperty("INTERFACE_LINK_LIBRARIES");
+  cmValue input = target->GetProperty("INTERFACE_LINK_LIBRARIES");
   if (input) {
     std::string prepro =
       cmGeneratorExpression::Preprocess(*input, preprocessRule);
@@ -196,7 +195,7 @@
 }
 
 static bool checkInterfaceDirs(const std::string& prepro,
-                               cmGeneratorTarget* target,
+                               cmGeneratorTarget const* target,
                                const std::string& prop)
 {
   std::string const& installDir =
@@ -335,14 +334,14 @@
 }
 
 void cmExportFileGenerator::PopulateSourcesInterface(
-  cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
+  cmGeneratorTarget const* gt,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
 {
-  cmGeneratorTarget* gt = tei->Target;
   assert(preprocessRule == cmGeneratorExpression::InstallInterface);
 
   const char* propName = "INTERFACE_SOURCES";
-  cmProp input = gt->GetProperty(propName);
+  cmValue input = gt->GetProperty(propName);
 
   if (!input) {
     return;
@@ -366,19 +365,21 @@
 }
 
 void cmExportFileGenerator::PopulateIncludeDirectoriesInterface(
-  cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
-  ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
+  cmGeneratorTarget const* target,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
+  ImportPropertyMap& properties, std::vector<std::string>& missingTargets,
+  cmTargetExport const& te)
 {
-  cmGeneratorTarget* target = tei->Target;
   assert(preprocessRule == cmGeneratorExpression::InstallInterface);
 
   const char* propName = "INTERFACE_INCLUDE_DIRECTORIES";
-  cmProp input = target->GetProperty(propName);
+  cmValue input = target->GetProperty(propName);
 
   cmGeneratorExpression ge;
 
   std::string dirs = cmGeneratorExpression::Preprocess(
-    tei->InterfaceIncludeDirectories, preprocessRule, true);
+    cmJoin(target->Target->GetInstallIncludeDirectoriesEntries(te), ";"),
+    preprocessRule, true);
   this->ReplaceInstallPrefix(dirs);
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs);
   std::string exportDirs =
@@ -424,14 +425,14 @@
 }
 
 void cmExportFileGenerator::PopulateLinkDependsInterface(
-  cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
+  cmGeneratorTarget const* gt,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
 {
-  cmGeneratorTarget* gt = tei->Target;
   assert(preprocessRule == cmGeneratorExpression::InstallInterface);
 
   const char* propName = "INTERFACE_LINK_DEPENDS";
-  cmProp input = gt->GetProperty(propName);
+  cmValue input = gt->GetProperty(propName);
 
   if (!input) {
     return;
@@ -455,14 +456,14 @@
 }
 
 void cmExportFileGenerator::PopulateLinkDirectoriesInterface(
-  cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
+  cmGeneratorTarget const* gt,
+  cmGeneratorExpression::PreprocessContext preprocessRule,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
 {
-  cmGeneratorTarget* gt = tei->Target;
   assert(preprocessRule == cmGeneratorExpression::InstallInterface);
 
   const char* propName = "INTERFACE_LINK_DIRECTORIES";
-  cmProp input = gt->GetProperty(propName);
+  cmValue input = gt->GetProperty(propName);
 
   if (!input) {
     return;
@@ -486,7 +487,7 @@
 }
 
 void cmExportFileGenerator::PopulateInterfaceProperty(
-  const std::string& propName, cmGeneratorTarget* target,
+  const std::string& propName, cmGeneratorTarget const* target,
   cmGeneratorExpression::PreprocessContext preprocessRule,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
 {
@@ -497,7 +498,7 @@
 void getPropertyContents(cmGeneratorTarget const* tgt, const std::string& prop,
                          std::set<std::string>& ifaceProperties)
 {
-  cmProp p = tgt->GetProperty(prop);
+  cmValue p = tgt->GetProperty(prop);
   if (!p) {
     return;
   }
@@ -505,7 +506,7 @@
   ifaceProperties.insert(content.begin(), content.end());
 }
 
-void getCompatibleInterfaceProperties(cmGeneratorTarget* target,
+void getCompatibleInterfaceProperties(cmGeneratorTarget const* target,
                                       std::set<std::string>& ifaceProperties,
                                       const std::string& config)
 {
@@ -544,7 +545,7 @@
 }
 
 void cmExportFileGenerator::PopulateCompatibleInterfaceProperties(
-  cmGeneratorTarget* gtarget, ImportPropertyMap& properties)
+  cmGeneratorTarget const* gtarget, ImportPropertyMap& properties)
 {
   this->PopulateInterfaceProperty("COMPATIBLE_INTERFACE_BOOL", gtarget,
                                   properties);
@@ -596,7 +597,7 @@
 }
 
 bool cmExportFileGenerator::AddTargetNamespace(
-  std::string& input, cmGeneratorTarget* target,
+  std::string& input, cmGeneratorTarget const* target,
   std::vector<std::string>& missingTargets)
 {
   cmGeneratorTarget::TargetOrString resolved =
@@ -627,7 +628,7 @@
 }
 
 void cmExportFileGenerator::ResolveTargetsInGeneratorExpressions(
-  std::string& input, cmGeneratorTarget* target,
+  std::string& input, cmGeneratorTarget const* target,
   std::vector<std::string>& missingTargets, FreeTargetsReplace replace)
 {
   if (replace == NoReplaceFreeTargets) {
@@ -654,7 +655,7 @@
 }
 
 void cmExportFileGenerator::ResolveTargetsInGeneratorExpression(
-  std::string& input, cmGeneratorTarget* target,
+  std::string& input, cmGeneratorTarget const* target,
   std::vector<std::string>& missingTargets)
 {
   std::string::size_type pos = 0;
@@ -744,7 +745,7 @@
 void cmExportFileGenerator::SetImportLinkInterface(
   const std::string& config, std::string const& suffix,
   cmGeneratorExpression::PreprocessContext preprocessRule,
-  cmGeneratorTarget* target, ImportPropertyMap& properties,
+  cmGeneratorTarget const* target, ImportPropertyMap& properties,
   std::vector<std::string>& missingTargets)
 {
   // Add the transitive link dependencies for this configuration.
@@ -761,12 +762,12 @@
     return;
   }
 
-  cmProp propContent;
+  cmValue propContent;
 
-  if (cmProp prop_suffixed =
+  if (cmValue prop_suffixed =
         target->GetProperty("LINK_INTERFACE_LIBRARIES" + suffix)) {
     propContent = prop_suffixed;
-  } else if (cmProp prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
+  } else if (cmValue prop = target->GetProperty("LINK_INTERFACE_LIBRARIES")) {
     propContent = prop;
   } else {
     return;
@@ -854,7 +855,7 @@
       cmGeneratorTarget::ManagedType::Native) {
     std::string prop = cmStrCat("IMPORTED_COMMON_LANGUAGE_RUNTIME", suffix);
     std::string propval;
-    if (cmProp p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
+    if (cmValue p = target->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
       propval = *p;
     } else if (target->IsCSharpOnly()) {
       // C# projects do not have the /clr flag, so we set the property
@@ -880,7 +881,7 @@
 
 template <typename T>
 void cmExportFileGenerator::SetImportLinkProperty(
-  std::string const& suffix, cmGeneratorTarget* target,
+  std::string const& suffix, cmGeneratorTarget const* target,
   const std::string& propName, std::vector<T> const& entries,
   ImportPropertyMap& properties, std::vector<std::string>& missingTargets,
   ImportLinkPropertyTargetNames targetNames)
@@ -917,20 +918,20 @@
   // Protect that file against use with older CMake versions.
   /* clang-format off */
   os << "# Generated by CMake\n\n";
-  os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.5)\n"
+  os << "if(\"${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}\" LESS 2.6)\n"
      << "   message(FATAL_ERROR \"CMake >= 2.6.0 required\")\n"
      << "endif()\n";
   /* clang-format on */
 
   // Isolate the file policy level.
   // Support CMake versions as far back as 2.6 but also support using NEW
-  // policy settings for up to CMake 3.19 (this upper limit may be reviewed
+  // policy settings for up to CMake 3.20 (this upper limit may be reviewed
   // and increased from time to time). This reduces the opportunity for CMake
   // warnings when an older export file is later used with newer CMake
   // versions.
   /* clang-format off */
   os << "cmake_policy(PUSH)\n"
-     << "cmake_policy(VERSION 2.6...3.19)\n";
+     << "cmake_policy(VERSION 2.6...3.20)\n";
   /* clang-format on */
 }
 
@@ -1211,11 +1212,11 @@
 }
 
 bool cmExportFileGenerator::PopulateExportProperties(
-  cmGeneratorTarget* gte, ImportPropertyMap& properties,
+  cmGeneratorTarget const* gte, ImportPropertyMap& properties,
   std::string& errorMessage)
 {
   const auto& targetProperties = gte->Target->GetProperties();
-  if (cmProp exportProperties =
+  if (cmValue exportProperties =
         targetProperties.GetPropertyValue("EXPORT_PROPERTIES")) {
     for (auto& prop : cmExpandedList(*exportProperties)) {
       /* Black list reserved properties */
@@ -1228,7 +1229,7 @@
         errorMessage = e.str();
         return false;
       }
-      cmProp propertyValue = targetProperties.GetPropertyValue(prop);
+      cmValue propertyValue = targetProperties.GetPropertyValue(prop);
       if (!propertyValue) {
         // Asked to export a property that isn't defined on the target. Do not
         // consider this an error, there's just nothing to export.
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index 45eaed0..29a6a98 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -16,6 +16,7 @@
 #include "cmVersionConfig.h"
 
 class cmGeneratorTarget;
+class cmTargetExport;
 
 #define STRINGIFY_HELPER(X) #X
 #define STRINGIFY(X) STRINGIFY_HELPER(X)
@@ -27,8 +28,6 @@
          CMake_VERSION_MINOR) "." STRINGIFY(CMake_VERSION_PATCH)              \
      : #major "." #minor ".0")
 
-class cmTargetExport;
-
 /** \class cmExportFileGenerator
  * \brief Generate a file exporting targets from a build or install tree.
  *
@@ -108,7 +107,7 @@
   };
   template <typename T>
   void SetImportLinkProperty(std::string const& suffix,
-                             cmGeneratorTarget* target,
+                             cmGeneratorTarget const* target,
                              const std::string& propName,
                              std::vector<T> const& entries,
                              ImportPropertyMap& properties,
@@ -127,44 +126,46 @@
    *  export set.  */
   virtual void HandleMissingTarget(std::string& link_libs,
                                    std::vector<std::string>& missingTargets,
-                                   cmGeneratorTarget* depender,
+                                   cmGeneratorTarget const* depender,
                                    cmGeneratorTarget* dependee) = 0;
-  void PopulateInterfaceProperty(const std::string&, cmGeneratorTarget* target,
+  void PopulateInterfaceProperty(const std::string&,
+                                 cmGeneratorTarget const* target,
                                  cmGeneratorExpression::PreprocessContext,
                                  ImportPropertyMap& properties,
                                  std::vector<std::string>& missingTargets);
   bool PopulateInterfaceLinkLibrariesProperty(
-    cmGeneratorTarget* target, cmGeneratorExpression::PreprocessContext,
+    cmGeneratorTarget const* target, cmGeneratorExpression::PreprocessContext,
     ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
   void PopulateInterfaceProperty(const std::string& propName,
-                                 cmGeneratorTarget* target,
+                                 cmGeneratorTarget const* target,
                                  ImportPropertyMap& properties);
-  void PopulateCompatibleInterfaceProperties(cmGeneratorTarget* target,
+  void PopulateCompatibleInterfaceProperties(cmGeneratorTarget const* target,
                                              ImportPropertyMap& properties);
   virtual void GenerateInterfaceProperties(
     cmGeneratorTarget const* target, std::ostream& os,
     const ImportPropertyMap& properties);
   void PopulateIncludeDirectoriesInterface(
-    cmTargetExport* target,
+    cmGeneratorTarget const* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
-    ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
+    ImportPropertyMap& properties, std::vector<std::string>& missingTargets,
+    cmTargetExport const& te);
   void PopulateSourcesInterface(
-    cmTargetExport* target,
+    cmGeneratorTarget const* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
     ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
   void PopulateLinkDirectoriesInterface(
-    cmTargetExport* target,
+    cmGeneratorTarget const* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
     ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
   void PopulateLinkDependsInterface(
-    cmTargetExport* target,
+    cmGeneratorTarget const* target,
     cmGeneratorExpression::PreprocessContext preprocessRule,
     ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
 
   void SetImportLinkInterface(
     const std::string& config, std::string const& suffix,
     cmGeneratorExpression::PreprocessContext preprocessRule,
-    cmGeneratorTarget* target, ImportPropertyMap& properties,
+    cmGeneratorTarget const* target, ImportPropertyMap& properties,
     std::vector<std::string>& missingTargets);
 
   enum FreeTargetsReplace
@@ -174,14 +175,14 @@
   };
 
   void ResolveTargetsInGeneratorExpressions(
-    std::string& input, cmGeneratorTarget* target,
+    std::string& input, cmGeneratorTarget const* target,
     std::vector<std::string>& missingTargets,
     FreeTargetsReplace replace = NoReplaceFreeTargets);
 
   virtual void GenerateRequiredCMakeVersion(std::ostream& os,
                                             const char* versionString);
 
-  bool PopulateExportProperties(cmGeneratorTarget* gte,
+  bool PopulateExportProperties(cmGeneratorTarget const* gte,
                                 ImportPropertyMap& properties,
                                 std::string& errorMessage);
 
@@ -205,20 +206,20 @@
 
 private:
   void PopulateInterfaceProperty(const std::string&, const std::string&,
-                                 cmGeneratorTarget* target,
+                                 cmGeneratorTarget const* target,
                                  cmGeneratorExpression::PreprocessContext,
                                  ImportPropertyMap& properties,
                                  std::vector<std::string>& missingTargets);
 
-  bool AddTargetNamespace(std::string& input, cmGeneratorTarget* target,
+  bool AddTargetNamespace(std::string& input, cmGeneratorTarget const* target,
                           std::vector<std::string>& missingTargets);
 
   void ResolveTargetsInGeneratorExpression(
-    std::string& input, cmGeneratorTarget* target,
+    std::string& input, cmGeneratorTarget const* target,
     std::vector<std::string>& missingTargets);
 
   virtual void ReplaceInstallPrefix(std::string& input);
 
-  virtual std::string InstallNameDir(cmGeneratorTarget* target,
+  virtual std::string InstallNameDir(cmGeneratorTarget const* target,
                                      const std::string& config) = 0;
 };
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 3c69c50..4a3c565 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -21,6 +21,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
+#include "cmValue.h"
 
 cmExportInstallFileGenerator::cmExportInstallFileGenerator(
   cmInstallExportGenerator* iegen)
@@ -85,8 +86,9 @@
     ImportPropertyMap properties;
 
     this->PopulateIncludeDirectoriesInterface(
-      te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
-    this->PopulateSourcesInterface(te, cmGeneratorExpression::InstallInterface,
+      gt, cmGeneratorExpression::InstallInterface, properties, missingTargets,
+      *te);
+    this->PopulateSourcesInterface(gt, cmGeneratorExpression::InstallInterface,
                                    properties, missingTargets);
     this->PopulateInterfaceProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES", gt,
                                     cmGeneratorExpression::InstallInterface,
@@ -110,9 +112,9 @@
                                     cmGeneratorExpression::InstallInterface,
                                     properties, missingTargets);
     this->PopulateLinkDirectoriesInterface(
-      te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
+      gt, cmGeneratorExpression::InstallInterface, properties, missingTargets);
     this->PopulateLinkDependsInterface(
-      te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
+      gt, cmGeneratorExpression::InstallInterface, properties, missingTargets);
 
     std::string errorMessage;
     if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
@@ -447,7 +449,7 @@
 
 void cmExportInstallFileGenerator::HandleMissingTarget(
   std::string& link_libs, std::vector<std::string>& missingTargets,
-  cmGeneratorTarget* depender, cmGeneratorTarget* dependee)
+  cmGeneratorTarget const* depender, cmGeneratorTarget* dependee)
 {
   const std::string name = dependee->GetName();
   cmGlobalGenerator* gg = dependee->GetLocalGenerator()->GetGlobalGenerator();
@@ -499,7 +501,7 @@
 }
 
 void cmExportInstallFileGenerator::ComplainAboutMissingTarget(
-  cmGeneratorTarget* depender, cmGeneratorTarget* dependee,
+  cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
   std::vector<std::string> const& exportFiles)
 {
   std::ostringstream e;
@@ -521,7 +523,7 @@
 }
 
 std::string cmExportInstallFileGenerator::InstallNameDir(
-  cmGeneratorTarget* target, const std::string& config)
+  cmGeneratorTarget const* target, const std::string& config)
 {
   std::string install_name_dir;
 
diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h
index 2d8de9d..5cec2e0 100644
--- a/Source/cmExportInstallFileGenerator.h
+++ b/Source/cmExportInstallFileGenerator.h
@@ -63,13 +63,13 @@
     cmTargetExport const* targetExport) const;
   void HandleMissingTarget(std::string& link_libs,
                            std::vector<std::string>& missingTargets,
-                           cmGeneratorTarget* depender,
+                           cmGeneratorTarget const* depender,
                            cmGeneratorTarget* dependee) override;
 
   void ReplaceInstallPrefix(std::string& input) override;
 
-  void ComplainAboutMissingTarget(cmGeneratorTarget* depender,
-                                  cmGeneratorTarget* dependee,
+  void ComplainAboutMissingTarget(cmGeneratorTarget const* depender,
+                                  cmGeneratorTarget const* dependee,
                                   std::vector<std::string> const& exportFiles);
 
   std::pair<std::vector<std::string>, std::string> FindNamespaces(
@@ -94,7 +94,7 @@
                                  ImportPropertyMap& properties,
                                  std::set<std::string>& importedLocations);
 
-  std::string InstallNameDir(cmGeneratorTarget* target,
+  std::string InstallNameDir(cmGeneratorTarget const* target,
                              const std::string& config) override;
 
   cmInstallExportGenerator* IEGen;
diff --git a/Source/cmExportLibraryDependenciesCommand.cxx b/Source/cmExportLibraryDependenciesCommand.cxx
index 7f31dd2..8aec12b 100644
--- a/Source/cmExportLibraryDependenciesCommand.cxx
+++ b/Source/cmExportLibraryDependenciesCommand.cxx
@@ -14,12 +14,12 @@
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 class cmListFileBacktrace;
@@ -96,7 +96,7 @@
           // Handle simple output name changes.  This command is
           // deprecated so we do not support full target name
           // translation (which requires per-configuration info).
-          if (cmProp outname = libtgt->GetProperty("OUTPUT_NAME")) {
+          if (cmValue outname = libtgt->GetProperty("OUTPUT_NAME")) {
             lib = *outname;
           }
         }
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx
index cac60e1..cbe3c4d 100644
--- a/Source/cmExportTryCompileFileGenerator.cxx
+++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -13,10 +13,10 @@
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 cmExportTryCompileFileGenerator::cmExportTryCompileFileGenerator(
   cmGlobalGenerator* gg, const std::vector<std::string>& targets,
@@ -60,7 +60,7 @@
   const std::string& propName, cmGeneratorTarget const* tgt,
   std::string const& language, std::set<cmGeneratorTarget const*>& emitted)
 {
-  cmProp prop = tgt->GetProperty(propName);
+  cmValue prop = tgt->GetProperty(propName);
   if (!prop) {
     return std::string();
   }
@@ -126,7 +126,7 @@
 }
 
 std::string cmExportTryCompileFileGenerator::InstallNameDir(
-  cmGeneratorTarget* target, const std::string& config)
+  cmGeneratorTarget const* target, const std::string& config)
 {
   std::string install_name_dir;
 
diff --git a/Source/cmExportTryCompileFileGenerator.h b/Source/cmExportTryCompileFileGenerator.h
index 6bf5781..127b8df 100644
--- a/Source/cmExportTryCompileFileGenerator.h
+++ b/Source/cmExportTryCompileFileGenerator.h
@@ -36,7 +36,8 @@
   {
   }
   void HandleMissingTarget(std::string&, std::vector<std::string>&,
-                           cmGeneratorTarget*, cmGeneratorTarget*) override
+                           cmGeneratorTarget const*,
+                           cmGeneratorTarget*) override
   {
   }
 
@@ -44,7 +45,7 @@
                           ImportPropertyMap& properties,
                           std::set<const cmGeneratorTarget*>& emitted);
 
-  std::string InstallNameDir(cmGeneratorTarget* target,
+  std::string InstallNameDir(cmGeneratorTarget const* target,
                              const std::string& config) override;
 
 private:
diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx
index df14261..e2c54d7 100644
--- a/Source/cmExtraCodeBlocksGenerator.cxx
+++ b/Source/cmExtraCodeBlocksGenerator.cxx
@@ -16,12 +16,12 @@
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
 
@@ -396,8 +396,8 @@
             CbpUnit& cbpUnit = allFiles[fullPath];
             cbpUnit.Targets.push_back(target.get());
           }
-        }
-        default: // intended fallthrough
+        } break;
+        default:
           break;
       }
     }
@@ -499,12 +499,12 @@
     if (target->GetType() == cmStateEnums::EXECUTABLE) {
       // Determine the directory where the executable target is created, and
       // set the working directory to this dir.
-      cmProp runtimeOutputDir =
+      cmValue runtimeOutputDir =
         makefile->GetDefinition("CMAKE_RUNTIME_OUTPUT_DIRECTORY");
       if (runtimeOutputDir) {
         workingDir = *runtimeOutputDir;
       } else {
-        cmProp executableOutputDir =
+        cmValue executableOutputDir =
           makefile->GetDefinition("EXECUTABLE_OUTPUT_PATH");
         if (executableOutputDir) {
           workingDir = *executableOutputDir;
diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx
index 549b08b..9e8ac5c 100644
--- a/Source/cmExtraCodeLiteGenerator.cxx
+++ b/Source/cmExtraCodeLiteGenerator.cxx
@@ -208,7 +208,7 @@
     case cmStateEnums::MODULE_LIBRARY: {
       projectType = "Dynamic Library";
     } break;
-    default: // intended fallthrough
+    default:
       break;
   }
 
@@ -233,8 +233,8 @@
           otherFiles.insert(fullPath);
         }
       }
-    }
-    default: // intended fallthrough
+    } break;
+    default:
       break;
   }
   return projectType;
@@ -556,7 +556,8 @@
     case cmStateEnums::SHARED_LIBRARY:
     case cmStateEnums::MODULE_LIBRARY:
       visualname = "lib" + targetName;
-    default: // intended fallthrough
+      break;
+    default:
       break;
   }
   xml.Attribute("Name", visualname);
diff --git a/Source/cmExtraEclipseCDT4Generator.cxx b/Source/cmExtraEclipseCDT4Generator.cxx
index 672089c..d9d5a4b 100644
--- a/Source/cmExtraEclipseCDT4Generator.cxx
+++ b/Source/cmExtraEclipseCDT4Generator.cxx
@@ -19,13 +19,13 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmSourceGroup.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmXMLWriter.h"
 #include "cmake.h"
 
@@ -189,7 +189,7 @@
   }
 
   fout << "eclipse.preferences.version=1\n";
-  cmProp encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING");
+  cmValue encoding = mf->GetDefinition("CMAKE_ECLIPSE_RESOURCE_ENCODING");
   if (encoding) {
     fout << "encoding/<project>=" << *encoding << '\n';
   }
@@ -244,7 +244,7 @@
   const bool envVarSet = cmSystemTools::GetEnv(envVar, envVarValue);
 
   std::string cacheEntryName = cmStrCat("CMAKE_ECLIPSE_ENVVAR_", envVar);
-  cmProp cacheValue = lg.GetState()->GetInitializedCacheValue(cacheEntryName);
+  cmValue cacheValue = lg.GetState()->GetInitializedCacheValue(cacheEntryName);
 
   // now we have both, decide which one to use
   std::string valueToUse;
@@ -415,7 +415,7 @@
     xml.Element("nature", n);
   }
 
-  if (cmProp extraNaturesProp =
+  if (cmValue extraNaturesProp =
         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_NATURES")) {
     std::vector<std::string> extraNatures = cmExpandedList(*extraNaturesProp);
     for (std::string const& n : extraNatures) {
@@ -754,7 +754,7 @@
   emitted.clear();
   for (const auto& lgen : this->GlobalGenerator->GetLocalGenerators()) {
 
-    if (cmProp cdefs =
+    if (cmValue cdefs =
           lgen->GetMakefile()->GetProperty("COMPILE_DEFINITIONS")) {
       // Expand the list.
       std::vector<std::string> defs;
@@ -793,7 +793,7 @@
     }
   }
   // add system defined c macros
-  cmProp cDefs =
+  cmValue cDefs =
     mf->GetDefinition("CMAKE_EXTRA_GENERATOR_C_SYSTEM_DEFINED_MACROS");
   if (this->CEnabled && cDefs) {
     // Expand the list.
@@ -825,7 +825,7 @@
     }
   }
   // add system defined c++ macros
-  cmProp cxxDefs =
+  cmValue cxxDefs =
     mf->GetDefinition("CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_DEFINED_MACROS");
   if (this->CXXEnabled && cxxDefs) {
     // Expand the list.
@@ -1032,7 +1032,7 @@
   xml.EndElement(); // storageModule
 
   // Append additional cproject contents without applying any XML formatting
-  if (cmProp extraCProjectContents =
+  if (cmValue extraCProjectContents =
         mf->GetState()->GetGlobalProperty("ECLIPSE_EXTRA_CPROJECT_CONTENTS")) {
     fout << *extraCProjectContents;
   }
diff --git a/Source/cmExtraKateGenerator.cxx b/Source/cmExtraKateGenerator.cxx
index 9153119..eec43c4 100644
--- a/Source/cmExtraKateGenerator.cxx
+++ b/Source/cmExtraKateGenerator.cxx
@@ -13,11 +13,11 @@
 #include "cmGlobalGenerator.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmExtraKateGenerator::cmExtraKateGenerator() = default;
 
@@ -128,7 +128,7 @@
             // only add the "edit_cache" target if it's not ccmake, because
             // this will not work within the IDE
             if (targetName == "edit_cache") {
-              cmProp editCommand =
+              cmValue editCommand =
                 localGen->GetMakefile()->GetDefinition("CMAKE_EDIT_COMMAND");
               if (!editCommand ||
                   strstr(editCommand->c_str(), "ccmake") != nullptr) {
diff --git a/Source/cmExtraSublimeTextGenerator.cxx b/Source/cmExtraSublimeTextGenerator.cxx
index 52965bb..fa93b04 100644
--- a/Source/cmExtraSublimeTextGenerator.cxx
+++ b/Source/cmExtraSublimeTextGenerator.cxx
@@ -17,11 +17,11 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 /*
@@ -364,12 +364,12 @@
                                                     language);
 
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
-  if (cmProp cflags = source->GetProperty(COMPILE_FLAGS)) {
+  if (cmValue cflags = source->GetProperty(COMPILE_FLAGS)) {
     lg->AppendFlags(flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
   }
 
   const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
-  if (cmProp coptions = source->GetProperty(COMPILE_OPTIONS)) {
+  if (cmValue coptions = source->GetProperty(COMPILE_OPTIONS)) {
     lg->AppendCompileOptions(
       flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
   }
@@ -393,14 +393,14 @@
   // Add preprocessor definitions for this target and configuration.
   lg->GetTargetDefines(target, config, language, defines);
   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
-  if (cmProp compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) {
+  if (cmValue compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) {
     lg->AppendDefines(
       defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS));
   }
 
   std::string defPropName =
     cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
-  if (cmProp config_compile_defs = source->GetProperty(defPropName)) {
+  if (cmValue config_compile_defs = source->GetProperty(defPropName)) {
     lg->AppendDefines(
       defines,
       genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
@@ -425,7 +425,7 @@
 
   // Add include directories for this source file
   const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
-  if (cmProp cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) {
+  if (cmValue cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) {
     lg->AppendIncludeDirectories(
       includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
       *source);
@@ -445,7 +445,7 @@
                                        const std::string& projectName,
                                        bool dryRun)
 {
-  cmProp sublExecutable =
+  cmValue sublExecutable =
     this->GlobalGenerator->GetCMakeInstance()->GetCacheDefinition(
       "CMAKE_SUBLIMETEXT_EXECUTABLE");
   if (!sublExecutable) {
diff --git a/Source/cmFileAPICache.cxx b/Source/cmFileAPICache.cxx
index ddae527..ba38ef7 100644
--- a/Source/cmFileAPICache.cxx
+++ b/Source/cmFileAPICache.cxx
@@ -10,8 +10,8 @@
 #include <cm3p/json/value.h>
 
 #include "cmFileAPI.h"
-#include "cmProperty.h"
 #include "cmState.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -95,7 +95,7 @@
 {
   Json::Value property = Json::objectValue;
   property["name"] = prop;
-  cmProp p = this->State->GetCacheEntryProperty(name, prop);
+  cmValue p = this->State->GetCacheEntryProperty(name, prop);
   property["value"] = p ? *p : "";
   return property;
 }
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index a2b5460..147181e 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -41,7 +41,6 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmSourceGroup.h"
 #include "cmState.h"
@@ -53,6 +52,7 @@
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
 #include "cmTargetExport.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -813,8 +813,7 @@
 Json::Value CodemodelConfig::DumpMinimumCMakeVersion(cmStateSnapshot s)
 {
   Json::Value minimumCMakeVersion;
-  if (std::string const* def =
-        s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
+  if (cmValue def = s.GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
     minimumCMakeVersion = Json::objectValue;
     minimumCMakeVersion["string"] = *def;
   }
@@ -1188,10 +1187,10 @@
 {
   CompileData& cd = this->CompileDataMap[lang];
   cd.Language = lang;
-  if (cmProp sysrootCompile =
+  if (cmValue sysrootCompile =
         this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
     cd.Sysroot = *sysrootCompile;
-  } else if (cmProp sysroot =
+  } else if (cmValue sysroot =
                this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) {
     cd.Sysroot = *sysroot;
   }
@@ -1260,7 +1259,7 @@
                                                     fd.Language);
 
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
-  if (cmProp cflags = sf->GetProperty(COMPILE_FLAGS)) {
+  if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) {
     std::string flags = genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS);
     fd.Flags.emplace_back(std::move(flags), JBTIndex());
   }
@@ -1354,7 +1353,7 @@
   std::set<std::string> configFileDefines;
   const std::string defPropName =
     "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(this->Config);
-  if (cmProp config_defs = sf->GetProperty(defPropName)) {
+  if (cmValue config_defs = sf->GetProperty(defPropName)) {
     lg->AppendDefines(
       configFileDefines,
       genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS));
@@ -1740,10 +1739,10 @@
       link["commandFragments"] = std::move(commandFragments);
     }
   }
-  if (cmProp sysrootLink =
+  if (cmValue sysrootLink =
         this->GT->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
     link["sysroot"] = this->DumpSysroot(*sysrootLink);
-  } else if (cmProp sysroot =
+  } else if (cmValue sysroot =
                this->GT->Makefile->GetDefinition("CMAKE_SYSROOT")) {
     link["sysroot"] = this->DumpSysroot(*sysroot);
   }
@@ -1869,7 +1868,7 @@
 Json::Value Target::DumpFolder()
 {
   Json::Value folder;
-  if (cmProp f = this->GT->GetProperty("FOLDER")) {
+  if (cmValue f = this->GT->GetProperty("FOLDER")) {
     folder = Json::objectValue;
     folder["name"] = *f;
   }
diff --git a/Source/cmFileAPIToolchains.cxx b/Source/cmFileAPIToolchains.cxx
index 722c114..b3540c9 100644
--- a/Source/cmFileAPIToolchains.cxx
+++ b/Source/cmFileAPIToolchains.cxx
@@ -11,9 +11,9 @@
 #include "cmFileAPI.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -136,7 +136,7 @@
       object[variable.ObjectKey] = jsonArray;
     }
   } else {
-    cmProp def = mf->GetDefinition(variableName);
+    cmValue def = mf->GetDefinition(variableName);
     if (def) {
       object[variable.ObjectKey] = *def;
     }
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 0ad59c7..c3ae228 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -31,6 +31,7 @@
 #include "cmArgumentParser.h"
 #include "cmCMakePath.h"
 #include "cmCryptoHash.h"
+#include "cmELF.h"
 #include "cmExecutionStatus.h"
 #include "cmFSPermissions.h"
 #include "cmFileCopier.h"
@@ -46,7 +47,6 @@
 #include "cmMessageType.h"
 #include "cmNewLineStyle.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmRuntimeDependencyArchive.h"
 #include "cmState.h"
@@ -54,6 +54,7 @@
 #include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
+#include "cmValue.h"
 #include "cmWorkingDirectory.h"
 #include "cmake.h"
 
@@ -64,41 +65,8 @@
 #  include "cmFileLockResult.h"
 #endif
 
-#if defined(CMake_USE_ELF_PARSER)
-#  include "cmELF.h"
-#endif
-
-#if defined(_WIN32)
-#  include <windows.h>
-#endif
-
 namespace {
 
-#if defined(_WIN32)
-// libcurl doesn't support file:// urls for unicode filenames on Windows.
-// Convert string from UTF-8 to ACP if this is a file:// URL.
-std::string fix_file_url_windows(const std::string& url)
-{
-  std::string ret = url;
-  if (strncmp(url.c_str(), "file://", 7) == 0) {
-    std::wstring wurl = cmsys::Encoding::ToWide(url);
-    if (!wurl.empty()) {
-      int mblen =
-        WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, NULL, 0, NULL, NULL);
-      if (mblen > 0) {
-        std::vector<char> chars(mblen);
-        mblen = WideCharToMultiByte(CP_ACP, 0, wurl.c_str(), -1, &chars[0],
-                                    mblen, NULL, NULL);
-        if (mblen > 0) {
-          ret = &chars[0];
-        }
-      }
-    }
-  }
-  return ret;
-}
-#endif
-
 bool HandleWriteImpl(std::vector<std::string> const& args, bool append,
                      cmExecutionStatus& status)
 {
@@ -672,8 +640,9 @@
       case cmPolicies::NEW:
         g.RecurseThroughSymlinksOff();
         break;
-      case cmPolicies::OLD:
       case cmPolicies::WARN:
+        CM_FALLTHROUGH;
+      case cmPolicies::OLD:
         g.RecurseThroughSymlinksOn();
         break;
     }
@@ -1242,8 +1211,17 @@
     return false;
   }
 
-#if defined(CMake_USE_ELF_PARSER)
   cmELF elf(fileNameArg.c_str());
+  if (!elf) {
+    if (arguments.Error.empty()) {
+      status.SetError(cmStrCat("READ_ELF given FILE:\n  ", fileNameArg,
+                               "\nthat is not a valid ELF file."));
+      return false;
+    }
+    status.GetMakefile().AddDefinition(arguments.Error,
+                                       "not a valid ELF file");
+    return true;
+  }
 
   if (!arguments.RPath.empty()) {
     if (cmELF::StringEntry const* se_rpath = elf.GetRPath()) {
@@ -1261,15 +1239,6 @@
   }
 
   return true;
-#else
-  std::string error = "ELF parser not available on this platform.";
-  if (arguments.Error.empty()) {
-    status.SetError(error);
-    return false;
-  }
-  status.GetMakefile().AddDefinition(arguments.Error, error);
-  return true;
-#endif
 }
 
 bool HandleInstallCommand(std::vector<std::string> const& args,
@@ -1803,7 +1772,7 @@
   std::string logVar;
   std::string statusVar;
   bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY");
-  cmProp cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
+  cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
   std::string netrc_level =
     status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
   std::string netrc_file =
@@ -1858,7 +1827,7 @@
     } else if (*i == "TLS_CAINFO") {
       ++i;
       if (i != args.end()) {
-        cainfo = &(*i);
+        cainfo = cmValue(*i);
       } else {
         status.SetError("DOWNLOAD missing file value for TLS_CAINFO.");
         return false;
@@ -1984,9 +1953,7 @@
     }
   }
 
-#  if defined(_WIN32)
-  url = fix_file_url_windows(url);
-#  endif
+  url = cmCurlFixFileURL(url);
 
   ::CURL* curl;
   ::curl_global_init(CURL_GLOBAL_DEFAULT);
@@ -2025,7 +1992,7 @@
 
   // check to see if a CAINFO file has been specified
   // command arg comes first
-  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cmToCStr(cainfo));
+  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
   if (!cainfo_err.empty()) {
     status.SetError(cainfo_err);
     return false;
@@ -2191,7 +2158,7 @@
   std::string statusVar;
   bool showProgress = false;
   bool tls_verify = status.GetMakefile().IsOn("CMAKE_TLS_VERIFY");
-  cmProp cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
+  cmValue cainfo = status.GetMakefile().GetDefinition("CMAKE_TLS_CAINFO");
   std::string userpwd;
   std::string netrc_level =
     status.GetMakefile().GetSafeDefinition("CMAKE_NETRC");
@@ -2244,7 +2211,7 @@
     } else if (*i == "TLS_CAINFO") {
       ++i;
       if (i != args.end()) {
-        cainfo = &(*i);
+        cainfo = cmValue(*i);
       } else {
         status.SetError("UPLOAD missing file value for TLS_CAINFO.");
         return false;
@@ -2300,9 +2267,7 @@
 
   unsigned long file_size = cmsys::SystemTools::FileLength(filename);
 
-#  if defined(_WIN32)
-  url = fix_file_url_windows(url);
-#  endif
+  url = cmCurlFixFileURL(url);
 
   ::CURL* curl;
   ::curl_global_init(CURL_GLOBAL_DEFAULT);
@@ -2345,7 +2310,7 @@
 
   // check to see if a CAINFO file has been specified
   // command arg comes first
-  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cmToCStr(cainfo));
+  std::string const& cainfo_err = cmCurlSetCAInfo(curl, cainfo);
   if (!cainfo_err.empty()) {
     status.SetError(cainfo_err);
     return false;
@@ -3210,9 +3175,12 @@
                             archive.GetUnresolvedPaths().begin(),
                             archive.GetUnresolvedPaths().end());
     } else {
-      auto it = archive.GetUnresolvedPaths().begin();
-      assert(it != archive.GetUnresolvedPaths().end());
-      status.SetError(cmStrCat("Could not resolve file ", *it));
+      std::ostringstream e;
+      e << "Could not resolve runtime dependencies:";
+      for (auto const& path : archive.GetUnresolvedPaths()) {
+        e << "\n  " << path;
+      }
+      status.SetError(e.str());
       cmSystemTools::SetFatalErrorOccured();
       return false;
     }
diff --git a/Source/cmFileCopier.cxx b/Source/cmFileCopier.cxx
index 237d234..63a4274 100644
--- a/Source/cmFileCopier.cxx
+++ b/Source/cmFileCopier.cxx
@@ -10,9 +10,9 @@
 #include "cmFSPermissions.h"
 #include "cmFileTimes.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 #ifdef _WIN32
 #  include "cmsys/FStream.hxx"
@@ -172,7 +172,7 @@
 bool cmFileCopier::GetDefaultDirectoryPermissions(mode_t** mode)
 {
   // check if default dir creation permissions were set
-  cmProp default_dir_install_permissions = this->Makefile->GetDefinition(
+  cmValue default_dir_install_permissions = this->Makefile->GetDefinition(
     "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS");
   if (cmNonempty(default_dir_install_permissions)) {
     std::vector<std::string> items =
diff --git a/Source/cmFileCopier.h b/Source/cmFileCopier.h
index 217d58d..ee9872d 100644
--- a/Source/cmFileCopier.h
+++ b/Source/cmFileCopier.h
@@ -67,8 +67,9 @@
 
   bool InstallSymlinkChain(std::string& fromFile, std::string& toFile);
   bool InstallSymlink(const std::string& fromFile, const std::string& toFile);
-  bool InstallFile(const std::string& fromFile, const std::string& toFile,
-                   MatchProperties match_properties);
+  virtual bool InstallFile(const std::string& fromFile,
+                           const std::string& toFile,
+                           MatchProperties match_properties);
   bool InstallDirectory(const std::string& source,
                         const std::string& destination,
                         MatchProperties match_properties);
diff --git a/Source/cmFileInstaller.cxx b/Source/cmFileInstaller.cxx
index c89be96..9bfbd13 100644
--- a/Source/cmFileInstaller.cxx
+++ b/Source/cmFileInstaller.cxx
@@ -3,7 +3,12 @@
 
 #include "cmFileInstaller.h"
 
+#include <map>
 #include <sstream>
+#include <utility>
+
+#include <cm/string_view>
+#include <cmext/string_view>
 
 #include "cm_sys_stat.h"
 
@@ -12,12 +17,14 @@
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 using namespace cmFSPermissions;
 
 cmFileInstaller::cmFileInstaller(cmExecutionStatus& status)
   : cmFileCopier(status, "INSTALL")
   , InstallType(cmInstallType_FILES)
+  , InstallMode(cmInstallMode::COPY)
   , Optional(false)
   , MessageAlways(false)
   , MessageLazy(false)
@@ -82,6 +89,93 @@
   return this->cmFileCopier::Install(fromFile, toFile);
 }
 
+bool cmFileInstaller::InstallFile(const std::string& fromFile,
+                                  const std::string& toFile,
+                                  MatchProperties match_properties)
+{
+  if (this->InstallMode == cmInstallMode::COPY) {
+    return this->cmFileCopier::InstallFile(fromFile, toFile, match_properties);
+  }
+
+  std::string newFromFile;
+
+  if (this->InstallMode == cmInstallMode::REL_SYMLINK ||
+      this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY ||
+      this->InstallMode == cmInstallMode::SYMLINK ||
+      this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) {
+    // Try to get a relative path.
+    std::string toDir = cmSystemTools::GetParentDirectory(toFile);
+    newFromFile = cmSystemTools::ForceToRelativePath(toDir, fromFile);
+
+    // Double check that we can restore the original path.
+    std::string reassembled =
+      cmSystemTools::CollapseFullPath(newFromFile, toDir);
+    if (!cmSystemTools::ComparePath(reassembled, fromFile)) {
+      if (this->InstallMode == cmInstallMode::SYMLINK ||
+          this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) {
+        // User does not mind, silently proceed with absolute path.
+        newFromFile = fromFile;
+      } else if (this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY) {
+        // User expects a relative symbolic link or a copy.
+        // Since an absolute symlink won't do, copy instead.
+        return this->cmFileCopier::InstallFile(fromFile, toFile,
+                                               match_properties);
+      } else {
+        // We cannot meet user's expectation (REL_SYMLINK)
+        auto e = cmStrCat(this->Name,
+                          " cannot determine relative path for symlink to \"",
+                          newFromFile, "\" at \"", toFile, "\".");
+        this->Status.SetError(e);
+        return false;
+      }
+    }
+  } else {
+    newFromFile = fromFile; // stick with absolute path
+  }
+
+  // Compare the symlink value to that at the destination if not
+  // always installing.
+  bool copy = true;
+  if (!this->Always) {
+    std::string oldSymlinkTarget;
+    if (cmSystemTools::ReadSymlink(toFile, oldSymlinkTarget)) {
+      if (newFromFile == oldSymlinkTarget) {
+        copy = false;
+      }
+    }
+  }
+
+  // Inform the user about this file installation.
+  this->ReportCopy(toFile, TypeLink, copy);
+
+  if (copy) {
+    // Remove the destination file so we can always create the symlink.
+    cmSystemTools::RemoveFile(toFile);
+
+    // Create destination directory if it doesn't exist
+    cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(toFile));
+
+    // Create the symlink.
+    if (!cmSystemTools::CreateSymlink(newFromFile, toFile)) {
+      if (this->InstallMode == cmInstallMode::ABS_SYMLINK_OR_COPY ||
+          this->InstallMode == cmInstallMode::REL_SYMLINK_OR_COPY ||
+          this->InstallMode == cmInstallMode::SYMLINK_OR_COPY) {
+        // Failed to create a symbolic link, fall back to copying.
+        return this->cmFileCopier::InstallFile(newFromFile, toFile,
+                                               match_properties);
+      }
+
+      auto e = cmStrCat(this->Name, " cannot create symlink to \"",
+                        newFromFile, "\" at \"", toFile,
+                        "\": ", cmSystemTools::GetLastSystemError(), "\".");
+      this->Status.SetError(e);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 void cmFileInstaller::DefaultFilePermissions()
 {
   this->cmFileCopier::DefaultFilePermissions();
@@ -141,6 +235,31 @@
     return false;
   }
 
+  static const std::map<cm::string_view, cmInstallMode> install_mode_dict{
+    { "ABS_SYMLINK"_s, cmInstallMode::ABS_SYMLINK },
+    { "ABS_SYMLINK_OR_COPY"_s, cmInstallMode::ABS_SYMLINK_OR_COPY },
+    { "REL_SYMLINK"_s, cmInstallMode::REL_SYMLINK },
+    { "REL_SYMLINK_OR_COPY"_s, cmInstallMode::REL_SYMLINK_OR_COPY },
+    { "SYMLINK"_s, cmInstallMode::SYMLINK },
+    { "SYMLINK_OR_COPY"_s, cmInstallMode::SYMLINK_OR_COPY }
+  };
+
+  std::string install_mode;
+  cmSystemTools::GetEnv("CMAKE_INSTALL_MODE", install_mode);
+  if (install_mode.empty() || install_mode == "COPY"_s) {
+    this->InstallMode = cmInstallMode::COPY;
+  } else {
+    auto it = install_mode_dict.find(install_mode);
+    if (it != install_mode_dict.end()) {
+      this->InstallMode = it->second;
+    } else {
+      auto e = cmStrCat("Unrecognized value '", install_mode,
+                        "' for environment variable CMAKE_INSTALL_MODE");
+      this->Status.SetError(e);
+      return false;
+    }
+  }
+
   return true;
 }
 
diff --git a/Source/cmFileInstaller.h b/Source/cmFileInstaller.h
index 3a905d3..3f6bd45 100644
--- a/Source/cmFileInstaller.h
+++ b/Source/cmFileInstaller.h
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "cmFileCopier.h"
+#include "cmInstallMode.h"
 #include "cmInstallType.h"
 
 class cmExecutionStatus;
@@ -19,6 +20,7 @@
 
 protected:
   cmInstallType InstallType;
+  cmInstallMode InstallMode;
   bool Optional;
   bool MessageAlways;
   bool MessageLazy;
@@ -35,7 +37,8 @@
   bool ReportMissing(const std::string& fromFile) override;
   bool Install(const std::string& fromFile,
                const std::string& toFile) override;
-
+  bool InstallFile(const std::string& fromFile, const std::string& toFile,
+                   MatchProperties match_properties) override;
   bool Parse(std::vector<std::string> const& args) override;
   enum
   {
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index 1038ac2..a123e44 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -13,13 +13,13 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSearchPath.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 class cmExecutionStatus;
@@ -301,9 +301,9 @@
 
 bool cmFindBase::CheckForVariableDefined()
 {
-  if (cmProp value = this->Makefile->GetDefinition(this->VariableName)) {
+  if (cmValue value = this->Makefile->GetDefinition(this->VariableName)) {
     cmState* state = this->Makefile->GetState();
-    cmProp cacheEntry = state->GetCacheEntryValue(this->VariableName);
+    cmValue cacheEntry = state->GetCacheEntryValue(this->VariableName);
     bool found = !cmIsNOTFOUND(*value);
     bool cached = cacheEntry != nullptr;
     auto cacheType = cached ? state->GetCacheEntryType(this->VariableName)
@@ -311,7 +311,7 @@
 
     if (cached && cacheType != cmStateEnums::UNINITIALIZED) {
       this->VariableType = cacheType;
-      if (const auto* hs =
+      if (const auto& hs =
             state->GetCacheEntryProperty(this->VariableName, "HELPSTRING")) {
         this->VariableDocumentation = *hs;
       }
@@ -336,7 +336,7 @@
   if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) ==
       cmPolicies::NEW) {
     // ensure the path returned by find_* command is absolute
-    const auto* existingValue =
+    const auto& existingValue =
       this->Makefile->GetDefinition(this->VariableName);
     std::string value;
     if (!existingValue->empty()) {
@@ -358,8 +358,8 @@
       // value.
       if (value != *existingValue || this->AlreadyInCacheWithoutMetaInfo) {
         this->Makefile->GetCMakeInstance()->AddCacheEntry(
-          this->VariableName, value.c_str(),
-          this->VariableDocumentation.c_str(), this->VariableType);
+          this->VariableName, value, this->VariableDocumentation.c_str(),
+          this->VariableType);
         if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) ==
             cmPolicies::NEW) {
           if (this->Makefile->IsNormalDefinitionSet(this->VariableName)) {
diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx
index d2f9619..bdc9207 100644
--- a/Source/cmFindCommon.cxx
+++ b/Source/cmFindCommon.cxx
@@ -11,9 +11,9 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmFindCommon::PathGroup cmFindCommon::PathGroup::All("ALL");
@@ -183,7 +183,7 @@
   };
 
   for (auto const& path : search_paths) {
-    cmProp def = this->Makefile->GetDefinition(path.second);
+    cmValue def = this->Makefile->GetDefinition(path.second);
     if (def) {
       path.first = !cmIsOn(*def);
     }
@@ -203,11 +203,11 @@
     return;
   }
 
-  cmProp sysroot = this->Makefile->GetDefinition("CMAKE_SYSROOT");
-  cmProp sysrootCompile =
+  cmValue sysroot = this->Makefile->GetDefinition("CMAKE_SYSROOT");
+  cmValue sysrootCompile =
     this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE");
-  cmProp sysrootLink = this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK");
-  cmProp rootPath = this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH");
+  cmValue sysrootLink = this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK");
+  cmValue rootPath = this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH");
   const bool noSysroot = !cmNonempty(sysroot);
   const bool noCompileSysroot = !cmNonempty(sysrootCompile);
   const bool noLinkSysroot = !cmNonempty(sysrootLink);
@@ -234,7 +234,7 @@
     cmSystemTools::ConvertToUnixSlashes(r);
   }
 
-  cmProp stagePrefix = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX");
+  cmValue stagePrefix = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX");
 
   // Copy the original set of unrooted paths.
   std::vector<std::string> unrootedPaths = paths;
diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx
index 0cbe637..ff04bab 100644
--- a/Source/cmFindLibraryCommand.cxx
+++ b/Source/cmFindLibraryCommand.cxx
@@ -12,11 +12,11 @@
 
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 class cmExecutionStatus;
 
@@ -46,7 +46,7 @@
 
   // add custom lib<qual> paths instead of using fixed lib32, lib64 or
   // libx32
-  if (cmProp customLib = this->Makefile->GetDefinition(
+  if (cmValue customLib = this->Makefile->GetDefinition(
         "CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX")) {
     this->AddArchitecturePaths(customLib->c_str());
   }
@@ -247,7 +247,7 @@
         cmStrCat(this->PrefixRegexStr, name, this->SuffixRegexStr);
       this->DebugSearches.FailedAt(path, regexName);
     }
-  };
+  }
 
   void DebugLibraryFound(std::string const& name, std::string const& path)
   {
@@ -256,9 +256,37 @@
         cmStrCat(this->PrefixRegexStr, name, this->SuffixRegexStr);
       this->DebugSearches.FoundAt(path, regexName);
     }
-  };
+  }
 };
 
+namespace {
+
+std::string const& get_prefixes(cmMakefile* mf)
+{
+#ifdef _WIN32
+  static std::string defaultPrefix = ";lib";
+#else
+  static std::string defaultPrefix = "lib";
+#endif
+  cmValue prefixProp = mf->GetDefinition("CMAKE_FIND_LIBRARY_PREFIXES");
+  return (prefixProp) ? *prefixProp : defaultPrefix;
+}
+
+std::string const& get_suffixes(cmMakefile* mf)
+{
+#ifdef _WIN32
+  static std::string defaultSuffix = ".lib;.dll.a;.a";
+#elif defined(__APPLE__)
+  static std::string defaultSuffix = ".tbd;.dylib;.so;.a";
+#elif defined(__hpux)
+  static std::string defaultSuffix = ".sl;.so;.a";
+#else
+  static std::string defaultSuffix = ".so;.a";
+#endif
+  cmValue suffixProp = mf->GetDefinition("CMAKE_FIND_LIBRARY_SUFFIXES");
+  return (suffixProp) ? *suffixProp : defaultSuffix;
+}
+}
 cmFindLibraryHelper::cmFindLibraryHelper(std::string debugName, cmMakefile* mf,
                                          cmFindBase const* base)
   : Makefile(mf)
@@ -268,10 +296,9 @@
   this->GG = this->Makefile->GetGlobalGenerator();
 
   // Collect the list of library name prefixes/suffixes to try.
-  std::string const& prefixes_list =
-    this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_PREFIXES");
-  std::string const& suffixes_list =
-    this->Makefile->GetRequiredDefinition("CMAKE_FIND_LIBRARY_SUFFIXES");
+  std::string const& prefixes_list = get_prefixes(this->Makefile);
+  std::string const& suffixes_list = get_suffixes(this->Makefile);
+
   cmExpandList(prefixes_list, this->Prefixes, true);
   cmExpandList(suffixes_list, this->Suffixes, true);
   this->RegexFromList(this->PrefixRegexStr, this->Prefixes);
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index fba736e..335ebbe 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -25,13 +25,13 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSearchPath.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 
 #if defined(__HAIKU__)
@@ -137,7 +137,7 @@
   }
 
   // Lookup required version of CMake.
-  if (cmProp rv =
+  if (cmValue rv =
         this->Makefile->GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION")) {
     unsigned int v[3] = { 0, 0, 0 };
     sscanf(rv->c_str(), "%u.%u.%u", &v[0], &v[1], &v[2]);
@@ -148,7 +148,7 @@
   this->DebugBuffer.clear();
 
   // Lookup target architecture, if any.
-  if (cmProp arch =
+  if (cmValue arch =
         this->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE")) {
     this->LibraryArchitecture = *arch;
   }
@@ -177,7 +177,7 @@
   // Check if User Package Registry should be disabled
   // The `CMAKE_FIND_USE_PACKAGE_REGISTRY` has
   // priority over the deprecated CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
-  if (cmProp def =
+  if (cmValue def =
         this->Makefile->GetDefinition("CMAKE_FIND_USE_PACKAGE_REGISTRY")) {
     this->NoUserRegistry = !cmIsOn(*def);
   } else if (this->Makefile->IsOn("CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY")) {
@@ -187,7 +187,7 @@
   // Check if System Package Registry should be disabled
   // The `CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY` has
   // priority over the deprecated CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
-  if (cmProp def = this->Makefile->GetDefinition(
+  if (cmValue def = this->Makefile->GetDefinition(
         "CMAKE_FIND_USE_SYSTEM_PACKAGE_REGISTRY")) {
     this->NoSystemRegistry = !cmIsOn(*def);
   } else if (this->Makefile->IsOn(
@@ -201,7 +201,7 @@
   }
 
   // Check if Sorting should be enabled
-  if (cmProp so =
+  if (cmValue so =
         this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_ORDER")) {
 
     if (*so == "NAME") {
@@ -212,7 +212,7 @@
       this->SortOrder = None;
     }
   }
-  if (cmProp sd =
+  if (cmValue sd =
         this->Makefile->GetDefinition("CMAKE_FIND_PACKAGE_SORT_DIRECTION")) {
     this->SortDirection = (*sd == "ASC") ? Asc : Dec;
   }
@@ -478,17 +478,35 @@
       this->VersionMaxPatch, this->VersionMaxTweak);
   }
 
+  const std::string makePackageRequiredVar =
+    cmStrCat("CMAKE_REQUIRE_FIND_PACKAGE_", this->Name);
+  const bool makePackageRequiredSet =
+    this->Makefile->IsOn(makePackageRequiredVar);
+  if (makePackageRequiredSet) {
+    if (this->Required) {
+      this->Makefile->IssueMessage(
+        MessageType::WARNING,
+        cmStrCat("for module ", this->Name,
+                 " already called with REQUIRED, thus ",
+                 makePackageRequiredVar, " has no effect."));
+    } else {
+      this->Required = true;
+    }
+  }
+
   std::string disableFindPackageVar =
     cmStrCat("CMAKE_DISABLE_FIND_PACKAGE_", this->Name);
   if (this->Makefile->IsOn(disableFindPackageVar)) {
     if (this->Required) {
       this->SetError(
-        cmStrCat("for module ", this->Name, " called with REQUIRED, but ",
-                 disableFindPackageVar,
+        cmStrCat("for module ", this->Name,
+                 (makePackageRequiredSet
+                    ? " was made REQUIRED with " + makePackageRequiredVar
+                    : " called with REQUIRED, "),
+                 " but ", disableFindPackageVar,
                  " is enabled. A REQUIRED package cannot be disabled."));
       return false;
     }
-
     return true;
   }
 
@@ -735,7 +753,7 @@
 void cmFindPackageCommand::AddFindDefinition(const std::string& var,
                                              cm::string_view value)
 {
-  if (cmProp old = this->Makefile->GetDefinition(var)) {
+  if (cmValue old = this->Makefile->GetDefinition(var)) {
     this->OriginalDefs[var].exists = true;
     this->OriginalDefs[var].value = *old;
   } else {
@@ -828,7 +846,7 @@
   this->ConsideredConfigs.clear();
 
   // Try to find the config file.
-  cmProp def = this->Makefile->GetDefinition(this->Variable);
+  cmValue def = this->Makefile->GetDefinition(this->Variable);
 
   // Try to load the config file if the directory is known
   bool fileFound = false;
@@ -1188,7 +1206,7 @@
 void cmFindPackageCommand::AppendToFoundProperty(bool found)
 {
   std::vector<std::string> foundContents;
-  cmProp foundProp =
+  cmValue foundProp =
     this->Makefile->GetState()->GetGlobalProperty("PACKAGES_FOUND");
   if (cmNonempty(foundProp)) {
     cmExpandList(*foundProp, foundContents, false);
@@ -1200,7 +1218,7 @@
   }
 
   std::vector<std::string> notFoundContents;
-  cmProp notFoundProp =
+  cmValue notFoundProp =
     this->Makefile->GetState()->GetGlobalProperty("PACKAGES_NOT_FOUND");
   if (cmNonempty(notFoundProp)) {
     cmExpandList(*notFoundProp, notFoundContents, false);
diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
index 4845a6d..dcb3626 100644
--- a/Source/cmForEachCommand.cxx
+++ b/Source/cmForEachCommand.cxx
@@ -30,6 +30,7 @@
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 class cmForEachFunctionBlocker : public cmFunctionBlocker
diff --git a/Source/cmFortranParser.h b/Source/cmFortranParser.h
index 1b14d17..70fe537 100644
--- a/Source/cmFortranParser.h
+++ b/Source/cmFortranParser.h
@@ -40,6 +40,8 @@
 /* Callbacks for parser.  */
 void cmFortranParser_Error(cmFortranParser* parser, const char* message);
 void cmFortranParser_RuleUse(cmFortranParser* parser, const char* module_name);
+void cmFortranParser_RuleUseIntrinsic(cmFortranParser* parser,
+                                      const char* module_name);
 void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
                                        const char* filename);
 void cmFortranParser_RuleInclude(cmFortranParser* parser, const char* name);
@@ -99,6 +101,9 @@
   std::set<std::string> Provides;
   std::set<std::string> Requires;
 
+  // Set of intrinsic modules.
+  std::set<std::string> Intrinsics;
+
   // Set of files included in the translation unit.
   std::set<std::string> Includes;
 };
diff --git a/Source/cmFortranParserImpl.cxx b/Source/cmFortranParserImpl.cxx
index 054a2a9..efcc5bb 100644
--- a/Source/cmFortranParserImpl.cxx
+++ b/Source/cmFortranParserImpl.cxx
@@ -197,6 +197,19 @@
   parser->Info.Requires.insert(parser->ModName(mod_name));
 }
 
+void cmFortranParser_RuleUseIntrinsic(cmFortranParser* parser,
+                                      const char* module_name)
+{
+  if (parser->InPPFalseBranch) {
+    return;
+  }
+
+  // syntax:   "use, intrinsic:: module_name"
+  // requires: "module_name.mod"
+  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
+  parser->Info.Intrinsics.insert(parser->ModName(mod_name));
+}
+
 void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
                                        const char* filename)
 {
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 217ebe5..c357ee1 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -37,7 +37,6 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStandardLevelResolver.h"
 #include "cmState.h"
@@ -46,6 +45,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
@@ -771,8 +771,7 @@
     }
 
     return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
-                                         parameters.front().c_str(),
-                                         compilerVersion.c_str())
+                                         parameters.front(), compilerVersion)
       ? "1"
       : "0";
   }
@@ -830,8 +829,7 @@
     const GeneratorExpressionContent* /*content*/,
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
-    return cmSystemTools::VersionCompare(Op, parameters.front().c_str(),
-                                         parameters[1].c_str())
+    return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1])
       ? "1"
       : "0";
   }
@@ -916,8 +914,8 @@
     }
 
     if (context->CurrentTarget && context->CurrentTarget->IsImported()) {
-      cmProp loc = nullptr;
-      cmProp imp = nullptr;
+      cmValue loc = nullptr;
+      cmValue imp = nullptr;
       std::string suffix;
       if (context->CurrentTarget->Target->GetMappedConfig(context->Config, loc,
                                                           imp, suffix)) {
@@ -927,7 +925,7 @@
         std::vector<std::string> mappedConfigs;
         std::string mapProp = cmStrCat(
           "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config));
-        if (cmProp mapValue = context->CurrentTarget->GetProperty(mapProp)) {
+        if (cmValue mapValue = context->CurrentTarget->GetProperty(mapProp)) {
           cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs);
 
           for (auto const& param : parameters) {
@@ -1504,7 +1502,7 @@
 
     std::string result;
     bool haveProp = false;
-    if (cmProp p = target->GetProperty(propertyName)) {
+    if (cmValue p = target->GetProperty(propertyName)) {
       result = *p;
       haveProp = true;
     } else if (evaluatingLinkLibraries) {
@@ -1654,8 +1652,8 @@
     std::vector<std::string> objects;
 
     if (gt->IsImported()) {
-      cmProp loc = nullptr;
-      cmProp imp = nullptr;
+      cmValue loc = nullptr;
+      cmValue imp = nullptr;
       std::string suffix;
       if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) {
         cmExpandList(*loc, objects);
@@ -1777,7 +1775,7 @@
       testedFeatures[lang].push_back(p);
 
       if (availableFeatures.find(lang) == availableFeatures.end()) {
-        const char* featuresKnown =
+        cmValue featuresKnown =
           standardResolver.CompileFeaturesAvailable(lang, &error);
         if (!featuresKnown) {
           reportError(context, content->GetOriginalExpression(), error);
@@ -1792,7 +1790,7 @@
     for (auto const& lit : testedFeatures) {
       std::vector<std::string> const& langAvailable =
         availableFeatures[lit.first];
-      cmProp standardDefault = context->LG->GetMakefile()->GetDefinition(
+      cmValue standardDefault = context->LG->GetMakefile()->GetDefinition(
         "CMAKE_" + lit.first + "_STANDARD_DEFAULT");
       for (std::string const& it : lit.second) {
         if (!cm::contains(langAvailable, it)) {
@@ -1806,7 +1804,8 @@
         if (!standardResolver.HaveStandardAvailable(target, lit.first,
                                                     context->Config, it)) {
           if (evalLL) {
-            cmProp l = target->GetLanguageStandard(lit.first, context->Config);
+            cmValue l =
+              target->GetLanguageStandard(lit.first, context->Config);
             if (!l) {
               l = standardDefault;
             }
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index f1ef130..8033ef5 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -58,11 +58,11 @@
 }
 
 template <>
-cmProp cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
+cmValue cmTargetPropertyComputer::GetSources<cmGeneratorTarget>(
   cmGeneratorTarget const* tgt, cmMessenger* /* messenger */,
   cmListFileBacktrace const& /* context */)
 {
-  return &tgt->GetSourcesProperty();
+  return tgt->GetSourcesProperty();
 }
 
 template <>
@@ -145,12 +145,10 @@
 class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
 {
 public:
-  TargetPropertyEntryString(std::string propertyValue,
-                            cmListFileBacktrace backtrace,
+  TargetPropertyEntryString(BT<std::string> propertyValue,
                             cmLinkImplItem const& item = NoLinkImplItem)
     : cmGeneratorTarget::TargetPropertyEntry(item)
     , PropertyValue(std::move(propertyValue))
-    , Backtrace(std::move(backtrace))
   {
   }
 
@@ -159,46 +157,46 @@
                               cmGeneratorExpressionDAGChecker*,
                               std::string const&) const override
   {
-    return this->PropertyValue;
+    return this->PropertyValue.Value;
   }
 
-  cmListFileBacktrace GetBacktrace() const override { return this->Backtrace; }
-  std::string const& GetInput() const override { return this->PropertyValue; }
+  cmListFileBacktrace GetBacktrace() const override
+  {
+    return this->PropertyValue.Backtrace;
+  }
+  std::string const& GetInput() const override
+  {
+    return this->PropertyValue.Value;
+  }
 
 private:
-  std::string PropertyValue;
-  cmListFileBacktrace Backtrace;
+  BT<std::string> PropertyValue;
 };
 
 std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>
-CreateTargetPropertyEntry(
-  const std::string& propertyValue,
-  cmListFileBacktrace backtrace = cmListFileBacktrace(),
-  bool evaluateForBuildsystem = false)
+CreateTargetPropertyEntry(const BT<std::string>& propertyValue,
+                          bool evaluateForBuildsystem = false)
 {
-  if (cmGeneratorExpression::Find(propertyValue) != std::string::npos) {
-    cmGeneratorExpression ge(std::move(backtrace));
+  if (cmGeneratorExpression::Find(propertyValue.Value) != std::string::npos) {
+    cmGeneratorExpression ge(propertyValue.Backtrace);
     std::unique_ptr<cmCompiledGeneratorExpression> cge =
-      ge.Parse(propertyValue);
+      ge.Parse(propertyValue.Value);
     cge->SetEvaluateForBuildsystem(evaluateForBuildsystem);
     return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>(
       cm::make_unique<TargetPropertyEntryGenex>(std::move(cge)));
   }
 
   return std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>(
-    cm::make_unique<TargetPropertyEntryString>(propertyValue,
-                                               std::move(backtrace)));
+    cm::make_unique<TargetPropertyEntryString>(propertyValue));
 }
 
 void CreatePropertyGeneratorExpressions(
-  cmStringRange entries, cmBacktraceRange backtraces,
+  cmBTStringRange entries,
   std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items,
   bool evaluateForBuildsystem = false)
 {
-  auto btIt = backtraces.begin();
-  for (auto it = entries.begin(); it != entries.end(); ++it, ++btIt) {
-    items.push_back(
-      CreateTargetPropertyEntry(*it, *btIt, evaluateForBuildsystem));
+  for (auto const& entry : entries) {
+    items.push_back(CreateTargetPropertyEntry(entry, evaluateForBuildsystem));
   }
 }
 
@@ -289,35 +287,27 @@
   this->GlobalGenerator->ComputeTargetObjectDirectory(this);
 
   CreatePropertyGeneratorExpressions(t->GetIncludeDirectoriesEntries(),
-                                     t->GetIncludeDirectoriesBacktraces(),
                                      this->IncludeDirectoriesEntries);
 
   CreatePropertyGeneratorExpressions(t->GetCompileOptionsEntries(),
-                                     t->GetCompileOptionsBacktraces(),
                                      this->CompileOptionsEntries);
 
   CreatePropertyGeneratorExpressions(t->GetCompileFeaturesEntries(),
-                                     t->GetCompileFeaturesBacktraces(),
                                      this->CompileFeaturesEntries);
 
   CreatePropertyGeneratorExpressions(t->GetCompileDefinitionsEntries(),
-                                     t->GetCompileDefinitionsBacktraces(),
                                      this->CompileDefinitionsEntries);
 
   CreatePropertyGeneratorExpressions(t->GetLinkOptionsEntries(),
-                                     t->GetLinkOptionsBacktraces(),
                                      this->LinkOptionsEntries);
 
   CreatePropertyGeneratorExpressions(t->GetLinkDirectoriesEntries(),
-                                     t->GetLinkDirectoriesBacktraces(),
                                      this->LinkDirectoriesEntries);
 
   CreatePropertyGeneratorExpressions(t->GetPrecompileHeadersEntries(),
-                                     t->GetPrecompileHeadersBacktraces(),
                                      this->PrecompileHeadersEntries);
 
   CreatePropertyGeneratorExpressions(t->GetSourceEntries(),
-                                     t->GetSourceBacktraces(),
                                      this->SourceEntries, true);
 
   this->PolicyMap = t->GetPolicyMap();
@@ -332,7 +322,7 @@
 
 cmGeneratorTarget::~cmGeneratorTarget() = default;
 
-const std::string& cmGeneratorTarget::GetSourcesProperty() const
+cmValue cmGeneratorTarget::GetSourcesProperty() const
 {
   std::vector<std::string> values;
   for (auto const& se : this->SourceEntries) {
@@ -341,7 +331,7 @@
   static std::string value;
   value.clear();
   value = cmJoin(values, ";");
-  return value;
+  return cmValue(value);
 }
 
 cmGlobalGenerator* cmGeneratorTarget::GetGlobalGenerator() const
@@ -366,7 +356,7 @@
 
 std::string cmGeneratorTarget::GetExportName() const
 {
-  cmProp exportName = this->GetProperty("EXPORT_NAME");
+  cmValue exportName = this->GetProperty("EXPORT_NAME");
 
   if (cmNonempty(exportName)) {
     if (!cmGeneratorExpression::IsValidTargetName(*exportName)) {
@@ -381,9 +371,9 @@
   return this->GetName();
 }
 
-cmProp cmGeneratorTarget::GetProperty(const std::string& prop) const
+cmValue cmGeneratorTarget::GetProperty(const std::string& prop) const
 {
-  if (cmProp result = cmTargetPropertyComputer::GetProperty(
+  if (cmValue result = cmTargetPropertyComputer::GetProperty(
         this, prop, this->Makefile->GetMessenger(), this->GetBacktrace())) {
     return result;
   }
@@ -396,13 +386,7 @@
 std::string const& cmGeneratorTarget::GetSafeProperty(
   std::string const& prop) const
 {
-  cmProp ret = this->GetProperty(prop);
-  if (ret) {
-    return *ret;
-  }
-
-  static std::string const s_empty;
-  return s_empty;
+  return this->GetProperty(prop);
 }
 
 const char* cmGeneratorTarget::GetOutputTargetType(
@@ -491,7 +475,7 @@
 
     std::string outName;
     for (std::string const& p : props) {
-      if (cmProp outNameProp = this->GetProperty(p)) {
+      if (cmValue outNameProp = this->GetProperty(p)) {
         outName = *outNameProp;
         break;
       }
@@ -519,7 +503,7 @@
   const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   if (this->IsImported()) {
-    cmProp prefix = this->GetFilePrefixInternal(config, artifact);
+    cmValue prefix = this->GetFilePrefixInternal(config, artifact);
     return prefix ? *prefix : std::string();
   }
 
@@ -533,7 +517,7 @@
   const std::string& config, cmStateEnums::ArtifactType artifact) const
 {
   if (this->IsImported()) {
-    cmProp suffix = this->GetFileSuffixInternal(config, artifact);
+    cmValue suffix = this->GetFileSuffixInternal(config, artifact);
     return suffix ? *suffix : std::string();
   }
 
@@ -546,7 +530,7 @@
 
 std::string cmGeneratorTarget::GetFilePostfix(const std::string& config) const
 {
-  cmProp postfix = nullptr;
+  cmValue postfix = nullptr;
   std::string frameworkPostfix;
   if (!config.empty()) {
     std::string configProp =
@@ -564,7 +548,7 @@
     // framework postfix.
     frameworkPostfix = this->GetFrameworkMultiConfigPostfix(config);
     if (!frameworkPostfix.empty()) {
-      postfix = &frameworkPostfix;
+      postfix = cmValue(frameworkPostfix);
     }
   }
   return postfix ? *postfix : std::string();
@@ -573,7 +557,7 @@
 std::string cmGeneratorTarget::GetFrameworkMultiConfigPostfix(
   const std::string& config) const
 {
-  cmProp postfix = nullptr;
+  cmValue postfix = nullptr;
   if (!config.empty()) {
     std::string configProp = cmStrCat("FRAMEWORK_MULTI_CONFIG_POSTFIX_",
                                       cmSystemTools::UpperCase(config));
@@ -588,7 +572,7 @@
   return postfix ? *postfix : std::string();
 }
 
-cmProp cmGeneratorTarget::GetFilePrefixInternal(
+cmValue cmGeneratorTarget::GetFilePrefixInternal(
   std::string const& config, cmStateEnums::ArtifactType artifact,
   const std::string& language) const
 {
@@ -618,7 +602,7 @@
   }
 
   // Compute prefix value.
-  cmProp targetPrefix =
+  cmValue targetPrefix =
     (isImportedLibraryArtifact ? this->GetProperty("IMPORT_PREFIX")
                                : this->GetProperty("PREFIX"));
 
@@ -639,7 +623,7 @@
   return targetPrefix;
 }
 
-cmProp cmGeneratorTarget::GetFileSuffixInternal(
+cmValue cmGeneratorTarget::GetFileSuffixInternal(
   std::string const& config, cmStateEnums::ArtifactType artifact,
   const std::string& language) const
 {
@@ -669,7 +653,7 @@
   }
 
   // Compute suffix value.
-  cmProp targetSuffix =
+  cmValue targetSuffix =
     (isImportedLibraryArtifact ? this->GetProperty("IMPORT_SUFFIX")
                                : this->GetProperty("SUFFIX"));
 
@@ -704,7 +688,8 @@
 {
   this->SourceEntries.insert(
     before ? this->SourceEntries.begin() : this->SourceEntries.end(),
-    CreateTargetPropertyEntry(src, this->Makefile->GetBacktrace(), true));
+    CreateTargetPropertyEntry(
+      BT<std::string>(src, this->Makefile->GetBacktrace()), true));
   this->ClearSourcesCache();
 }
 
@@ -725,11 +710,13 @@
 void cmGeneratorTarget::AddIncludeDirectory(const std::string& src,
                                             bool before)
 {
-  this->Target->InsertInclude(src, this->Makefile->GetBacktrace(), before);
+  this->Target->InsertInclude(
+    BT<std::string>(src, this->Makefile->GetBacktrace()), before);
   this->IncludeDirectoriesEntries.insert(
     before ? this->IncludeDirectoriesEntries.begin()
            : this->IncludeDirectoriesEntries.end(),
-    CreateTargetPropertyEntry(src, this->Makefile->GetBacktrace(), true));
+    CreateTargetPropertyEntry(
+      BT<std::string>(src, this->Makefile->GetBacktrace()), true));
 }
 
 std::vector<cmSourceFile*> const* cmGeneratorTarget::GetSourceDepends(
@@ -751,7 +738,7 @@
                              std::vector<std::string>& result,
                              bool excludeImported, std::string const& language)
 {
-  if (cmProp dirs =
+  if (cmValue dirs =
         depTgt->GetProperty("INTERFACE_SYSTEM_INCLUDE_DIRECTORIES")) {
     cmExpandList(cmGeneratorExpression::Evaluate(*dirs, lg, config, headTarget,
                                                  dagChecker, depTgt, language),
@@ -761,7 +748,7 @@
     return;
   }
 
-  if (cmProp dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) {
+  if (cmValue dirs = depTgt->GetProperty("INTERFACE_INCLUDE_DIRECTORIES")) {
     cmExpandList(cmGeneratorExpression::Evaluate(*dirs, lg, config, headTarget,
                                                  dagChecker, depTgt, language),
                  result);
@@ -813,17 +800,17 @@
   }
 }
 
-cmProp cmGeneratorTarget::GetFeature(const std::string& feature,
-                                     const std::string& config) const
+cmValue cmGeneratorTarget::GetFeature(const std::string& feature,
+                                      const std::string& config) const
 {
   if (!config.empty()) {
     std::string featureConfig =
       cmStrCat(feature, '_', cmSystemTools::UpperCase(config));
-    if (cmProp value = this->GetProperty(featureConfig)) {
+    if (cmValue value = this->GetProperty(featureConfig)) {
       return value;
     }
   }
-  if (cmProp value = this->GetProperty(feature)) {
+  if (cmValue value = this->GetProperty(feature)) {
     return value;
   }
   return this->LocalGenerator->GetFeature(feature, config);
@@ -851,7 +838,7 @@
 bool cmGeneratorTarget::IsIPOEnabled(std::string const& lang,
                                      std::string const& config) const
 {
-  cmProp feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config);
+  cmValue feature = this->GetFeature("INTERPROCEDURAL_OPTIMIZATION", config);
 
   if (!cmIsOn(feature)) {
     // 'INTERPROCEDURAL_OPTIMIZATION' is off, no need to check policies
@@ -959,23 +946,23 @@
     cmStrCat(lang, "_STANDARD"));
 }
 
-cmProp cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
-                                              std::string const& config) const
+cmValue cmGeneratorTarget::GetLanguageStandard(std::string const& lang,
+                                               std::string const& config) const
 {
   BTs<std::string> const* languageStandard =
     this->GetLanguageStandardProperty(lang, config);
 
   if (languageStandard) {
-    return &(languageStandard->Value);
+    return cmValue(languageStandard->Value);
   }
 
   return nullptr;
 }
 
-cmProp cmGeneratorTarget::GetPropertyWithPairedLanguageSupport(
+cmValue cmGeneratorTarget::GetPropertyWithPairedLanguageSupport(
   std::string const& lang, const char* suffix) const
 {
-  cmProp propertyValue = this->Target->GetProperty(cmStrCat(lang, suffix));
+  cmValue propertyValue = this->Target->GetProperty(cmStrCat(lang, suffix));
   if (!propertyValue) {
     // Check if we should use the value set by another language.
     if (lang == "OBJC") {
@@ -988,7 +975,7 @@
   return propertyValue;
 }
 
-cmProp cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const
+cmValue cmGeneratorTarget::GetLanguageExtensions(std::string const& lang) const
 {
   return this->GetPropertyWithPairedLanguageSupport(lang, "_EXTENSIONS");
 }
@@ -1150,6 +1137,7 @@
       if (!this->SourceEntries.empty()) {
         return true;
       }
+      break;
     case cmStateEnums::UNKNOWN_LIBRARY:
       break;
   }
@@ -1183,7 +1171,7 @@
   // Now handle the deprecated build-time configuration location.
   std::string const noConfig;
   location = this->GetDirectory(noConfig);
-  cmProp cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR");
+  cmValue cfgid = this->Makefile->GetDefinition("CMAKE_CFG_INTDIR");
   if (cfgid && (*cfgid != ".")) {
     location += "/";
     location += *cfgid;
@@ -1343,7 +1331,7 @@
   cmGeneratorTarget const* headTarget =
     context->HeadTarget ? context->HeadTarget : this;
 
-  if (cmProp p = this->GetProperty(prop)) {
+  if (cmValue p = this->GetProperty(prop)) {
     result = cmGeneratorExpressionNode::EvaluateDependentExpression(
       *p, context->LG, context, headTarget, &dagChecker, this);
   }
@@ -1466,7 +1454,7 @@
           auto* lg = dependency->GetLocalGenerator();
           EvaluatedTargetPropertyEntry entry{ library, library.Backtrace };
 
-          if (cmProp val = dependency->GetProperty(propertyName)) {
+          if (cmValue val = dependency->GetProperty(propertyName)) {
             entry.Values.emplace_back(*val);
           } else {
             if (mode == IncludeDirectoryFallBack::BINARY) {
@@ -1681,9 +1669,9 @@
     // for TARGET_OBJECTS instead for backwards compatibility with OLD
     // behavior of CMP0024 and CMP0026 only.
 
-    cmStringRange sourceEntries = this->Target->GetSourceEntries();
-    for (std::string const& entry : sourceEntries) {
-      std::vector<std::string> items = cmExpandedList(entry);
+    cmBTStringRange sourceEntries = this->Target->GetSourceEntries();
+    for (auto const& entry : sourceEntries) {
+      std::vector<std::string> items = cmExpandedList(entry.Value);
       for (std::string const& item : items) {
         if (cmHasLiteralPrefix(item, "$<TARGET_OBJECTS:") &&
             item.back() == '>') {
@@ -1998,12 +1986,12 @@
   // Check for a per-configuration output directory target property.
   std::string configUpper = cmSystemTools::UpperCase(config);
   std::string configProp = cmStrCat("COMPILE_PDB_NAME_", configUpper);
-  cmProp config_name = this->GetProperty(configProp);
+  cmValue config_name = this->GetProperty(configProp);
   if (cmNonempty(config_name)) {
     return prefix + *config_name + ".pdb";
   }
 
-  cmProp name = this->GetProperty("COMPILE_PDB_NAME");
+  cmValue name = this->GetProperty("COMPILE_PDB_NAME");
   if (cmNonempty(name)) {
     return prefix + *name + ".pdb";
   }
@@ -2142,34 +2130,29 @@
     return true;
   }
 
-#if defined(CMake_USE_ELF_PARSER) || defined(CMake_USE_XCOFF_PARSER)
   // Enable if the rpath flag uses a separator and the target uses
   // binaries we know how to edit.
   std::string ll = this->GetLinkerLanguage(config);
   if (!ll.empty()) {
     std::string sepVar =
       cmStrCat("CMAKE_SHARED_LIBRARY_RUNTIME_", ll, "_FLAG_SEP");
-    cmProp sep = this->Makefile->GetDefinition(sepVar);
+    cmValue sep = this->Makefile->GetDefinition(sepVar);
     if (cmNonempty(sep)) {
       // TODO: Add binary format check to ABI detection and get rid of
       // CMAKE_EXECUTABLE_FORMAT.
-      if (cmProp fmt =
+      if (cmValue fmt =
             this->Makefile->GetDefinition("CMAKE_EXECUTABLE_FORMAT")) {
-#  if defined(CMake_USE_ELF_PARSER)
         if (*fmt == "ELF") {
           return true;
         }
-#  endif
-#  if defined(CMake_USE_XCOFF_PARSER)
+#if defined(CMake_USE_XCOFF_PARSER)
         if (*fmt == "XCOFF") {
           return true;
         }
-#  endif
+#endif
       }
     }
   }
-#endif
-  static_cast<void>(config);
   return false;
 }
 
@@ -2188,6 +2171,21 @@
 bool cmGeneratorTarget::HasMacOSXRpathInstallNameDir(
   const std::string& config) const
 {
+  TargetPtrToBoolMap& cache = this->MacOSXRpathInstallNameDirCache[config];
+  const auto lookup = cache.find(this->Target);
+
+  if (lookup != cache.cend()) {
+    return lookup->second;
+  }
+
+  const bool result = this->DetermineHasMacOSXRpathInstallNameDir(config);
+  cache[this->Target] = result;
+  return result;
+}
+
+bool cmGeneratorTarget::DetermineHasMacOSXRpathInstallNameDir(
+  const std::string& config) const
+{
   bool install_name_is_rpath = false;
   bool macosx_rpath = false;
 
@@ -2195,7 +2193,7 @@
     if (this->GetType() != cmStateEnums::SHARED_LIBRARY) {
       return false;
     }
-    cmProp install_name = this->GetProperty("INSTALL_NAME_DIR");
+    cmValue install_name = this->GetProperty("INSTALL_NAME_DIR");
     bool use_install_name = this->MacOSXUseInstallNameDir();
     if (install_name && use_install_name && *install_name == "@rpath") {
       install_name_is_rpath = true;
@@ -2253,7 +2251,7 @@
     return false;
   }
 
-  cmProp macosx_rpath_str = this->GetProperty("MACOSX_RPATH");
+  cmValue macosx_rpath_str = this->GetProperty("MACOSX_RPATH");
   if (macosx_rpath_str) {
     return this->GetPropertyAsBool("MACOSX_RPATH");
   }
@@ -2270,7 +2268,7 @@
 
 bool cmGeneratorTarget::MacOSXUseInstallNameDir() const
 {
-  cmProp build_with_install_name =
+  cmValue build_with_install_name =
     this->GetProperty("BUILD_WITH_INSTALL_NAME_DIR");
   if (build_with_install_name) {
     return cmIsOn(*build_with_install_name);
@@ -2363,7 +2361,7 @@
 {
   std::string fpath = cmStrCat(
     this->GetFullName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
-  cmProp ext = this->GetProperty("BUNDLE_EXTENSION");
+  cmValue ext = this->GetProperty("BUNDLE_EXTENSION");
   fpath += (ext ? *ext : "app");
   if (shouldAddContentLevel(level) &&
       !this->Makefile->PlatformIsAppleEmbedded()) {
@@ -2393,7 +2391,7 @@
   std::string fpath = cmStrCat(
     this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
   std::string ext;
-  if (cmProp p = this->GetProperty("BUNDLE_EXTENSION")) {
+  if (cmValue p = this->GetProperty("BUNDLE_EXTENSION")) {
     ext = *p;
   } else {
     if (this->IsXCTestOnApple()) {
@@ -2418,7 +2416,7 @@
 {
   std::string fpath = cmStrCat(
     this->GetOutputName(config, cmStateEnums::RuntimeBinaryArtifact), '.');
-  cmProp ext = this->GetProperty("BUNDLE_EXTENSION");
+  cmValue ext = this->GetProperty("BUNDLE_EXTENSION");
   fpath += (ext ? *ext : "framework");
   if (shouldAddFullLevel(level) &&
       !this->Makefile->PlatformIsAppleEmbedded()) {
@@ -2470,7 +2468,7 @@
 {
   if (this->Makefile->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
     std::string dir;
-    cmProp install_name_dir = this->GetProperty("INSTALL_NAME_DIR");
+    cmValue install_name_dir = this->GetProperty("INSTALL_NAME_DIR");
 
     if (this->CanGenerateInstallNameDir(INSTALL_NAME_FOR_INSTALL)) {
       if (cmNonempty(install_name_dir)) {
@@ -2519,7 +2517,7 @@
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
       this->GetType() == cmStateEnums::MODULE_LIBRARY ||
       this->IsExecutableWithExports()) {
-    if (cmProp custom_export_name = this->GetProperty("DEFINE_SYMBOL")) {
+    if (cmValue custom_export_name = this->GetProperty("DEFINE_SYMBOL")) {
       this->ExportMacro = *custom_export_name;
     } else {
       std::string in = cmStrCat(this->GetName(), "_EXPORTS");
@@ -2561,6 +2559,7 @@
           } break;
           case cmPolicies::OLD:
             noMessage = true;
+            break;
           case cmPolicies::REQUIRED_IF_USED:
           case cmPolicies::REQUIRED_ALWAYS:
           case cmPolicies::NEW:
@@ -2817,7 +2816,7 @@
     return effectiveFolder;
   }
 
-  cmProp targetFolder = this->GetProperty("FOLDER");
+  cmValue targetFolder = this->GetProperty("FOLDER");
   if (targetFolder) {
     effectiveFolder += *targetFolder;
   }
@@ -3045,7 +3044,7 @@
     this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf];
 
     // Queue dependencies added explicitly by the user.
-    if (cmProp additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) {
+    if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) {
       std::vector<std::string> objDeps = cmExpandedList(*additionalDeps);
       for (std::string& objDep : objDeps) {
         if (cmSystemTools::FileIsFullPath(objDep)) {
@@ -3243,7 +3242,7 @@
   if (!this->Makefile->IsOn("APPLE")) {
     return;
   }
-  cmProp archs = nullptr;
+  cmValue archs = nullptr;
   if (!config.empty()) {
     std::string defVarName =
       cmStrCat("OSX_ARCHITECTURES_", cmSystemTools::UpperCase(config));
@@ -3263,13 +3262,14 @@
 void cmGeneratorTarget::AddExplicitLanguageFlags(std::string& flags,
                                                  cmSourceFile const& sf) const
 {
-  cmProp lang = sf.GetProperty("LANGUAGE");
+  cmValue lang = sf.GetProperty("LANGUAGE");
   if (!lang) {
     return;
   }
 
   switch (this->GetPolicyStatusCMP0119()) {
     case cmPolicies::WARN:
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       // The OLD behavior is to not add explicit language flags.
       return;
@@ -3568,6 +3568,7 @@
             } break;
             case cmPolicies::OLD:
               noMessage = true;
+              break;
             case cmPolicies::REQUIRED_IF_USED:
             case cmPolicies::REQUIRED_ALWAYS:
             case cmPolicies::NEW:
@@ -3644,7 +3645,7 @@
     // If this target has ISPC sources make sure to add the header
     // directory to other compilation units
     if (cm::contains(this->GetAllConfigCompileLanguages(), "ISPC")) {
-      if (cmProp val = this->GetProperty(propertyName)) {
+      if (cmValue val = this->GetProperty(propertyName)) {
         includes.emplace_back(*val);
       } else {
         includes.emplace_back(this->GetObjectDirectory(config));
@@ -3974,7 +3975,7 @@
   if (!config.empty()) {
     std::string configPropName =
       "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config);
-    cmProp configProp = this->GetProperty(configPropName);
+    cmValue configProp = this->GetProperty(configPropName);
     if (configProp) {
       switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0043)) {
         case cmPolicies::WARN: {
@@ -4049,7 +4050,7 @@
     return std::string();
   }
   const cmGeneratorTarget* generatorTarget = this;
-  cmProp pchReuseFrom =
+  cmValue pchReuseFrom =
     generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
 
   const auto inserted =
@@ -4090,8 +4091,10 @@
 
     const std::string filename_tmp = cmStrCat(filename, ".tmp");
     if (!pchReuseFrom) {
-      cmProp pchPrologue = this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
-      cmProp pchEpilogue = this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
+      cmValue pchPrologue =
+        this->Makefile->GetDefinition("CMAKE_PCH_PROLOGUE");
+      cmValue pchEpilogue =
+        this->Makefile->GetDefinition("CMAKE_PCH_EPILOGUE");
 
       std::string firstHeaderOnDisk;
       {
@@ -4105,7 +4108,7 @@
         if (this->GetGlobalGenerator()->IsXcode()) {
           file << "#ifndef CMAKE_SKIP_PRECOMPILE_HEADERS\n";
         }
-        if (language == "CXX") {
+        if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) {
           file << "#ifdef __cplusplus\n";
         }
         for (auto const& header_bt : headers) {
@@ -4123,7 +4126,7 @@
             firstHeaderOnDisk = header_bt.Value;
           }
         }
-        if (language == "CXX") {
+        if (language == "CXX" && !this->GetGlobalGenerator()->IsXcode()) {
           file << "#endif // __cplusplus\n";
         }
         if (this->GetGlobalGenerator()->IsXcode()) {
@@ -4162,7 +4165,7 @@
     std::string& filename = inserted.first->second;
 
     const cmGeneratorTarget* generatorTarget = this;
-    cmProp pchReuseFrom =
+    cmValue pchReuseFrom =
       generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
     if (pchReuseFrom) {
       generatorTarget =
@@ -4260,7 +4263,7 @@
       };
 
       cmGeneratorTarget* generatorTarget = this;
-      cmProp pchReuseFrom =
+      cmValue pchReuseFrom =
         generatorTarget->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
       if (pchReuseFrom) {
         generatorTarget =
@@ -4466,6 +4469,13 @@
 
   // Last step: replace "LINKER:" prefixed elements by
   // actual linker wrapper
+  return this->ResolveLinkerWrapper(result, language);
+}
+
+std::vector<BT<std::string>>& cmGeneratorTarget::ResolveLinkerWrapper(
+  std::vector<BT<std::string>>& result, const std::string& language) const
+{
+  // replace "LINKER:" prefixed elements by actual linker wrapper
   const std::string wrapper(this->Makefile->GetSafeDefinition(
     "CMAKE_" + language +
     (this->IsDeviceLink() ? "_DEVICE_LINKER_WRAPPER_FLAG"
@@ -4549,7 +4559,7 @@
                                              nullptr, nullptr);
 
   EvaluatedTargetPropertyEntries entries;
-  if (cmProp linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
+  if (cmValue linkOptions = this->GetProperty("STATIC_LIBRARY_OPTIONS")) {
     std::vector<std::string> options = cmExpandedList(*linkOptions);
     for (const auto& option : options) {
       std::unique_ptr<TargetPropertyEntry> entry =
@@ -4700,7 +4710,7 @@
                                              nullptr);
 
   EvaluatedTargetPropertyEntries entries;
-  if (cmProp linkDepends = this->GetProperty("LINK_DEPENDS")) {
+  if (cmValue linkDepends = this->GetProperty("LINK_DEPENDS")) {
     std::vector<std::string> depends = cmExpandedList(*linkDepends);
     for (const auto& depend : depends) {
       std::unique_ptr<TargetPropertyEntry> entry =
@@ -4781,7 +4791,7 @@
     }
 
     std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
-    cmProp currentLanguageStandard = this->GetLanguageStandard(lang, config);
+    cmValue currentLanguageStandard = this->GetLanguageStandard(lang, config);
 
     std::string newRequiredStandard;
     if (!standardResolver.GetNewRequiredStandard(
@@ -4821,7 +4831,7 @@
         this->LanguageStandardMap[key] = *standardToCopy;
         generatorTargetLanguageStandard = &this->LanguageStandardMap[key];
       } else {
-        cmProp defaultStandard = this->Makefile->GetDefinition(
+        cmValue defaultStandard = this->Makefile->GetDefinition(
           cmStrCat("CMAKE_", language.second, "_STANDARD_DEFAULT"));
         if (defaultStandard) {
           this->LanguageStandardMap[key] = BTs<std::string>(*defaultStandard);
@@ -4924,8 +4934,8 @@
   }
 
   // Check for library version properties.
-  cmProp version = this->GetProperty("VERSION");
-  cmProp soversion = this->GetProperty("SOVERSION");
+  cmValue version = this->GetProperty("VERSION");
+  cmValue soversion = this->GetProperty("SOVERSION");
   if (!this->HasSOName(config) ||
       this->Makefile->IsOn("CMAKE_PLATFORM_NO_VERSIONED_SONAME") ||
       this->IsFrameworkOnApple()) {
@@ -4966,11 +4976,11 @@
     // The library's soname.
     this->ComputeVersionedName(targetNames.SharedObject, prefix,
                                targetNames.Base, suffix, targetNames.Output,
-                               cmToCStr(soversion));
+                               soversion);
 
     // The library's real name on disk.
     this->ComputeVersionedName(targetNames.Real, prefix, targetNames.Base,
-                               suffix, targetNames.Output, cmToCStr(version));
+                               suffix, targetNames.Output, version);
   }
 
   // The import library name.
@@ -5003,10 +5013,10 @@
 // This versioning is supported only for executables and then only
 // when the platform supports symbolic links.
 #if defined(_WIN32) && !defined(__CYGWIN__)
-  const char* version = nullptr;
+  cmValue version;
 #else
   // Check for executable version properties.
-  const char* version = cmToCStr(this->GetProperty("VERSION"));
+  cmValue version = this->GetProperty("VERSION");
   if (this->GetType() != cmStateEnums::EXECUTABLE ||
       this->Makefile->IsOn("XCODE")) {
     version = nullptr;
@@ -5030,7 +5040,7 @@
 #endif
   if (version) {
     targetNames.Real += "-";
-    targetNames.Real += version;
+    targetNames.Real += *version;
   }
 #if defined(__CYGWIN__)
   targetNames.Real += suffix;
@@ -5100,8 +5110,8 @@
 
   // retrieve prefix and suffix
   std::string ll = this->GetLinkerLanguage(config);
-  cmProp targetPrefix = this->GetFilePrefixInternal(config, artifact, ll);
-  cmProp targetSuffix = this->GetFileSuffixInternal(config, artifact, ll);
+  cmValue targetPrefix = this->GetFilePrefixInternal(config, artifact, ll);
+  cmValue targetSuffix = this->GetFileSuffixInternal(config, artifact, ll);
 
   // The implib option is only allowed for shared libraries, module
   // libraries, and executables.
@@ -5119,13 +5129,13 @@
   if (this->IsFrameworkOnApple()) {
     fw_prefix =
       cmStrCat(this->GetFrameworkDirectory(config, ContentLevel), '/');
-    targetPrefix = &fw_prefix;
+    targetPrefix = cmValue(fw_prefix);
     targetSuffix = nullptr;
   }
 
   if (this->IsCFBundleOnApple()) {
     fw_prefix = cmStrCat(this->GetCFBundleDirectory(config, FullLevel), '/');
-    targetPrefix = &fw_prefix;
+    targetPrefix = cmValue(fw_prefix);
     targetSuffix = nullptr;
   }
 
@@ -5141,13 +5151,13 @@
   // EXECUTABLE_SUFFIX attribute.
   if (this->IsFrameworkOnApple() &&
       this->GetGlobalGenerator()->GetName() == "Xcode") {
-    targetSuffix = &configPostfix;
+    targetSuffix = cmValue(configPostfix);
   } else {
     outBase += configPostfix;
   }
 
   // Name shared libraries with their version number on some platforms.
-  if (cmProp soversion = this->GetProperty("SOVERSION")) {
+  if (cmValue soversion = this->GetProperty("SOVERSION")) {
     if (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
         !isImportedLibraryArtifact &&
         this->Makefile->IsOn("CMAKE_SHARED_LIBRARY_NAME_WITH_VERSION")) {
@@ -5183,7 +5193,7 @@
   props.emplace_back("PDB_NAME");
 
   for (std::string const& p : props) {
-    if (cmProp outName = this->GetProperty(p)) {
+    if (cmValue outName = this->GetProperty(p)) {
       base = *outName;
       break;
     }
@@ -5210,7 +5220,7 @@
   props.emplace_back("PDB_NAME");
 
   for (std::string const& p : props) {
-    if (cmProp outName = this->GetProperty(p)) {
+    if (cmValue outName = this->GetProperty(p)) {
       base = *outName;
       break;
     }
@@ -5293,7 +5303,7 @@
   } else {
     // Handle the MACOSX_PACKAGE_LOCATION property on source files that
     // were not listed in one of the other lists.
-    if (cmProp location = sf->GetProperty("MACOSX_PACKAGE_LOCATION")) {
+    if (cmValue location = sf->GetProperty("MACOSX_PACKAGE_LOCATION")) {
       flags.MacFolder = location->c_str();
       const bool stripResources =
         this->GlobalGenerator->ShouldStripResourcePath(this->Makefile);
@@ -5323,7 +5333,7 @@
   this->SourceFileFlagsConstructed = true;
 
   // Process public headers to mark the source files.
-  if (cmProp files = this->GetProperty("PUBLIC_HEADER")) {
+  if (cmValue files = this->GetProperty("PUBLIC_HEADER")) {
     std::vector<std::string> relFiles = cmExpandedList(*files);
     for (std::string const& relFile : relFiles) {
       if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
@@ -5336,7 +5346,7 @@
 
   // Process private headers after public headers so that they take
   // precedence if a file is listed in both.
-  if (cmProp files = this->GetProperty("PRIVATE_HEADER")) {
+  if (cmValue files = this->GetProperty("PRIVATE_HEADER")) {
     std::vector<std::string> relFiles = cmExpandedList(*files);
     for (std::string const& relFile : relFiles) {
       if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
@@ -5348,7 +5358,7 @@
   }
 
   // Mark sources listed as resources.
-  if (cmProp files = this->GetProperty("RESOURCE")) {
+  if (cmValue files = this->GetProperty("RESOURCE")) {
     std::vector<std::string> relFiles = cmExpandedList(*files);
     for (std::string const& relFile : relFiles) {
       if (cmSourceFile* sf = this->Makefile->GetSource(relFile)) {
@@ -5376,7 +5386,7 @@
       this->GetLinkImplementationClosure(config);
     for (cmGeneratorTarget const* li : deps) {
 #define CM_READ_COMPATIBLE_INTERFACE(X, x)                                    \
-  if (cmProp prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) {            \
+  if (cmValue prop = li->GetProperty("COMPATIBLE_INTERFACE_" #X)) {           \
     std::vector<std::string> props;                                           \
     cmExpandList(*prop, props);                                               \
     compat.Props##x.insert(props.begin(), props.end());                       \
@@ -5486,7 +5496,7 @@
                               const std::string& config, CompatibleType t,
                               PropertyType* /*unused*/)
 {
-  cmProp prop = dependee->GetProperty(propName);
+  cmValue prop = dependee->GetProperty(propName);
   if (!prop) {
     return;
   }
@@ -5672,6 +5682,11 @@
   return value;
 }
 template <>
+std::string valueAsString<cmValue>(cmValue value)
+{
+  return value ? value : std::string("(unset)");
+}
+template <>
 std::string valueAsString<std::nullptr_t>(std::nullptr_t /*unused*/)
 {
   return "(unset)";
@@ -5721,7 +5736,7 @@
     return tgt->GetPropertyAsBool(prop);
   }
 
-  cmProp value = tgt->GetProperty(prop);
+  cmValue value = tgt->GetProperty(prop);
   return cmIsOn(genexInterpreter->Evaluate(value ? *value : "", prop));
 }
 
@@ -5730,10 +5745,10 @@
   cmGeneratorTarget const* tgt, const std::string& prop,
   cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  cmProp value = tgt->GetProperty(prop);
+  cmValue value = tgt->GetProperty(prop);
 
   if (genexInterpreter == nullptr) {
-    return cmToCStr(value);
+    return value.GetCStr();
   }
 
   return genexInterpreter->Evaluate(value ? *value : "", prop).c_str();
@@ -5744,10 +5759,10 @@
   cmGeneratorTarget const* tgt, const std::string& prop,
   cmGeneratorExpressionInterpreter* genexInterpreter)
 {
-  cmProp value = tgt->GetProperty(prop);
+  cmValue value = tgt->GetProperty(prop);
 
   if (genexInterpreter == nullptr) {
-    return valueAsString(cmToCStr(value));
+    return valueAsString(value);
   }
 
   return genexInterpreter->Evaluate(value ? *value : "", prop);
@@ -6121,7 +6136,7 @@
 
   assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
 
-  if (cmProp version = this->GetProperty(property)) {
+  if (cmValue version = this->GetProperty(property)) {
     // Try to parse the version number and store the results that were
     // successfully parsed.
     int parsed_major;
@@ -6149,12 +6164,12 @@
 {
   // This is activated by the presence of a default selection whether or
   // not it is overridden by a property.
-  cmProp runtimeLibraryDefault = this->Makefile->GetDefinition(
+  cmValue runtimeLibraryDefault = this->Makefile->GetDefinition(
     cmStrCat("CMAKE_", lang, "_RUNTIME_LIBRARY_DEFAULT"));
   if (!cmNonempty(runtimeLibraryDefault)) {
     return std::string();
   }
-  cmProp runtimeLibraryValue =
+  cmValue runtimeLibraryValue =
     this->Target->GetProperty(cmStrCat(lang, "_RUNTIME_LIBRARY"));
   if (!runtimeLibraryValue) {
     runtimeLibraryValue = runtimeLibraryDefault;
@@ -6175,12 +6190,21 @@
   return this->FortranModuleDirectory;
 }
 
+bool cmGeneratorTarget::IsFortranBuildingInstrinsicModules() const
+{
+  if (cmValue prop =
+        this->GetProperty("Fortran_BUILDING_INSTRINSIC_MODULES")) {
+    return cmIsOn(*prop);
+  }
+  return false;
+}
+
 std::string cmGeneratorTarget::CreateFortranModuleDirectory(
   std::string const& working_dir) const
 {
   std::string mod_dir;
   std::string target_mod_dir;
-  if (cmProp prop = this->GetProperty("Fortran_MODULE_DIRECTORY")) {
+  if (cmValue prop = this->GetProperty("Fortran_MODULE_DIRECTORY")) {
     target_mod_dir = *prop;
   } else {
     std::string const& default_mod_dir =
@@ -6189,7 +6213,7 @@
       target_mod_dir = default_mod_dir;
     }
   }
-  cmProp moddir_flag =
+  cmValue moddir_flag =
     this->Makefile->GetDefinition("CMAKE_Fortran_MODDIR_FLAG");
   if (!target_mod_dir.empty() && moddir_flag) {
     // Compute the full path to the module directory.
@@ -6272,26 +6296,23 @@
 {
   assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
 
-  if (cmProp fversion = this->GetProperty("FRAMEWORK_VERSION")) {
+  if (cmValue fversion = this->GetProperty("FRAMEWORK_VERSION")) {
     return *fversion;
   }
-  if (cmProp tversion = this->GetProperty("VERSION")) {
+  if (cmValue tversion = this->GetProperty("VERSION")) {
     return *tversion;
   }
   return "A";
 }
 
-void cmGeneratorTarget::ComputeVersionedName(std::string& vName,
-                                             std::string const& prefix,
-                                             std::string const& base,
-                                             std::string const& suffix,
-                                             std::string const& name,
-                                             const char* version) const
+void cmGeneratorTarget::ComputeVersionedName(
+  std::string& vName, std::string const& prefix, std::string const& base,
+  std::string const& suffix, std::string const& name, cmValue version) const
 {
   vName = this->Makefile->IsOn("APPLE") ? (prefix + base) : name;
   if (version) {
     vName += ".";
-    vName += version;
+    vName += *version;
   }
   vName += this->Makefile->IsOn("APPLE") ? suffix : std::string();
 }
@@ -6358,16 +6379,16 @@
   if (name == this->GetName() || name.empty()) {
     return maybeItem;
   }
-  maybeItem = this->ResolveLinkItem(name, bt, scope->LG);
+  maybeItem = this->ResolveLinkItem(BT<std::string>(name, bt), scope->LG);
   return maybeItem;
 }
 
-void cmGeneratorTarget::ExpandLinkItems(
-  std::string const& prop, std::string const& value, std::string const& config,
-  cmGeneratorTarget const* headTarget, bool usage_requirements_only,
-  std::vector<cmLinkItem>& items, std::vector<cmLinkItem>& objects,
-  bool& hadHeadSensitiveCondition, bool& hadContextSensitiveCondition,
-  bool& hadLinkLanguageSensitiveCondition) const
+void cmGeneratorTarget::ExpandLinkItems(std::string const& prop,
+                                        std::string const& value,
+                                        std::string const& config,
+                                        cmGeneratorTarget const* headTarget,
+                                        bool usage_requirements_only,
+                                        cmLinkInterface& iface) const
 {
   // Keep this logic in sync with ComputeLinkImplementationLibraries.
   cmGeneratorExpression ge;
@@ -6389,24 +6410,27 @@
   for (std::string const& lib : libs) {
     if (cm::optional<cmLinkItem> maybeItem =
           this->LookupLinkItem(lib, cge->GetBacktrace(), &scope)) {
-      if (!maybeItem->Target) {
+      cmLinkItem item = std::move(*maybeItem);
+
+      if (!item.Target) {
         // Report explicitly linked object files separately.
-        std::string const& maybeObj = maybeItem->AsStr();
+        std::string const& maybeObj = item.AsStr();
         if (cmSystemTools::FileIsFullPath(maybeObj)) {
           cmSourceFile const* sf =
             mf->GetSource(maybeObj, cmSourceFileLocationKind::Known);
           if (sf && sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
-            objects.emplace_back(std::move(*maybeItem));
+            iface.Objects.emplace_back(std::move(item));
             continue;
           }
         }
       }
-      items.emplace_back(std::move(*maybeItem));
+
+      iface.Libraries.emplace_back(std::move(item));
     }
   }
-  hadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition();
-  hadContextSensitiveCondition = cge->GetHadContextSensitiveCondition();
-  hadLinkLanguageSensitiveCondition =
+  iface.HadHeadSensitiveCondition = cge->GetHadHeadSensitiveCondition();
+  iface.HadContextSensitiveCondition = cge->GetHadContextSensitiveCondition();
+  iface.HadLinkLanguageSensitiveCondition =
     cge->GetHadLinkLanguageSensitiveCondition();
 }
 
@@ -6531,9 +6555,9 @@
     // How many repetitions are needed if this library has cyclic
     // dependencies?
     std::string propName = cmStrCat("LINK_INTERFACE_MULTIPLICITY", suffix);
-    if (cmProp config_reps = this->GetProperty(propName)) {
+    if (cmValue config_reps = this->GetProperty(propName)) {
       sscanf(config_reps->c_str(), "%u", &iface.Multiplicity);
-    } else if (cmProp reps =
+    } else if (cmValue reps =
                  this->GetProperty("LINK_INTERFACE_MULTIPLICITY")) {
       sscanf(reps->c_str(), "%u", &iface.Multiplicity);
     }
@@ -6690,14 +6714,14 @@
   }
 
   // Select an output directory.
-  if (cmProp config_outdir = this->GetProperty(configProp)) {
+  if (cmValue config_outdir = this->GetProperty(configProp)) {
     // Use the user-specified per-configuration output directory.
     out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator,
                                           config, this);
 
     // Skip per-configuration subdirectory.
     conf.clear();
-  } else if (cmProp outdir = this->GetProperty(propertyName)) {
+  } else if (cmValue outdir = this->GetProperty(propertyName)) {
     // Use the user-specified output directory.
     out = cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator,
                                           config, this);
@@ -6760,14 +6784,14 @@
   }
 
   // Select an output directory.
-  if (cmProp config_outdir = this->GetProperty(configProp)) {
+  if (cmValue config_outdir = this->GetProperty(configProp)) {
     // Use the user-specified per-configuration output directory.
     out = cmGeneratorExpression::Evaluate(*config_outdir, this->LocalGenerator,
                                           config);
 
     // Skip per-configuration subdirectory.
     conf.clear();
-  } else if (cmProp outdir = this->GetProperty(propertyName)) {
+  } else if (cmValue outdir = this->GetProperty(propertyName)) {
     // Use the user-specified output directory.
     out =
       cmGeneratorExpression::Evaluate(*outdir, this->LocalGenerator, config);
@@ -6820,7 +6844,7 @@
                                  const std::string& prop,
                                  std::string& rpath) const
 {
-  cmProp value = this->GetProperty(prop);
+  cmValue value = this->GetProperty(prop);
   if (!value) {
     return false;
   }
@@ -6845,7 +6869,7 @@
 
   // An explicit list of interface libraries may be set for shared
   // libraries and executables that export symbols.
-  cmProp explicitLibraries = nullptr;
+  cmValue explicitLibraries = nullptr;
   std::string linkIfaceProp;
   bool const cmp0022NEW = (this->GetPolicyStatusCMP0022() != cmPolicies::OLD &&
                            this->GetPolicyStatusCMP0022() != cmPolicies::WARN);
@@ -6874,7 +6898,7 @@
       !this->PolicyWarnedCMP0022) {
     // Compare the explicitly set old link interface properties to the
     // preferred new link interface property one and warn if different.
-    cmProp newExplicitLibraries =
+    cmValue newExplicitLibraries =
       this->GetProperty("INTERFACE_LINK_LIBRARIES");
     if (newExplicitLibraries &&
         (*newExplicitLibraries != *explicitLibraries)) {
@@ -6903,23 +6927,20 @@
     return;
   }
   iface.Exists = true;
+
+  // If CMP0022 is NEW then the plain tll signature sets the
+  // INTERFACE_LINK_LIBRARIES property.  Even if the project
+  // clears it, the link interface is still explicit.
   iface.Explicit = cmp0022NEW || explicitLibraries;
 
   if (explicitLibraries) {
     // The interface libraries have been explicitly set.
     this->ExpandLinkItems(linkIfaceProp, *explicitLibraries, config,
-                          headTarget, usage_requirements_only, iface.Libraries,
-                          iface.Objects, iface.HadHeadSensitiveCondition,
-                          iface.HadContextSensitiveCondition,
-                          iface.HadLinkLanguageSensitiveCondition);
-    return;
+                          headTarget, usage_requirements_only, iface);
   }
 
-  // If CMP0022 is NEW then the plain tll signature sets the
-  // INTERFACE_LINK_LIBRARIES, so if we get here then the project
-  // cleared the property explicitly and we should not fall back
-  // to the link implementation.
-  if (cmp0022NEW) {
+  // If the link interface is explicit, do not fall back to the link impl.
+  if (iface.Explicit) {
     return;
   }
 
@@ -6932,22 +6953,15 @@
         !this->PolicyWarnedCMP0022 && !usage_requirements_only) {
       // Compare the link implementation fallback link interface to the
       // preferred new link interface property and warn if different.
-      std::vector<cmLinkItem> ifaceLibs;
-      std::vector<cmLinkItem> ifaceObjects;
+      cmLinkInterface ifaceNew;
       static const std::string newProp = "INTERFACE_LINK_LIBRARIES";
-      if (cmProp newExplicitLibraries = this->GetProperty(newProp)) {
-        bool hadHeadSensitiveConditionDummy = false;
-        bool hadContextSensitiveConditionDummy = false;
-        bool hadLinkLanguageSensitiveConditionDummy = false;
+      if (cmValue newExplicitLibraries = this->GetProperty(newProp)) {
         this->ExpandLinkItems(newProp, *newExplicitLibraries, config,
-                              headTarget, usage_requirements_only, ifaceLibs,
-                              ifaceObjects, hadHeadSensitiveConditionDummy,
-                              hadContextSensitiveConditionDummy,
-                              hadLinkLanguageSensitiveConditionDummy);
+                              headTarget, usage_requirements_only, ifaceNew);
       }
-      if (ifaceLibs != iface.Libraries) {
+      if (ifaceNew.Libraries != iface.Libraries) {
         std::string oldLibraries = cmJoin(impl->Libraries, ";");
-        std::string newLibraries = cmJoin(ifaceLibs, ";");
+        std::string newLibraries = cmJoin(ifaceNew.Libraries, ";");
         if (oldLibraries.empty()) {
           oldLibraries = "(empty)";
         }
@@ -7009,7 +7023,7 @@
 
   std::string const& runtimeLibrary =
     currentTarget->GetRuntimeLinkLibrary(lang, config);
-  if (cmProp runtimeLinkOptions = currentTarget->Makefile->GetDefinition(
+  if (cmValue runtimeLinkOptions = currentTarget->Makefile->GetDefinition(
         "CMAKE_" + lang + "_RUNTIME_LIBRARIES_" + runtimeLibrary)) {
     std::vector<std::string> libsVec = cmExpandedList(*runtimeLinkOptions);
     result.reserve(libsVec.size());
@@ -7085,10 +7099,7 @@
     iface.Multiplicity = info->Multiplicity;
     cmExpandList(info->Languages, iface.Languages);
     this->ExpandLinkItems(info->LibrariesProp, info->Libraries, config,
-                          headTarget, usage_requirements_only, iface.Libraries,
-                          iface.Objects, iface.HadHeadSensitiveCondition,
-                          iface.HadContextSensitiveCondition,
-                          iface.HadLinkLanguageSensitiveCondition);
+                          headTarget, usage_requirements_only, iface);
     std::vector<std::string> deps = cmExpandedList(info->SharedDeps);
     LookupLinkItemScope scope{ this->LocalGenerator };
     for (std::string const& dep : deps) {
@@ -7150,8 +7161,8 @@
   // Initialize members.
   info.NoSOName = false;
 
-  cmProp loc = nullptr;
-  cmProp imp = nullptr;
+  cmValue loc = nullptr;
+  cmValue imp = nullptr;
   std::string suffix;
   if (!this->Target->GetMappedConfig(desired_config, loc, imp, suffix)) {
     return;
@@ -7160,7 +7171,7 @@
   // Get the link interface.
   {
     std::string linkProp = "INTERFACE_LINK_LIBRARIES";
-    cmProp propertyLibs = this->GetProperty(linkProp);
+    cmValue propertyLibs = this->GetProperty(linkProp);
 
     if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
       if (!propertyLibs) {
@@ -7193,9 +7204,9 @@
     info.Location = *loc;
   } else {
     std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
-    if (cmProp config_location = this->GetProperty(impProp)) {
+    if (cmValue config_location = this->GetProperty(impProp)) {
       info.Location = *config_location;
-    } else if (cmProp location = this->GetProperty("IMPORTED_LOCATION")) {
+    } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) {
       info.Location = *location;
     }
   }
@@ -7203,9 +7214,9 @@
   // Get the soname.
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
     std::string soProp = cmStrCat("IMPORTED_SONAME", suffix);
-    if (cmProp config_soname = this->GetProperty(soProp)) {
+    if (cmValue config_soname = this->GetProperty(soProp)) {
       info.SOName = *config_soname;
-    } else if (cmProp soname = this->GetProperty("IMPORTED_SONAME")) {
+    } else if (cmValue soname = this->GetProperty("IMPORTED_SONAME")) {
       info.SOName = *soname;
     }
   }
@@ -7213,9 +7224,9 @@
   // Get the "no-soname" mark.
   if (this->GetType() == cmStateEnums::SHARED_LIBRARY) {
     std::string soProp = cmStrCat("IMPORTED_NO_SONAME", suffix);
-    if (cmProp config_no_soname = this->GetProperty(soProp)) {
+    if (cmValue config_no_soname = this->GetProperty(soProp)) {
       info.NoSOName = cmIsOn(*config_no_soname);
-    } else if (cmProp no_soname = this->GetProperty("IMPORTED_NO_SONAME")) {
+    } else if (cmValue no_soname = this->GetProperty("IMPORTED_NO_SONAME")) {
       info.NoSOName = cmIsOn(*no_soname);
     }
   }
@@ -7226,9 +7237,9 @@
   } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
              this->IsExecutableWithExports()) {
     std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
-    if (cmProp config_implib = this->GetProperty(impProp)) {
+    if (cmValue config_implib = this->GetProperty(impProp)) {
       info.ImportLibrary = *config_implib;
-    } else if (cmProp implib = this->GetProperty("IMPORTED_IMPLIB")) {
+    } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) {
       info.ImportLibrary = *implib;
     }
   }
@@ -7237,9 +7248,9 @@
   {
     std::string linkProp =
       cmStrCat("IMPORTED_LINK_DEPENDENT_LIBRARIES", suffix);
-    if (cmProp config_libs = this->GetProperty(linkProp)) {
+    if (cmValue config_libs = this->GetProperty(linkProp)) {
       info.SharedDeps = *config_libs;
-    } else if (cmProp libs =
+    } else if (cmValue libs =
                  this->GetProperty("IMPORTED_LINK_DEPENDENT_LIBRARIES")) {
       info.SharedDeps = *libs;
     }
@@ -7249,9 +7260,9 @@
   if (this->LinkLanguagePropagatesToDependents()) {
     std::string linkProp =
       cmStrCat("IMPORTED_LINK_INTERFACE_LANGUAGES", suffix);
-    if (cmProp config_libs = this->GetProperty(linkProp)) {
+    if (cmValue config_libs = this->GetProperty(linkProp)) {
       info.Languages = *config_libs;
-    } else if (cmProp libs =
+    } else if (cmValue libs =
                  this->GetProperty("IMPORTED_LINK_INTERFACE_LANGUAGES")) {
       info.Languages = *libs;
     }
@@ -7260,9 +7271,9 @@
   // Get information if target is managed assembly.
   {
     std::string linkProp = "IMPORTED_COMMON_LANGUAGE_RUNTIME";
-    if (cmProp pc = this->GetProperty(linkProp + suffix)) {
+    if (cmValue pc = this->GetProperty(linkProp + suffix)) {
       info.Managed = this->CheckManagedType(*pc);
-    } else if (cmProp p = this->GetProperty(linkProp)) {
+    } else if (cmValue p = this->GetProperty(linkProp)) {
       info.Managed = this->CheckManagedType(*p);
     }
   }
@@ -7271,9 +7282,9 @@
   if (this->GetType() == cmStateEnums::STATIC_LIBRARY) {
     std::string linkProp =
       cmStrCat("IMPORTED_LINK_INTERFACE_MULTIPLICITY", suffix);
-    if (cmProp config_reps = this->GetProperty(linkProp)) {
+    if (cmValue config_reps = this->GetProperty(linkProp)) {
       sscanf(config_reps->c_str(), "%u", &info.Multiplicity);
-    } else if (cmProp reps =
+    } else if (cmValue reps =
                  this->GetProperty("IMPORTED_LINK_INTERFACE_MULTIPLICITY")) {
       sscanf(reps->c_str(), "%u", &info.Multiplicity);
     }
@@ -7381,9 +7392,9 @@
   // there is no cmGeneratorTarget at configure-time, so search the SOURCES
   // for TARGET_OBJECTS instead for backwards compatibility with OLD
   // behavior of CMP0024 and CMP0026 only.
-  cmStringRange rng = this->Target->GetSourceEntries();
-  for (std::string const& entry : rng) {
-    std::vector<std::string> files = cmExpandedList(entry);
+  cmBTStringRange rng = this->Target->GetSourceEntries();
+  for (auto const& entry : rng) {
+    std::vector<std::string> files = cmExpandedList(entry.Value);
     for (std::string const& li : files) {
       if (cmHasLiteralPrefix(li, "$<TARGET_OBJECTS:") && li.back() == '>') {
         std::string objLibName = li.substr(17, li.size() - 18);
@@ -7426,6 +7437,7 @@
         cm->IssueMessage(MessageType::AUTHOR_WARNING, w.str(),
                          this->GetBacktrace());
       }
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::NEW: {
@@ -7452,14 +7464,14 @@
 
 bool cmGeneratorTarget::IsDeprecated() const
 {
-  cmProp deprecation = this->GetProperty("DEPRECATION");
+  cmValue deprecation = this->GetProperty("DEPRECATION");
   return cmNonempty(deprecation);
 }
 
 std::string cmGeneratorTarget::GetDeprecation() const
 {
   // find DEPRECATION property
-  if (cmProp deprecation = this->GetProperty("DEPRECATION")) {
+  if (cmValue deprecation = this->GetProperty("DEPRECATION")) {
     return *deprecation;
   }
   return std::string();
@@ -7530,7 +7542,7 @@
   std::set<std::string> languages = this->GetAllConfigCompileLanguages();
   // Consider an explicit linker language property, but *not* the
   // computed linker language that may depend on linked targets.
-  cmProp linkLang = this->GetProperty("LINKER_LANGUAGE");
+  cmValue linkLang = this->GetProperty("LINKER_LANGUAGE");
   if (cmNonempty(linkLang)) {
     languages.insert(*linkLang);
   }
@@ -7611,23 +7623,21 @@
 {
   cmLocalGenerator const* lg = this->LocalGenerator;
   cmMakefile const* mf = lg->GetMakefile();
-  cmStringRange entryRange = this->Target->GetLinkImplementationEntries();
-  cmBacktraceRange btRange = this->Target->GetLinkImplementationBacktraces();
-  cmBacktraceRange::const_iterator btIt = btRange.begin();
+  cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries();
   // Collect libraries directly linked in this configuration.
-  for (cmStringRange::const_iterator le = entryRange.begin(),
-                                     end = entryRange.end();
-       le != end; ++le, ++btIt) {
+  for (auto const& entry : entryRange) {
     std::vector<std::string> llibs;
     // Keep this logic in sync with ExpandLinkItems.
     cmGeneratorExpressionDAGChecker dagChecker(this, "LINK_LIBRARIES", nullptr,
                                                nullptr);
-    cmGeneratorExpression ge(*btIt);
-    std::unique_ptr<cmCompiledGeneratorExpression> const cge = ge.Parse(*le);
+    cmGeneratorExpression ge(entry.Backtrace);
+    std::unique_ptr<cmCompiledGeneratorExpression> const cge =
+      ge.Parse(entry.Value);
     cge->SetEvaluateForBuildsystem(true);
     std::string const& evaluated =
       cge->Evaluate(this->LocalGenerator, config, head, &dagChecker, nullptr,
                     this->LinkerLanguage);
+    bool const fromGenex = evaluated != entry.Value;
     cmExpandList(evaluated, llibs);
     if (cge->GetHadHeadSensitiveCondition()) {
       impl.HadHeadSensitiveCondition = true;
@@ -7665,6 +7675,7 @@
             } break;
             case cmPolicies::OLD:
               noMessage = true;
+              break;
             case cmPolicies::REQUIRED_IF_USED:
             case cmPolicies::REQUIRED_ALWAYS:
             case cmPolicies::NEW:
@@ -7685,7 +7696,8 @@
       }
 
       // The entry is meant for this configuration.
-      cmLinkItem item = this->ResolveLinkItem(name, *btIt, lg);
+      cmLinkItem item =
+        this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg);
       if (!item.Target) {
         // Report explicitly linked object files separately.
         std::string const& maybeObj = item.AsStr();
@@ -7699,7 +7711,7 @@
         }
       }
 
-      impl.Libraries.emplace_back(std::move(item), evaluated != *le);
+      impl.Libraries.emplace_back(std::move(item), fromGenex);
     }
 
     std::set<std::string> const& seenProps = cge->GetSeenTargetProperties();
@@ -7727,7 +7739,7 @@
       }
       // Support OLD behavior for CMP0003.
       impl.WrongConfigLibraries.push_back(
-        this->ResolveLinkItem(name, cmListFileBacktrace()));
+        this->ResolveLinkItem(BT<std::string>(name)));
     }
   }
 }
@@ -7753,16 +7765,16 @@
 }
 
 cmLinkItem cmGeneratorTarget::ResolveLinkItem(
-  std::string const& name, cmListFileBacktrace const& bt) const
+  BT<std::string> const& name) const
 {
-  return this->ResolveLinkItem(name, bt, this->LocalGenerator);
+  return this->ResolveLinkItem(name, this->LocalGenerator);
 }
 
-cmLinkItem cmGeneratorTarget::ResolveLinkItem(std::string const& name,
-                                              cmListFileBacktrace const& bt,
+cmLinkItem cmGeneratorTarget::ResolveLinkItem(BT<std::string> const& name,
                                               cmLocalGenerator const* lg) const
 {
-  TargetOrString resolved = this->ResolveTargetReference(name, lg);
+  auto bt = name.Backtrace;
+  TargetOrString resolved = this->ResolveTargetReference(name.Value, lg);
 
   if (!resolved.Target) {
     return cmLinkItem(resolved.String, false, bt);
@@ -7940,7 +7952,7 @@
   }
 
   // Check for explicitly set clr target property.
-  if (cmProp clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
+  if (cmValue clr = this->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
     return this->CheckManagedType(*clr);
   }
 
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index 6d2aa85..9906963 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -19,8 +19,8 @@
 #include "cmLinkItem.h"
 #include "cmListFileCache.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
+#include "cmValue.h"
 
 class cmComputeLinkInformation;
 class cmCustomCommand;
@@ -89,7 +89,7 @@
 
   std::vector<std::string> GetPropertyKeys() const;
   //! Might return a nullptr if the property is not set or invalid
-  cmProp GetProperty(const std::string& prop) const;
+  cmValue GetProperty(const std::string& prop) const;
   //! Always returns a valid pointer
   std::string const& GetSafeProperty(std::string const& prop) const;
   bool GetPropertyAsBool(const std::string& prop) const;
@@ -163,10 +163,10 @@
   BTs<std::string> const* GetLanguageStandardProperty(
     std::string const& lang, std::string const& config) const;
 
-  cmProp GetLanguageStandard(std::string const& lang,
-                             std::string const& config) const;
+  cmValue GetLanguageStandard(std::string const& lang,
+                              std::string const& config) const;
 
-  cmProp GetLanguageExtensions(std::string const& lang) const;
+  cmValue GetLanguageExtensions(std::string const& lang) const;
 
   bool GetLanguageStandardRequired(std::string const& lang) const;
 
@@ -187,8 +187,8 @@
 
   void ComputeObjectMapping();
 
-  cmProp GetFeature(const std::string& feature,
-                    const std::string& config) const;
+  cmValue GetFeature(const std::string& feature,
+                     const std::string& config) const;
 
   const char* GetLinkPIEProperty(const std::string& config) const;
 
@@ -221,7 +221,7 @@
     {
       this->PreviousState = target.SetDeviceLink(true);
     }
-    ~DeviceLinkSetter() { this->Target.SetDeviceLink(this->PreviousState); };
+    ~DeviceLinkSetter() { this->Target.SetDeviceLink(this->PreviousState); }
 
   private:
     cmGeneratorTarget& Target;
@@ -409,10 +409,8 @@
   TargetOrString ResolveTargetReference(std::string const& name,
                                         cmLocalGenerator const* lg) const;
 
-  cmLinkItem ResolveLinkItem(std::string const& name,
-                             cmListFileBacktrace const& bt) const;
-  cmLinkItem ResolveLinkItem(std::string const& name,
-                             cmListFileBacktrace const& bt,
+  cmLinkItem ResolveLinkItem(BT<std::string> const& name) const;
+  cmLinkItem ResolveLinkItem(BT<std::string> const& name,
                              cmLocalGenerator const* lg) const;
 
   // Compute the set of languages compiled by the target.  This is
@@ -498,6 +496,9 @@
   std::vector<BT<std::string>> GetLinkOptions(
     std::string const& config, std::string const& language) const;
 
+  std::vector<BT<std::string>>& ResolveLinkerWrapper(
+    std::vector<BT<std::string>>& result, const std::string& language) const;
+
   void GetStaticLibraryLinkOptions(std::vector<std::string>& result,
                                    const std::string& config,
                                    const std::string& language) const;
@@ -832,8 +833,9 @@
                                     std::string const& config) const;
 
   std::string GetFortranModuleDirectory(std::string const& working_dir) const;
+  bool IsFortranBuildingInstrinsicModules() const;
 
-  const std::string& GetSourcesProperty() const;
+  cmValue GetSourcesProperty() const;
 
   void AddISPCGeneratedHeader(std::string const& header,
                               std::string const& config);
@@ -864,6 +866,11 @@
   mutable std::map<cmSourceFile const*, std::string> Objects;
   std::set<cmSourceFile const*> ExplicitObjectName;
 
+  using TargetPtrToBoolMap = std::unordered_map<cmTarget*, bool>;
+  mutable std::unordered_map<std::string, TargetPtrToBoolMap>
+    MacOSXRpathInstallNameDirCache;
+  bool DetermineHasMacOSXRpathInstallNameDir(const std::string& config) const;
+
   // "config/language" is the key
   mutable std::map<std::string, std::vector<std::string>> SystemIncludesCache;
 
@@ -877,12 +884,12 @@
 
   bool NeedImportLibraryName(std::string const& config) const;
 
-  cmProp GetFilePrefixInternal(std::string const& config,
-                               cmStateEnums::ArtifactType artifact,
-                               const std::string& language = "") const;
-  cmProp GetFileSuffixInternal(std::string const& config,
-                               cmStateEnums::ArtifactType artifact,
-                               const std::string& language = "") const;
+  cmValue GetFilePrefixInternal(std::string const& config,
+                                cmStateEnums::ArtifactType artifact,
+                                const std::string& language = "") const;
+  cmValue GetFileSuffixInternal(std::string const& config,
+                                cmStateEnums::ArtifactType artifact,
+                                const std::string& language = "") const;
 
   std::string GetFullNameInternal(const std::string& config,
                                   cmStateEnums::ArtifactType artifact) const;
@@ -901,8 +908,7 @@
 
   void ComputeVersionedName(std::string& vName, std::string const& prefix,
                             std::string const& base, std::string const& suffix,
-                            std::string const& name,
-                            const char* version) const;
+                            std::string const& name, cmValue version) const;
 
   struct CompatibleInterfacesBase
   {
@@ -1037,11 +1043,8 @@
                        std::string const& config,
                        const cmGeneratorTarget* headTarget,
                        bool usage_requirements_only,
-                       std::vector<cmLinkItem>& items,
-                       std::vector<cmLinkItem>& objects,
-                       bool& hadHeadSensitiveCondition,
-                       bool& hadContextSensitiveCondition,
-                       bool& hadLinkLanguageSensitiveCondition) const;
+                       cmLinkInterface& iface) const;
+
   struct LookupLinkItemScope
   {
     cmLocalGenerator const* LG;
@@ -1113,8 +1116,8 @@
 
   mutable std::map<std::string, BTs<std::string>> LanguageStandardMap;
 
-  cmProp GetPropertyWithPairedLanguageSupport(std::string const& lang,
-                                              const char* suffix) const;
+  cmValue GetPropertyWithPairedLanguageSupport(std::string const& lang,
+                                               const char* suffix) const;
 
   void ComputeLinkImplementationRuntimeLibraries(
     const std::string& config, cmOptionalLinkImplementation& impl) const;
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx
index 79cbe44..42bd206 100644
--- a/Source/cmGetCMakePropertyCommand.cxx
+++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -7,9 +7,9 @@
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 // cmGetCMakePropertyCommand
 bool cmGetCMakePropertyCommand(std::vector<std::string> const& args,
@@ -24,12 +24,12 @@
   std::string output = "NOTFOUND";
 
   if (args[1] == "VARIABLES") {
-    if (cmProp varsProp = status.GetMakefile().GetProperty("VARIABLES")) {
+    if (cmValue varsProp = status.GetMakefile().GetProperty("VARIABLES")) {
       output = *varsProp;
     }
   } else if (args[1] == "MACROS") {
     output.clear();
-    if (cmProp macrosProp = status.GetMakefile().GetProperty("MACROS")) {
+    if (cmValue macrosProp = status.GetMakefile().GetProperty("MACROS")) {
       output = *macrosProp;
     }
   } else if (args[1] == "COMPONENTS") {
@@ -37,7 +37,7 @@
       status.GetMakefile().GetGlobalGenerator()->GetInstallComponents();
     output = cmJoin(*components, ";");
   } else {
-    cmProp prop = nullptr;
+    cmValue prop = nullptr;
     if (!args[1].empty()) {
       prop = status.GetMakefile().GetState()->GetGlobalProperty(args[1]);
     }
diff --git a/Source/cmGetDirectoryPropertyCommand.cxx b/Source/cmGetDirectoryPropertyCommand.cxx
index 7fbd479..d892cfa 100644
--- a/Source/cmGetDirectoryPropertyCommand.cxx
+++ b/Source/cmGetDirectoryPropertyCommand.cxx
@@ -7,12 +7,14 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 void StoreResult(cmMakefile& makefile, std::string const& variable,
                  const char* prop);
+void StoreResult(cmMakefile& makefile, std::string const& variable,
+                 cmValue prop);
 }
 
 // cmGetDirectoryPropertyCommand
@@ -76,7 +78,6 @@
     return false;
   }
 
-  const char* prop = nullptr;
   if (*i == "DEFINITIONS") {
     switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0059)) {
       case cmPolicies::WARN:
@@ -94,8 +95,7 @@
         break;
     }
   }
-  prop = cmToCStr(dir->GetProperty(*i));
-  StoreResult(status.GetMakefile(), variable, prop);
+  StoreResult(status.GetMakefile(), variable, dir->GetProperty(*i));
   return true;
 }
 
@@ -105,4 +105,9 @@
 {
   makefile.AddDefinition(variable, prop ? prop : "");
 }
+void StoreResult(cmMakefile& makefile, std::string const& variable,
+                 cmValue prop)
+{
+  makefile.AddDefinition(variable, prop);
+}
 }
diff --git a/Source/cmGetFilenameComponentCommand.cxx b/Source/cmGetFilenameComponentCommand.cxx
index 40e8a05..abe7d32 100644
--- a/Source/cmGetFilenameComponentCommand.cxx
+++ b/Source/cmGetFilenameComponentCommand.cxx
@@ -4,10 +4,10 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // cmGetFilenameComponentCommand
 bool cmGetFilenameComponentCommand(std::vector<std::string> const& args,
@@ -22,7 +22,7 @@
   // Check and see if the value has been stored in the cache
   // already, if so use that value
   if (args.size() >= 4 && args.back() == "CACHE") {
-    cmProp cacheValue = status.GetMakefile().GetDefinition(args.front());
+    cmValue cacheValue = status.GetMakefile().GetDefinition(args.front());
     if (cacheValue && !cmIsNOTFOUND(*cacheValue)) {
       return true;
     }
diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx
index cb657f9..162860a 100644
--- a/Source/cmGetPropertyCommand.cxx
+++ b/Source/cmGetPropertyCommand.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGetPropertyCommand.h"
 
+#include <cstddef>
+
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmInstalledFile.h"
@@ -18,6 +20,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTest.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 class cmMessenger;
@@ -32,10 +35,6 @@
   OutSet
 };
 
-// Implementation of result storage.
-bool StoreResult(OutType infoType, cmMakefile& makefile,
-                 const std::string& variable, const char* value);
-
 // Implementation of each property type.
 bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
                       OutType infoType, const std::string& variable,
@@ -253,8 +252,10 @@
 
 namespace {
 
+// Implementation of result storage.
+template <typename ValueType>
 bool StoreResult(OutType infoType, cmMakefile& makefile,
-                 const std::string& variable, const char* value)
+                 const std::string& variable, ValueType value)
 {
   if (infoType == OutSet) {
     makefile.AddDefinition(variable, value ? "1" : "0");
@@ -268,6 +269,12 @@
   }
   return true;
 }
+template <>
+bool StoreResult(OutType infoType, cmMakefile& makefile,
+                 const std::string& variable, std::nullptr_t value)
+{
+  return StoreResult(infoType, makefile, variable, cmValue(value));
+}
 
 bool HandleGlobalMode(cmExecutionStatus& status, const std::string& name,
                       OutType infoType, const std::string& variable,
@@ -280,9 +287,8 @@
 
   // Get the property.
   cmake* cm = status.GetMakefile().GetCMakeInstance();
-  return StoreResult(
-    infoType, status.GetMakefile(), variable,
-    cmToCStr(cm->GetState()->GetGlobalProperty(propertyName)));
+  return StoreResult(infoType, status.GetMakefile(), variable,
+                     cm->GetState()->GetGlobalProperty(propertyName));
 }
 
 bool HandleDirectoryMode(cmExecutionStatus& status, const std::string& name,
@@ -329,7 +335,7 @@
 
   // Get the property.
   return StoreResult(infoType, status.GetMakefile(), variable,
-                     cmToCStr(mf->GetProperty(propertyName)));
+                     mf->GetProperty(propertyName));
 }
 
 bool HandleTargetMode(cmExecutionStatus& status, const std::string& name,
@@ -361,12 +367,11 @@
     }
     cmListFileBacktrace bt = status.GetMakefile().GetBacktrace();
     cmMessenger* messenger = status.GetMakefile().GetMessenger();
-    cmProp prop = target->GetComputedProperty(propertyName, messenger, bt);
+    cmValue prop = target->GetComputedProperty(propertyName, messenger, bt);
     if (!prop) {
       prop = target->GetProperty(propertyName);
     }
-    return StoreResult(infoType, status.GetMakefile(), variable,
-                       cmToCStr(prop));
+    return StoreResult(infoType, status.GetMakefile(), variable, prop);
   }
   status.SetError(cmStrCat("could not find TARGET ", name,
                            ".  Perhaps it has not yet been created."));
@@ -391,7 +396,7 @@
   if (cmSourceFile* sf =
         directory_makefile.GetOrCreateSource(source_file_absolute_path)) {
     return StoreResult(infoType, status.GetMakefile(), variable,
-                       cmToCStr(sf->GetPropertyForUser(propertyName)));
+                       sf->GetPropertyForUser(propertyName));
   }
   status.SetError(
     cmStrCat("given SOURCE name that could not be found or created: ",
@@ -428,9 +433,8 @@
     return false;
   }
 
-  return StoreResult(
-    infoType, status.GetMakefile(), variable,
-    cmToCStr(status.GetMakefile().GetDefinition(propertyName)));
+  return StoreResult(infoType, status.GetMakefile(), variable,
+                     status.GetMakefile().GetDefinition(propertyName));
 }
 
 bool HandleCacheMode(cmExecutionStatus& status, const std::string& name,
@@ -442,12 +446,12 @@
     return false;
   }
 
-  cmProp value = nullptr;
+  cmValue value = nullptr;
   if (status.GetMakefile().GetState()->GetCacheEntryValue(name)) {
     value = status.GetMakefile().GetState()->GetCacheEntryProperty(
       name, propertyName);
   }
-  StoreResult(infoType, status.GetMakefile(), variable, cmToCStr(value));
+  StoreResult(infoType, status.GetMakefile(), variable, value);
   return true;
 }
 
diff --git a/Source/cmGetSourceFilePropertyCommand.cxx b/Source/cmGetSourceFilePropertyCommand.cxx
index 5301b66..40ae112 100644
--- a/Source/cmGetSourceFilePropertyCommand.cxx
+++ b/Source/cmGetSourceFilePropertyCommand.cxx
@@ -4,9 +4,9 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmSetPropertyCommand.h"
 #include "cmSourceFile.h"
+#include "cmValue.h"
 
 bool cmGetSourceFilePropertyCommand(std::vector<std::string> const& args,
                                     cmExecutionStatus& status)
@@ -58,7 +58,7 @@
   }
 
   if (sf) {
-    cmProp prop = nullptr;
+    cmValue prop = nullptr;
     if (!args[property_arg_index].empty()) {
       prop = sf->GetPropertyForUser(args[property_arg_index]);
     }
diff --git a/Source/cmGetTargetPropertyCommand.cxx b/Source/cmGetTargetPropertyCommand.cxx
index 78a17d2..12c8221 100644
--- a/Source/cmGetTargetPropertyCommand.cxx
+++ b/Source/cmGetTargetPropertyCommand.cxx
@@ -10,8 +10,8 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 class cmMessenger;
 
@@ -42,7 +42,7 @@
         }
       }
     } else if (!args[2].empty()) {
-      cmProp prop_cstr = nullptr;
+      cmValue prop_cstr = nullptr;
       cmListFileBacktrace bt = mf.GetBacktrace();
       cmMessenger* messenger = mf.GetMessenger();
       prop_cstr = tgt->GetComputedProperty(args[2], messenger, bt);
@@ -62,6 +62,7 @@
       case cmPolicies::WARN:
         issueMessage = true;
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0045) << "\n";
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::REQUIRED_IF_USED:
@@ -69,6 +70,7 @@
       case cmPolicies::NEW:
         issueMessage = true;
         messageType = MessageType::FATAL_ERROR;
+        break;
     }
     if (issueMessage) {
       e << "get_target_property() called with non-existent target \""
diff --git a/Source/cmGetTestPropertyCommand.cxx b/Source/cmGetTestPropertyCommand.cxx
index cf8c1d5..a4ac9f6 100644
--- a/Source/cmGetTestPropertyCommand.cxx
+++ b/Source/cmGetTestPropertyCommand.cxx
@@ -5,6 +5,7 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmTest.h"
+#include "cmValue.h"
 
 bool cmGetTestPropertyCommand(std::vector<std::string> const& args,
                               cmExecutionStatus& status)
@@ -19,12 +20,12 @@
   cmMakefile& mf = status.GetMakefile();
   cmTest* test = mf.GetTest(testName);
   if (test) {
-    const char* prop = nullptr;
+    cmValue prop;
     if (!args[1].empty()) {
       prop = test->GetProperty(args[1]);
     }
     if (prop) {
-      mf.AddDefinition(var, prop);
+      mf.AddDefinition(var, prop->c_str());
       return true;
     }
   }
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 32238e4..47cefae 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -19,7 +19,6 @@
 #include "cmLocalGhsMultiGenerator.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
 #include "cmSourceGroup.h"
@@ -29,6 +28,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target)
   : GeneratorTarget(target)
@@ -43,7 +43,7 @@
 #endif
 {
   // Store the configuration name that is being used
-  if (cmProp config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) {
+  if (cmValue config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) {
     // Use the build type given by the user.
     this->ConfigName = *config;
   } else {
@@ -376,7 +376,7 @@
 #ifdef _WIN32
   std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%";
 #else
-  std::string check_error = "if [[ $? -ne 0 ]]; then exit 1; fi";
+  std::string check_error = "if [ $? -ne 0 ]; then exit 1; fi";
 #endif
 
 #ifdef _WIN32
@@ -453,7 +453,7 @@
   std::ostream& fout, const cmSourceFile* sf, std::string const& propName,
   std::string const& propFlag)
 {
-  cmProp prop = sf->GetProperty(propName);
+  cmValue prop = sf->GetProperty(propName);
   if (prop) {
     std::vector<std::string> list = cmExpandedList(*prop);
     for (const std::string& p : list) {
@@ -705,7 +705,7 @@
 void cmGhsMultiTargetGenerator::WriteObjectLangOverride(
   std::ostream& fout, const cmSourceFile* sourceFile)
 {
-  cmProp rawLangProp = sourceFile->GetProperty("LANGUAGE");
+  cmValue rawLangProp = sourceFile->GetProperty("LANGUAGE");
   if (rawLangProp) {
     std::string sourceLangProp(*rawLangProp);
     std::string const& extension = sourceFile->GetExtension();
@@ -717,7 +717,7 @@
 
 bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp()
 {
-  if (cmProp p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) {
+  if (cmValue p = this->GeneratorTarget->GetProperty("ghs_integrity_app")) {
     return cmIsOn(*p);
   }
   std::vector<cmSourceFile*> sources;
diff --git a/Source/cmGlobalCommonGenerator.cxx b/Source/cmGlobalCommonGenerator.cxx
index 9e5bbca..3ae66f0 100644
--- a/Source/cmGlobalCommonGenerator.cxx
+++ b/Source/cmGlobalCommonGenerator.cxx
@@ -11,13 +11,12 @@
 #include "cmGeneratorTarget.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
-#include "cmStringAlgorithms.h"
-
-class cmake;
+#include "cmSystemTools.h"
+#include "cmValue.h"
+#include "cmake.h"
 
 cmGlobalCommonGenerator::cmGlobalCommonGenerator(cmake* cm)
   : cmGlobalGenerator(cm)
@@ -48,7 +47,7 @@
       DirectoryTarget::Target t;
       t.GT = gt.get();
       const std::string EXCLUDE_FROM_ALL("EXCLUDE_FROM_ALL");
-      if (cmProp exclude = gt->GetProperty(EXCLUDE_FROM_ALL)) {
+      if (cmValue exclude = gt->GetProperty(EXCLUDE_FROM_ALL)) {
         for (const std::string& config : configs) {
           cmGeneratorExpressionInterpreter genexInterpreter(lg.get(), config,
                                                             gt.get());
@@ -95,3 +94,33 @@
   }
   return !t.ExcludedFromAllInConfigs.empty();
 }
+
+std::string cmGlobalCommonGenerator::GetEditCacheCommand() const
+{
+  // If generating for an extra IDE, the edit_cache target cannot
+  // launch a terminal-interactive tool, so always use cmake-gui.
+  if (!this->GetExtraGeneratorName().empty()) {
+    return cmSystemTools::GetCMakeGUICommand();
+  }
+
+  // Use an internal cache entry to track the latest dialog used
+  // to edit the cache, and use that for the edit_cache target.
+  cmake* cm = this->GetCMakeInstance();
+  std::string editCacheCommand = cm->GetCMakeEditCommand();
+  if (!cm->GetCacheDefinition("CMAKE_EDIT_COMMAND") ||
+      !editCacheCommand.empty()) {
+    if (this->SupportsDirectConsole() && editCacheCommand.empty()) {
+      editCacheCommand = cmSystemTools::GetCMakeCursesCommand();
+    }
+    if (editCacheCommand.empty()) {
+      editCacheCommand = cmSystemTools::GetCMakeGUICommand();
+    }
+    if (!editCacheCommand.empty()) {
+      cm->AddCacheEntry("CMAKE_EDIT_COMMAND", editCacheCommand,
+                        "Path to cache edit program executable.",
+                        cmStateEnums::INTERNAL);
+    }
+  }
+  cmValue edit_cmd = cm->GetCacheDefinition("CMAKE_EDIT_COMMAND");
+  return edit_cmd ? *edit_cmd : std::string();
+}
diff --git a/Source/cmGlobalCommonGenerator.h b/Source/cmGlobalCommonGenerator.h
index 2aa9d27..fed9ce8 100644
--- a/Source/cmGlobalCommonGenerator.h
+++ b/Source/cmGlobalCommonGenerator.h
@@ -42,4 +42,9 @@
   std::map<std::string, DirectoryTarget> ComputeDirectoryTargets() const;
   bool IsExcludedFromAllInConfig(const DirectoryTarget::Target& t,
                                  const std::string& config);
+
+protected:
+  virtual bool SupportsDirectConsole() const { return true; }
+  const char* GetEditCacheTargetName() const override { return "edit_cache"; }
+  std::string GetEditCacheCommand() const override;
 };
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 9193778..97ad7ab 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -44,12 +44,12 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmWorkingDirectory.h"
 #include "cmake.h"
@@ -199,7 +199,7 @@
 {
   std::string makeProgram = inMakeProgram;
   if (cmIsOff(makeProgram)) {
-    cmProp makeProgramCSTR =
+    cmValue makeProgramCSTR =
       this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
     if (cmIsOff(makeProgramCSTR)) {
       makeProgram = makeDefault;
@@ -235,14 +235,14 @@
   if (!optional && (path.empty() || !cmSystemTools::FileExists(path))) {
     return;
   }
-  cmProp cname =
+  cmValue cname =
     this->GetCMakeInstance()->GetState()->GetInitializedCacheValue(langComp);
 
   // Split compiler from arguments
   std::vector<std::string> cnameArgVec;
   if (cname && !cname->empty()) {
     cmExpandList(*cname, cnameArgVec);
-    cname = &cnameArgVec.front();
+    cname = cmValue(cnameArgVec.front());
   }
 
   std::string changeVars;
@@ -258,7 +258,7 @@
     cmSystemTools::ConvertToUnixSlashes(cnameString);
     cmSystemTools::ConvertToUnixSlashes(pathString);
     if (cnameString != pathString) {
-      cmProp cvars = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+      cmValue cvars = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
         "__CMAKE_DELETE_CACHE_CHANGE_VARS_");
       if (cvars) {
         changeVars += *cvars;
@@ -498,6 +498,19 @@
 void cmGlobalGenerator::EnableLanguage(
   std::vector<std::string> const& languages, cmMakefile* mf, bool optional)
 {
+  if (!this->IsMultiConfig() &&
+      !this->GetCMakeInstance()->GetIsInTryCompile()) {
+    std::string envBuildType;
+    if (!mf->GetDefinition("CMAKE_BUILD_TYPE") &&
+        cmSystemTools::GetEnv("CMAKE_BUILD_TYPE", envBuildType)) {
+      mf->AddCacheDefinition(
+        "CMAKE_BUILD_TYPE", envBuildType,
+        "Choose the type of build.  Options include: empty, "
+        "Debug, Release, RelWithDebInfo, MinSizeRel.",
+        cmStateEnums::STRING);
+    }
+  }
+
   if (languages.empty()) {
     cmSystemTools::Error("EnableLanguage must have a lang specified!");
     cmSystemTools::SetFatalErrorOccured();
@@ -581,16 +594,6 @@
     }
   }
 
-  if (readCMakeSystem) {
-    // Find the native build tool for this generator.
-    // This has to be done early so that MSBuild can be used to examine the
-    // cross-compilation environment.
-    if (this->GetFindMakeProgramStage() == FindMakeProgramStage::Early &&
-        !this->FindMakeProgram(mf)) {
-      return;
-    }
-  }
-
   //  Load the CMakeDetermineSystem.cmake file and find out
   // what platform we are running on
   if (!mf->GetDefinition("CMAKE_SYSTEM")) {
@@ -664,8 +667,7 @@
     }
 
     // Find the native build tool for this generator.
-    if (this->GetFindMakeProgramStage() == FindMakeProgramStage::Late &&
-        !this->FindMakeProgram(mf)) {
+    if (!this->FindMakeProgram(mf)) {
       return;
     }
   }
@@ -789,7 +791,7 @@
     std::string compilerName = cmStrCat("CMAKE_", lang, "_COMPILER");
     std::string compilerEnv = cmStrCat("CMAKE_", lang, "_COMPILER_ENV_VAR");
     std::ostringstream noCompiler;
-    cmProp compilerFile = mf->GetDefinition(compilerName);
+    cmValue compilerFile = mf->GetDefinition(compilerName);
     if (!cmNonempty(compilerFile) || cmIsNOTFOUND(*compilerFile)) {
       /* clang-format off */
       noCompiler <<
@@ -826,7 +828,7 @@
         cmSystemTools::RemoveFile(compilerLangFile);
         if (!this->CMakeInstance->GetIsInTryCompile()) {
           this->PrintCompilerAdvice(noCompiler, lang,
-                                    cmToCStr(mf->GetDefinition(compilerEnv)));
+                                    mf->GetDefinition(compilerEnv));
           mf->IssueMessage(MessageType::FATAL_ERROR, noCompiler.str());
           fatalError = true;
         }
@@ -910,7 +912,7 @@
 
 void cmGlobalGenerator::PrintCompilerAdvice(std::ostream& os,
                                             std::string const& lang,
-                                            const char* envVar) const
+                                            cmValue envVar) const
 {
   // Subclasses override this method if they do not support this advice.
   os << "Tell CMake where to find the compiler by setting ";
@@ -953,6 +955,7 @@
         mf->IssueMessage(
           MessageType::FATAL_ERROR,
           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0025));
+        break;
       case cmPolicies::NEW:
         // NEW behavior is to keep AppleClang.
         break;
@@ -1019,6 +1022,7 @@
         mf->IssueMessage(
           MessageType::FATAL_ERROR,
           cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0089));
+        break;
       case cmPolicies::NEW:
         // NEW behavior is to keep AppleClang.
         break;
@@ -1105,7 +1109,7 @@
   }
 
   std::string linkerPrefVar = "CMAKE_" + l + "_LINKER_PREFERENCE";
-  cmProp linkerPref = mf->GetDefinition(linkerPrefVar);
+  cmValue linkerPref = mf->GetDefinition(linkerPrefVar);
   int preference = 0;
   if (cmNonempty(linkerPref)) {
     if (sscanf(linkerPref->c_str(), "%d", &preference) != 1) {
@@ -1131,7 +1135,7 @@
   this->LanguageToLinkerPreference[l] = preference;
 
   std::string outputExtensionVar = "CMAKE_" + l + "_OUTPUT_EXTENSION";
-  if (cmProp p = mf->GetDefinition(outputExtensionVar)) {
+  if (cmValue p = mf->GetDefinition(outputExtensionVar)) {
     std::string outputExtension = *p;
     this->LanguageToOutputExtension[l] = outputExtension;
     this->OutputExtensions[outputExtension] = outputExtension;
@@ -1167,10 +1171,10 @@
   }
 }
 
-const char* cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
+cmValue cmGlobalGenerator::GetGlobalSetting(std::string const& name) const
 {
   assert(!this->Makefiles.empty());
-  return cmToCStr(this->Makefiles[0]->GetDefinition(name));
+  return this->Makefiles[0]->GetDefinition(name);
 }
 
 bool cmGlobalGenerator::GlobalSettingIsOn(std::string const& name) const
@@ -1183,7 +1187,7 @@
   std::string const& name) const
 {
   assert(!this->Makefiles.empty());
-  return this->Makefiles[0]->GetSafeDefinition(name);
+  return this->Makefiles[0]->GetDefinition(name);
 }
 
 bool cmGlobalGenerator::IgnoreFile(const char* ext) const
@@ -1251,10 +1255,8 @@
     this->CreateDefaultGlobalTargets(globalTargets);
 
     for (const auto& mf : this->Makefiles) {
-      auto& targets = mf->GetTargets();
       for (GlobalTargetInfo const& globalTarget : globalTargets) {
-        targets.emplace(globalTarget.Name,
-                        this->CreateGlobalTarget(globalTarget, mf.get()));
+        this->CreateGlobalTarget(globalTarget, mf.get());
       }
     }
   }
@@ -1401,6 +1403,9 @@
                                          this->SupportsDefaultConfigs())) {
     return false;
   }
+  if (!this->InspectConfigTypeVariables()) {
+    return false;
+  }
 
   // Some generators track files replaced during the Generate.
   // Start with an empty vector:
@@ -1708,10 +1713,8 @@
 
   // Construct per-target generator information.
   for (const auto& mf : this->Makefiles) {
-    const cmStringRange noconfig_compile_definitions =
+    const cmBTStringRange noconfig_compile_definitions =
       mf->GetCompileDefinitionsEntries();
-    const cmBacktraceRange noconfig_compile_definitions_bts =
-      mf->GetCompileDefinitionsBacktraces();
 
     for (auto& target : mf->GetTargets()) {
       cmTarget* t = &target.second;
@@ -1725,12 +1728,8 @@
         continue;
       }
 
-      {
-        auto btIt = noconfig_compile_definitions_bts.begin();
-        auto it = noconfig_compile_definitions.begin();
-        for (; it != noconfig_compile_definitions.end(); ++it, ++btIt) {
-          t->InsertCompileDefinition(*it, *btIt);
-        }
+      for (auto const& def : noconfig_compile_definitions) {
+        t->InsertCompileDefinition(def);
       }
 
       cmPolicies::PolicyStatus polSt =
@@ -1742,7 +1741,7 @@
         for (std::string const& c : configs) {
           std::string defPropName =
             cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(c));
-          if (cmProp val = mf->GetProperty(defPropName)) {
+          if (cmValue val = mf->GetProperty(defPropName)) {
             t->AppendProperty(defPropName, *val);
           }
         }
@@ -1771,9 +1770,8 @@
   std::map<cmTarget*, cmGeneratorTarget*> const& importedMap)
 {
   if (targetTypes == AllTargets) {
-    for (auto& target : mf->GetTargets()) {
-      cmTarget* t = &target.second;
-      lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(t, lg));
+    for (cmTarget* target : mf->GetOrderedTargets()) {
+      lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(target, lg));
     }
   }
 
@@ -1857,7 +1855,7 @@
         }
       }
       std::vector<std::string> incs;
-      cmProp incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
+      cmValue incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
       if (!incDirProp) {
         continue;
       }
@@ -2121,7 +2119,7 @@
 
   // update progress
   // estimate how many lg there will be
-  cmProp numGenC = this->CMakeInstance->GetState()->GetInitializedCacheValue(
+  cmValue numGenC = this->CMakeInstance->GetState()->GetInitializedCacheValue(
     "CMAKE_NUMBER_OF_MAKEFILES");
 
   if (!numGenC) {
@@ -2180,11 +2178,10 @@
 {
   this->SetConfiguredFilesPath(gen);
   this->TryCompileOuterMakefile = mf;
-  cmProp make =
+  cmValue make =
     gen->GetCMakeInstance()->GetCacheDefinition("CMAKE_MAKE_PROGRAM");
-  this->GetCMakeInstance()->AddCacheEntry("CMAKE_MAKE_PROGRAM", cmToCStr(make),
-                                          "make program",
-                                          cmStateEnums::FILEPATH);
+  this->GetCMakeInstance()->AddCacheEntry(
+    "CMAKE_MAKE_PROGRAM", make, "make program", cmStateEnums::FILEPATH);
   // copy the enabled languages
   this->GetCMakeInstance()->GetState()->SetEnabledLanguages(
     gen->GetCMakeInstance()->GetState()->GetEnabledLanguages());
@@ -2244,7 +2241,7 @@
   }
   cmMakefile* mf = root->GetMakefile();
   const std::string EXCLUDE_FROM_ALL = "EXCLUDE_FROM_ALL";
-  if (cmProp exclude = target->GetProperty(EXCLUDE_FROM_ALL)) {
+  if (cmValue exclude = target->GetProperty(EXCLUDE_FROM_ALL)) {
     // Expand the property value per configuration.
     unsigned int trueCount = 0;
     unsigned int falseCount = 0;
@@ -2538,7 +2535,7 @@
   if (this->GetPreinstallTargetName()) {
     gti.Depends.emplace_back(this->GetPreinstallTargetName());
   } else {
-    cmProp noPackageAll =
+    cmValue noPackageAll =
       mf->GetDefinition("CMAKE_SKIP_PACKAGE_ALL_DEPENDENCY");
     if (cmIsOff(noPackageAll)) {
       gti.Depends.emplace_back(this->GetAllTargetName());
@@ -2719,7 +2716,7 @@
     if (this->GetPreinstallTargetName()) {
       gti.Depends.emplace_back(this->GetPreinstallTargetName());
     } else {
-      cmProp noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
+      cmValue noall = mf->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
       if (cmIsOff(noall)) {
         gti.Depends.emplace_back(this->GetAllTargetName());
       }
@@ -2740,7 +2737,7 @@
         singleLine.push_back(cfgArg);
         cfgArg = "-DEFFECTIVE_PLATFORM_NAME=$(EFFECTIVE_PLATFORM_NAME)";
       } else {
-        cfgArg += cmToCStr(mf->GetDefinition("CMAKE_CFG_INTDIR"));
+        cfgArg += *mf->GetDefinition("CMAKE_CFG_INTDIR");
       }
       singleLine.push_back(cfgArg);
     }
@@ -2785,7 +2782,7 @@
 
 std::string cmGlobalGenerator::GetPredefinedTargetsFolder() const
 {
-  cmProp prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+  cmValue prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
     "PREDEFINED_TARGETS_FOLDER");
 
   if (prop) {
@@ -2797,7 +2794,7 @@
 
 bool cmGlobalGenerator::UseFolderProperty() const
 {
-  cmProp prop =
+  cmValue prop =
     this->GetCMakeInstance()->GetState()->GetGlobalProperty("USE_FOLDERS");
 
   // If this property is defined, let the setter turn this on or off...
@@ -2812,12 +2809,19 @@
   return false;
 }
 
-cmTarget cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti,
-                                               cmMakefile* mf)
+void cmGlobalGenerator::CreateGlobalTarget(GlobalTargetInfo const& gti,
+                                           cmMakefile* mf)
 {
   // Package
-  cmTarget target(gti.Name, cmStateEnums::GLOBAL_TARGET,
-                  cmTarget::VisibilityNormal, mf, gti.PerConfig);
+  auto tb =
+    mf->CreateNewTarget(gti.Name, cmStateEnums::GLOBAL_TARGET, gti.PerConfig);
+
+  // Do nothing if gti.Name is already used
+  if (!tb.second) {
+    return;
+  }
+
+  cmTarget& target = tb.first;
   target.SetProperty("EXCLUDE_FROM_ALL", "TRUE");
 
   std::vector<std::string> no_outputs;
@@ -2841,8 +2845,6 @@
   if (this->UseFolderProperty()) {
     target.SetProperty("FOLDER", this->GetPredefinedTargetsFolder());
   }
-
-  return target;
 }
 
 std::string cmGlobalGenerator::GenerateRuleFile(
@@ -3143,10 +3145,10 @@
 
 #ifndef CMAKE_BOOTSTRAP
   // Check whether labels are enabled for this target.
-  cmProp targetLabels = target->GetProperty("LABELS");
-  cmProp directoryLabels =
+  cmValue targetLabels = target->GetProperty("LABELS");
+  cmValue directoryLabels =
     target->Target->GetMakefile()->GetProperty("LABELS");
-  cmProp cmakeDirectoryLabels =
+  cmValue cmakeDirectoryLabels =
     target->Target->GetMakefile()->GetDefinition("CMAKE_DIRECTORY_LABELS");
   if (targetLabels || directoryLabels || cmakeDirectoryLabels) {
     Json::Value lj_root(Json::objectValue);
@@ -3214,7 +3216,7 @@
       std::string const& sfp = sf->ResolveFullPath();
       fout << sfp << "\n";
       lj_source["file"] = sfp;
-      if (cmProp svalue = sf->GetProperty("LABELS")) {
+      if (cmValue svalue = sf->GetProperty("LABELS")) {
         labels.clear();
         Json::Value& lj_source_labels = lj_source["labels"] = Json::arrayValue;
         cmExpandList(*svalue, labels);
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 147146e..96696aa 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -29,6 +29,7 @@
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
 #include "cmTransformDepfile.h"
+#include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
 #  include <cm3p/json/value.h>
@@ -152,6 +153,8 @@
    */
   virtual void Configure();
 
+  virtual bool InspectConfigTypeVariables() { return true; }
+
   bool Compute();
   virtual void AddExtraIDETargets() {}
 
@@ -308,7 +311,7 @@
 
   cmExportSetMap& GetExportSets() { return this->ExportSets; }
 
-  const char* GetGlobalSetting(std::string const& name) const;
+  cmValue GetGlobalSetting(std::string const& name) const;
   bool GlobalSettingIsOn(std::string const& name) const;
   std::string GetSafeGlobalSetting(std::string const& name) const;
 
@@ -443,6 +446,8 @@
 
   virtual bool IsVisualStudio() const { return false; }
 
+  virtual bool IsVisualStudioAtLeast10() const { return false; }
+
   virtual bool IsNinja() const { return false; }
 
   /** Return true if we know the exact location of object files.
@@ -549,7 +554,7 @@
   virtual bool CheckLanguages(std::vector<std::string> const& languages,
                               cmMakefile* mf) const;
   virtual void PrintCompilerAdvice(std::ostream& os, std::string const& lang,
-                                   const char* envVar) const;
+                                   cmValue envVar) const;
 
   virtual bool ComputeTargetDepends();
 
@@ -596,7 +601,7 @@
   void AddGlobalTarget_RebuildCache(
     std::vector<GlobalTargetInfo>& targets) const;
   void AddGlobalTarget_Install(std::vector<GlobalTargetInfo>& targets);
-  cmTarget CreateGlobalTarget(GlobalTargetInfo const& gti, cmMakefile* mf);
+  void CreateGlobalTarget(GlobalTargetInfo const& gti, cmMakefile* mf);
 
   std::string FindMakeProgramFile;
   std::string ConfiguredFilesPath;
@@ -622,17 +627,6 @@
 
   std::string GetPredefinedTargetsFolder() const;
 
-  enum class FindMakeProgramStage
-  {
-    Early,
-    Late,
-  };
-
-  virtual FindMakeProgramStage GetFindMakeProgramStage() const
-  {
-    return FindMakeProgramStage::Late;
-  }
-
 private:
   using TargetMap = std::unordered_map<std::string, cmTarget*>;
   using GeneratorTargetMap =
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 7cf3e93..b1c0488 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -18,11 +18,11 @@
 #include "cmLocalGenerator.h"
 #include "cmLocalGhsMultiGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -99,7 +99,7 @@
   /* set the build tool to use */
   std::string gbuild(tsp + ((tsp.back() == '/') ? "" : "/") +
                      DEFAULT_BUILD_PROGRAM);
-  cmProp prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
+  cmValue prevTool = mf->GetDefinition("CMAKE_MAKE_PROGRAM");
 
   /* check if the toolset changed from last generate */
   if (prevTool && (gbuild != *prevTool)) {
@@ -186,8 +186,7 @@
 
   mf->AddDefinition("GHSMULTI", "1"); // identifier for user CMake files
 
-  const char* tgtPlatform =
-    cmToCStrSafe(mf->GetDefinition("GHS_TARGET_PLATFORM"));
+  const char* tgtPlatform = mf->GetDefinition("GHS_TARGET_PLATFORM")->c_str();
   if (!tgtPlatform) {
     cmSystemTools::Message("Green Hills MULTI: GHS_TARGET_PLATFORM not "
                            "specified; defaulting to \"integrity\"");
@@ -216,7 +215,7 @@
 void cmGlobalGhsMultiGenerator::GetToolset(cmMakefile* mf, std::string& tsd,
                                            const std::string& ts)
 {
-  cmProp ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT");
+  cmValue ghsRoot = mf->GetDefinition("GHS_TOOLSET_ROOT");
 
   if (cmNonempty(ghsRoot)) {
     tsd = *ghsRoot;
@@ -334,7 +333,7 @@
   fout << "# Top Level Project File\n";
 
   // Specify BSP option if supplied by user
-  cmProp bspName =
+  cmValue bspName =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_BSP_NAME");
   if (!cmIsOff(bspName)) {
     fout << "    -bsp " << *bspName << '\n';
@@ -343,7 +342,7 @@
   // Specify OS DIR if supplied by user
   // -- not all platforms require this entry in the project file
   if (!cmIsOff(this->OsDir)) {
-    cmProp osDirOption =
+    cmValue osDirOption =
       this->GetCMakeInstance()->GetCacheDefinition("GHS_OS_DIR_OPTION");
     std::replace(this->OsDir.begin(), this->OsDir.end(), '\\', '/');
     fout << "    ";
@@ -378,8 +377,8 @@
   std::ostream& fout, cmGeneratorTarget const* target,
   std::string& rootBinaryDir)
 {
-  cmProp projName = target->GetProperty("GENERATOR_FILE_NAME");
-  cmProp projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
+  cmValue projName = target->GetProperty("GENERATOR_FILE_NAME");
+  cmValue projType = target->GetProperty("GENERATOR_FILE_NAME_EXT");
   if (projName && projType) {
     cmLocalGenerator* lg = target->GetLocalGenerator();
     std::string dir = lg->GetCurrentBinaryDirectory();
@@ -564,7 +563,7 @@
 {
   GeneratedMakeCommand makeCommand = {};
   std::string gbuild;
-  if (cmProp gbuildCached =
+  if (cmValue gbuildCached =
         this->CMakeInstance->GetCacheDefinition("CMAKE_MAKE_PROGRAM")) {
     gbuild = *gbuildCached;
   }
@@ -617,7 +616,7 @@
                                             cmLocalGenerator* root)
 {
   fout << "macro PROJ_NAME=" << root->GetProjectName() << '\n';
-  cmProp ghsGpjMacros =
+  cmValue ghsGpjMacros =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS");
   if (ghsGpjMacros) {
     std::vector<std::string> expandedList = cmExpandedList(*ghsGpjMacros);
@@ -632,15 +631,15 @@
 {
   /* set primary target */
   std::string tgt;
-  cmProp t =
+  cmValue t =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET");
   if (cmNonempty(t)) {
     tgt = *t;
     this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET");
   } else {
-    cmProp a =
+    cmValue a =
       this->GetCMakeInstance()->GetCacheDefinition("CMAKE_GENERATOR_PLATFORM");
-    cmProp p =
+    cmValue p =
       this->GetCMakeInstance()->GetCacheDefinition("GHS_TARGET_PLATFORM");
     tgt = cmStrCat((a ? *a : ""), '_', (p ? *p : ""), ".tgt");
   }
@@ -653,7 +652,7 @@
        << "/CMakeFiles/custom_target.bod" << '\n';
   /* clang-format on */
 
-  cmProp const customization =
+  cmValue const customization =
     this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION");
   if (cmNonempty(customization)) {
     fout << "customization="
diff --git a/Source/cmGlobalJOMMakefileGenerator.cxx b/Source/cmGlobalJOMMakefileGenerator.cxx
index fc3123a..40deebb 100644
--- a/Source/cmGlobalJOMMakefileGenerator.cxx
+++ b/Source/cmGlobalJOMMakefileGenerator.cxx
@@ -39,8 +39,9 @@
   entry.Brief = "Generates JOM makefiles.";
 }
 
-void cmGlobalJOMMakefileGenerator::PrintCompilerAdvice(
-  std::ostream& os, std::string const& lang, const char* envVar) const
+void cmGlobalJOMMakefileGenerator::PrintCompilerAdvice(std::ostream& os,
+                                                       std::string const& lang,
+                                                       cmValue envVar) const
 {
   if (lang == "CXX" || lang == "C") {
     /* clang-format off */
diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h
index 2d58f91..58860dd 100644
--- a/Source/cmGlobalJOMMakefileGenerator.h
+++ b/Source/cmGlobalJOMMakefileGenerator.h
@@ -50,5 +50,5 @@
 
 private:
   void PrintCompilerAdvice(std::ostream& os, std::string const& lang,
-                           const char* envVar) const override;
+                           cmValue envVar) const override;
 };
diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx
index f08b1da..a038f87 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.cxx
+++ b/Source/cmGlobalNMakeMakefileGenerator.cxx
@@ -42,7 +42,7 @@
   if (!this->cmGlobalGenerator::FindMakeProgram(mf)) {
     return false;
   }
-  if (cmProp nmakeCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
+  if (cmValue nmakeCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
     std::vector<std::string> command{ *nmakeCommand, "-?" };
     std::string out;
     std::string err;
@@ -70,7 +70,7 @@
 void cmGlobalNMakeMakefileGenerator::CheckNMakeFeatures()
 {
   this->NMakeSupportsUTF8 = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NMakeVersion.c_str(), "9");
+    cmSystemTools::OP_LESS, this->NMakeVersion, "9");
 }
 
 void cmGlobalNMakeMakefileGenerator::GetDocumentation(
@@ -81,7 +81,7 @@
 }
 
 void cmGlobalNMakeMakefileGenerator::PrintCompilerAdvice(
-  std::ostream& os, std::string const& lang, const char* envVar) const
+  std::ostream& os, std::string const& lang, cmValue envVar) const
 {
   if (lang == "CXX" || lang == "C") {
     /* clang-format off */
diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h
index 402b89f..4f202b5 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.h
+++ b/Source/cmGlobalNMakeMakefileGenerator.h
@@ -61,5 +61,5 @@
   void CheckNMakeFeatures();
 
   void PrintCompilerAdvice(std::ostream& os, std::string const& lang,
-                           const char* envVar) const override;
+                           cmValue envVar) const override;
 };
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 971a462..7122b9f 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -35,7 +35,6 @@
 #include "cmMessageType.h"
 #include "cmNinjaLinkLineComputer.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmScanDepFormat.h"
 #include "cmState.h"
@@ -46,6 +45,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -382,7 +382,7 @@
     if (restat) {
       vars["restat"] = "1";
     }
-    if (uses_terminal && this->SupportsConsolePool()) {
+    if (uses_terminal && this->SupportsDirectConsole()) {
       vars["pool"] = "console";
     } else if (!job_pool.empty()) {
       vars["pool"] = job_pool;
@@ -558,9 +558,8 @@
 void cmGlobalNinjaGenerator::Generate()
 {
   // Check minimum Ninja version.
-  if (cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
-                                    this->NinjaVersion.c_str(),
-                                    RequiredNinjaVersion().c_str())) {
+  if (cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                    RequiredNinjaVersion())) {
     std::ostringstream msg;
     msg << "The detected version of Ninja (" << this->NinjaVersion;
     msg << ") is less than the version of Ninja required by CMake (";
@@ -569,9 +568,6 @@
                                            msg.str());
     return;
   }
-  if (!this->InspectConfigTypeVariables()) {
-    return;
-  }
   if (!this->OpenBuildFileStreams()) {
     return;
   }
@@ -695,7 +691,7 @@
   if (!this->cmGlobalGenerator::FindMakeProgram(mf)) {
     return false;
   }
-  if (cmProp ninjaCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
+  if (cmValue ninjaCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
     this->NinjaCommand = *ninjaCommand;
     std::vector<std::string> command;
     command.push_back(this->NinjaCommand);
@@ -721,21 +717,21 @@
 
 void cmGlobalNinjaGenerator::CheckNinjaFeatures()
 {
-  this->NinjaSupportsConsolePool = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForConsolePool().c_str());
+  this->NinjaSupportsConsolePool =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForConsolePool());
   this->NinjaSupportsImplicitOuts = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    cmGlobalNinjaGenerator::RequiredNinjaVersionForImplicitOuts().c_str());
-  this->NinjaSupportsManifestRestat = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForManifestRestat().c_str());
-  this->NinjaSupportsMultilineDepfile = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForMultilineDepfile().c_str());
-  this->NinjaSupportsDyndeps = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForDyndeps().c_str());
+    cmSystemTools::OP_LESS, this->NinjaVersion,
+    cmGlobalNinjaGenerator::RequiredNinjaVersionForImplicitOuts());
+  this->NinjaSupportsManifestRestat =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForManifestRestat());
+  this->NinjaSupportsMultilineDepfile =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForMultilineDepfile());
+  this->NinjaSupportsDyndeps =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForDyndeps());
   if (!this->NinjaSupportsDyndeps) {
     // The ninja version number is not new enough to have upstream support.
     // Our ninja branch adds ".dyndep-#" to its version number,
@@ -753,21 +749,21 @@
   }
   this->NinjaSupportsUnconditionalRecompactTool =
     !cmSystemTools::VersionCompare(
-      cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-      RequiredNinjaVersionForUnconditionalRecompactTool().c_str());
-  this->NinjaSupportsRestatTool = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForRestatTool().c_str());
-  this->NinjaSupportsMultipleOutputs = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForMultipleOutputs().c_str());
+      cmSystemTools::OP_LESS, this->NinjaVersion,
+      RequiredNinjaVersionForUnconditionalRecompactTool());
+  this->NinjaSupportsRestatTool =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForRestatTool());
+  this->NinjaSupportsMultipleOutputs =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForMultipleOutputs());
   this->NinjaSupportsMetadataOnRegeneration = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForMetadataOnRegeneration().c_str());
+    cmSystemTools::OP_LESS, this->NinjaVersion,
+    RequiredNinjaVersionForMetadataOnRegeneration());
 #ifdef _WIN32
-  this->NinjaSupportsCodePage = !cmSystemTools::VersionCompare(
-    cmSystemTools::OP_LESS, this->NinjaVersion.c_str(),
-    RequiredNinjaVersionForCodePage().c_str());
+  this->NinjaSupportsCodePage =
+    !cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, this->NinjaVersion,
+                                   RequiredNinjaVersionForCodePage());
   if (this->NinjaSupportsCodePage) {
     this->CheckNinjaCodePage();
   } else {
@@ -920,14 +916,7 @@
   std::vector<std::string> const& langs, cmMakefile* mf, bool optional)
 {
   if (this->IsMultiConfig()) {
-    if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
-      mf->AddCacheDefinition(
-        "CMAKE_CONFIGURATION_TYPES", "Debug;Release;RelWithDebInfo",
-        "Semicolon separated list of supported configuration types, only "
-        "supports Debug, Release, MinSizeRel, and RelWithDebInfo, anything "
-        "else will be ignored",
-        cmStateEnums::STRING);
-    }
+    mf->InitCMAKE_CONFIGURATION_TYPES("Debug;Release;RelWithDebInfo");
   }
 
   this->cmGlobalGenerator::EnableLanguage(langs, mf, optional);
@@ -936,29 +925,21 @@
       continue;
     }
     this->ResolveLanguageCompiler(l, mf, optional);
-  }
 #ifdef _WIN32
-  const bool clangGnuMode =
-    ((mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "Clang") &&
-     (mf->GetSafeDefinition("CMAKE_C_COMPILER_FRONTEND_VARIANT") == "GNU")) ||
-    ((mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "Clang") &&
-     (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_FRONTEND_VARIANT") == "GNU"));
-
-  if (clangGnuMode ||
-      ((mf->GetSafeDefinition("CMAKE_C_SIMULATE_ID") != "MSVC") &&
-       (mf->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID") != "MSVC") &&
-       (mf->IsOn("CMAKE_COMPILER_IS_MINGW") ||
-        (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "GNU") ||
-        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "GNU") ||
-        (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "Clang") ||
-        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "Clang") ||
-        (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "ARMClang") ||
-        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "ARMClang") ||
-        (mf->GetSafeDefinition("CMAKE_C_COMPILER_ID") == "QCC") ||
-        (mf->GetSafeDefinition("CMAKE_CXX_COMPILER_ID") == "QCC")))) {
-    this->UsingGCCOnWindows = true;
-  }
+    std::string const& compilerId =
+      mf->GetSafeDefinition(cmStrCat("CMAKE_", l, "_COMPILER_ID"));
+    std::string const& simulateId =
+      mf->GetSafeDefinition(cmStrCat("CMAKE_", l, "_SIMULATE_ID"));
+    std::string const& compilerFrontendVariant = mf->GetSafeDefinition(
+      cmStrCat("CMAKE_", l, "_COMPILER_FRONTEND_VARIANT"));
+    if ((compilerId == "Clang" && compilerFrontendVariant == "GNU") ||
+        (simulateId != "MSVC" &&
+         (compilerId == "GNU" || compilerId == "QCC" ||
+          cmHasLiteralSuffix(compilerId, "Clang")))) {
+      this->UsingGCCOnWindows = true;
+    }
 #endif
+  }
 }
 
 // Implemented by:
@@ -1021,13 +1002,6 @@
 
 // Private virtual overrides
 
-std::string cmGlobalNinjaGenerator::GetEditCacheCommand() const
-{
-  // Ninja by design does not run interactive tools in the terminal,
-  // so our only choice is cmake-gui.
-  return cmSystemTools::GetCMakeGUICommand();
-}
-
 void cmGlobalNinjaGenerator::ComputeTargetObjectDirectory(
   cmGeneratorTarget* gt) const
 {
@@ -1286,7 +1260,7 @@
         break;
       }
     }
-    // FALLTHROUGH
+      CM_FALLTHROUGH;
     case cmStateEnums::EXECUTABLE: {
       outputs.push_back(this->ConvertToNinjaPath(target->GetFullPath(
         config, cmStateEnums::RuntimeBinaryArtifact, realname)));
@@ -1298,7 +1272,7 @@
         break;
       }
     }
-    // FALLTHROUGH
+      CM_FALLTHROUGH;
     case cmStateEnums::GLOBAL_TARGET:
     case cmStateEnums::INTERFACE_LIBRARY:
     case cmStateEnums::UTILITY: {
@@ -1849,7 +1823,7 @@
 
   // Use 'console' pool to get non buffered output of the CMake re-run call
   // Available since Ninja 1.5
-  if (this->SupportsConsolePool()) {
+  if (this->SupportsDirectConsole()) {
     reBuild.Variables["pool"] = "console";
   }
 
@@ -1943,7 +1917,7 @@
   return "ninja";
 }
 
-bool cmGlobalNinjaGenerator::SupportsConsolePool() const
+bool cmGlobalNinjaGenerator::SupportsDirectConsole() const
 {
   return this->NinjaSupportsConsolePool;
 }
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index bb4ce2b..84fc06c 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -220,7 +220,6 @@
   {
     return "package_source";
   }
-  const char* GetEditCacheTargetName() const override { return "edit_cache"; }
   const char* GetRebuildCacheTargetName() const override
   {
     return "rebuild_cache";
@@ -406,7 +405,7 @@
     return "1.10.2";
   }
   static std::string RequiredNinjaVersionForCodePage() { return "1.11"; }
-  bool SupportsConsolePool() const;
+  bool SupportsDirectConsole() const override;
   bool SupportsImplicitOuts() const;
   bool SupportsManifestRestat() const;
   bool SupportsMultilineDepfile() const;
@@ -482,14 +481,11 @@
     const std::set<std::string>& all, const std::set<std::string>& defaults,
     const std::vector<std::string>& items);
 
-  virtual bool InspectConfigTypeVariables() { return true; }
-
   std::set<std::string> CrossConfigs;
   std::set<std::string> DefaultConfigs;
   std::string DefaultFileConfig;
 
 private:
-  std::string GetEditCacheCommand() const override;
   bool FindMakeProgram(cmMakefile* mf) override;
   void CheckNinjaFeatures();
   void CheckNinjaCodePage();
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index 9c3de1e..0556540 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -20,12 +20,12 @@
 #include "cmMakefile.h"
 #include "cmMakefileTargetGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTargetDepend.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3(cmake* cm)
@@ -78,36 +78,6 @@
   entry.Brief = "Generates standard UNIX makefiles.";
 }
 
-std::string cmGlobalUnixMakefileGenerator3::GetEditCacheCommand() const
-{
-  // If generating for an extra IDE, the edit_cache target cannot
-  // launch a terminal-interactive tool, so always use cmake-gui.
-  if (!this->GetExtraGeneratorName().empty()) {
-    return cmSystemTools::GetCMakeGUICommand();
-  }
-
-  // Use an internal cache entry to track the latest dialog used
-  // to edit the cache, and use that for the edit_cache target.
-  cmake* cm = this->GetCMakeInstance();
-  std::string editCacheCommand = cm->GetCMakeEditCommand();
-  if (!cm->GetCacheDefinition("CMAKE_EDIT_COMMAND") ||
-      !editCacheCommand.empty()) {
-    if (editCacheCommand.empty()) {
-      editCacheCommand = cmSystemTools::GetCMakeCursesCommand();
-    }
-    if (editCacheCommand.empty()) {
-      editCacheCommand = cmSystemTools::GetCMakeGUICommand();
-    }
-    if (!editCacheCommand.empty()) {
-      cm->AddCacheEntry("CMAKE_EDIT_COMMAND", editCacheCommand.c_str(),
-                        "Path to cache edit program executable.",
-                        cmStateEnums::INTERNAL);
-    }
-  }
-  cmProp edit_cmd = cm->GetCacheDefinition("CMAKE_EDIT_COMMAND");
-  return edit_cmd ? *edit_cmd : std::string();
-}
-
 void cmGlobalUnixMakefileGenerator3::ComputeTargetObjectDirectory(
   cmGeneratorTarget* gt) const
 {
@@ -714,7 +684,7 @@
       }
 
       bool targetMessages = true;
-      if (cmProp tgtMsg =
+      if (cmValue tgtMsg =
             this->GetCMakeInstance()->GetState()->GetGlobalProperty(
               "TARGET_MESSAGES")) {
         targetMessages = cmIsOn(*tgtMsg);
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index 7c950cc..94ee476 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -228,7 +228,6 @@
   {
     return "package_source";
   }
-  const char* GetEditCacheTargetName() const override { return "edit_cache"; }
   const char* GetRebuildCacheTargetName() const override
   {
     return "rebuild_cache";
@@ -278,7 +277,6 @@
 
 private:
   const char* GetBuildIgnoreErrorsFlag() const override { return "-i"; }
-  std::string GetEditCacheCommand() const override;
 
   std::map<cmStateSnapshot, std::set<cmGeneratorTarget const*>,
            cmStateSnapshot::StrictWeakOrder>
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index fdb7155..6cab492 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -88,7 +88,7 @@
   void GetDocumentation(cmDocumentationEntry& entry) const override
   {
     entry.Name = std::string(vs10generatorName) + " [arch]";
-    entry.Brief = "Generates Visual Studio 2010 project files.  "
+    entry.Brief = "Deprecated.  Generates Visual Studio 2010 project files.  "
                   "Optional [arch] can be \"Win64\" or \"IA64\".";
   }
 
@@ -139,7 +139,6 @@
     "ProductDir",
     vc10Express, cmSystemTools::KeyWOW64_32);
   this->CudaEnabled = false;
-  this->MSBuildCommandInitialized = false;
   {
     std::string envPlatformToolset;
     if (cmSystemTools::GetEnv("PlatformToolset", envPlatformToolset) &&
@@ -581,6 +580,13 @@
 
   this->DefaultPlatformToolset = this->SelectWindowsCEToolset();
 
+  if (this->GetVersion() == cmGlobalVisualStudioGenerator::VS12) {
+    // VS 12 .NET CF defaults to .NET framework 3.9 for Windows CE.
+    this->DefaultTargetFrameworkVersion = "v3.9";
+    this->DefaultTargetFrameworkIdentifier = "WindowsEmbeddedCompact";
+    this->DefaultTargetFrameworkTargetsVersion = "v8.0";
+  }
+
   return true;
 }
 
@@ -857,6 +863,12 @@
   return this->MSBuildCommand;
 }
 
+cm::optional<std::string>
+cmGlobalVisualStudio10Generator::FindMSBuildCommandEarly(cmMakefile*)
+{
+  return this->GetMSBuildCommand();
+}
+
 std::string cmGlobalVisualStudio10Generator::FindMSBuildCommand()
 {
   std::string msbuild;
diff --git a/Source/cmGlobalVisualStudio10Generator.h b/Source/cmGlobalVisualStudio10Generator.h
index b7ae1ee..6e62390 100644
--- a/Source/cmGlobalVisualStudio10Generator.h
+++ b/Source/cmGlobalVisualStudio10Generator.h
@@ -20,6 +20,8 @@
 public:
   static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory();
 
+  bool IsVisualStudioAtLeast10() const override { return true; }
+
   bool MatchesGeneratorName(const std::string& name) const override;
 
   bool SetSystemName(std::string const& s, cmMakefile* mf) override;
@@ -132,6 +134,8 @@
 
   bool GetSupportsUnityBuilds() const { return this->SupportsUnityBuilds; }
 
+  virtual cm::optional<std::string> FindMSBuildCommandEarly(cmMakefile* mf);
+
   bool FindMakeProgram(cmMakefile* mf) override;
 
   bool IsIPOSupported() const override { return true; }
@@ -222,6 +226,7 @@
   bool SystemIsWindowsPhone = false;
   bool SystemIsWindowsStore = false;
   bool SystemIsAndroid = false;
+  bool MSBuildCommandInitialized = false;
 
 private:
   class Factory;
@@ -243,7 +248,6 @@
   LongestSourcePath LongestSource;
 
   std::string MSBuildCommand;
-  bool MSBuildCommandInitialized;
   std::set<std::string> AndroidExecutableWarnings;
   virtual std::string FindMSBuildCommand();
   std::string FindDevEnvCommand() override;
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index b46f1b9..ff1642f 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -239,7 +239,7 @@
 {
   // if the given value is set, it can either be OFF/FALSE or a valid SDK
   // string
-  if (cmProp value = mf->GetDefinition(
+  if (cmValue value = mf->GetDefinition(
         "CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM")) {
 
     // If the value is some off/false value, then there is NO maximum set.
diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx
index 0083c40..50975ff 100644
--- a/Source/cmGlobalVisualStudio71Generator.cxx
+++ b/Source/cmGlobalVisualStudio71Generator.cxx
@@ -102,7 +102,7 @@
     ext = ".csproj";
     project = "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"";
   }
-  cmProp targetExt = t->GetProperty("GENERATOR_FILE_NAME_EXT");
+  cmValue targetExt = t->GetProperty("GENERATOR_FILE_NAME_EXT");
   if (targetExt) {
     ext = *targetExt;
   }
@@ -157,11 +157,11 @@
 // executables to the libraries it uses are also done here
 void cmGlobalVisualStudio71Generator::WriteExternalProject(
   std::ostream& fout, const std::string& name, const std::string& location,
-  const char* typeGuid,
-  const std::set<BT<std::pair<std::string, bool>>>& depends)
+  cmValue typeGuid, const std::set<BT<std::pair<std::string, bool>>>& depends)
 {
   fout << "Project(\"{"
-       << (typeGuid ? typeGuid : this->ExternalProjectType(location))
+       << (typeGuid ? typeGuid
+                    : std::string(this->ExternalProjectType(location)))
        << "}\") = \"" << name << "\", \""
        << this->ConvertToSolutionPath(location) << "\", \"{"
        << this->GetGUID(name) << "}\"\n";
@@ -198,8 +198,8 @@
     std::vector<std::string> mapConfig;
     const char* dstConfig = i.c_str();
     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
-      if (cmProp m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
-                                        cmSystemTools::UpperCase(i))) {
+      if (cmValue m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
+                                         cmSystemTools::UpperCase(i))) {
         cmExpandList(*m, mapConfig);
         if (!mapConfig.empty()) {
           dstConfig = mapConfig[0].c_str();
diff --git a/Source/cmGlobalVisualStudio71Generator.h b/Source/cmGlobalVisualStudio71Generator.h
index 7d38199..cb3b8c1 100644
--- a/Source/cmGlobalVisualStudio71Generator.h
+++ b/Source/cmGlobalVisualStudio71Generator.h
@@ -33,7 +33,7 @@
     const std::string& platformMapping = "") override;
   void WriteExternalProject(
     std::ostream& fout, const std::string& name, const std::string& path,
-    const char* typeGuid,
+    cmValue typeGuid,
     const std::set<BT<std::pair<std::string, bool>>>& depends) override;
 
   // Folders are not supported by VS 7.1.
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 0c85a044..6876e61 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -37,6 +37,7 @@
     cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue },
   { "PrecompiledHeaderThrough", "Yu", "Precompiled Header Name", "",
     cmVS7FlagTable::UserValueRequired },
+  { "UsePrecompiledHeader", "Y-", "Don't use precompiled header", "0", 0 },
   { "WholeProgramOptimization", "LTCG", "WholeProgramOptimization", "true",
     0 },
 
@@ -107,14 +108,7 @@
 {
   mf->AddDefinition("CMAKE_GENERATOR_RC", "rc");
   mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
-  if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
-    mf->AddCacheDefinition(
-      "CMAKE_CONFIGURATION_TYPES", "Debug;Release;MinSizeRel;RelWithDebInfo",
-      "Semicolon separated list of supported configuration types, "
-      "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, "
-      "anything else will be ignored.",
-      cmStateEnums::STRING);
-  }
+  mf->InitCMAKE_CONFIGURATION_TYPES("Debug;Release;MinSizeRel;RelWithDebInfo");
 
   // Create list of configurations requested by user's cache, if any.
   this->cmGlobalVisualStudioGenerator::EnableLanguage(lang, mf, optional);
@@ -303,6 +297,25 @@
     this->CallVisualStudioMacro(MacroReload,
                                 GetSLNFile(this->LocalGenerators[0].get()));
   }
+
+  if (this->Version == VS10 && !this->CMakeInstance->GetIsInTryCompile()) {
+    std::string cmakeWarnVS10;
+    if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
+          "CMAKE_WARN_VS10")) {
+      this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS10");
+      cmakeWarnVS10 = *cached;
+    } else {
+      cmSystemTools::GetEnv("CMAKE_WARN_VS10", cmakeWarnVS10);
+    }
+    if (cmakeWarnVS10.empty() || !cmIsOff(cmakeWarnVS10)) {
+      this->CMakeInstance->IssueMessage(
+        MessageType::WARNING,
+        "The \"Visual Studio 10 2010\" generator is deprecated "
+        "and will be removed in a future version of CMake."
+        "\n"
+        "Add CMAKE_WARN_VS10=OFF to the cache to disable this warning.");
+    }
+  }
 }
 
 void cmGlobalVisualStudio7Generator::OutputSLNFile(
@@ -342,17 +355,17 @@
     if (!target->IsInBuildSystem()) {
       continue;
     }
-    cmProp expath = target->GetProperty("EXTERNAL_MSPROJECT");
+    cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     if (expath) {
       std::set<std::string> allConfigurations(configs.begin(), configs.end());
-      cmProp mapping = target->GetProperty("VS_PLATFORM_MAPPING");
+      cmValue mapping = target->GetProperty("VS_PLATFORM_MAPPING");
       this->WriteProjectConfigurations(fout, target->GetName(), *target,
                                        configs, allConfigurations,
                                        mapping ? *mapping : "");
     } else {
       const std::set<std::string>& configsPartOfDefaultBuild =
         this->IsPartOfDefaultBuild(configs, projectTargets, target);
-      cmProp vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
+      cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
       if (vcprojName) {
         this->WriteProjectConfigurations(fout, *vcprojName, *target, configs,
                                          configsPartOfDefaultBuild);
@@ -374,18 +387,17 @@
     bool written = false;
 
     // handle external vc project files
-    cmProp expath = target->GetProperty("EXTERNAL_MSPROJECT");
+    cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     if (expath) {
       std::string project = target->GetName();
       std::string location = *expath;
 
-      this->WriteExternalProject(
-        fout, project, location,
-        cmToCStr(target->GetProperty("VS_PROJECT_TYPE")),
-        target->GetUtilities());
+      this->WriteExternalProject(fout, project, location,
+                                 target->GetProperty("VS_PROJECT_TYPE"),
+                                 target->GetUtilities());
       written = true;
     } else {
-      cmProp vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
+      cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
       if (vcprojName) {
         cmLocalGenerator* lg = target->GetLocalGenerator();
         std::string dir = lg->GetCurrentBinaryDirectory();
@@ -438,7 +450,7 @@
     if (!target->IsInBuildSystem()) {
       continue;
     }
-    cmProp vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
+    cmValue vcprojName = target->GetProperty("GENERATOR_FILE_NAME");
     if (vcprojName) {
       std::string dir =
         target->GetLocalGenerator()->GetCurrentSourceDirectory();
@@ -526,7 +538,7 @@
           extensibilityAddInsOverridden = true;
         }
         fout << "\tGlobalSection(" << name << ") = " << sectionType << "\n";
-        cmProp p = root->GetMakefile()->GetProperty(it);
+        cmValue p = root->GetMakefile()->GetProperty(it);
         std::vector<std::string> keyValuePairs = cmExpandedList(p ? *p : "");
         for (std::string const& itPair : keyValuePairs) {
           const std::string::size_type posEqual = itPair.find('=');
@@ -625,7 +637,7 @@
 std::string cmGlobalVisualStudio7Generator::GetGUID(std::string const& name)
 {
   std::string const& guidStoreName = name + "_GUID_CMAKE";
-  if (cmProp storedGUID =
+  if (cmValue storedGUID =
         this->CMakeInstance->GetCacheDefinition(guidStoreName)) {
     return *storedGUID;
   }
@@ -675,7 +687,7 @@
           "CMAKE_VS_INCLUDE_" + t + "_TO_DEFAULT_BUILD";
         // inspect CMAKE_VS_INCLUDE_<t>_TO_DEFAULT_BUILD properties
         for (std::string const& i : configs) {
-          cmProp propertyValue =
+          cmValue propertyValue =
             target->Target->GetMakefile()->GetDefinition(propertyName);
           if (propertyValue &&
               cmIsOn(cmGeneratorExpression::Evaluate(
diff --git a/Source/cmGlobalVisualStudio7Generator.h b/Source/cmGlobalVisualStudio7Generator.h
index 148762e..8e762cf 100644
--- a/Source/cmGlobalVisualStudio7Generator.h
+++ b/Source/cmGlobalVisualStudio7Generator.h
@@ -6,6 +6,7 @@
 
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
+#include "cmValue.h"
 
 class cmTarget;
 struct cmIDEFlagTable;
@@ -142,7 +143,7 @@
 
   virtual void WriteExternalProject(
     std::ostream& fout, const std::string& name, const std::string& path,
-    const char* typeGuid,
+    cmValue typeGuid,
     const std::set<BT<std::pair<std::string, bool>>>& dependencies) = 0;
 
   std::string ConvertToSolutionPath(const std::string& path);
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index b19212e..1e45813 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -67,12 +67,55 @@
 bool cmGlobalVisualStudio8Generator::SetGeneratorPlatform(std::string const& p,
                                                           cmMakefile* mf)
 {
-  if (!this->PlatformInGeneratorName) {
-    this->GeneratorPlatform = p;
-    return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform("", mf);
-  } else {
+  if (this->PlatformInGeneratorName) {
+    // This is an old-style generator name that contains the platform name.
+    // No explicit platform specification is supported, so pass it through
+    // to our base class implementation, which errors on non-empty platforms.
     return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform(p, mf);
   }
+
+  this->GeneratorPlatform = p;
+
+  // FIXME: Add CMAKE_GENERATOR_PLATFORM field to set the framework.
+  // For now, just report the generator's default, if any.
+  if (cm::optional<std::string> const& targetFrameworkVersion =
+        this->GetTargetFrameworkVersion()) {
+    mf->AddDefinition("CMAKE_VS_TARGET_FRAMEWORK_VERSION",
+                      *targetFrameworkVersion);
+  }
+  if (cm::optional<std::string> const& targetFrameworkIdentifier =
+        this->GetTargetFrameworkIdentifier()) {
+    mf->AddDefinition("CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER",
+                      *targetFrameworkIdentifier);
+  }
+  if (cm::optional<std::string> const& targetFrameworkTargetsVersion =
+        this->GetTargetFrameworkTargetsVersion()) {
+    mf->AddDefinition("CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION",
+                      *targetFrameworkTargetsVersion);
+  }
+
+  // The generator name does not contain the platform name, and so supports
+  // explicit platform specification.  We handled that above, so pass an
+  // empty platform name to our base class implementation so it does not error.
+  return this->cmGlobalVisualStudio7Generator::SetGeneratorPlatform("", mf);
+}
+
+cm::optional<std::string> const&
+cmGlobalVisualStudio8Generator::GetTargetFrameworkVersion() const
+{
+  return this->DefaultTargetFrameworkVersion;
+}
+
+cm::optional<std::string> const&
+cmGlobalVisualStudio8Generator::GetTargetFrameworkIdentifier() const
+{
+  return this->DefaultTargetFrameworkIdentifier;
+}
+
+cm::optional<std::string> const&
+cmGlobalVisualStudio8Generator::GetTargetFrameworkTargetsVersion() const
+{
+  return this->DefaultTargetFrameworkTargetsVersion;
 }
 
 std::string cmGlobalVisualStudio8Generator::GetGenerateStampList()
@@ -245,8 +288,8 @@
     std::vector<std::string> mapConfig;
     const char* dstConfig = i.c_str();
     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
-      if (cmProp m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
-                                        cmSystemTools::UpperCase(i))) {
+      if (cmValue m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
+                                         cmSystemTools::UpperCase(i))) {
         cmExpandList(*m, mapConfig);
         if (!mapConfig.empty()) {
           dstConfig = mapConfig[0].c_str();
@@ -287,14 +330,14 @@
     return false;
   }
 
-  if (cmProp prop = target.GetProperty("VS_SOLUTION_DEPLOY")) {
+  if (cmValue prop = target.GetProperty("VS_SOLUTION_DEPLOY")) {
     // If set, it dictates behavior
     return cmIsOn(
       cmGeneratorExpression::Evaluate(*prop, target.LocalGenerator, config));
   }
 
   // To be deprecated, disable deployment even if target supports it.
-  if (cmProp prop = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) {
+  if (cmValue prop = target.GetProperty("VS_NO_SOLUTION_DEPLOY")) {
     if (cmIsOn(cmGeneratorExpression::Evaluate(*prop, target.LocalGenerator,
                                                config))) {
       // If true, always disable deployment
@@ -370,6 +413,7 @@
     cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue },
   { "PrecompiledHeaderThrough", "Yu", "Precompiled Header Name", "",
     cmVS7FlagTable::UserValueRequired },
+  { "UsePrecompiledHeader", "Y-", "Don't use precompiled header", "0", 0 },
   // There is no YX option in the VS8 IDE.
 
   // Exception handling mode.  If no entries match, it will be FALSE.
diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h
index 96e3553..b6ecdf0 100644
--- a/Source/cmGlobalVisualStudio8Generator.h
+++ b/Source/cmGlobalVisualStudio8Generator.h
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include <cm/optional>
+
 #include "cmGlobalVisualStudio71Generator.h"
 
 /** \class cmGlobalVisualStudio8Generator
@@ -24,6 +26,10 @@
 
   bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override;
 
+  cm::optional<std::string> const& GetTargetFrameworkVersion() const;
+  cm::optional<std::string> const& GetTargetFrameworkIdentifier() const;
+  cm::optional<std::string> const& GetTargetFrameworkTargetsVersion() const;
+
   /**
    * Override Configure and Generate to add the build-system check
    * target.
@@ -76,4 +82,8 @@
 
   std::string Name;
   std::string WindowsCEVersion;
+
+  cm::optional<std::string> DefaultTargetFrameworkVersion;
+  cm::optional<std::string> DefaultTargetFrameworkIdentifier;
+  cm::optional<std::string> DefaultTargetFrameworkTargetsVersion;
 };
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index cdecea0..f9bd67e 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -519,7 +519,7 @@
 std::string cmGlobalVisualStudioGenerator::GetStartupProjectName(
   cmLocalGenerator const* root) const
 {
-  cmProp n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
+  cmValue n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT");
   if (cmNonempty(n)) {
     std::string startup = *n;
     if (this->FindTarget(startup)) {
@@ -820,7 +820,7 @@
   // This allows the project to control the language choice in
   // a target with none of its own sources, e.g. when also using
   // object libraries.
-  cmProp linkLang = gt->GetProperty("LINKER_LANGUAGE");
+  cmValue linkLang = gt->GetProperty("LINKER_LANGUAGE");
   if (cmNonempty(linkLang)) {
     languages.insert(*linkLang);
   }
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 151f39f..23c8a02 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -167,11 +167,6 @@
 
   void WriteSLNHeader(std::ostream& fout);
 
-  FindMakeProgramStage GetFindMakeProgramStage() const override
-  {
-    return FindMakeProgramStage::Early;
-  }
-
   bool ComputeTargetDepends() override;
   class VSDependSet : public std::set<std::string>
   {
@@ -201,7 +196,7 @@
 private:
   virtual std::string GetVSMakeProgram() = 0;
   void PrintCompilerAdvice(std::ostream&, std::string const&,
-                           const char*) const override
+                           cmValue) const override
   {
   }
 
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index d0ad53e..b5a6b9f 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -401,6 +401,12 @@
     this->DefaultPlatformName = VSHostPlatformName();
     this->DefaultPlatformToolsetHostArchitecture = VSHostArchitecture();
   }
+  if (this->Version >= cmGlobalVisualStudioGenerator::VS17) {
+    // FIXME: Search for an existing framework?  Under '%ProgramFiles(x86)%',
+    // see 'Reference Assemblies\Microsoft\Framework\.NETFramework'.
+    // Use a version installed by VS 2022 without a separate component.
+    this->DefaultTargetFrameworkVersion = "v4.7.2";
+  }
 }
 
 bool cmGlobalVisualStudioVersionedGenerator::MatchesGeneratorName(
@@ -436,6 +442,11 @@
 bool cmGlobalVisualStudioVersionedGenerator::SetGeneratorInstance(
   std::string const& i, cmMakefile* mf)
 {
+  if (this->LastGeneratorInstanceString &&
+      i == *(this->LastGeneratorInstanceString)) {
+    return true;
+  }
+
   if (!i.empty()) {
     if (!this->vsSetupAPIHelper.SetVSInstance(i)) {
       std::ostringstream e;
@@ -467,11 +478,16 @@
   // Save the selected instance persistently.
   std::string genInstance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
   if (vsInstance != genInstance) {
-    this->CMakeInstance->AddCacheEntry(
-      "CMAKE_GENERATOR_INSTANCE", vsInstance.c_str(),
-      "Generator instance identifier.", cmStateEnums::INTERNAL);
+    this->CMakeInstance->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", vsInstance,
+                                       "Generator instance identifier.",
+                                       cmStateEnums::INTERNAL);
   }
 
+  // The selected instance may have a different MSBuild than previously found.
+  this->MSBuildCommandInitialized = false;
+
+  this->LastGeneratorInstanceString = i;
+
   return true;
 }
 
@@ -712,6 +728,17 @@
   return std::string();
 }
 
+cm::optional<std::string>
+cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommandEarly(cmMakefile* mf)
+{
+  std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
+  if (!this->SetGeneratorInstance(instance, mf)) {
+    cmSystemTools::SetFatalErrorOccured();
+    return {};
+  }
+  return this->cmGlobalVisualStudio14Generator::FindMSBuildCommandEarly(mf);
+}
+
 std::string cmGlobalVisualStudioVersionedGenerator::FindMSBuildCommand()
 {
   std::string msbuild;
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.h b/Source/cmGlobalVisualStudioVersionedGenerator.h
index f07492d..2aed65b 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.h
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.h
@@ -8,6 +8,8 @@
 #include <memory>
 #include <string>
 
+#include <cm/optional>
+
 #include "cmGlobalVisualStudio14Generator.h"
 #include "cmVSSetupHelper.h"
 
@@ -29,6 +31,8 @@
 
   bool GetVSInstance(std::string& dir) const;
 
+  cm::optional<std::string> FindMSBuildCommandEarly(cmMakefile* mf) override;
+
   cm::optional<std::string> GetVSInstanceVersion() const override;
 
   AuxToolset FindAuxToolset(std::string& version,
@@ -72,4 +76,5 @@
   class Factory17;
   friend class Factory17;
   mutable cmVSSetupAPIHelper vsSetupAPIHelper;
+  cm::optional<std::string> LastGeneratorInstanceString;
 };
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index 77403b0..f34ef62 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -431,14 +431,7 @@
 {
   mf->AddDefinition("XCODE", "1");
   mf->AddDefinition("XCODE_VERSION", this->VersionString);
-  if (!mf->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
-    mf->AddCacheDefinition(
-      "CMAKE_CONFIGURATION_TYPES", "Debug;Release;MinSizeRel;RelWithDebInfo",
-      "Semicolon separated list of supported configuration types, "
-      "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, "
-      "anything else will be ignored.",
-      cmStateEnums::STRING);
-  }
+  mf->InitCMAKE_CONFIGURATION_TYPES("Debug;Release;MinSizeRel;RelWithDebInfo");
   mf->AddDefinition("CMAKE_GENERATOR_NO_COMPILER_ENV", "1");
   this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
   this->ComputeArchitectures(mf);
@@ -948,11 +941,11 @@
   gtgt->AddExplicitLanguageFlags(flags, *sf);
 
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
-  if (cmProp cflags = sf->GetProperty(COMPILE_FLAGS)) {
+  if (cmValue cflags = sf->GetProperty(COMPILE_FLAGS)) {
     lg->AppendFlags(flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
   }
   const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
-  if (cmProp coptions = sf->GetProperty(COMPILE_OPTIONS)) {
+  if (cmValue coptions = sf->GetProperty(COMPILE_OPTIONS)) {
     lg->AppendCompileOptions(
       flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
   }
@@ -960,7 +953,7 @@
   // Add per-source definitions.
   BuildObjectListOrString flagsBuild(this, false);
   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
-  if (cmProp compile_defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
+  if (cmValue compile_defs = sf->GetProperty(COMPILE_DEFINITIONS)) {
     this->AppendDefines(
       flagsBuild,
       genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS).c_str(),
@@ -981,7 +974,7 @@
   // Add per-source include directories.
   std::vector<std::string> includes;
   const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
-  if (cmProp cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
+  if (cmValue cincludes = sf->GetProperty(INCLUDE_DIRECTORIES)) {
     lg->AppendIncludeDirectories(
       includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
       *sf);
@@ -1013,7 +1006,7 @@
   }
 
   // Add user-specified file attributes.
-  cmProp extraFileAttributes = sf->GetProperty("XCODE_FILE_ATTRIBUTES");
+  cmValue extraFileAttributes = sf->GetProperty("XCODE_FILE_ATTRIBUTES");
   if (extraFileAttributes) {
     // Expand the list of attributes.
     std::vector<std::string> attributes = cmExpandedList(*extraFileAttributes);
@@ -1104,7 +1097,7 @@
   } else if (ext == "h") {
     sourcecode += ".c.h";
   } else if (ext == "hxx" || ext == "hpp" || ext == "txx" || ext == "pch" ||
-             ext == "hh") {
+             ext == "hh" || ext == "inl") {
     sourcecode += ".cpp.h";
   } else if (ext == "png" || ext == "gif" || ext == "jpg") {
     keepLastKnownFileType = true;
@@ -1201,9 +1194,9 @@
   bool useLastKnownFileType = false;
   std::string fileType;
   if (sf) {
-    if (cmProp e = sf->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
+    if (cmValue e = sf->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
       fileType = *e;
-    } else if (cmProp l = sf->GetProperty("XCODE_LAST_KNOWN_FILE_TYPE")) {
+    } else if (cmValue l = sf->GetProperty("XCODE_LAST_KNOWN_FILE_TYPE")) {
       useLastKnownFileType = true;
       fileType = *l;
     }
@@ -1674,7 +1667,7 @@
     fout << "\n";
   }
   if (cmSourceFile* sf = mf->GetOrCreateSource(fname)) {
-    sf->SetProperty("LANGUAGE", llang.c_str());
+    sf->SetProperty("LANGUAGE", llang);
     gtgt->AddSource(fname);
   }
 }
@@ -1763,9 +1756,6 @@
       if (sourceFile->GetCustomCommand() &&
           visited.insert(sourceFile).second) {
         commands.push_back(*sourceFile->GetCustomCommand());
-        if (this->XcodeBuildSystem >= BuildSystem::Twelve) {
-          this->CustomCommandRoots[sourceFile].insert(gtgt);
-        }
       }
     }
     // create custom commands phase
@@ -2420,7 +2410,7 @@
     this->CurrentLocalGenerator->GetStaticLibraryFlags(
       extraLinkOptions, configName, llang, gtgt);
   } else {
-    cmProp targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
+    cmValue targetLinkFlags = gtgt->GetProperty("LINK_FLAGS");
     if (targetLinkFlags) {
       this->CurrentLocalGenerator->AppendFlags(extraLinkOptions,
                                                *targetLinkFlags);
@@ -2428,7 +2418,7 @@
     if (!configName.empty()) {
       std::string linkFlagsVar =
         cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(configName));
-      if (cmProp linkFlags = gtgt->GetProperty(linkFlagsVar)) {
+      if (cmValue linkFlags = gtgt->GetProperty(linkFlagsVar)) {
         this->CurrentLocalGenerator->AppendFlags(extraLinkOptions, *linkFlags);
       }
     }
@@ -2465,8 +2455,8 @@
   std::string pnsuffix;
   gtgt->GetFullNameComponents(pnprefix, pnbase, pnsuffix, configName);
 
-  cmProp version = gtgt->GetProperty("VERSION");
-  cmProp soversion = gtgt->GetProperty("SOVERSION");
+  cmValue version = gtgt->GetProperty("VERSION");
+  cmValue soversion = gtgt->GetProperty("SOVERSION");
   if (!gtgt->HasSOName(configName) || gtgt->IsFrameworkOnApple()) {
     version = nullptr;
     soversion = nullptr;
@@ -2530,7 +2520,7 @@
         std::string fw_version = gtgt->GetFrameworkVersion();
         buildSettings->AddAttribute("FRAMEWORK_VERSION",
                                     this->CreateString(fw_version));
-        cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
+        cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
         if (ext) {
           buildSettings->AddAttribute("WRAPPER_EXTENSION",
                                       this->CreateString(*ext));
@@ -2571,7 +2561,7 @@
           extraLinkOptions += " ";
           extraLinkOptions += createFlags;
         }
-        cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
+        cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
         if (ext) {
           buildSettings->AddAttribute("WRAPPER_EXTENSION",
                                       this->CreateString(*ext));
@@ -2605,7 +2595,7 @@
         std::string fw_version = gtgt->GetFrameworkVersion();
         buildSettings->AddAttribute("FRAMEWORK_VERSION",
                                     this->CreateString(fw_version));
-        cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
+        cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
         if (ext) {
           buildSettings->AddAttribute("WRAPPER_EXTENSION",
                                       this->CreateString(*ext));
@@ -2644,7 +2634,7 @@
 
       // Handle bundles and normal executables separately.
       if (gtgt->GetPropertyAsBool("MACOSX_BUNDLE")) {
-        cmProp ext = gtgt->GetProperty("BUNDLE_EXTENSION");
+        cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
         if (ext) {
           buildSettings->AddAttribute("WRAPPER_EXTENSION",
                                       this->CreateString(*ext));
@@ -3090,7 +3080,7 @@
 const char* cmGlobalXCodeGenerator::GetTargetFileType(
   cmGeneratorTarget* target)
 {
-  if (cmProp e = target->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
+  if (cmValue e = target->GetProperty("XCODE_EXPLICIT_FILE_TYPE")) {
     return e->c_str();
   }
 
@@ -3123,7 +3113,7 @@
 const char* cmGlobalXCodeGenerator::GetTargetProductType(
   cmGeneratorTarget* target)
 {
-  if (cmProp e = target->GetProperty("XCODE_PRODUCT_TYPE")) {
+  if (cmValue e = target->GetProperty("XCODE_PRODUCT_TYPE")) {
     return e->c_str();
   }
 
@@ -3242,15 +3232,14 @@
                                                   const std::string& id)
 {
   std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
-  cmProp storedGUID = this->CMakeInstance->GetCacheDefinition(guidStoreName);
+  cmValue storedGUID = this->CMakeInstance->GetCacheDefinition(guidStoreName);
 
   if (storedGUID) {
     return *storedGUID;
   }
 
-  this->CMakeInstance->AddCacheEntry(guidStoreName, id.c_str(),
-                                     "Stored Xcode object GUID",
-                                     cmStateEnums::INTERNAL);
+  this->CMakeInstance->AddCacheEntry(
+    guidStoreName, id, "Stored Xcode object GUID", cmStateEnums::INTERNAL);
 
   return id;
 }
@@ -3419,7 +3408,7 @@
   std::map<std::string, std::vector<std::string>> targetProductNameMap;
   bool useLinkPhase = false;
   bool forceLinkPhase = false;
-  cmProp prop =
+  cmValue prop =
     target->GetTarget()->GetProperty("XCODE_LINK_BUILD_PHASE_MODE");
   if (prop) {
     if (*prop == "BUILT_ONLY") {
@@ -3792,7 +3781,7 @@
   if (!(isFrameworkTarget || isBundleTarget || isCFBundleTarget)) {
     return;
   }
-  cmProp files = gt->GetProperty(embedPropertyName);
+  cmValue files = gt->GetProperty(embedPropertyName);
   if (!files) {
     return;
   }
@@ -3807,7 +3796,7 @@
                                     this->CreateString(dstSubfolderSpec));
   copyFilesBuildPhase->AddAttribute(
     "name", this->CreateString(copyFilesBuildPhaseName));
-  if (cmProp fwEmbedPath =
+  if (cmValue fwEmbedPath =
         gt->GetProperty(cmStrCat(embedPropertyName, "_PATH"))) {
     copyFilesBuildPhase->AddAttribute("dstPath",
                                       this->CreateString(*fwEmbedPath));
@@ -3817,7 +3806,7 @@
   copyFilesBuildPhase->AddAttribute("runOnlyForDeploymentPostprocessing",
                                     this->CreateString("0"));
   cmXCodeObject* buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
-  // Collect all embedded frameworks and add them to build phase
+  // Collect all embedded frameworks and dylibs and add them to build phase
   std::vector<std::string> relFiles = cmExpandedList(*files);
   for (std::string const& relFile : relFiles) {
     cmXCodeObject* buildFile{ nullptr };
@@ -3847,7 +3836,8 @@
       } else {
         buildFile = it->second;
       }
-    } else if (cmSystemTools::IsPathToFramework(relFile)) {
+    } else if (cmSystemTools::IsPathToFramework(relFile) ||
+               cmSystemTools::IsPathToMacOSSharedLibrary(relFile)) {
       // This is a regular string path - create file reference
       auto it = this->EmbeddedLibRefs.find(relFile);
       if (it == this->EmbeddedLibRefs.end()) {
@@ -3913,6 +3903,8 @@
 {
   static const auto dstSubfolderSpec = "10";
 
+  // Despite the name, by default Xcode uses "Embed Frameworks" build phase for
+  // both frameworks and dynamic libraries
   this->AddEmbeddedObjects(target, "Embed Frameworks",
                            "XCODE_EMBED_FRAMEWORKS", dstSubfolderSpec,
                            NoActionOnCopyByDefault);
@@ -4191,8 +4183,8 @@
                            this->CreateString(defaultConfigName));
   cmXCodeObject* buildSettings =
     this->CreateObject(cmXCodeObject::ATTRIBUTE_GROUP);
-  cmProp sysroot = this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT");
-  cmProp deploymentTarget =
+  cmValue sysroot = this->CurrentMakefile->GetDefinition("CMAKE_OSX_SYSROOT");
+  cmValue deploymentTarget =
     this->CurrentMakefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
   if (sysroot) {
     buildSettings->AddAttribute("SDKROOT", this->CreateString(*sysroot));
@@ -4225,7 +4217,7 @@
   }
   if (this->GetLanguageEnabled("Swift")) {
     std::string swiftVersion;
-    if (cmProp vers = this->CurrentMakefile->GetDefinition(
+    if (cmValue vers = this->CurrentMakefile->GetDefinition(
           "CMAKE_Swift_LANGUAGE_VERSION")) {
       swiftVersion = *vers;
     } else if (this->XcodeVersion >= 102) {
@@ -4348,7 +4340,7 @@
 void cmGlobalXCodeGenerator::ComputeArchitectures(cmMakefile* mf)
 {
   this->Architectures.clear();
-  cmProp sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT");
+  cmValue sysroot = mf->GetDefinition("CMAKE_OSX_SYSROOT");
   if (sysroot) {
     mf->GetDefExpandList("CMAKE_OSX_ARCHITECTURES", this->Architectures);
   }
@@ -4361,7 +4353,7 @@
     // With no ARCHS we use ONLY_ACTIVE_ARCH and possibly a
     // platform-specific default ARCHS placeholder value.
     // Look up the arch that Xcode chooses in this case.
-    if (cmProp arch = mf->GetDefinition("CMAKE_XCODE_ARCHS")) {
+    if (cmValue arch = mf->GetDefinition("CMAKE_XCODE_ARCHS")) {
       this->ObjectDirArchDefault = *arch;
       // We expect only one arch but choose the first just in case.
       std::string::size_type pos = this->ObjectDirArchDefault.find(';');
@@ -4555,7 +4547,7 @@
       continue;
     }
 
-    cmProp testee = obj->GetTarget()->GetProperty("XCTEST_TESTEE");
+    cmValue testee = obj->GetTarget()->GetProperty("XCTEST_TESTEE");
     if (!testee) {
       continue;
     }
@@ -4738,7 +4730,7 @@
 {
   if (!varNameLang.empty()) {
     std::string varName = cmStrCat(varNamePrefix, varNameLang, varNameSuffix);
-    if (cmProp varValue = this->CurrentMakefile->GetDefinition(varName)) {
+    if (cmValue varValue = this->CurrentMakefile->GetDefinition(varName)) {
       if (!varValue->empty()) {
         return *varValue;
       }
@@ -4860,7 +4852,7 @@
 
 bool cmGlobalXCodeGenerator::UseEffectivePlatformName(cmMakefile* mf) const
 {
-  cmProp epnValue = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
+  cmValue epnValue = this->GetCMakeInstance()->GetState()->GetGlobalProperty(
     "XCODE_EMIT_EFFECTIVE_PLATFORM_NAME");
 
   if (!epnValue) {
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 1e1b344..4d7ee90 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -138,11 +138,6 @@
   void AddExtraIDETargets() override;
   void Generate() override;
 
-  FindMakeProgramStage GetFindMakeProgramStage() const override
-  {
-    return FindMakeProgramStage::Early;
-  }
-
 private:
   enum EmbedActionFlags
   {
@@ -327,7 +322,7 @@
   bool XcodeBuildCommandInitialized;
 
   void PrintCompilerAdvice(std::ostream&, std::string const&,
-                           const char*) const override
+                           cmValue) const override
   {
   }
 
diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx
index 122bda5..ab18e2a 100644
--- a/Source/cmGraphVizWriter.cxx
+++ b/Source/cmGraphVizWriter.cxx
@@ -17,11 +17,11 @@
 #include "cmLinkItem.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -232,7 +232,7 @@
 
 #define set_if_set(var, cmakeDefinition)                                      \
   do {                                                                        \
-    cmProp value = mf.GetDefinition(cmakeDefinition);                         \
+    cmValue value = mf.GetDefinition(cmakeDefinition);                        \
     if (value) {                                                              \
       (var) = *value;                                                         \
     }                                                                         \
@@ -244,7 +244,7 @@
 
 #define set_bool_if_set(var, cmakeDefinition)                                 \
   do {                                                                        \
-    cmProp value = mf.GetDefinition(cmakeDefinition);                         \
+    cmValue value = mf.GetDefinition(cmakeDefinition);                        \
     if (value) {                                                              \
       (var) = cmIsOn(*value);                                                 \
     }                                                                         \
diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx
index ce1f030..7b0320c 100644
--- a/Source/cmIncludeCommand.cxx
+++ b/Source/cmIncludeCommand.cxx
@@ -120,6 +120,7 @@
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0024) << "\n";
         modal = "should";
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::REQUIRED_IF_USED:
diff --git a/Source/cmIncludeDirectoryCommand.cxx b/Source/cmIncludeDirectoryCommand.cxx
index b408f72..23427a1 100644
--- a/Source/cmIncludeDirectoryCommand.cxx
+++ b/Source/cmIncludeDirectoryCommand.cxx
@@ -13,6 +13,7 @@
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 static void GetIncludes(cmMakefile& mf, const std::string& arg,
                         std::vector<std::string>& incs);
diff --git a/Source/cmIncludeExternalMSProjectCommand.cxx b/Source/cmIncludeExternalMSProjectCommand.cxx
index 5b532ce..c8b4a39 100644
--- a/Source/cmIncludeExternalMSProjectCommand.cxx
+++ b/Source/cmIncludeExternalMSProjectCommand.cxx
@@ -77,9 +77,8 @@
 
     if (!customGuid.empty()) {
       std::string guidVariable = utility_name + "_GUID_CMAKE";
-      mf.GetCMakeInstance()->AddCacheEntry(guidVariable, customGuid.c_str(),
-                                           "Stored GUID",
-                                           cmStateEnums::INTERNAL);
+      mf.GetCMakeInstance()->AddCacheEntry(
+        guidVariable, customGuid, "Stored GUID", cmStateEnums::INTERNAL);
     }
 
     // Create a target instance for this utility.
diff --git a/Source/cmIncludeGuardCommand.cxx b/Source/cmIncludeGuardCommand.cxx
index aefd098..e0a6958 100644
--- a/Source/cmIncludeGuardCommand.cxx
+++ b/Source/cmIncludeGuardCommand.cxx
@@ -7,6 +7,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
diff --git a/Source/cmIncludeRegularExpressionCommand.cxx b/Source/cmIncludeRegularExpressionCommand.cxx
index 655ebd6..cdcc7df 100644
--- a/Source/cmIncludeRegularExpressionCommand.cxx
+++ b/Source/cmIncludeRegularExpressionCommand.cxx
@@ -14,7 +14,7 @@
   }
 
   cmMakefile& mf = status.GetMakefile();
-  mf.SetIncludeRegularExpression(args[0].c_str());
+  mf.SetIncludeRegularExpression(args[0]);
 
   if (args.size() > 1) {
     mf.SetComplainRegularExpression(args[1]);
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 79109b5..92e3bb5 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -35,7 +35,7 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
+#include "cmRange.h"
 #include "cmRuntimeDependencyArchive.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -43,6 +43,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetExport.h"
+#include "cmValue.h"
 
 namespace {
 
@@ -681,8 +682,8 @@
         te->LibraryGenerator = libraryGenerator.get();
         te->RuntimeGenerator = runtimeGenerator.get();
         te->ObjectsGenerator = objectGenerator.get();
-        te->InterfaceIncludeDirectories =
-          cmJoin(includesArgs.GetIncludeDirs(), ";");
+        target.AddInstallIncludeDirectories(
+          *te, cmMakeRange(includesArgs.GetIncludeDirs()));
         te->NamelinkOnly = namelinkOnly;
         helper.Makefile->GetGlobalGenerator()
           ->GetExportSets()[exports]
@@ -925,7 +926,7 @@
     }
 
     if (createInstallGeneratorsForTargetFileSets && !namelinkOnly) {
-      cmProp files = target.GetProperty("PRIVATE_HEADER");
+      cmValue files = target.GetProperty("PRIVATE_HEADER");
       if (cmNonempty(files)) {
         std::vector<std::string> relFiles = cmExpandedList(*files);
         std::vector<std::string> absFiles;
@@ -1390,6 +1391,7 @@
         case cmPolicies::WARN:
           e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0062) << "\n";
           modal = "should";
+          CM_FALLTHROUGH;
         case cmPolicies::OLD:
           break;
         case cmPolicies::REQUIRED_IF_USED:
@@ -1397,6 +1399,7 @@
         case cmPolicies::NEW:
           modal = "may";
           messageType = MessageType::FATAL_ERROR;
+          break;
       }
       if (modal) {
         e << "The file\n  " << file
diff --git a/Source/cmInstallMode.h b/Source/cmInstallMode.h
new file mode 100644
index 0000000..5343d34
--- /dev/null
+++ b/Source/cmInstallMode.h
@@ -0,0 +1,17 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+/**
+ * Enumerate types known to file(INSTALL).
+ */
+enum class cmInstallMode
+{
+  COPY,
+  ABS_SYMLINK,
+  ABS_SYMLINK_OR_COPY,
+  REL_SYMLINK,
+  REL_SYMLINK_OR_COPY,
+  SYMLINK,
+  SYMLINK_OR_COPY
+};
diff --git a/Source/cmInstallSubdirectoryGenerator.cxx b/Source/cmInstallSubdirectoryGenerator.cxx
index 794694e..0a8e065 100644
--- a/Source/cmInstallSubdirectoryGenerator.cxx
+++ b/Source/cmInstallSubdirectoryGenerator.cxx
@@ -57,6 +57,7 @@
       this->LocalGenerator->GetPolicyStatus(cmPolicies::CMP0082);
     switch (status) {
       case cmPolicies::WARN:
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior is handled in cmLocalGenerator::GenerateInstallRules()
         break;
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 35165cf..ae11afc 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -2,11 +2,13 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmInstallTargetGenerator.h"
 
+#include <algorithm>
 #include <cassert>
 #include <map>
 #include <set>
 #include <sstream>
 #include <utility>
+#include <vector>
 
 #include "cmComputeLinkInformation.h"
 #include "cmGeneratorExpression.h"
@@ -18,11 +20,11 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -208,7 +210,7 @@
 
         // Get App Bundle Extension
         std::string ext;
-        if (cmProp p = this->Target->GetProperty("BUNDLE_EXTENSION")) {
+        if (cmValue p = this->Target->GetProperty("BUNDLE_EXTENSION")) {
           ext = *p;
         } else {
           ext = "app";
@@ -680,34 +682,53 @@
            " this limitation.";
       mf->IssueMessage(MessageType::WARNING, msg.str());
     } else {
-      // Note: These paths are kept unique to avoid
-      // install_name_tool corruption.
-      std::set<std::string> runpaths;
-      for (std::string const& i : oldRuntimeDirs) {
-        std::string runpath =
-          mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config);
-
-        if (runpaths.find(runpath) == runpaths.end()) {
-          runpaths.insert(runpath);
-          os << indent << "execute_process(COMMAND " << installNameTool
-             << "\n";
-          os << indent << "  -delete_rpath \"" << runpath << "\"\n";
-          os << indent << "  \"" << toDestDirPath << "\")\n";
+      // To be consistent with older versions, runpath changes must be ordered,
+      // deleted first, then added, *and* the same path must only appear once.
+      std::map<std::string, std::string> runpath_change;
+      std::vector<std::string> ordered;
+      for (std::string const& dir : oldRuntimeDirs) {
+        // Normalize path and add to map of changes to make
+        auto iter_inserted = runpath_change.insert(
+          { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config),
+            "delete" });
+        if (iter_inserted.second) {
+          // Add path to ordered list of changes
+          ordered.push_back(iter_inserted.first->first);
         }
       }
 
-      runpaths.clear();
-      for (std::string const& i : newRuntimeDirs) {
-        std::string runpath =
-          mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config);
-
-        if (runpaths.find(runpath) == runpaths.end()) {
-          os << indent << "execute_process(COMMAND " << installNameTool
-             << "\n";
-          os << indent << "  -add_rpath \"" << runpath << "\"\n";
-          os << indent << "  \"" << toDestDirPath << "\")\n";
+      for (std::string const& dir : newRuntimeDirs) {
+        // Normalize path and add to map of changes to make
+        auto iter_inserted = runpath_change.insert(
+          { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config), "add" });
+        if (iter_inserted.second) {
+          // Add path to ordered list of changes
+          ordered.push_back(iter_inserted.first->first);
+        } else if (iter_inserted.first->second != "add") {
+          // Rpath was requested to be deleted and then later re-added. Drop it
+          // from the list by marking as an empty value.
+          iter_inserted.first->second.clear();
         }
       }
+
+      // Remove rpaths that are unchanged (value was set to empty)
+      ordered.erase(
+        std::remove_if(ordered.begin(), ordered.end(),
+                       [&runpath_change](const std::string& runpath) {
+                         return runpath_change.find(runpath)->second.empty();
+                       }),
+        ordered.end());
+
+      if (!ordered.empty()) {
+        os << indent << "execute_process(COMMAND " << installNameTool << "\n";
+        for (std::string const& runpath : ordered) {
+          // Either 'add_rpath' or 'delete_rpath' since we've removed empty
+          // entries
+          os << indent << "  -" << runpath_change.find(runpath)->second
+             << "_rpath \"" << runpath << "\"\n";
+        }
+        os << indent << "  \"" << toDestDirPath << "\")\n";
+      }
     }
   } else {
     // Construct the original rpath string to be replaced.
@@ -825,7 +846,7 @@
     return;
   }
 
-  cmProp xcodeVersion = mf->GetDefinition("XCODE_VERSION");
+  cmValue xcodeVersion = mf->GetDefinition("XCODE_VERSION");
   if (!xcodeVersion ||
       cmSystemTools::VersionCompareGreater("6", *xcodeVersion)) {
     return;
diff --git a/Source/cmInstalledFile.cxx b/Source/cmInstalledFile.cxx
index 32395d1..0974eea 100644
--- a/Source/cmInstalledFile.cxx
+++ b/Source/cmInstalledFile.cxx
@@ -8,6 +8,7 @@
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 cmInstalledFile::cmInstalledFile() = default;
 
diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx
index 2914046..1ec071b 100644
--- a/Source/cmLinkDirectoriesCommand.cxx
+++ b/Source/cmLinkDirectoriesCommand.cxx
@@ -62,7 +62,7 @@
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0015);
         mf.IssueMessage(MessageType::AUTHOR_WARNING, e.str());
-        break;
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior does not convert
         break;
diff --git a/Source/cmLinkItem.cxx b/Source/cmLinkItem.cxx
index 4e50d70..62e7ef4 100644
--- a/Source/cmLinkItem.cxx
+++ b/Source/cmLinkItem.cxx
@@ -32,7 +32,11 @@
 {
   // Order among targets.
   if (l.Target && r.Target) {
-    return l.Target < r.Target;
+    if (l.Target != r.Target) {
+      return l.Target < r.Target;
+    }
+    // Order identical targets via cross-config.
+    return l.Cross < r.Cross;
   }
   // Order targets before strings.
   if (l.Target) {
@@ -42,10 +46,10 @@
     return false;
   }
   // Order among strings.
-  if (l.String < r.String) {
-    return true;
+  if (l.String != r.String) {
+    return l.String < r.String;
   }
-  // Order among cross-config.
+  // Order identical strings via cross-config.
   return l.Cross < r.Cross;
 }
 
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index 2ffff96..43f161b 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -16,11 +16,11 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 class cmOutputConverter;
 
@@ -177,7 +177,7 @@
     return false;
   }
 
-  if (cmProp resolveDeviceSymbols =
+  if (cmValue resolveDeviceSymbols =
         target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
     // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
     // to honor the value no matter what it is.
diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx
index 09cd88e..7d42fc8 100644
--- a/Source/cmListCommand.cxx
+++ b/Source/cmListCommand.cxx
@@ -25,12 +25,12 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSubcommandTable.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 
@@ -46,7 +46,7 @@
           cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0121),
                    " Invalid list index \"", arg, "\".");
         mf.IssueMessage(MessageType::AUTHOR_WARNING, warn);
-        break;
+        CM_FALLTHROUGH;
       }
       case cmPolicies::OLD:
         // OLD behavior is to allow compatibility, so just ignore the
@@ -79,7 +79,7 @@
                    const cmMakefile& makefile)
 {
   // get the old value
-  cmProp cacheValue = makefile.GetDefinition(var);
+  cmValue cacheValue = makefile.GetDefinition(var);
   if (!cacheValue) {
     return false;
   }
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index 4f7c959..2e444f2 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -548,7 +548,7 @@
   }
   cmListFileContext lfc = this->TopEntry->Context;
   cmStateSnapshot bottom = this->GetBottom();
-  if (!bottom.GetState()->GetIsInTryCompile()) {
+  if (bottom.GetState()->GetProjectKind() == cmState::ProjectKind::Normal) {
     lfc.FilePath = cmSystemTools::RelativeIfUnder(
       bottom.GetState()->GetSourceDirectory(), lfc.FilePath);
   }
@@ -579,7 +579,7 @@
       out << "Call Stack (most recent call first):\n";
     }
     cmListFileContext lfc = cur->Context;
-    if (!bottom.GetState()->GetIsInTryCompile()) {
+    if (bottom.GetState()->GetProjectKind() == cmState::ProjectKind::Normal) {
       lfc.FilePath = cmSystemTools::RelativeIfUnder(
         bottom.GetState()->GetSourceDirectory(), lfc.FilePath);
     }
diff --git a/Source/cmLocalCommonGenerator.cxx b/Source/cmLocalCommonGenerator.cxx
index 211525a..eca7a9e 100644
--- a/Source/cmLocalCommonGenerator.cxx
+++ b/Source/cmLocalCommonGenerator.cxx
@@ -8,11 +8,11 @@
 #include "cmGeneratorTarget.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 class cmGlobalGenerator;
 
@@ -82,7 +82,7 @@
   // If there is a separate module path flag then duplicate the
   // include path with it.  This compiler does not search the include
   // path for modules.
-  if (cmProp modpath_flag =
+  if (cmValue modpath_flag =
         this->Makefile->GetDefinition("CMAKE_Fortran_MODPATH_FLAG")) {
     std::vector<std::string> includes;
     this->GetIncludeDirectories(includes, target, "C", config);
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 3b282de..fc5e6e5 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -37,7 +37,6 @@
 #include "cmInstallTargetGenerator.h"
 #include "cmLinkLineComputer.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
@@ -50,6 +49,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTestGenerator.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -107,10 +107,9 @@
   {
     std::vector<std::string> cpath;
     cmSystemTools::GetPath(cpath, "CPATH");
-    for (std::string& cp : cpath) {
+    for (std::string const& cp : cpath) {
       if (cmSystemTools::FileIsFullPath(cp)) {
-        cp = cmSystemTools::CollapseFullPath(cp);
-        this->EnvCPATH.emplace(std::move(cp));
+        this->EnvCPATH.emplace_back(cmSystemTools::CollapseFullPath(cp));
       }
     }
   }
@@ -118,21 +117,21 @@
   std::vector<std::string> enabledLanguages =
     this->GetState()->GetEnabledLanguages();
 
-  if (cmProp sysrootCompile =
+  if (cmValue sysrootCompile =
         this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
     this->CompilerSysroot = *sysrootCompile;
   } else {
     this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
   }
 
-  if (cmProp sysrootLink =
+  if (cmValue sysrootLink =
         this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
     this->LinkerSysroot = *sysrootLink;
   } else {
     this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
   }
 
-  if (cmProp appleArchSysroots =
+  if (cmValue appleArchSysroots =
         this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
     std::string const& appleArchs =
       this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES");
@@ -222,7 +221,7 @@
 #else
   this->ObjectPathMax = 1000;
 #endif
-  cmProp plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
+  cmValue plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
   if (cmNonempty(plen)) {
     unsigned int pmax;
     if (sscanf(plen->c_str(), "%u", &pmax) == 1) {
@@ -330,12 +329,12 @@
     fout << "set(CTEST_RESOURCE_SPEC_FILE \"" << resourceSpecFile << "\")\n";
   }
 
-  cmProp testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE");
+  cmValue testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE");
   if (testIncludeFile) {
     fout << "include(\"" << *testIncludeFile << "\")\n";
   }
 
-  cmProp testIncludeFiles = this->Makefile->GetProperty("TEST_INCLUDE_FILES");
+  cmValue testIncludeFiles = this->Makefile->GetProperty("TEST_INCLUDE_FILES");
   if (testIncludeFiles) {
     std::vector<std::string> includesList = cmExpandedList(*testIncludeFiles);
     for (std::string const& i : includesList) {
@@ -359,9 +358,9 @@
   }
 
   // Add directory labels property
-  cmProp directoryLabels =
+  cmValue directoryLabels =
     this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS");
-  cmProp labels = this->Makefile->GetProperty("LABELS");
+  cmValue labels = this->Makefile->GetProperty("LABELS");
 
   if (labels || directoryLabels) {
     fout << "set_directory_properties(PROPERTIES LABELS ";
@@ -426,27 +425,26 @@
 void cmLocalGenerator::GenerateInstallRules()
 {
   // Compute the install prefix.
-  const char* prefix =
-    cmToCStr(this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX"));
+  cmValue installPrefix =
+    this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
+  std::string prefix = installPrefix;
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
-  std::string prefix_win32;
-  if (!prefix) {
-    if (!cmSystemTools::GetEnv("SystemDrive", prefix_win32)) {
-      prefix_win32 = "C:";
+  if (!installPrefix) {
+    if (!cmSystemTools::GetEnv("SystemDrive", prefix)) {
+      prefix = "C:";
     }
-    cmProp project_name = this->Makefile->GetDefinition("PROJECT_NAME");
+    cmValue project_name = this->Makefile->GetDefinition("PROJECT_NAME");
     if (cmNonempty(project_name)) {
-      prefix_win32 += "/Program Files/";
-      prefix_win32 += *project_name;
+      prefix += "/Program Files/";
+      prefix += *project_name;
     } else {
-      prefix_win32 += "/InstalledCMakeProject";
+      prefix += "/InstalledCMakeProject";
     }
-    prefix = prefix_win32.c_str();
   }
 #elif defined(__HAIKU__)
   char dir[B_PATH_NAME_LENGTH];
-  if (!prefix) {
+  if (!installPrefix) {
     if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
         B_OK) {
       prefix = dir;
@@ -455,13 +453,13 @@
     }
   }
 #else
-  if (!prefix) {
+  if (!installPrefix) {
     prefix = "/usr/local";
   }
 #endif
-  if (cmProp stagingPrefix =
+  if (cmValue stagingPrefix =
         this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) {
-    prefix = stagingPrefix->c_str();
+    prefix = *stagingPrefix;
   }
 
   // Compute the set of configurations.
@@ -541,7 +539,7 @@
   /* clang-format on */
 
   // Copy user-specified install options to the install code.
-  if (cmProp so_no_exe =
+  if (cmValue so_no_exe =
         this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) {
     /* clang-format off */
     fout <<
@@ -554,7 +552,7 @@
   }
 
   // Copy cmake cross compile state to install code.
-  if (cmProp crosscompiling =
+  if (cmValue crosscompiling =
         this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) {
     /* clang-format off */
     fout <<
@@ -567,7 +565,7 @@
   }
 
   // Write default directory permissions.
-  if (cmProp defaultDirPermissions = this->Makefile->GetDefinition(
+  if (cmValue defaultDirPermissions = this->Makefile->GetDefinition(
         "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) {
     /* clang-format off */
     fout <<
@@ -583,7 +581,7 @@
   // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM so that
   // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
   // has same platform variable as when running cmake
-  if (cmProp platform = this->Makefile->GetDefinition(
+  if (cmValue platform = this->Makefile->GetDefinition(
         "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) {
     /* clang-format off */
     fout <<
@@ -599,7 +597,7 @@
   // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL so that
   // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
   // has same tool selected as when running cmake
-  if (cmProp command =
+  if (cmValue command =
         this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) {
     /* clang-format off */
     fout <<
@@ -615,7 +613,7 @@
   // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND so that
   // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)`
   // has same path to the tool as when running cmake
-  if (cmProp command = this->Makefile->GetDefinition(
+  if (cmValue command = this->Makefile->GetDefinition(
         "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) {
     /* clang-format off */
     fout <<
@@ -633,7 +631,7 @@
   // CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND has consistent
   // logic to fallback to CMAKE_OBJDUMP when `objdump` is
   // not on the path
-  if (cmProp command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) {
+  if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) {
     /* clang-format off */
     fout <<
       "# Set default install directory permissions.\n"
@@ -826,8 +824,8 @@
   return this->Makefile->GetStateSnapshot();
 }
 
-cmProp cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
-                                         const std::string& prop)
+cmValue cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
+                                          const std::string& prop)
 {
   if (target) {
     return target->GetProperty(prop);
@@ -859,36 +857,39 @@
 
   std::string const& includeFlag =
     this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
-  const char* sep = cmToCStr(
-    this->Makefile->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang)));
   bool quotePaths = false;
   if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
     quotePaths = true;
   }
+  std::string sep = " ";
   bool repeatFlag = true;
   // should the include flag be repeated like ie. -IA -IB
-  if (!sep) {
-    sep = " ";
-  } else {
+  if (cmValue incSep = this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang))) {
     // if there is a separator then the flag is not repeated but is only
     // given once i.e.  -classpath a:b:c
+    sep = incSep;
     repeatFlag = false;
   }
 
   // Support special system include flag if it is available and the
   // normal flag is repeated for each directory.
-  cmProp sysIncludeFlag = nullptr;
+  cmValue sysIncludeFlag = nullptr;
+  cmValue sysIncludeFlagWarning = nullptr;
   if (repeatFlag) {
     sysIncludeFlag = this->Makefile->GetDefinition(
       cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
+    sysIncludeFlagWarning = this->Makefile->GetDefinition(
+      cmStrCat("_CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING"));
   }
 
-  cmProp fwSearchFlag = this->Makefile->GetDefinition(
+  cmValue fwSearchFlag = this->Makefile->GetDefinition(
     cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG"));
-  cmProp sysFwSearchFlag = this->Makefile->GetDefinition(
+  cmValue sysFwSearchFlag = this->Makefile->GetDefinition(
     cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
 
   bool flagUsed = false;
+  bool sysIncludeFlagUsed = false;
   std::set<std::string> emitted;
 #ifdef __APPLE__
   emitted.insert("/System/Library/Frameworks");
@@ -915,6 +916,7 @@
       if (sysIncludeFlag && target &&
           target->IsSystemIncludeDirectory(i, config, lang)) {
         includeFlags << *sysIncludeFlag;
+        sysIncludeFlagUsed = true;
       } else {
         includeFlags << includeFlag;
       }
@@ -931,6 +933,9 @@
     }
     includeFlags << sep;
   }
+  if (sysIncludeFlagUsed && sysIncludeFlagWarning) {
+    includeFlags << *sysIncludeFlagWarning;
+  }
   std::string flags = includeFlags.str();
   // remove trailing separators
   if ((sep[0] != ' ') && !flags.empty() && flags.back() == sep[0]) {
@@ -956,10 +961,10 @@
 {
   std::string langFlagRegexVar = cmStrCat("CMAKE_", lang, "_FLAG_REGEX");
 
-  if (cmProp langFlagRegexStr =
+  if (cmValue langFlagRegexStr =
         this->Makefile->GetDefinition(langFlagRegexVar)) {
     // Filter flags acceptable to this language.
-    if (cmProp targetFlags = target->GetProperty("COMPILE_FLAGS")) {
+    if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) {
       std::vector<std::string> opts;
       cmSystemTools::ParseWindowsCommandLine(targetFlags->c_str(), opts);
       // Re-escape these flags since COMPILE_FLAGS were already parsed
@@ -977,7 +982,7 @@
                                langFlagRegexStr->c_str());
   } else {
     // Use all flags.
-    if (cmProp targetFlags = target->GetProperty("COMPILE_FLAGS")) {
+    if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) {
       // COMPILE_FLAGS are not escaped for historical reasons.
       std::string compileFlags;
       this->AppendFlags(compileFlags, *targetFlags);
@@ -993,7 +998,7 @@
 
   cmStandardLevelResolver standardResolver(this->Makefile);
   for (auto const& it : target->GetMaxLanguageStandards()) {
-    cmProp standard = target->GetLanguageStandard(it.first, config);
+    cmValue standard = target->GetLanguageStandard(it.first, config);
     if (!standard) {
       continue;
     }
@@ -1024,7 +1029,7 @@
 
   // Add compile flag for the MSVC compiler only.
   cmMakefile* mf = this->GetMakefile();
-  if (cmProp jmc =
+  if (cmValue jmc =
         mf->GetDefinition("CMAKE_" + lang + "_COMPILE_OPTIONS_JMC")) {
 
     // Handle Just My Code debugging flags, /JMC.
@@ -1033,7 +1038,7 @@
         cmGeneratorTarget::ManagedType::Managed) {
       // add /JMC flags if target property VS_JUST_MY_CODE_DEBUGGING is set
       // to ON
-      if (cmProp jmcExprGen =
+      if (cmValue jmcExprGen =
             target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) {
         std::string isJMCEnabled =
           cmGeneratorExpression::Evaluate(*jmcExprGen, this, config);
@@ -1239,19 +1244,31 @@
     }
   }
 
+  bool const isCorCxx = (lang == "C" || lang == "CXX");
+
+  // Resolve symlinks in CPATH for comparison with resolved include paths.
+  // We do this here instead of when EnvCPATH is populated in case symlinks
+  // on disk have changed in the meantime.
+  std::set<std::string> resolvedEnvCPATH;
+  if (isCorCxx) {
+    for (std::string const& i : this->EnvCPATH) {
+      resolvedEnvCPATH.emplace(this->GlobalGenerator->GetRealPath(i));
+    }
+  }
+
   // Checks if this is not an excluded (implicit) include directory.
-  auto notExcluded = [this, &implicitSet, &implicitExclude,
-                      &lang](std::string const& dir) {
-    return (
-      // Do not exclude directories that are not in an excluded set.
-      ((!cm::contains(implicitSet, this->GlobalGenerator->GetRealPath(dir))) &&
-       (!cm::contains(implicitExclude, dir)))
+  auto notExcluded = [this, &implicitSet, &implicitExclude, &resolvedEnvCPATH,
+                      isCorCxx](std::string const& dir) -> bool {
+    std::string const& real_dir = this->GlobalGenerator->GetRealPath(dir);
+    return
+      // Do not exclude directories that are not in any excluded set.
+      !(cm::contains(implicitSet, real_dir) ||
+        cm::contains(implicitExclude, dir))
       // Do not exclude entries of the CPATH environment variable even though
       // they are implicitly searched by the compiler.  They are meant to be
       // user-specified directories that can be re-ordered or converted to
       // -isystem without breaking real compiler builtin headers.
-      ||
-      ((lang == "C" || lang == "CXX") && cm::contains(this->EnvCPATH, dir)));
+      || (isCorCxx && cm::contains(resolvedEnvCPATH, real_dir));
   };
 
   // Get the target-specific include directories.
@@ -1480,7 +1497,7 @@
         }
       }
 
-      cmProp targetLinkFlags = target->GetProperty("LINK_FLAGS");
+      cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
       if (targetLinkFlags) {
         sharedLibFlags += *targetLinkFlags;
         sharedLibFlags += " ";
@@ -1560,7 +1577,7 @@
         exeFlags += " ";
       }
 
-      cmProp targetLinkFlags = target->GetProperty("LINK_FLAGS");
+      cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
       if (targetLinkFlags) {
         exeFlags += *targetLinkFlags;
         exeFlags += " ";
@@ -1656,7 +1673,7 @@
   }
 
   std::string fwSearchFlagVar = "CMAKE_" + lang + "_FRAMEWORK_SEARCH_FLAG";
-  cmProp fwSearchFlag = mf->GetDefinition(fwSearchFlagVar);
+  cmValue fwSearchFlag = mf->GetDefinition(fwSearchFlagVar);
   if (!cmNonempty(fwSearchFlag)) {
     return std::string();
   }
@@ -1765,7 +1782,7 @@
   std::string linkLanguage = cli.GetLinkLanguage();
 
   std::string libPathFlag;
-  if (cmProp value = this->Makefile->GetDefinition(
+  if (cmValue value = this->Makefile->GetDefinition(
         "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) {
     libPathFlag = *value;
   } else {
@@ -1774,7 +1791,7 @@
   }
 
   std::string libPathTerminator;
-  if (cmProp value = this->Makefile->GetDefinition(
+  if (cmValue value = this->Makefile->GetDefinition(
         "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) {
     libPathTerminator = *value;
   } else {
@@ -1850,17 +1867,17 @@
 }
 
 bool cmLocalGenerator::AllAppleArchSysrootsAreTheSame(
-  const std::vector<std::string>& archs, const char* sysroot)
+  const std::vector<std::string>& archs, cmValue sysroot)
 {
   if (!sysroot) {
     return false;
   }
 
   return std::all_of(archs.begin(), archs.end(),
-                     [this, &sysroot](std::string const& arch) -> bool {
+                     [this, sysroot](std::string const& arch) -> bool {
                        std::string const& archSysroot =
                          this->AppleArchSysroots[arch];
-                       return cmIsOff(archSysroot) || archSysroot == sysroot;
+                       return cmIsOff(archSysroot) || sysroot == archSysroot;
                      });
 }
 
@@ -1885,15 +1902,15 @@
       }
     }
 
-    cmProp sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
+    cmValue sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
     if (sysroot && *sysroot == "/") {
       sysroot = nullptr;
     }
     std::string sysrootFlagVar = "CMAKE_" + lang + "_SYSROOT_FLAG";
-    cmProp sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar);
+    cmValue sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar);
     if (cmNonempty(sysrootFlag)) {
       if (!this->AppleArchSysroots.empty() &&
-          !this->AllAppleArchSysrootsAreTheSame(archs, cmToCStr(sysroot))) {
+          !this->AllAppleArchSysrootsAreTheSame(archs, sysroot)) {
         for (std::string const& arch : archs) {
           std::string const& archSysroot = this->AppleArchSysroots[arch];
           if (cmIsOff(archSysroot)) {
@@ -1914,11 +1931,11 @@
       }
     }
 
-    cmProp deploymentTarget =
+    cmValue deploymentTarget =
       this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
     std::string deploymentTargetFlagVar =
       "CMAKE_" + lang + "_OSX_DEPLOYMENT_TARGET_FLAG";
-    cmProp deploymentTargetFlag =
+    cmValue deploymentTargetFlag =
       this->Makefile->GetDefinition(deploymentTargetFlagVar);
     if (cmNonempty(deploymentTargetFlag) && cmNonempty(deploymentTarget)) {
       flags += " ";
@@ -1943,11 +1960,11 @@
   std::string compilerSimulateId = this->Makefile->GetSafeDefinition(
     cmStrCat("CMAKE_", lang, "_SIMULATE_ID"));
   if (lang == "Swift") {
-    if (cmProp v = target->GetProperty("Swift_LANGUAGE_VERSION")) {
-      if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
-                                        cmToCStr(this->Makefile->GetDefinition(
-                                          "CMAKE_Swift_COMPILER_VERSION")),
-                                        "4.2")) {
+    if (cmValue v = target->GetProperty("Swift_LANGUAGE_VERSION")) {
+      if (cmSystemTools::VersionCompare(
+            cmSystemTools::OP_GREATER_EQUAL,
+            this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
+            "4.2")) {
         this->AppendFlags(flags, "-swift-version " + *v);
       }
     }
@@ -1974,7 +1991,7 @@
 
   // Add VFS Overlay for Clang compilers
   if (compiler == "Clang") {
-    if (cmProp vfsOverlay =
+    if (cmValue vfsOverlay =
           this->Makefile->GetDefinition("CMAKE_CLANG_VFS_OVERLAY")) {
       if (compilerSimulateId == "MSVC") {
         this->AppendCompileOptions(
@@ -1989,10 +2006,10 @@
   }
   // Add MSVC runtime library flags.  This is activated by the presence
   // of a default selection whether or not it is overridden by a property.
-  cmProp msvcRuntimeLibraryDefault =
+  cmValue msvcRuntimeLibraryDefault =
     this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT");
   if (cmNonempty(msvcRuntimeLibraryDefault)) {
-    cmProp msvcRuntimeLibraryValue =
+    cmValue msvcRuntimeLibraryValue =
       target->GetProperty("MSVC_RUNTIME_LIBRARY");
     if (!msvcRuntimeLibraryValue) {
       msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
@@ -2000,7 +2017,7 @@
     std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate(
       *msvcRuntimeLibraryValue, this, config, target);
     if (!msvcRuntimeLibrary.empty()) {
-      if (cmProp msvcRuntimeLibraryOptions = this->Makefile->GetDefinition(
+      if (cmValue msvcRuntimeLibraryOptions = this->Makefile->GetDefinition(
             "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
             msvcRuntimeLibrary)) {
         this->AppendCompileOptions(flags, *msvcRuntimeLibraryOptions);
@@ -2180,7 +2197,7 @@
   std::string const& optionFlagDef =
     standardResolver.GetCompileOptionDef(target, lang, config);
   if (!optionFlagDef.empty()) {
-    cmProp opt = target->Target->GetMakefile()->GetDefinition(optionFlagDef);
+    cmValue opt = target->Target->GetMakefile()->GetDefinition(optionFlagDef);
     if (opt) {
       std::vector<std::string> optVec = cmExpandedList(*opt);
       for (std::string const& i : optVec) {
@@ -2197,13 +2214,13 @@
                                        std::string* warnCMP0063)
 {
   std::string compileOption = "CMAKE_" + lang + "_COMPILE_OPTIONS_VISIBILITY";
-  cmProp opt = lg->GetMakefile()->GetDefinition(compileOption);
+  cmValue opt = lg->GetMakefile()->GetDefinition(compileOption);
   if (!opt) {
     return;
   }
   std::string flagDefine = lang + "_VISIBILITY_PRESET";
 
-  cmProp prop = target->GetProperty(flagDefine);
+  cmValue prop = target->GetProperty(flagDefine);
   if (!prop) {
     return;
   }
@@ -2233,7 +2250,7 @@
 {
   std::string compileOption =
     cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN");
-  cmProp opt = lg->GetMakefile()->GetDefinition(compileOption);
+  cmValue opt = lg->GetMakefile()->GetDefinition(compileOption);
   if (!opt) {
     return;
   }
@@ -2430,7 +2447,7 @@
     return;
   }
 
-  cmProp ispcHeaderSuffixProp = target->GetProperty("ISPC_HEADER_SUFFIX");
+  cmValue ispcHeaderSuffixProp = target->GetProperty("ISPC_HEADER_SUFFIX");
   assert(ispcHeaderSuffixProp);
 
   std::vector<std::string> ispcArchSuffixes =
@@ -2443,7 +2460,7 @@
 
     std::string rootObjectDir = target->GetObjectDirectory(config);
     std::string headerDir = rootObjectDir;
-    if (cmProp prop = target->GetProperty("ISPC_HEADER_DIRECTORY")) {
+    if (cmValue prop = target->GetProperty("ISPC_HEADER_DIRECTORY")) {
       headerDir = cmSystemTools::CollapseFullPath(
         cmStrCat(this->GetBinaryDirectory(), '/', *prop));
     }
@@ -2493,6 +2510,16 @@
     static const std::array<std::string, 4> langs = { { "C", "CXX", "OBJC",
                                                         "OBJCXX" } };
 
+    bool haveAnyPch = false;
+    if (this->GetGlobalGenerator()->IsXcode()) {
+      for (const std::string& lang : langs) {
+        const std::string pchHeader = target->GetPchHeader(config, lang, "");
+        if (!pchHeader.empty()) {
+          haveAnyPch = true;
+        }
+      }
+    }
+
     for (const std::string& lang : langs) {
       auto langSources = std::count_if(
         sources.begin(), sources.end(), [lang](cmSourceFile* sf) {
@@ -2533,17 +2560,22 @@
         const std::string pchHeader = target->GetPchHeader(config, lang, arch);
 
         if (pchSource.empty() || pchHeader.empty()) {
+          if (this->GetGlobalGenerator()->IsXcode() && haveAnyPch) {
+            for (auto* sf : sources) {
+              sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON");
+            }
+          }
           continue;
         }
 
-        const std::string pchExtension =
-          this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
+        cmValue pchExtension =
+          this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
 
-        if (pchExtension.empty()) {
+        if (pchExtension.IsEmpty()) {
           continue;
         }
 
-        cmProp ReuseFrom =
+        cmValue ReuseFrom =
           target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
 
         auto* pch_sf = this->Makefile->GetOrCreateSource(
@@ -2590,8 +2622,8 @@
 
                 // MSVC 2008 is producing both .pdb and .idb files with /Zi.
                 bool msvc2008OrLess =
-                  cmSystemTools::VersionCompare(
-                    cmSystemTools::OP_LESS, compilerVersion.c_str(), "16.0") &&
+                  cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
+                                                compilerVersion, "16.0") &&
                   compilerId == "MSVC";
                 // but not when used via toolset -Tv90
                 if (this->Makefile->GetSafeDefinition(
@@ -2622,7 +2654,8 @@
                   cmStrCat(" ",
                            this->ConvertToOutputFormat(pchSourceObj, SHELL)),
                   true);
-              } else {
+              } else if (reuseTarget->GetType() ==
+                         cmStateEnums::OBJECT_LIBRARY) {
                 target->Target->AppendProperty(
                   "INTERFACE_LINK_LIBRARIES",
                   cmStrCat("$<$<CONFIG:", config,
@@ -2630,7 +2663,7 @@
               }
             }
           } else {
-            pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
+            pch_sf->SetProperty("PCH_EXTENSION", pchExtension);
           }
 
           // Add pchHeader to source files, which will
@@ -2665,8 +2698,8 @@
     cmStrCat(target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/",
              target->GetName(), ".dir/");
 
-  const std::string copy_script =
-    cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake");
+  const std::string copy_script = cmStrCat(
+    target_compile_pdb_dir, "copy_idb_pdb_", config.c_str(), ".cmake");
   cmGeneratedFileStream file(copy_script);
 
   file << "# CMake generated file\n";
@@ -2721,7 +2754,7 @@
   bool stdPipesUTF8 = true;
 
   auto configGenex = [&](cm::string_view expr) -> std::string {
-    if (this->GetGlobalGenerator()->IsVisualStudio()) {
+    if (this->GetGlobalGenerator()->IsMultiConfig()) {
       return cmStrCat("$<$<CONFIG:", config, ">:", expr, ">");
     }
     return std::string(expr);
@@ -2740,8 +2773,8 @@
   std::vector<std::string> no_byproducts;
 
   std::vector<std::string> outputs;
-  outputs.push_back(
-    cmStrCat(target_compile_pdb_dir, pdb_prefix, ReuseFrom, ".pdb"));
+  outputs.push_back(configGenex(
+    cmStrCat(target_compile_pdb_dir, pdb_prefix, ReuseFrom, ".pdb")));
 
   if (this->GetGlobalGenerator()->IsVisualStudio()) {
     this->AddCustomCommandToTarget(
@@ -2771,13 +2804,13 @@
                                  std::string const& filename)
 {
   target->AddSourceFileToUnityBatch(sf->ResolveFullPath());
-  sf->SetProperty("UNITY_SOURCE_FILE", filename.c_str());
+  sf->SetProperty("UNITY_SOURCE_FILE", filename);
 }
 }
 
 void cmLocalGenerator::IncludeFileInUnitySources(
   cmGeneratedFileStream& unity_file, std::string const& sf_full_path,
-  cmProp beforeInclude, cmProp afterInclude, cmProp uniqueIdName) const
+  cmValue beforeInclude, cmValue afterInclude, cmValue uniqueIdName) const
 {
   if (cmNonempty(uniqueIdName)) {
     std::string pathToHash;
@@ -2815,14 +2848,14 @@
 
 std::vector<std::string> cmLocalGenerator::AddUnityFilesModeAuto(
   cmGeneratorTarget* target, std::string const& lang,
-  std::vector<cmSourceFile*> const& filtered_sources, cmProp beforeInclude,
-  cmProp afterInclude, std::string const& filename_base, size_t batchSize)
+  std::vector<cmSourceFile*> const& filtered_sources, cmValue beforeInclude,
+  cmValue afterInclude, std::string const& filename_base, size_t batchSize)
 {
   if (batchSize == 0) {
     batchSize = filtered_sources.size();
   }
 
-  cmProp uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID");
+  cmValue uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID");
 
   std::vector<std::string> unity_files;
   for (size_t itemsLeft = filtered_sources.size(), chunk, batch = 0;
@@ -2858,8 +2891,8 @@
 
 std::vector<std::string> cmLocalGenerator::AddUnityFilesModeGroup(
   cmGeneratorTarget* target, std::string const& lang,
-  std::vector<cmSourceFile*> const& filtered_sources, cmProp beforeInclude,
-  cmProp afterInclude, std::string const& filename_base)
+  std::vector<cmSourceFile*> const& filtered_sources, cmValue beforeInclude,
+  cmValue afterInclude, std::string const& filename_base)
 {
   std::vector<std::string> unity_files;
 
@@ -2867,7 +2900,7 @@
   // without a group
   std::unordered_map<std::string, std::vector<cmSourceFile*>> explicit_mapping;
   for (cmSourceFile* sf : filtered_sources) {
-    if (cmProp value = sf->GetProperty("UNITY_GROUP")) {
+    if (cmValue value = sf->GetProperty("UNITY_GROUP")) {
       auto i = explicit_mapping.find(*value);
       if (i == explicit_mapping.end()) {
         std::vector<cmSourceFile*> sources{ sf };
@@ -2878,7 +2911,7 @@
     }
   }
 
-  cmProp uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID");
+  cmValue uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID");
 
   for (auto const& item : explicit_mapping) {
     auto const& name = item.first;
@@ -2925,15 +2958,15 @@
   std::vector<cmSourceFile*> sources;
   target->GetSourceFiles(sources, config);
 
-  cmProp batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
+  cmValue batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
   const size_t unityBatchSize = batchSizeString
     ? static_cast<size_t>(std::atoi(batchSizeString->c_str()))
     : 0;
 
-  cmProp beforeInclude =
+  cmValue beforeInclude =
     target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE");
-  cmProp afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
-  cmProp unityMode = target->GetProperty("UNITY_BUILD_MODE");
+  cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
+  cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE");
 
   for (std::string lang : { "C", "CXX" }) {
     std::vector<cmSourceFile*> filtered_sources;
@@ -2969,7 +3002,7 @@
       auto* unity = this->GetMakefile()->GetOrCreateSource(file);
       target->AddSource(file, true);
       unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON");
-      unity->SetProperty("UNITY_SOURCE_FILE", file.c_str());
+      unity->SetProperty("UNITY_SOURCE_FILE", file);
     }
   }
 }
@@ -2993,7 +3026,7 @@
   }
 
   const std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_IPO";
-  cmProp rawFlagsList = this->Makefile->GetDefinition(name);
+  cmValue rawFlagsList = this->Makefile->GetDefinition(name);
   if (!rawFlagsList) {
     return;
   }
@@ -3039,6 +3072,30 @@
   }
 }
 
+bool cmLocalGenerator::AppendLWYUFlags(std::string& flags,
+                                       const cmGeneratorTarget* target,
+                                       const std::string& lang)
+{
+  auto useLWYU = target->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
+    (target->GetType() == cmStateEnums::TargetType::EXECUTABLE ||
+     target->GetType() == cmStateEnums::TargetType::SHARED_LIBRARY ||
+     target->GetType() == cmStateEnums::TargetType::MODULE_LIBRARY);
+
+  if (useLWYU) {
+    const auto& lwyuFlag = this->GetMakefile()->GetSafeDefinition(
+      cmStrCat("CMAKE_", lang, "_LINK_WHAT_YOU_USE_FLAG"));
+    useLWYU = !lwyuFlag.empty();
+
+    if (useLWYU) {
+      std::vector<BT<std::string>> lwyuOpts;
+      lwyuOpts.emplace_back(lwyuFlag);
+      this->AppendFlags(flags, target->ResolveLinkerWrapper(lwyuOpts, lang));
+    }
+  }
+
+  return useLWYU;
+}
+
 void cmLocalGenerator::AppendCompileOptions(std::string& options,
                                             std::string const& options_list,
                                             const char* regex) const
@@ -3184,7 +3241,7 @@
   // Lookup the define flag for the current language.
   std::string dflag = "-D";
   if (!lang.empty()) {
-    cmProp df =
+    cmValue df =
       this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG"));
     if (cmNonempty(df)) {
       dflag = *df;
@@ -3231,7 +3288,7 @@
                                             const std::string& lang,
                                             const char* feature)
 {
-  cmProp optionList = this->Makefile->GetDefinition(
+  cmValue optionList = this->Makefile->GetDefinition(
     cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature));
   if (optionList) {
     std::vector<std::string> options = cmExpandedList(*optionList);
@@ -3241,8 +3298,8 @@
   }
 }
 
-cmProp cmLocalGenerator::GetFeature(const std::string& feature,
-                                    const std::string& config)
+cmValue cmLocalGenerator::GetFeature(const std::string& feature,
+                                     const std::string& config)
 {
   std::string featureName = feature;
   // TODO: Define accumulation policy for features (prepend, append,
@@ -3253,7 +3310,7 @@
   }
   cmStateSnapshot snp = this->StateSnapshot;
   while (snp.IsValid()) {
-    if (cmProp value = snp.GetDirectory().GetProperty(featureName)) {
+    if (cmValue value = snp.GetDirectory().GetProperty(featureName)) {
       return value;
     }
     snp = snp.GetBuildsystemDirectoryParent();
@@ -3318,7 +3375,7 @@
     }
 
     // Include the user-specified pre-install script for this target.
-    if (cmProp preinstall = l->GetProperty("PRE_INSTALL_SCRIPT")) {
+    if (cmValue preinstall = l->GetProperty("PRE_INSTALL_SCRIPT")) {
       cmInstallScriptGenerator g(*preinstall, false, "", false, false);
       g.Generate(os, config, configurationTypes);
     }
@@ -3371,7 +3428,7 @@
     }
 
     // Include the user-specified post-install script for this target.
-    if (cmProp postinstall = l->GetProperty("POST_INSTALL_SCRIPT")) {
+    if (cmValue postinstall = l->GetProperty("POST_INSTALL_SCRIPT")) {
       cmInstallScriptGenerator g(*postinstall, false, "", false, false);
       g.Generate(os, config, configurationTypes);
     }
@@ -3596,8 +3653,8 @@
   // Ensure that for the CMakeFiles/<target>.dir/generated_source_file
   // we don't end up having:
   // CMakeFiles/<target>.dir/CMakeFiles/<target>.dir/generated_source_file.obj
-  cmProp unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE");
-  cmProp pchExtension = source.GetProperty("PCH_EXTENSION");
+  cmValue unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE");
+  cmValue pchExtension = source.GetProperty("PCH_EXTENSION");
   const bool isPchObject = objectName.find("cmake_pch") != std::string::npos;
   if (unitySourceFile || pchExtension || isPchObject) {
     if (pchExtension) {
@@ -3696,7 +3753,7 @@
     unsigned int major = 0;
     unsigned int minor = 0;
     unsigned int patch = 0;
-    if (cmProp value =
+    if (cmValue value =
           this->Makefile->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY")) {
       switch (sscanf(value->c_str(), "%u.%u.%u", &major, &minor, &patch)) {
         case 2:
@@ -3723,8 +3780,9 @@
   // variable.
   switch (this->GetPolicyStatus(cmPolicies::CMP0001)) {
     case cmPolicies::WARN:
-    // WARN is just OLD without warning because user code does not
-    // always affect whether this check is done.
+      // WARN is just OLD without warning because user code does not
+      // always affect whether this check is done.
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       // Old behavior is to check the variable.
       break;
@@ -3791,7 +3849,7 @@
 static void cmLGInfoProp(cmMakefile* mf, cmGeneratorTarget* target,
                          const std::string& prop)
 {
-  if (cmProp val = target->GetProperty(prop)) {
+  if (cmValue val = target->GetProperty(prop)) {
     mf->AddDefinition(prop, *val);
   }
 }
@@ -3801,7 +3859,7 @@
                                               const std::string& fname)
 {
   // Find the Info.plist template.
-  cmProp in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST");
+  cmValue in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST");
   std::string inFile = cmNonempty(in) ? *in : "MacOSXBundleInfo.plist.in";
   if (!cmSystemTools::FileIsFullPath(inFile)) {
     std::string inMod = this->Makefile->GetModulesFile(inFile);
@@ -3840,7 +3898,7 @@
   const std::string& fname)
 {
   // Find the Info.plist template.
-  cmProp in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST");
+  cmValue in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST");
   std::string inFile = cmNonempty(in) ? *in : "MacOSXFrameworkInfo.plist.in";
   if (!cmSystemTools::FileIsFullPath(inFile)) {
     std::string inMod = this->Makefile->GetModulesFile(inFile);
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index 993280a..3614c84 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -20,8 +20,8 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmStateSnapshot.h"
+#include "cmValue.h"
 
 class cmCompiledGeneratorExpression;
 class cmComputeLinkInformation;
@@ -171,6 +171,8 @@
                                             cmGeneratorTarget* target,
                                             const std::string& config,
                                             const std::string& lang);
+  bool AppendLWYUFlags(std::string& flags, const cmGeneratorTarget* target,
+                       const std::string& lang);
 
   enum class IncludePathStyle
   {
@@ -245,7 +247,7 @@
   void AppendFeatureOptions(std::string& flags, const std::string& lang,
                             const char* feature);
 
-  cmProp GetFeature(const std::string& feature, const std::string& config);
+  cmValue GetFeature(const std::string& feature, const std::string& config);
 
   /** \brief Get absolute path to dependency \a name
    *
@@ -545,7 +547,7 @@
   void CreateEvaluationFileOutputs(const std::string& config);
   void ProcessEvaluationFiles(std::vector<std::string>& generatedFiles);
 
-  cmProp GetRuleLauncher(cmGeneratorTarget* target, const std::string& prop);
+  cmValue GetRuleLauncher(cmGeneratorTarget* target, const std::string& prop);
 
 protected:
   // The default implementation ignores the IncludePathStyle and always
@@ -587,7 +589,7 @@
   std::string::size_type ObjectPathMax;
   std::set<std::string> ObjectMaxPathViolations;
 
-  std::set<std::string> EnvCPATH;
+  std::vector<std::string> EnvCPATH;
 
   using GeneratorTargetMap =
     std::unordered_map<std::string, cmGeneratorTarget*>;
@@ -649,7 +651,7 @@
 
   void ComputeObjectMaxPath();
   bool AllAppleArchSysrootsAreTheSame(const std::vector<std::string>& archs,
-                                      const char* sysroot);
+                                      cmValue sysroot);
 
   void CopyPchCompilePdb(const std::string& config, cmGeneratorTarget* target,
                          const std::string& ReuseFrom,
@@ -657,16 +659,16 @@
                          std::vector<std::string> const& extensions);
   void IncludeFileInUnitySources(cmGeneratedFileStream& unity_file,
                                  std::string const& sf_full_path,
-                                 cmProp beforeInclude, cmProp afterInclude,
-                                 cmProp uniqueIdName) const;
+                                 cmValue beforeInclude, cmValue afterInclude,
+                                 cmValue uniqueIdName) const;
   std::vector<std::string> AddUnityFilesModeAuto(
     cmGeneratorTarget* target, std::string const& lang,
-    std::vector<cmSourceFile*> const& filtered_sources, cmProp beforeInclude,
-    cmProp afterInclude, std::string const& filename_base, size_t batchSize);
+    std::vector<cmSourceFile*> const& filtered_sources, cmValue beforeInclude,
+    cmValue afterInclude, std::string const& filename_base, size_t batchSize);
   std::vector<std::string> AddUnityFilesModeGroup(
     cmGeneratorTarget* target, std::string const& lang,
-    std::vector<cmSourceFile*> const& filtered_sources, cmProp beforeInclude,
-    cmProp afterInclude, std::string const& filename_base);
+    std::vector<cmSourceFile*> const& filtered_sources, cmValue beforeInclude,
+    cmValue afterInclude, std::string const& filename_base);
 };
 
 #if !defined(CMAKE_BOOTSTRAP)
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index e88f33c..8556fe6 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -27,7 +27,6 @@
 #include "cmMessageType.h"
 #include "cmNinjaTargetGenerator.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
@@ -35,6 +34,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmLocalNinjaGenerator::cmLocalNinjaGenerator(cmGlobalGenerator* gg,
@@ -279,7 +279,7 @@
   std::string requiredVersion = cmGlobalNinjaGenerator::RequiredNinjaVersion();
 
   // Ninja generator uses the 'console' pool if available (>= 1.5)
-  if (this->GetGlobalNinjaGenerator()->SupportsConsolePool()) {
+  if (this->GetGlobalNinjaGenerator()->SupportsDirectConsole()) {
     requiredVersion =
       cmGlobalNinjaGenerator::RequiredNinjaVersionForConsolePool();
   }
@@ -311,7 +311,7 @@
 {
   cmGlobalNinjaGenerator::WriteDivider(os);
 
-  cmProp jobpools =
+  cmValue jobpools =
     this->GetCMakeInstance()->GetState()->GetGlobalProperty("JOB_POOLS");
   if (!jobpools) {
     jobpools = this->GetMakefile()->GetDefinition("CMAKE_JOB_POOLS");
@@ -796,8 +796,9 @@
 
   bool transformDepfile = false;
   switch (cc.GetCMP0116Status()) {
-    case cmPolicies::OLD:
     case cmPolicies::WARN:
+      CM_FALLTHROUGH;
+    case cmPolicies::OLD:
       break;
     case cmPolicies::REQUIRED_IF_USED:
     case cmPolicies::REQUIRED_ALWAYS:
@@ -869,7 +870,7 @@
 std::string cmLocalNinjaGenerator::MakeCustomLauncher(
   cmCustomCommandGenerator const& ccg)
 {
-  cmProp property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
+  cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
 
   if (!cmNonempty(property_value)) {
     return std::string();
@@ -903,7 +904,7 @@
 
 void cmLocalNinjaGenerator::AdditionalCleanFiles(const std::string& config)
 {
-  if (cmProp prop_value =
+  if (cmValue prop_value =
         this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) {
     std::vector<std::string> cleanFiles;
     {
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 0667c55..7e39b91 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -33,7 +33,6 @@
 #include "cmMakefile.h"
 #include "cmMakefileTargetGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
@@ -43,6 +42,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTargetDepend.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -570,7 +570,7 @@
 
   // Mark the rule as symbolic if requested.
   if (symbolic) {
-    if (cmProp sym =
+    if (cmValue sym =
           this->Makefile->GetDefinition("CMAKE_MAKE_SYMBOLIC_RULE")) {
       os << tgt << space << ": " << *sym << "\n";
     }
@@ -884,7 +884,7 @@
 {
   // Add a dependency on the rule file itself unless an option to skip
   // it is specifically enabled by the user or project.
-  cmProp nodep = this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY");
+  cmValue nodep = this->Makefile->GetDefinition("CMAKE_SKIP_RULE_DEPENDENCY");
   if (cmIsOff(nodep)) {
     depends.emplace_back(ruleFileName);
   }
@@ -999,7 +999,7 @@
 
       std::string launcher;
       // Short-circuit if there is no launcher.
-      cmProp val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM");
+      cmValue val = this->GetRuleLauncher(target, "RULE_LAUNCH_CUSTOM");
       if (cmNonempty(val)) {
         // Expand rule variables referenced in the given launcher command.
         cmRulePlaceholderExpander::RuleVariables vars;
@@ -1125,7 +1125,7 @@
 {
   std::vector<std::string> cleanFiles;
   // Look for additional files registered for cleaning in this directory.
-  if (cmProp prop_value =
+  if (cmValue prop_value =
         this->Makefile->GetProperty("ADDITIONAL_CLEAN_FILES")) {
     cmExpandList(cmGeneratorExpression::Evaluate(
                    *prop_value, this,
@@ -1501,18 +1501,18 @@
   // Lookup useful directory information.
   if (haveDirectoryInfo) {
     // Test whether we need to force Unix paths.
-    if (cmProp force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) {
+    if (cmValue force = mf->GetDefinition("CMAKE_FORCE_UNIX_PATHS")) {
       if (!cmIsOff(force)) {
         cmSystemTools::SetForceUnixPaths(true);
       }
     }
 
     // Setup relative path top directories.
-    if (cmProp relativePathTopSource =
+    if (cmValue relativePathTopSource =
           mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_SOURCE")) {
       this->SetRelativePathTopSource(*relativePathTopSource);
     }
-    if (cmProp relativePathTopBinary =
+    if (cmValue relativePathTopBinary =
           mf->GetDefinition("CMAKE_RELATIVE_PATH_TOP_BINARY")) {
       this->SetRelativePathTopBinary(*relativePathTopBinary);
     }
@@ -1582,7 +1582,7 @@
   cmMakefile* mf = this->Makefile;
 
   // Get the string listing the multiple output pairs.
-  cmProp pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS");
+  cmValue pairs_string = mf->GetDefinition("CMAKE_MULTIPLE_OUTPUT_PAIRS");
   if (!pairs_string) {
     return;
   }
@@ -1654,7 +1654,7 @@
       std::vector<std::string> commands;
       std::vector<std::string> depends;
 
-      cmProp p = gt->GetProperty("EchoString");
+      cmValue p = gt->GetProperty("EchoString");
       const char* text = p ? p->c_str() : "Running external command ...";
       depends.reserve(gt->GetUtilities().size());
       for (BT<std::pair<std::string, bool>> const& u : gt->GetUtilities()) {
@@ -1754,7 +1754,7 @@
   recursiveTarget = cmStrCat(this->GetCurrentBinaryDirectory(), "/preinstall");
   commands.clear();
   depends.clear();
-  cmProp noall =
+  cmValue noall =
     this->Makefile->GetDefinition("CMAKE_SKIP_INSTALL_ALL_DEPENDENCY");
   if (cmIsOff(noall)) {
     // Drive the build before installing.
@@ -1804,7 +1804,7 @@
                                                       bool verbose)
 {
   // Get the list of target files to check
-  cmProp infoDef = mf->GetDefinition("CMAKE_DEPEND_INFO_FILES");
+  cmValue infoDef = mf->GetDefinition("CMAKE_DEPEND_INFO_FILES");
   if (!infoDef) {
     return;
   }
@@ -1903,7 +1903,7 @@
 
       // Tell the dependency scanner what compiler is used.
       std::string cidVar = cmStrCat("CMAKE_", lang, "_COMPILER_ID");
-      cmProp cid = this->Makefile->GetDefinition(cidVar);
+      cmValue cid = this->Makefile->GetDefinition(cidVar);
       if (cmNonempty(cid)) {
         cmakefileStream << "set(CMAKE_" << lang << "_COMPILER_ID \"" << *cid
                         << "\")\n";
@@ -1960,11 +1960,11 @@
     // Store include transform rule properties.  Write the directory
     // rules first because they may be overridden by later target rules.
     std::vector<std::string> transformRules;
-    if (cmProp xform =
+    if (cmValue xform =
           this->Makefile->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) {
       cmExpandList(*xform, transformRules);
     }
-    if (cmProp xform =
+    if (cmValue xform =
           target->GetProperty("IMPLICIT_DEPENDS_INCLUDE_TRANSFORM")) {
       cmExpandList(*xform, transformRules);
     }
diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h
index 14dd0ba..78aa7f9 100644
--- a/Source/cmLocalUnixMakefileGenerator3.h
+++ b/Source/cmLocalUnixMakefileGenerator3.h
@@ -299,7 +299,7 @@
     cmGeneratorTarget const* target)
   {
     return this->CommandsVisited[target];
-  };
+  }
 
   std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>>
     CommandsVisited;
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 151470b..96dda53 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -69,7 +69,7 @@
     if (!l->IsInBuildSystem()) {
       continue;
     }
-    cmProp path = l->GetProperty("EXTERNAL_MSPROJECT");
+    cmValue path = l->GetProperty("EXTERNAL_MSPROJECT");
     if (path) {
       this->ReadAndStoreExternalGUID(l->GetName(), path->c_str());
     }
@@ -402,6 +402,7 @@
     cmVS7FlagTable::UserValueIgnored | cmVS7FlagTable::Continue },
   { "PrecompiledHeaderThrough", "Yc", "Precompiled Header Name", "",
     cmVS7FlagTable::UserValueRequired },
+  { "UsePrecompiledHeader", "Y-", "Don't use precompiled header", "0", 0 },
   { "PrecompiledHeaderFile", "Fp", "Generated Precompiled Header", "",
     cmVS7FlagTable::UserValue },
   // The YX and Yu options are in a per-global-generator table because
@@ -582,7 +583,7 @@
   const std::string& libName, cmGeneratorTarget* target)
 {
   std::string mfcFlag;
-  if (cmProp p = this->Makefile->GetDefinition("CMAKE_MFC_FLAG")) {
+  if (cmValue p = this->Makefile->GetDefinition("CMAKE_MFC_FLAG")) {
     mfcFlag = cmGeneratorExpression::Evaluate(*p, this, configName);
   } else {
     mfcFlag = "0";
@@ -781,7 +782,7 @@
   fout << "\t\t\t<Tool\n"
        << "\t\t\t\tName=\"" << tool << "\"\n";
   if (this->FortranProject) {
-    cmProp target_mod_dir = target->GetProperty("Fortran_MODULE_DIRECTORY");
+    cmValue target_mod_dir = target->GetProperty("Fortran_MODULE_DIRECTORY");
     std::string modDir;
     if (target_mod_dir) {
       modDir = this->MaybeRelativeToCurBinDir(*target_mod_dir);
@@ -938,7 +939,7 @@
       " " + GetBuildTypeLinkerFlags("CMAKE_MODULE_LINKER_FLAGS", configName);
   }
 
-  cmProp targetLinkFlags = target->GetProperty("LINK_FLAGS");
+  cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
   if (targetLinkFlags) {
     extraLinkOptions += " ";
     extraLinkOptions += *targetLinkFlags;
@@ -1077,7 +1078,7 @@
         }
       }
       std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE");
-      cmProp stackVal = this->Makefile->GetDefinition(stackVar);
+      cmValue stackVal = this->Makefile->GetDefinition(stackVar);
       if (stackVal) {
         fout << "\t\t\t\tStackReserveSize=\"" << *stackVal << "\"\n";
       }
@@ -1168,7 +1169,7 @@
              << "\"\n";
       }
       std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE");
-      cmProp stackVal = this->Makefile->GetDefinition(stackVar);
+      cmValue stackVal = this->Makefile->GetDefinition(stackVar);
       if (stackVal) {
         fout << "\t\t\t\tStackReserveSize=\"" << *stackVal << "\"";
       }
@@ -1209,8 +1210,8 @@
   std::ostream& fout, std::string const& config, cmGeneratorTarget* target)
 {
   if (this->WindowsCEProject) {
-    cmProp dir = target->GetProperty("DEPLOYMENT_REMOTE_DIRECTORY");
-    cmProp additionalFiles =
+    cmValue dir = target->GetProperty("DEPLOYMENT_REMOTE_DIRECTORY");
+    cmValue additionalFiles =
       target->GetProperty("DEPLOYMENT_ADDITIONAL_FILES");
 
     if (!dir && !additionalFiles) {
@@ -1447,12 +1448,12 @@
       needfc = true;
     }
     const std::string COMPILE_FLAGS("COMPILE_FLAGS");
-    if (cmProp cflags = sf.GetProperty(COMPILE_FLAGS)) {
+    if (cmValue cflags = sf.GetProperty(COMPILE_FLAGS)) {
       fc.CompileFlags = genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS);
       needfc = true;
     }
     const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
-    if (cmProp coptions = sf.GetProperty(COMPILE_OPTIONS)) {
+    if (cmValue coptions = sf.GetProperty(COMPILE_OPTIONS)) {
       lg->AppendCompileOptions(
         fc.CompileFlags,
         genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
@@ -1504,25 +1505,25 @@
       }
     }
     const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
-    if (cmProp cdefs = sf.GetProperty(COMPILE_DEFINITIONS)) {
+    if (cmValue cdefs = sf.GetProperty(COMPILE_DEFINITIONS)) {
       fc.CompileDefs = genexInterpreter.Evaluate(*cdefs, COMPILE_DEFINITIONS);
       needfc = true;
     }
     std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
-    if (cmProp ccdefs = sf.GetProperty(defPropName)) {
+    if (cmValue ccdefs = sf.GetProperty(defPropName)) {
       fc.CompileDefsConfig =
         genexInterpreter.Evaluate(*ccdefs, COMPILE_DEFINITIONS);
       needfc = true;
     }
 
     const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
-    if (cmProp cincs = sf.GetProperty(INCLUDE_DIRECTORIES)) {
+    if (cmValue cincs = sf.GetProperty(INCLUDE_DIRECTORIES)) {
       fc.IncludeDirs = genexInterpreter.Evaluate(*cincs, INCLUDE_DIRECTORIES);
       needfc = true;
     }
 
     // Check for extra object-file dependencies.
-    if (cmProp deps = sf.GetProperty("OBJECT_DEPENDS")) {
+    if (cmValue deps = sf.GetProperty("OBJECT_DEPENDS")) {
       std::vector<std::string> depends = cmExpandedList(*deps);
       const char* sep = "";
       for (const std::string& d : depends) {
@@ -1897,9 +1898,9 @@
 {
   // if we have all the required Source code control tags
   // then add that to the project
-  cmProp vsProjectname = target->GetProperty("VS_SCC_PROJECTNAME");
-  cmProp vsLocalpath = target->GetProperty("VS_SCC_LOCALPATH");
-  cmProp vsProvider = target->GetProperty("VS_SCC_PROVIDER");
+  cmValue vsProjectname = target->GetProperty("VS_SCC_PROJECTNAME");
+  cmValue vsLocalpath = target->GetProperty("VS_SCC_LOCALPATH");
+  cmValue vsProvider = target->GetProperty("VS_SCC_PROVIDER");
 
   if (vsProvider && vsLocalpath && vsProjectname) {
     /* clang-format off */
@@ -1908,7 +1909,7 @@
          << "\tSccProvider=\"" << *vsProvider << "\"\n";
     /* clang-format on */
 
-    cmProp vsAuxPath = target->GetProperty("VS_SCC_AUXPATH");
+    cmValue vsAuxPath = target->GetProperty("VS_SCC_AUXPATH");
     if (vsAuxPath) {
       fout << "\tSccAuxPath=\"" << *vsAuxPath << "\"\n";
     }
@@ -1928,7 +1929,7 @@
        << "\tProjectCreator=\"Intel Fortran\"\n"
        << "\tVersion=\"" << gg->GetIntelProjectVersion() << "\"\n";
   /* clang-format on */
-  cmProp p = target->GetProperty("VS_KEYWORD");
+  cmValue p = target->GetProperty("VS_KEYWORD");
   const char* keyword = p ? p->c_str() : "Console Application";
   const char* projectType = 0;
   switch (target->GetType()) {
@@ -1990,14 +1991,14 @@
        << "\tProjectType=\"Visual C++\"\n";
   /* clang-format on */
   fout << "\tVersion=\"" << (gg->GetVersion() / 10) << ".00\"\n";
-  cmProp p = target->GetProperty("PROJECT_LABEL");
+  cmValue p = target->GetProperty("PROJECT_LABEL");
   const std::string projLabel = p ? *p : libName;
   p = target->GetProperty("VS_KEYWORD");
   const std::string keyword = p ? *p : "Win32Proj";
   fout << "\tName=\"" << projLabel << "\"\n";
   fout << "\tProjectGUID=\"{" << gg->GetGUID(libName) << "}\"\n";
   this->WriteProjectSCC(fout, target);
-  if (cmProp targetFrameworkVersion =
+  if (cmValue targetFrameworkVersion =
         target->GetProperty("VS_DOTNET_TARGET_FRAMEWORK_VERSION")) {
     fout << "\tTargetFrameworkVersion=\"" << *targetFrameworkVersion << "\"\n";
   }
@@ -2142,7 +2143,7 @@
   std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
   // save the GUID in the cache
   this->GlobalGenerator->GetCMakeInstance()->AddCacheEntry(
-    guidStoreName, parser.GUID.c_str(), "Stored GUID", cmStateEnums::INTERNAL);
+    guidStoreName, parser.GUID, "Stored GUID", cmStateEnums::INTERNAL);
 }
 
 std::string cmLocalVisualStudio7Generator::GetTargetDirectory(
diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx
index 002f484..03213ef 100644
--- a/Source/cmLocalVisualStudioGenerator.cxx
+++ b/Source/cmLocalVisualStudioGenerator.cxx
@@ -171,8 +171,9 @@
 
   // for visual studio IDE add extra stuff to the PATH
   // if CMAKE_MSVCIDE_RUN_PATH is set.
-  if (this->Makefile->GetDefinition("MSVC_IDE")) {
-    cmProp extraPath = this->Makefile->GetDefinition("CMAKE_MSVCIDE_RUN_PATH");
+  if (this->GetGlobalGenerator()->IsVisualStudio()) {
+    cmValue extraPath =
+      this->Makefile->GetDefinition("CMAKE_MSVCIDE_RUN_PATH");
     if (extraPath) {
       script += newline;
       newline = newline_text;
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index c970abe..88f3cc7 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -190,7 +190,7 @@
 {
   // Warn if a <pkg>_ROOT variable we may use is set.
   std::string const varName = pkg + "_ROOT";
-  cmProp var = this->GetDefinition(varName);
+  cmValue var = this->GetDefinition(varName);
   std::string env;
   cmSystemTools::GetEnv(varName, env);
 
@@ -212,59 +212,31 @@
   }
 }
 
-cmStringRange cmMakefile::GetIncludeDirectoriesEntries() const
+cmBTStringRange cmMakefile::GetIncludeDirectoriesEntries() const
 {
   return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries();
 }
 
-cmBacktraceRange cmMakefile::GetIncludeDirectoriesBacktraces() const
-{
-  return this->StateSnapshot.GetDirectory()
-    .GetIncludeDirectoriesEntryBacktraces();
-}
-
-cmStringRange cmMakefile::GetCompileOptionsEntries() const
+cmBTStringRange cmMakefile::GetCompileOptionsEntries() const
 {
   return this->StateSnapshot.GetDirectory().GetCompileOptionsEntries();
 }
 
-cmBacktraceRange cmMakefile::GetCompileOptionsBacktraces() const
-{
-  return this->StateSnapshot.GetDirectory().GetCompileOptionsEntryBacktraces();
-}
-
-cmStringRange cmMakefile::GetCompileDefinitionsEntries() const
+cmBTStringRange cmMakefile::GetCompileDefinitionsEntries() const
 {
   return this->StateSnapshot.GetDirectory().GetCompileDefinitionsEntries();
 }
 
-cmBacktraceRange cmMakefile::GetCompileDefinitionsBacktraces() const
-{
-  return this->StateSnapshot.GetDirectory()
-    .GetCompileDefinitionsEntryBacktraces();
-}
-
-cmStringRange cmMakefile::GetLinkOptionsEntries() const
+cmBTStringRange cmMakefile::GetLinkOptionsEntries() const
 {
   return this->StateSnapshot.GetDirectory().GetLinkOptionsEntries();
 }
 
-cmBacktraceRange cmMakefile::GetLinkOptionsBacktraces() const
-{
-  return this->StateSnapshot.GetDirectory().GetLinkOptionsEntryBacktraces();
-}
-
-cmStringRange cmMakefile::GetLinkDirectoriesEntries() const
+cmBTStringRange cmMakefile::GetLinkDirectoriesEntries() const
 {
   return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries();
 }
 
-cmBacktraceRange cmMakefile::GetLinkDirectoriesBacktraces() const
-{
-  return this->StateSnapshot.GetDirectory()
-    .GetLinkDirectoriesEntryBacktraces();
-}
-
 cmListFileBacktrace cmMakefile::GetBacktrace() const
 {
   return this->Backtrace;
@@ -428,7 +400,7 @@
 
   // Check for maximum recursion depth.
   int depth = CMake_DEFAULT_RECURSION_LIMIT;
-  cmProp depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
+  cmValue depthStr = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
   if (depthStr) {
     std::istringstream s(*depthStr);
     int d;
@@ -614,7 +586,7 @@
 bool cmMakefile::ReadDependentFile(const std::string& filename,
                                    bool noPolicyScope)
 {
-  if (cmProp def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) {
+  if (cmValue def = this->GetDefinition("CMAKE_CURRENT_LIST_FILE")) {
     this->AddDefinition("CMAKE_PARENT_LIST_FILE", *def);
   }
   std::string filenametoread = cmSystemTools::CollapseFullPath(
@@ -846,6 +818,7 @@
         // version.
         this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
                                                msg.str(), this->Backtrace);
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior is to use policy version 2.4 set in
         // cmListFileCache.
@@ -857,7 +830,7 @@
         this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
                                                msg.str(), this->Backtrace);
         cmSystemTools::SetFatalErrorOccured();
-        return;
+        break;
     }
   }
 }
@@ -952,10 +925,9 @@
 void cmMakefile::Generate(cmLocalGenerator& lg)
 {
   this->DoGenerate(lg);
-  cmProp oldValue = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY");
+  cmValue oldValue = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY");
   if (oldValue &&
-      cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, oldValue->c_str(),
-                                    "2.4")) {
+      cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, oldValue, "2.4")) {
     this->GetCMakeInstance()->IssueMessage(
       MessageType::FATAL_ERROR,
       "You have set CMAKE_BACKWARDS_COMPATIBILITY to a CMake version less "
@@ -1030,6 +1002,7 @@
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0040) << "\n";
         issueMessage = true;
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::NEW:
@@ -1037,6 +1010,7 @@
       case cmPolicies::REQUIRED_ALWAYS:
         issueMessage = true;
         messageType = MessageType::FATAL_ERROR;
+        break;
     }
 
     if (issueMessage) {
@@ -1386,10 +1360,10 @@
 {
   if (before) {
     this->StateSnapshot.GetDirectory().PrependLinkDirectoriesEntry(
-      directory, this->Backtrace);
+      BT<std::string>(directory, this->Backtrace));
   } else {
     this->StateSnapshot.GetDirectory().AppendLinkDirectoriesEntry(
-      directory, this->Backtrace);
+      BT<std::string>(directory, this->Backtrace));
   }
 }
 
@@ -1434,7 +1408,7 @@
   const char* define = def.c_str() + 2;
 
   if (remove) {
-    if (cmProp cdefs = this->GetProperty("COMPILE_DEFINITIONS")) {
+    if (cmValue cdefs = this->GetProperty("COMPILE_DEFINITIONS")) {
       // Expand the list.
       std::vector<std::string> defs = cmExpandedList(*cdefs);
 
@@ -1444,7 +1418,7 @@
       std::string ndefs = cmJoin(cmMakeRange(defBegin, defEnd), ";");
 
       // Store the new list.
-      this->SetProperty("COMPILE_DEFINITIONS", ndefs.c_str());
+      this->SetProperty("COMPILE_DEFINITIONS", ndefs);
     }
   } else {
     // Append the definition to the directory property.
@@ -1465,30 +1439,29 @@
   // Include transform property.  There is no per-config version.
   {
     const char* prop = "IMPLICIT_DEPENDS_INCLUDE_TRANSFORM";
-    this->SetProperty(prop, cmToCStr(parent->GetProperty(prop)));
+    this->SetProperty(prop, parent->GetProperty(prop));
   }
 
   // compile definitions property and per-config versions
   cmPolicies::PolicyStatus polSt = this->GetPolicyStatus(cmPolicies::CMP0043);
   if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
     this->SetProperty("COMPILE_DEFINITIONS",
-                      cmToCStr(parent->GetProperty("COMPILE_DEFINITIONS")));
+                      parent->GetProperty("COMPILE_DEFINITIONS"));
     std::vector<std::string> configs =
       this->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
     for (std::string const& config : configs) {
       std::string defPropName =
         cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
-      cmProp prop = parent->GetProperty(defPropName);
-      this->SetProperty(defPropName, cmToCStr(prop));
+      cmValue prop = parent->GetProperty(defPropName);
+      this->SetProperty(defPropName, prop);
     }
   }
 
   // labels
-  this->SetProperty("LABELS", cmToCStr(parent->GetProperty("LABELS")));
+  this->SetProperty("LABELS", parent->GetProperty("LABELS"));
 
   // link libraries
-  this->SetProperty("LINK_LIBRARIES",
-                    cmToCStr(parent->GetProperty("LINK_LIBRARIES")));
+  this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES"));
 
   // the initial project name
   this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName());
@@ -1781,6 +1754,7 @@
           << cmPolicies::GetPolicyWarning(cmPolicies::CMP0014);
         /* clang-format on */
         this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior does not warn.
         break;
@@ -1791,6 +1765,7 @@
       case cmPolicies::NEW:
         // NEW behavior prints the error.
         this->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        break;
     }
     return;
   }
@@ -1877,16 +1852,16 @@
   std::string entryString = cmJoin(incs, ";");
   if (before) {
     this->StateSnapshot.GetDirectory().PrependIncludeDirectoriesEntry(
-      entryString, this->Backtrace);
+      BT<std::string>(entryString, this->Backtrace));
   } else {
     this->StateSnapshot.GetDirectory().AppendIncludeDirectoriesEntry(
-      entryString, this->Backtrace);
+      BT<std::string>(entryString, this->Backtrace));
   }
 
   // Property on each target:
   for (auto& target : this->Targets) {
     cmTarget& t = target.second;
-    t.InsertInclude(entryString, this->Backtrace, before);
+    t.InsertInclude(BT<std::string>(entryString, this->Backtrace), before);
   }
 }
 
@@ -1927,7 +1902,7 @@
                                     cmStateEnums::CacheEntryType type,
                                     bool force)
 {
-  cmProp existingValue = this->GetState()->GetInitializedCacheValue(name);
+  cmValue existingValue = this->GetState()->GetInitializedCacheValue(name);
   // must be outside the following if() to keep it alive long enough
   std::string nvalue;
 
@@ -1956,7 +1931,7 @@
         nvalue += files[cc];
       }
 
-      this->GetCMakeInstance()->AddCacheEntry(name, nvalue.c_str(), doc, type);
+      this->GetCMakeInstance()->AddCacheEntry(name, nvalue, doc, type);
       nvalue = *this->GetState()->GetInitializedCacheValue(name);
       value = nvalue.c_str();
     }
@@ -2045,7 +2020,7 @@
     default:;
   }
 
-  if (cmProp linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
+  if (cmValue linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
     std::vector<std::string> linkLibs = cmExpandedList(*linkLibsProp);
 
     for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) {
@@ -2118,15 +2093,23 @@
 cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type,
                                    const std::string& name)
 {
-  auto it = this->Targets
-              .emplace(name,
-                       cmTarget(name, type, cmTarget::VisibilityNormal, this,
-                                cmTarget::PerConfig::Yes))
-              .first;
+  return &this->CreateNewTarget(name, type).first;
+}
+
+std::pair<cmTarget&, bool> cmMakefile::CreateNewTarget(
+  const std::string& name, cmStateEnums::TargetType type,
+  cmTarget::PerConfig perConfig)
+{
+  auto ib = this->Targets.emplace(
+    name, cmTarget(name, type, cmTarget::VisibilityNormal, this, perConfig));
+  auto it = ib.first;
+  if (!ib.second) {
+    return std::make_pair(std::ref(it->second), false);
+  }
   this->OrderedTargets.push_back(&it->second);
   this->GetGlobalGenerator()->IndexTarget(&it->second);
   this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name);
-  return &it->second;
+  return std::make_pair(std::ref(it->second), true);
 }
 
 cmTarget* cmMakefile::AddNewUtilityTarget(const std::string& utilityName,
@@ -2235,7 +2218,7 @@
 cmSourceGroup* cmMakefile::GetOrCreateSourceGroup(const std::string& name)
 {
   std::string delimiters;
-  if (cmProp p = this->GetDefinition("SOURCE_GROUP_DELIMITER")) {
+  if (cmValue p = this->GetDefinition("SOURCE_GROUP_DELIMITER")) {
     delimiters = *p;
   } else {
     delimiters = "/\\";
@@ -2288,7 +2271,7 @@
   }
   std::ostringstream w;
 
-  cmProp includeDirs = this->GetProperty("INCLUDE_DIRECTORIES");
+  cmValue includeDirs = this->GetProperty("INCLUDE_DIRECTORIES");
   if (includeDirs && mightExpandVariablesCMP0019(includeDirs->c_str())) {
     std::string dirs = *includeDirs;
     this->ExpandVariablesInString(dirs, true, true);
@@ -2300,7 +2283,7 @@
         << "  " << dirs << "\n";
       /* clang-format on */
     }
-    this->SetProperty("INCLUDE_DIRECTORIES", dirs.c_str());
+    this->SetProperty("INCLUDE_DIRECTORIES", dirs);
   }
 
   // Also for each target's INCLUDE_DIRECTORIES property:
@@ -2326,7 +2309,7 @@
     }
   }
 
-  if (cmProp linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) {
+  if (cmValue linkDirsProp = this->GetProperty("LINK_DIRECTORIES")) {
     if (mightExpandVariablesCMP0019(linkDirsProp->c_str())) {
       std::string d = *linkDirsProp;
       const std::string orig = d;
@@ -2342,7 +2325,7 @@
     }
   }
 
-  if (cmProp linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
+  if (cmValue linkLibsProp = this->GetProperty("LINK_LIBRARIES")) {
     std::vector<std::string> linkLibs = cmExpandedList(*linkLibsProp);
 
     for (auto l = linkLibs.begin(); l != linkLibs.end(); ++l) {
@@ -2386,7 +2369,7 @@
 
 bool cmMakefile::IsSet(const std::string& name) const
 {
-  cmProp value = this->GetDefinition(name);
+  cmValue value = this->GetDefinition(name);
   if (!value) {
     return false;
   }
@@ -2404,12 +2387,12 @@
 
 bool cmMakefile::PlatformIs32Bit() const
 {
-  if (cmProp plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) {
+  if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) {
     if (*plat_abi == "ELF X32") {
       return false;
     }
   }
-  if (cmProp sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) {
+  if (cmValue sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) {
     return atoi(sizeof_dptr->c_str()) == 4;
   }
   return false;
@@ -2417,7 +2400,7 @@
 
 bool cmMakefile::PlatformIs64Bit() const
 {
-  if (cmProp sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) {
+  if (cmValue sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) {
     return atoi(sizeof_dptr->c_str()) == 8;
   }
   return false;
@@ -2425,7 +2408,7 @@
 
 bool cmMakefile::PlatformIsx32() const
 {
-  if (cmProp plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) {
+  if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) {
     if (*plat_abi == "ELF X32") {
       return true;
     }
@@ -2475,7 +2458,7 @@
     name += language;
   }
   name += "_FLAG";
-  return cmToCStr(this->GetDefinition(name));
+  return this->GetDefinition(name).GetCStr();
 }
 
 bool cmMakefile::CanIWriteThisFile(std::string const& fileName) const
@@ -2498,7 +2481,7 @@
   const std::string& name) const
 {
   static std::string const empty;
-  cmProp def = this->GetDefinition(name);
+  cmValue def = this->GetDefinition(name);
   if (!def) {
     cmSystemTools::Error("Error required internal CMake variable not "
                          "set, cmake may not be built correctly.\n"
@@ -2511,7 +2494,7 @@
 
 bool cmMakefile::IsDefinitionSet(const std::string& name) const
 {
-  cmProp def = this->StateSnapshot.GetDefinition(name);
+  cmValue def = this->StateSnapshot.GetDefinition(name);
   if (!def) {
     def = this->GetState()->GetInitializedCacheValue(name);
   }
@@ -2528,7 +2511,7 @@
 
 bool cmMakefile::IsNormalDefinitionSet(const std::string& name) const
 {
-  cmProp def = this->StateSnapshot.GetDefinition(name);
+  cmValue def = this->StateSnapshot.GetDefinition(name);
 #ifndef CMAKE_BOOTSTRAP
   if (cmVariableWatch* vv = this->GetVariableWatch()) {
     if (!def) {
@@ -2540,9 +2523,9 @@
   return def != nullptr;
 }
 
-cmProp cmMakefile::GetDefinition(const std::string& name) const
+cmValue cmMakefile::GetDefinition(const std::string& name) const
 {
-  cmProp def = this->StateSnapshot.GetDefinition(name);
+  cmValue def = this->StateSnapshot.GetDefinition(name);
   if (!def) {
     def = this->GetState()->GetInitializedCacheValue(name);
   }
@@ -2553,7 +2536,7 @@
       vv->VariableAccessed(name,
                            def ? cmVariableWatch::VARIABLE_READ_ACCESS
                                : cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS,
-                           cmToCStr(def), this);
+                           def.GetCStr(), this);
 
     if (watch_function_executed) {
       // A callback was executed and may have caused re-allocation of the
@@ -2571,19 +2554,14 @@
 
 const std::string& cmMakefile::GetSafeDefinition(const std::string& name) const
 {
-  static std::string const empty;
-  cmProp def = this->GetDefinition(name);
-  if (!def) {
-    return empty;
-  }
-  return *def;
+  return this->GetDefinition(name);
 }
 
 bool cmMakefile::GetDefExpandList(const std::string& name,
                                   std::vector<std::string>& out,
                                   bool emptyArgs) const
 {
-  cmProp def = this->GetDefinition(name);
+  cmValue def = this->GetDefinition(name);
   if (!def) {
     return false;
   }
@@ -2736,7 +2714,7 @@
 
       // Lookup the definition of VAR.
       std::string var(first + 1, last - first - 2);
-      if (cmProp val = this->GetDefinition(var)) {
+      if (cmValue val = this->GetDefinition(var)) {
         // Store the value in the output escaping as requested.
         if (escapeQuotes) {
           source.append(cmEscapeQuotes(*val));
@@ -2808,6 +2786,7 @@
         case cmPolicies::REQUIRED_ALWAYS:
           error << "\n"
                 << cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0010);
+          break;
         case cmPolicies::NEW:
           // NEW behavior is to report the error.
           break;
@@ -2941,7 +2920,7 @@
           openstack.pop_back();
           result.append(last, in - last);
           std::string const& lookup = result.substr(var.loc);
-          cmProp value = nullptr;
+          cmValue value = nullptr;
           std::string varresult;
           std::string svalue;
           switch (var.domain) {
@@ -2959,7 +2938,7 @@
               break;
             case ENVIRONMENT:
               if (cmSystemTools::GetEnv(lookup, svalue)) {
-                value = &svalue;
+                value = cmValue(svalue);
               }
               break;
             case CACHE:
@@ -3086,7 +3065,7 @@
             if (filename && variable == lineVar) {
               varresult = std::to_string(line);
             } else {
-              cmProp def = this->GetDefinition(variable);
+              cmValue def = this->GetDefinition(variable);
               if (def) {
                 varresult = *def;
               } else if (!this->SuppressSideEffects) {
@@ -3105,8 +3084,8 @@
             break;
           }
         }
-      // Failed to find a valid @ expansion; treat it as literal.
-      /* FALLTHROUGH */
+        // Failed to find a valid @ expansion; treat it as literal.
+        CM_FALLTHROUGH;
       default: {
         if (!openstack.empty() &&
             !(isalnum(inc) || inc == '_' || inc == '/' || inc == '.' ||
@@ -3182,6 +3161,24 @@
   }
 }
 
+void cmMakefile::InitCMAKE_CONFIGURATION_TYPES(std::string const& genDefault)
+{
+  if (this->GetDefinition("CMAKE_CONFIGURATION_TYPES")) {
+    return;
+  }
+  std::string initConfigs;
+  if (this->GetCMakeInstance()->GetIsInTryCompile() ||
+      !cmSystemTools::GetEnv("CMAKE_CONFIGURATION_TYPES", initConfigs)) {
+    initConfigs = genDefault;
+  }
+  this->AddCacheDefinition(
+    "CMAKE_CONFIGURATION_TYPES", initConfigs,
+    "Semicolon separated list of supported configuration types, "
+    "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, "
+    "anything else will be ignored.",
+    cmStateEnums::STRING);
+}
+
 std::string cmMakefile::GetDefaultConfiguration() const
 {
   if (this->GetGlobalGenerator()->IsMultiConfig()) {
@@ -3542,8 +3539,8 @@
   // make sure the same generator is used
   // use this program as the cmake to be run, it should not
   // be run that way but the cmake object requires a vailid path
-  cmake cm(cmake::RoleProject, cmState::Project);
-  cm.SetIsInTryCompile(true);
+  cmake cm(cmake::RoleProject, cmState::Project,
+           cmState::ProjectKind::TryCompile);
   auto gg = cm.CreateGlobalGenerator(this->GetGlobalGenerator()->GetName());
   if (!gg) {
     this->IssueMessage(MessageType::INTERNAL_ERROR,
@@ -3565,18 +3562,19 @@
   cm.SetGeneratorToolset(this->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET"));
   cm.LoadCache();
   if (!cm.GetGlobalGenerator()->IsMultiConfig()) {
-    if (cmProp config =
+    if (cmValue config =
           this->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION")) {
       // Tell the single-configuration generator which one to use.
       // Add this before the user-provided CMake arguments in case
       // one of the arguments is -DCMAKE_BUILD_TYPE=...
-      cm.AddCacheEntry("CMAKE_BUILD_TYPE", config->c_str(),
-                       "Build configuration", cmStateEnums::STRING);
+      cm.AddCacheEntry("CMAKE_BUILD_TYPE", config, "Build configuration",
+                       cmStateEnums::STRING);
     }
   }
-  cmProp recursionDepth = this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
+  cmValue recursionDepth =
+    this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH");
   if (recursionDepth) {
-    cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth->c_str(),
+    cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth,
                      "Maximum recursion depth", cmStateEnums::STRING);
   }
   // if cmake args were provided then pass them in
@@ -3709,7 +3707,7 @@
   std::string moduleInCMakeModulePath;
 
   // Always search in CMAKE_MODULE_PATH:
-  cmProp cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH");
+  cmValue cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH");
   if (cmakeModulePath) {
     std::vector<std::string> modulePath = cmExpandedList(*cmakeModulePath);
 
@@ -3750,7 +3748,7 @@
   }
 
   if (!moduleInCMakeModulePath.empty() && !moduleInCMakeRoot.empty()) {
-    cmProp currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE");
+    cmValue currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE");
     std::string mods = cmSystemTools::GetCMakeRoot() + "/Modules/";
     if (currentFile && cmSystemTools::IsSubDirectory(*currentFile, mods)) {
       switch (this->GetPolicyStatus(cmPolicies::CMP0017)) {
@@ -3807,7 +3805,7 @@
 
     // Replace #cmakedefine instances.
     if (this->cmDefineRegex.find(line)) {
-      cmProp def = this->GetDefinition(this->cmDefineRegex.match(2));
+      cmValue def = this->GetDefinition(this->cmDefineRegex.match(2));
       if (!cmIsOff(def)) {
         const std::string indentation = this->cmDefineRegex.match(1);
         cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine",
@@ -3820,7 +3818,7 @@
       }
     } else if (this->cmDefine01Regex.find(line)) {
       const std::string indentation = this->cmDefine01Regex.match(1);
-      cmProp def = this->GetDefinition(this->cmDefine01Regex.match(2));
+      cmValue def = this->GetDefinition(this->cmDefine01Regex.match(2));
       cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine01",
                                    "#" + indentation + "define");
       output += line;
@@ -3966,6 +3964,10 @@
 {
   this->StateSnapshot.GetDirectory().SetProperty(prop, value, this->Backtrace);
 }
+void cmMakefile::SetProperty(const std::string& prop, cmValue value)
+{
+  this->StateSnapshot.GetDirectory().SetProperty(prop, value, this->Backtrace);
+}
 
 void cmMakefile::AppendProperty(const std::string& prop,
                                 const std::string& value, bool asString)
@@ -3974,26 +3976,25 @@
                                                     this->Backtrace);
 }
 
-cmProp cmMakefile::GetProperty(const std::string& prop) const
+cmValue cmMakefile::GetProperty(const std::string& prop) const
 {
   // Check for computed properties.
   static std::string output;
   if (prop == "TESTS") {
     std::vector<std::string> keys;
     // get list of keys
-    std::transform(this->Tests.begin(), this->Tests.end(),
-                   std::back_inserter(keys),
-                   [](decltype(this->Tests)::value_type const& pair) {
-                     return pair.first;
-                   });
+    const auto* t = this;
+    std::transform(
+      t->Tests.begin(), t->Tests.end(), std::back_inserter(keys),
+      [](decltype(t->Tests)::value_type const& pair) { return pair.first; });
     output = cmJoin(keys, ";");
-    return &output;
+    return cmValue(output);
   }
 
   return this->StateSnapshot.GetDirectory().GetProperty(prop);
 }
 
-cmProp cmMakefile::GetProperty(const std::string& prop, bool chain) const
+cmValue cmMakefile::GetProperty(const std::string& prop, bool chain) const
 {
   return this->StateSnapshot.GetDirectory().GetProperty(prop, chain);
 }
@@ -4052,7 +4053,7 @@
 void cmMakefile::AddCMakeDependFilesFromUser()
 {
   std::vector<std::string> deps;
-  if (cmProp deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) {
+  if (cmValue deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) {
     cmExpandList(*deps_str, deps);
   }
   for (std::string const& dep : deps) {
@@ -4341,7 +4342,7 @@
 
 void cmMakefile::ClearMatches()
 {
-  cmProp nMatchesStr = this->GetDefinition(nMatchesVariable);
+  cmValue nMatchesStr = this->GetDefinition(nMatchesVariable);
   if (!nMatchesStr) {
     return;
   }
@@ -4394,7 +4395,7 @@
 bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) const
 {
   // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting.
-  if (cmProp val = this->GetDefinition(var)) {
+  if (cmValue val = this->GetDefinition(var)) {
     return cmIsOn(val);
   }
   // Enable optional policy warnings with --debug-output, --trace,
@@ -4426,13 +4427,12 @@
     return false;
   }
 
-  // Deprecate old policies, especially those that require a lot
-  // of code to maintain the old behavior.
-  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0081 &&
+  // Deprecate old policies.
+  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0088 &&
       !(this->GetCMakeInstance()->GetIsInTryCompile() &&
         (
           // Policies set by cmCoreTryCompile::TryCompileCode.
-          id == cmPolicies::CMP0065))) {
+          id == cmPolicies::CMP0065 || id == cmPolicies::CMP0083))) {
     this->IssueMessage(MessageType::DEPRECATION_WARNING,
                        cmPolicies::GetPolicyDeprecatedWarning(id));
   }
@@ -4511,7 +4511,8 @@
   bool ignoreErrors = true;
   switch (this->GetPolicyStatus(cmPolicies::CMP0061)) {
     case cmPolicies::WARN:
-    // No warning for this policy!
+      // No warning for this policy!
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       break;
     case cmPolicies::REQUIRED_IF_USED:
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 77e9c74..671cdab 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -13,6 +13,7 @@
 #include <stack>
 #include <string>
 #include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include <cm/optional>
@@ -28,11 +29,10 @@
 #include "cmMessageType.h"
 #include "cmNewLineStyle.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmSourceFileLocationKind.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
-#include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 // IWYU does not see that 'std::unordered_map<std::string, cmTarget>'
 // will not compile without the complete type.
@@ -230,6 +230,10 @@
   cmTarget* AddImportedTarget(const std::string& name,
                               cmStateEnums::TargetType type, bool global);
 
+  std::pair<cmTarget&, bool> CreateNewTarget(
+    const std::string& name, cmStateEnums::TargetType type,
+    cmTarget::PerConfig perConfig = cmTarget::PerConfig::Yes);
+
   cmTarget* AddNewTarget(cmStateEnums::TargetType type,
                          const std::string& name);
 
@@ -282,6 +286,10 @@
    * can be used in CMake to refer to lists, directories, etc.
    */
   void AddDefinition(const std::string& name, cm::string_view value);
+  void AddDefinition(const std::string& name, cmValue value)
+  {
+    this->AddDefinition(name, *value);
+  }
   /**
    * Add bool variable definition to the build.
    */
@@ -310,6 +318,8 @@
    */
   void SetProjectName(std::string const& name);
 
+  void InitCMAKE_CONFIGURATION_TYPES(std::string const& genDefault);
+
   /* Get the default configuration */
   std::string GetDefaultConfiguration() const;
 
@@ -392,13 +402,13 @@
    * Set a regular expression that include files must match
    * in order to be considered as part of the depend information.
    */
-  void SetIncludeRegularExpression(const char* regex)
+  void SetIncludeRegularExpression(const std::string& regex)
   {
-    this->SetProperty("INCLUDE_REGULAR_EXPRESSION", regex);
+    this->SetProperty("INCLUDE_REGULAR_EXPRESSION", regex.c_str());
   }
-  const char* GetIncludeRegularExpression() const
+  const std::string& GetIncludeRegularExpression() const
   {
-    return cmToCStr(this->GetProperty("INCLUDE_REGULAR_EXPRESSION"));
+    return this->GetProperty("INCLUDE_REGULAR_EXPRESSION");
   }
 
   /**
@@ -482,7 +492,7 @@
    * If the variable is not found in this makefile instance, the
    * cache is then queried.
    */
-  cmProp GetDefinition(const std::string&) const;
+  cmValue GetDefinition(const std::string&) const;
   const std::string& GetSafeDefinition(const std::string&) const;
   const std::string& GetRequiredDefinition(const std::string& name) const;
   bool IsDefinitionSet(const std::string&) const;
@@ -762,10 +772,15 @@
 
   //! Set/Get a property of this directory
   void SetProperty(const std::string& prop, const char* value);
+  void SetProperty(const std::string& prop, cmValue value);
+  void SetProperty(const std::string& prop, const std::string& value)
+  {
+    this->SetProperty(prop, cmValue(value));
+  }
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString = false);
-  cmProp GetProperty(const std::string& prop) const;
-  cmProp GetProperty(const std::string& prop, bool chain) const;
+  cmValue GetProperty(const std::string& prop) const;
+  cmValue GetProperty(const std::string& prop, bool chain) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   std::vector<std::string> GetPropertyKeys() const;
 
@@ -867,16 +882,11 @@
   bool CheckCMP0037(std::string const& targetName,
                     cmStateEnums::TargetType targetType) const;
 
-  cmStringRange GetIncludeDirectoriesEntries() const;
-  cmBacktraceRange GetIncludeDirectoriesBacktraces() const;
-  cmStringRange GetCompileOptionsEntries() const;
-  cmBacktraceRange GetCompileOptionsBacktraces() const;
-  cmStringRange GetCompileDefinitionsEntries() const;
-  cmBacktraceRange GetCompileDefinitionsBacktraces() const;
-  cmStringRange GetLinkOptionsEntries() const;
-  cmBacktraceRange GetLinkOptionsBacktraces() const;
-  cmStringRange GetLinkDirectoriesEntries() const;
-  cmBacktraceRange GetLinkDirectoriesBacktraces() const;
+  cmBTStringRange GetIncludeDirectoriesEntries() const;
+  cmBTStringRange GetCompileOptionsEntries() const;
+  cmBTStringRange GetCompileDefinitionsEntries() const;
+  cmBTStringRange GetLinkOptionsEntries() const;
+  cmBTStringRange GetLinkDirectoriesEntries() const;
 
   std::set<std::string> const& GetSystemIncludeDirectories() const
   {
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 3a2744e..575fb05 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -21,7 +21,6 @@
 #include "cmMakefile.h"
 #include "cmOSXBundleGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
@@ -29,6 +28,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
   cmGeneratorTarget* target)
@@ -228,8 +228,8 @@
 
     std::string launcher;
 
-    cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
-                                                       "RULE_LAUNCH_LINK");
+    cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
+                                                        "RULE_LAUNCH_LINK");
     if (cmNonempty(val)) {
       launcher = cmStrCat(*val, ' ');
     }
@@ -397,9 +397,8 @@
                                     this->LocalGenerator->GetLinkLibsCMP0065(
                                       linkLanguage, *this->GeneratorTarget));
 
-  if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
-    this->LocalGenerator->AppendFlags(linkFlags, " -Wl,--no-as-needed");
-  }
+  this->UseLWYU = this->LocalGenerator->AppendLWYUFlags(
+    linkFlags, this->GeneratorTarget, linkLanguage);
 
   // Add language feature flags.
   this->LocalGenerator->AddLanguageFlagsForLinking(
@@ -577,18 +576,24 @@
       vars.Launcher = linkerLauncher.c_str();
     }
 
-    if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
-      std::string cmakeCommand =
-        cmStrCat(this->LocalGenerator->ConvertToOutputFormat(
-                   cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
-                 " -E __run_co_compile --lwyu=", targetOutPathReal);
-      real_link_commands.push_back(std::move(cmakeCommand));
+    if (this->UseLWYU) {
+      cmValue lwyuCheck =
+        this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
+      if (lwyuCheck) {
+        std::string cmakeCommand = cmStrCat(
+          this->LocalGenerator->ConvertToOutputFormat(
+            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
+          " -E __run_co_compile --lwyu=");
+        cmakeCommand += this->LocalGenerator->EscapeForShell(*lwyuCheck);
+        cmakeCommand += cmStrCat(" --source=", targetOutPathReal);
+        real_link_commands.push_back(std::move(cmakeCommand));
+      }
     }
 
     std::string launcher;
 
-    cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
-                                                       "RULE_LAUNCH_LINK");
+    cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
+                                                        "RULE_LAUNCH_LINK");
     if (cmNonempty(val)) {
       launcher = cmStrCat(*val, ' ');
     }
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index d0e3837..ace73a7 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -21,7 +21,6 @@
 #include "cmMakefile.h"
 #include "cmOSXBundleGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
@@ -29,6 +28,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
   cmGeneratorTarget* target)
@@ -178,9 +178,9 @@
   this->AddModuleDefinitionFlag(linkLineComputer.get(), extraFlags,
                                 this->GetConfigName());
 
-  if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
-    this->LocalGenerator->AppendFlags(extraFlags, " -Wl,--no-as-needed");
-  }
+  this->UseLWYU = this->LocalGenerator->AppendLWYUFlags(
+    extraFlags, this->GeneratorTarget, linkLanguage);
+
   this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
 }
 
@@ -362,8 +362,8 @@
     vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
 
     std::string launcher;
-    cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
-                                                       "RULE_LAUNCH_LINK");
+    cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
+                                                        "RULE_LAUNCH_LINK");
     if (cmNonempty(val)) {
       launcher = cmStrCat(*val, ' ');
     }
@@ -811,8 +811,8 @@
     }
 
     std::string launcher;
-    cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
-                                                       "RULE_LAUNCH_LINK");
+    cmValue val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
+                                                        "RULE_LAUNCH_LINK");
     if (cmNonempty(val)) {
       launcher = cmStrCat(*val, ' ');
     }
@@ -871,13 +871,18 @@
       // Get the set of commands.
       std::string linkRule = this->GetLinkRule(linkRuleVar);
       cmExpandList(linkRule, real_link_commands);
-      if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
-          (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY)) {
-        std::string cmakeCommand = cmStrCat(
-          this->LocalGenerator->ConvertToOutputFormat(
-            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
-          " -E __run_co_compile --lwyu=", targetOutPathReal);
-        real_link_commands.push_back(std::move(cmakeCommand));
+      if (this->UseLWYU) {
+        cmValue lwyuCheck =
+          this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
+        if (lwyuCheck) {
+          std::string cmakeCommand = cmStrCat(
+            this->LocalGenerator->ConvertToOutputFormat(
+              cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
+            " -E __run_co_compile --lwyu=");
+          cmakeCommand += this->LocalGenerator->EscapeForShell(*lwyuCheck);
+          cmakeCommand += cmStrCat(" --source=", targetOutPathReal);
+          real_link_commands.push_back(std::move(cmakeCommand));
+        }
       }
 
       // Expand placeholders.
diff --git a/Source/cmMakefileProfilingData.cxx b/Source/cmMakefileProfilingData.cxx
index 86188db..337f78b 100644
--- a/Source/cmMakefileProfilingData.cxx
+++ b/Source/cmMakefileProfilingData.cxx
@@ -29,7 +29,7 @@
   }
 
   this->ProfileStream << "[";
-};
+}
 
 cmMakefileProfilingData::~cmMakefileProfilingData() noexcept
 {
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 9458277..5f138ba 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -36,7 +36,6 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
@@ -47,6 +46,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target)
@@ -59,11 +59,13 @@
     this->LocalGenerator->GetGlobalGenerator());
   cmake* cm = this->GlobalGenerator->GetCMakeInstance();
   this->NoRuleMessages = false;
-  if (cmProp ruleStatus = cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
+  if (cmValue ruleStatus =
+        cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
     this->NoRuleMessages = cmIsOff(*ruleStatus);
   }
   switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) {
     case cmPolicies::WARN:
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       this->CMP0113New = false;
       break;
@@ -202,14 +204,14 @@
   };
 
   // Look for additional files registered for cleaning in this directory.
-  if (cmProp prop_value =
+  if (cmValue prop_value =
         this->Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) {
     std::vector<std::string> const files = evaluatedFiles(*prop_value);
     this->CleanFiles.insert(files.begin(), files.end());
   }
 
   // Look for additional files registered for cleaning in this target.
-  if (cmProp prop_value =
+  if (cmValue prop_value =
         this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
     std::vector<std::string> const files = evaluatedFiles(*prop_value);
     // For relative path support
@@ -289,14 +291,13 @@
   this->GeneratorTarget->GetExtraSources(extraSources, this->GetConfigName());
   this->OSXBundleGenerator->GenerateMacOSXContentStatements(
     extraSources, this->MacOSXContentGenerator.get(), this->GetConfigName());
-  cmProp pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+  cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
   std::vector<cmSourceFile const*> externalObjects;
   this->GeneratorTarget->GetExternalObjects(externalObjects,
                                             this->GetConfigName());
   for (cmSourceFile const* sf : externalObjects) {
     auto const& objectFileName = sf->GetFullPath();
-    if (!cmSystemTools::StringEndsWith(objectFileName,
-                                       cmToCStr(pchExtension))) {
+    if (!cmHasSuffix(objectFileName, pchExtension)) {
       this->ExternalObjects.push_back(objectFileName);
     }
   }
@@ -671,12 +672,12 @@
       cmSystemTools::GetFilenameWithoutLastExtension(objectName);
     ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
 
-    cmProp ispcSuffixProp =
+    cmValue ispcSuffixProp =
       this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
     assert(ispcSuffixProp);
 
     std::string directory = this->GeneratorTarget->GetObjectDirectory(config);
-    if (cmProp prop =
+    if (cmValue prop =
           this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) {
       directory =
         cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop);
@@ -688,7 +689,7 @@
 
   // Add flags from source file properties.
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
-  if (cmProp cflags = source.GetProperty(COMPILE_FLAGS)) {
+  if (cmValue cflags = source.GetProperty(COMPILE_FLAGS)) {
     const std::string& evaluatedFlags =
       genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS);
     this->LocalGenerator->AppendFlags(flags, evaluatedFlags);
@@ -698,7 +699,7 @@
   }
 
   const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
-  if (cmProp coptions = source.GetProperty(COMPILE_OPTIONS)) {
+  if (cmValue coptions = source.GetProperty(COMPILE_OPTIONS)) {
     const std::string& evaluatedOptions =
       genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS);
     this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions);
@@ -732,7 +733,7 @@
   std::vector<std::string> includes;
 
   const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
-  if (cmProp cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) {
+  if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) {
     const std::string& evaluatedIncludes =
       genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES);
     this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes,
@@ -748,7 +749,7 @@
 
   // Add source-specific preprocessor definitions.
   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
-  if (cmProp compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) {
+  if (cmValue compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) {
     const std::string& evaluatedDefs =
       genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS);
     this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
@@ -757,7 +758,7 @@
                           << "\n";
   }
   std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
-  if (cmProp config_compile_defs = source.GetProperty(defPropName)) {
+  if (cmValue config_compile_defs = source.GetProperty(defPropName)) {
     const std::string& evaluatedDefs =
       genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS);
     this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
@@ -889,7 +890,7 @@
   bool lang_has_preprocessor =
     ((lang == "C") || (lang == "CXX") || (lang == "OBJC") ||
      (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") ||
-     lang == "ISPC" || lang == "ASM");
+     lang == "ISPC" || lang == "HIP" || lang == "ASM");
   bool const lang_has_assembly = lang_has_preprocessor;
   bool const lang_can_export_cmds = lang_has_preprocessor;
 
@@ -947,11 +948,11 @@
                                this->GetIncludes(lang, this->GetConfigName()));
       }
 
-      cmProp eliminate[] = {
+      cmValue eliminate[] = {
         this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"),
         this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE")
       };
-      for (cmProp el : eliminate) {
+      for (cmValue el : eliminate) {
         if (el) {
           cmSystemTools::ReplaceString(compileCommand, *el, "");
         }
@@ -968,7 +969,7 @@
          lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
          lang == "OBJCXX")) {
       std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
-      cmProp clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
+      cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
       if (cmNonempty(clauncher)) {
         compilerLauncher = *clauncher;
       }
@@ -978,10 +979,10 @@
     if (!compileCommands.empty() &&
         (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
       std::string const tidy_prop = lang + "_CLANG_TIDY";
-      cmProp tidy = this->GeneratorTarget->GetProperty(tidy_prop);
-      cmProp iwyu = nullptr;
-      cmProp cpplint = nullptr;
-      cmProp cppcheck = nullptr;
+      cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
+      cmValue iwyu = nullptr;
+      cmValue cpplint = nullptr;
+      cmValue cppcheck = nullptr;
       if (lang == "C" || lang == "CXX") {
         std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
         iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
@@ -1002,12 +1003,30 @@
         }
         if (cmNonempty(iwyu)) {
           run_iwyu += " --iwyu=";
-          run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
+
+          // Only add --driver-mode if it is not already specified, as adding
+          // it unconditionally might override a user-specified driver-mode
+          if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
+            cmValue p = this->Makefile->GetDefinition(
+              cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
+            std::string driverMode;
+
+            if (cmNonempty(p)) {
+              driverMode = *p;
+            } else {
+              driverMode = lang == "C" ? "gcc" : "g++";
+            }
+
+            run_iwyu += this->LocalGenerator->EscapeForShell(
+              cmStrCat(*iwyu, ";--driver-mode=", driverMode));
+          } else {
+            run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
+          }
         }
         if (cmNonempty(tidy)) {
           run_iwyu += " --tidy=";
-          cmProp p = this->Makefile->GetDefinition("CMAKE_" + lang +
-                                                   "_CLANG_TIDY_DRIVER_MODE");
+          cmValue p = this->Makefile->GetDefinition("CMAKE_" + lang +
+                                                    "_CLANG_TIDY_DRIVER_MODE");
           std::string driverMode;
           if (cmNonempty(p)) {
             driverMode = *p;
@@ -1051,7 +1070,7 @@
 
     std::string launcher;
     {
-      cmProp val = this->LocalGenerator->GetRuleLauncher(
+      cmValue val = this->LocalGenerator->GetRuleLauncher(
         this->GeneratorTarget, "RULE_LAUNCH_COMPILE");
       if (cmNonempty(val)) {
         launcher = cmStrCat(*val, ' ');
@@ -1121,7 +1140,7 @@
 
   // Check for extra outputs created by the compilation.
   std::vector<std::string> outputs(1, relativeObj);
-  if (cmProp extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) {
+  if (cmValue extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) {
     std::string evaluated_outputs = cmGeneratorExpression::Evaluate(
       *extra_outputs_str, this->LocalGenerator, config);
 
@@ -1176,7 +1195,7 @@
 
       std::string preprocessRuleVar =
         cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE");
-      if (cmProp preprocessRule =
+      if (cmValue preprocessRule =
             this->Makefile->GetDefinition(preprocessRuleVar)) {
         std::vector<std::string> preprocessCommands =
           cmExpandedList(*preprocessRule);
@@ -1221,7 +1240,7 @@
 
       std::string assemblyRuleVar =
         cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE");
-      if (cmProp assemblyRule =
+      if (cmValue assemblyRule =
             this->Makefile->GetDefinition(assemblyRuleVar)) {
         std::vector<std::string> assemblyCommands =
           cmExpandedList(*assemblyRule);
@@ -1400,6 +1419,13 @@
     << "set(CMAKE_Fortran_TARGET_MODULE_DIR \""
     << this->GeneratorTarget->GetFortranModuleDirectory(working_dir)
     << "\")\n";
+
+  if (this->GeneratorTarget->IsFortranBuildingInstrinsicModules()) {
+    *this->InfoFileStream
+      << "\n"
+      << "# Fortran compiler is building intrinsic modules.\n"
+      << "set(CMAKE_Fortran_TARGET_BUILDING_INSTRINSIC_MODULES ON) \n";
+  }
   /* clang-format on */
 
   // and now write the rule to use it
@@ -1485,7 +1511,7 @@
   // Create the list of dependencies known at cmake time.  These are
   // shared between the object file and dependency scanning rule.
   depends.push_back(source.GetFullPath());
-  if (cmProp objectDeps = source.GetProperty("OBJECT_DEPENDS")) {
+  if (cmValue objectDeps = source.GetProperty("OBJECT_DEPENDS")) {
     cmExpandList(*objectDeps, depends);
   }
 }
@@ -1724,10 +1750,10 @@
   std::string object;
   const auto& lineContinue = this->GlobalGenerator->LineContinueDirective;
 
-  cmProp pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+  cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
 
   for (std::string const& obj : this->Objects) {
-    if (cmSystemTools::StringEndsWith(obj, cmToCStr(pchExtension))) {
+    if (cmHasSuffix(obj, pchExtension)) {
       continue;
     }
     *this->BuildFileStream << " " << lineContinue;
@@ -1811,13 +1837,13 @@
 void cmMakefileTargetGenerator::WriteObjectsStrings(
   std::vector<std::string>& objStrings, std::string::size_type limit)
 {
-  cmProp pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
+  cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
 
   cmMakefileTargetGeneratorObjectStrings helper(
     objStrings, this->LocalGenerator,
     this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit);
   for (std::string const& obj : this->Objects) {
-    if (cmSystemTools::StringEndsWith(obj, cmToCStr(pchExtension))) {
+    if (cmHasSuffix(obj, pchExtension)) {
       continue;
     }
     helper.Feed(obj);
@@ -1946,7 +1972,7 @@
       cmStrCat("CMAKE_",
                this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
                "_GNUtoMS_RULE");
-    if (cmProp rule = this->Makefile->GetDefinition(ruleVar)) {
+    if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
       linkRule += *rule;
     }
   }
@@ -1995,7 +2021,7 @@
   // Check for an explicit setting one way or the other.
   std::string const responseVar =
     "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS";
-  if (cmProp val = this->Makefile->GetDefinition(responseVar)) {
+  if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
     if (!val->empty()) {
       return cmIsOn(val);
     }
@@ -2034,7 +2060,7 @@
   // Check for an explicit setting one way or the other.
   std::string const responseVar =
     "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES";
-  if (cmProp val = this->Makefile->GetDefinition(responseVar)) {
+  if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
     if (!val->empty()) {
       return cmIsOn(val);
     }
@@ -2109,7 +2135,7 @@
                this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
                "_RESPONSE_FILE_LINK_FLAG");
     std::string responseFlag;
-    if (cmProp p = this->Makefile->GetDefinition(responseFlagVar)) {
+    if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
       responseFlag = *p;
     } else {
       responseFlag = "@";
@@ -2149,7 +2175,7 @@
                this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
                "_RESPONSE_FILE_LINK_FLAG");
     std::string responseFlag;
-    if (cmProp p = this->Makefile->GetDefinition(responseFlagVar)) {
+    if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
       responseFlag = *p;
     } else {
       responseFlag = "@";
@@ -2244,7 +2270,7 @@
   cmd += this->LocalGenerator->ConvertToOutputFormat(
     this->LocalGenerator->MaybeRelativeToCurBinDir(objlist_file),
     cmOutputConverter::SHELL);
-  cmProp nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
+  cmValue nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
   if (cmNonempty(nm_executable)) {
     cmd += " --nm=";
     cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
diff --git a/Source/cmMarkAsAdvancedCommand.cxx b/Source/cmMarkAsAdvancedCommand.cxx
index 45043fa..73e5f33 100644
--- a/Source/cmMarkAsAdvancedCommand.cxx
+++ b/Source/cmMarkAsAdvancedCommand.cxx
@@ -10,6 +10,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 // cmMarkAsAdvancedCommand
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 5c21d1b..517d529 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -32,7 +32,6 @@
 #include "cmNinjaTypes.h"
 #include "cmOSXBundleGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
 #include "cmState.h"
@@ -41,6 +40,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
   cmGeneratorTarget* target)
@@ -267,7 +267,7 @@
     vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS";
 
     std::string launcher;
-    cmProp val = this->GetLocalGenerator()->GetRuleLauncher(
+    cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
     if (cmNonempty(val)) {
       launcher = cmStrCat(*val, ' ');
@@ -390,7 +390,7 @@
 
     // build response file name
     std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
-    cmProp flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+    cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
 
     if (flag) {
       responseFlag = *flag;
@@ -462,7 +462,7 @@
     }
 
     std::string launcher;
-    cmProp val = this->GetLocalGenerator()->GetRuleLauncher(
+    cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
       this->GetGeneratorTarget(), "RULE_LAUNCH_LINK");
     if (cmNonempty(val)) {
       launcher = cmStrCat(*val, ' ');
@@ -569,29 +569,35 @@
     // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
     std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
       this->TargetLinkLanguage(config), config);
-    cmProp linkCmd = mf->GetDefinition(linkCmdVar);
+    cmValue linkCmd = mf->GetDefinition(linkCmdVar);
     if (linkCmd) {
       std::string linkCmdStr = *linkCmd;
       if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) {
         std::string ruleVar =
           cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config),
                    "_GNUtoMS_RULE");
-        if (cmProp rule = this->Makefile->GetDefinition(ruleVar)) {
+        if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
           linkCmdStr += *rule;
         }
       }
       cmExpandList(linkCmdStr, linkCmds);
-      if (this->GetGeneratorTarget()->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
-        std::string cmakeCommand = cmStrCat(
-          this->GetLocalGenerator()->ConvertToOutputFormat(
-            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
-          " -E __run_co_compile --lwyu=");
-        cmGeneratorTarget& gt = *this->GetGeneratorTarget();
-        std::string targetOutputReal = this->ConvertToNinjaPath(
-          gt.GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
-                         /*realname=*/true));
-        cmakeCommand += targetOutputReal;
-        linkCmds.push_back(std::move(cmakeCommand));
+      if (this->UseLWYU) {
+        cmValue lwyuCheck = mf->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
+        if (lwyuCheck) {
+          std::string cmakeCommand = cmStrCat(
+            this->GetLocalGenerator()->ConvertToOutputFormat(
+              cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
+            " -E __run_co_compile --lwyu=");
+          cmakeCommand +=
+            this->GetLocalGenerator()->EscapeForShell(*lwyuCheck);
+
+          std::string targetOutputReal =
+            this->ConvertToNinjaPath(this->GetGeneratorTarget()->GetFullPath(
+              config, cmStateEnums::RuntimeBinaryArtifact,
+              /*realname=*/true));
+          cmakeCommand += cmStrCat(" --source=", targetOutputReal);
+          linkCmds.push_back(std::move(cmakeCommand));
+        }
       }
       return linkCmds;
     }
@@ -896,7 +902,7 @@
     const std::string impLibPath = localGen.ConvertToOutputFormat(
       targetOutputImplib, cmOutputConverter::SHELL);
     vars["TARGET_IMPLIB"] = impLibPath;
-    this->EnsureParentDirectoryExists(impLibPath);
+    this->EnsureParentDirectoryExists(targetOutputImplib);
   }
 
   const std::string objPath =
@@ -1038,7 +1044,7 @@
     }();
 
     vars["SWIFT_MODULE_NAME"] = [gt]() -> std::string {
-      if (cmProp name = gt->GetProperty("Swift_MODULE_NAME")) {
+      if (cmValue name = gt->GetProperty("Swift_MODULE_NAME")) {
         return *name;
       }
       return gt->GetName();
@@ -1047,13 +1053,13 @@
     vars["SWIFT_MODULE"] = [this](const std::string& module) -> std::string {
       std::string directory =
         this->GetLocalGenerator()->GetCurrentBinaryDirectory();
-      if (cmProp prop = this->GetGeneratorTarget()->GetProperty(
+      if (cmValue prop = this->GetGeneratorTarget()->GetProperty(
             "Swift_MODULE_DIRECTORY")) {
         directory = *prop;
       }
 
       std::string name = module + ".swiftmodule";
-      if (cmProp prop =
+      if (cmValue prop =
             this->GetGeneratorTarget()->GetProperty("Swift_MODULE")) {
         name = *prop;
       }
@@ -1156,12 +1162,11 @@
 
   this->AddModuleDefinitionFlag(linkLineComputer.get(), vars["LINK_FLAGS"],
                                 config);
-  if (gt->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
-      (gt->GetType() == cmStateEnums::TargetType::EXECUTABLE ||
-       gt->GetType() == cmStateEnums::TargetType::SHARED_LIBRARY ||
-       gt->GetType() == cmStateEnums::TargetType::MODULE_LIBRARY)) {
-    vars["LINK_FLAGS"] += " -Wl,--no-as-needed";
-  }
+
+  this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
+    vars["LINK_FLAGS"], this->GetGeneratorTarget(),
+    this->TargetLinkLanguage(config));
+
   vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
 
   vars["MANIFESTS"] = this->GetManifests(config);
@@ -1206,7 +1211,7 @@
     const std::string impLibPath = localGen.ConvertToOutputFormat(
       targetOutputImplib, cmOutputConverter::SHELL);
     vars["TARGET_IMPLIB"] = impLibPath;
-    this->EnsureParentDirectoryExists(impLibPath);
+    this->EnsureParentDirectoryExists(targetOutputImplib);
     if (gt->HasImportLibrary(config)) {
       // Some linkers may update a binary without touching its import lib.
       byproducts.ExplicitOuts.emplace_back(targetOutputImplib);
@@ -1226,7 +1231,7 @@
     gt->GetFullNameComponents(prefix, base, suffix, config);
     std::string dbg_suffix = ".dbg";
     // TODO: Where to document?
-    if (cmProp d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
+    if (cmValue d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
       dbg_suffix = *d;
     }
     vars["TARGET_PDB"] = base + suffix + dbg_suffix;
@@ -1292,7 +1297,7 @@
     cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
       obj_list_file, cmOutputConverter::SHELL);
 
-    cmProp nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM");
+    cmValue nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM");
     if (cmNonempty(nm_executable)) {
       cmd += " --nm=";
       cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
@@ -1346,7 +1351,7 @@
   // build response file name
   std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
 
-  cmProp flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+  cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
 
   bool const lang_supports_response =
     !(this->TargetLinkLanguage(config) == "RC" ||
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 609848b..57657b1 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -31,7 +31,6 @@
 #include "cmNinjaNormalTargetGenerator.h"
 #include "cmNinjaUtilityTargetGenerator.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
@@ -40,6 +39,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New(
@@ -224,13 +224,13 @@
     this->LocalGenerator, config, this->GeneratorTarget, language);
 
   const std::string COMPILE_FLAGS("COMPILE_FLAGS");
-  if (cmProp cflags = source->GetProperty(COMPILE_FLAGS)) {
+  if (cmValue cflags = source->GetProperty(COMPILE_FLAGS)) {
     this->LocalGenerator->AppendFlags(
       flags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
   }
 
   const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
-  if (cmProp coptions = source->GetProperty(COMPILE_OPTIONS)) {
+  if (cmValue coptions = source->GetProperty(COMPILE_OPTIONS)) {
     this->LocalGenerator->AppendCompileOptions(
       flags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
   }
@@ -290,14 +290,14 @@
   }
 
   const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
-  if (cmProp compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) {
+  if (cmValue compile_defs = source->GetProperty(COMPILE_DEFINITIONS)) {
     this->LocalGenerator->AppendDefines(
       defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS));
   }
 
   std::string defPropName =
     cmStrCat("COMPILE_DEFINITIONS_", cmSystemTools::UpperCase(config));
-  if (cmProp config_compile_defs = source->GetProperty(defPropName)) {
+  if (cmValue config_compile_defs = source->GetProperty(defPropName)) {
     this->LocalGenerator->AppendDefines(
       defines,
       genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
@@ -318,7 +318,7 @@
     this->LocalGenerator, config, this->GeneratorTarget, language);
 
   const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
-  if (cmProp cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) {
+  if (cmValue cincludes = source->GetProperty(INCLUDE_DIRECTORIES)) {
     this->LocalGenerator->AppendIncludeDirectories(
       includes, genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES),
       *source);
@@ -638,7 +638,7 @@
     cmLocalGenerator::SHELL);
 
   std::string launcher;
-  cmProp val = this->GetLocalGenerator()->GetRuleLauncher(
+  cmValue val = this->GetLocalGenerator()->GetRuleLauncher(
     this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE");
   if (cmNonempty(val)) {
     launcher = cmStrCat(*val, ' ');
@@ -771,11 +771,14 @@
     if (!mf->GetIsSourceFileTryCompile()) {
       rule.DepType = "gcc";
       rule.DepFile = "$DEP_FILE";
-      cmProp d = mf->GetDefinition("CMAKE_C_COMPILER");
+      cmValue d = mf->GetDefinition("CMAKE_C_COMPILER");
       const std::string cl =
         d ? *d : mf->GetSafeDefinition("CMAKE_CXX_COMPILER");
-      cldeps = cmStrCat('"', cmSystemTools::GetCMClDepsCommand(), "\" ", lang,
-                        ' ', vars.Source, " $DEP_FILE $out \"",
+      std::string cmcldepsPath;
+      cmSystemTools::GetShortPath(cmSystemTools::GetCMClDepsCommand(),
+                                  cmcldepsPath);
+      cldeps = cmStrCat(cmcldepsPath, ' ', lang, ' ', vars.Source,
+                        " $DEP_FILE $out \"",
                         mf->GetSafeDefinition("CMAKE_CL_SHOWINCLUDES_PREFIX"),
                         "\" \"", cl, "\" ");
     }
@@ -840,7 +843,7 @@
        lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
        lang == "OBJCXX")) {
     std::string const clauncher_prop = cmStrCat(lang, "_COMPILER_LAUNCHER");
-    cmProp clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
+    cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
     if (cmNonempty(clauncher)) {
       compilerLauncher = *clauncher;
     }
@@ -850,10 +853,10 @@
   if (!compileCmds.empty() &&
       (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
     std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY");
-    cmProp tidy = this->GeneratorTarget->GetProperty(tidy_prop);
-    cmProp iwyu = nullptr;
-    cmProp cpplint = nullptr;
-    cmProp cppcheck = nullptr;
+    cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
+    cmValue iwyu = nullptr;
+    cmValue cpplint = nullptr;
+    cmValue cppcheck = nullptr;
     if (lang == "C" || lang == "CXX") {
       std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE");
       iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
@@ -874,12 +877,30 @@
         compilerLauncher.clear();
       }
       if (cmNonempty(iwyu)) {
-        run_iwyu += cmStrCat(" --iwyu=",
-                             this->GetLocalGenerator()->EscapeForShell(*iwyu));
+        run_iwyu += " --iwyu=";
+
+        // Only add --driver-mode if it is not already specified, as adding
+        // it unconditionally might override a user-specified driver-mode
+        if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
+          cmValue p = this->Makefile->GetDefinition(
+            cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
+          std::string driverMode;
+
+          if (cmNonempty(p)) {
+            driverMode = *p;
+          } else {
+            driverMode = lang == "C" ? "gcc" : "g++";
+          }
+
+          run_iwyu += this->LocalGenerator->EscapeForShell(
+            cmStrCat(*iwyu, ";--driver-mode=", driverMode));
+        } else {
+          run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
+        }
       }
       if (cmNonempty(tidy)) {
         run_iwyu += " --tidy=";
-        cmProp p = this->Makefile->GetDefinition(
+        cmValue p = this->Makefile->GetDefinition(
           cmStrCat("CMAKE_", lang, "_CLANG_TIDY_DRIVER_MODE"));
         std::string driverMode;
         if (cmNonempty(p)) {
@@ -986,7 +1007,7 @@
       config);
   }
   if (firstForConfig) {
-    cmProp pchExtension =
+    cmValue pchExtension =
       this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
 
     std::vector<cmSourceFile const*> externalObjects;
@@ -994,8 +1015,7 @@
     for (cmSourceFile const* sf : externalObjects) {
       auto objectFileName = this->GetGlobalGenerator()->ExpandCFGIntDir(
         this->ConvertToNinjaPath(sf->GetFullPath()), config);
-      if (!cmSystemTools::StringEndsWith(objectFileName,
-                                         cmToCStr(pchExtension))) {
+      if (!cmHasSuffix(objectFileName, pchExtension)) {
         this->Configs[config].Objects.push_back(objectFileName);
       }
     }
@@ -1092,7 +1112,7 @@
                "output-file-map.json");
     std::string const targetSwiftDepsPath = [this, config]() -> std::string {
       cmGeneratorTarget const* target = this->GeneratorTarget;
-      if (cmProp name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
+      if (cmValue name = target->GetProperty("Swift_DEPENDENCIES_FILE")) {
         return *name;
       }
       return this->ConvertToNinjaPath(
@@ -1216,7 +1236,7 @@
   // build response file name
   std::string cmakeLinkVar = cmStrCat(cmakeVarLang, "_RESPONSE_FILE_FLAG");
 
-  cmProp flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
+  cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
 
   bool const lang_supports_response =
     !(language == "RC" || (language == "CUDA" && !flag));
@@ -1258,10 +1278,9 @@
 
   objBuild.Outputs.push_back(objectFileName);
   if (firstForConfig) {
-    cmProp pchExtension =
+    cmValue pchExtension =
       this->GetMakefile()->GetDefinition("CMAKE_PCH_EXTENSION");
-    if (!cmSystemTools::StringEndsWith(objectFileName,
-                                       cmToCStr(pchExtension))) {
+    if (!cmHasSuffix(objectFileName, pchExtension)) {
       // Add this object to the list of object files.
       this->Configs[config].Objects.push_back(objectFileName);
     }
@@ -1299,7 +1318,7 @@
     }
   }
 
-  if (cmProp objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
+  if (cmValue objectDeps = source->GetProperty("OBJECT_DEPENDS")) {
     std::vector<std::string> objDepList = cmExpandedList(*objectDeps);
     std::copy(objDepList.begin(), objDepList.end(),
               std::back_inserter(depList));
@@ -1445,13 +1464,13 @@
       cmSystemTools::GetFilenameWithoutLastExtension(objectName);
     ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
 
-    cmProp ispcSuffixProp =
+    cmValue ispcSuffixProp =
       this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
     assert(ispcSuffixProp);
 
     std::string ispcHeaderDirectory =
       this->GeneratorTarget->GetObjectDirectory(config);
-    if (cmProp prop =
+    if (cmValue prop =
           this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) {
       ispcHeaderDirectory =
         cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop);
@@ -1502,7 +1521,7 @@
                                            objBuild, commandLineLengthLimit);
   }
 
-  if (cmProp objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
+  if (cmValue objectOutputs = source->GetProperty("OBJECT_OUTPUTS")) {
     std::string evaluatedObjectOutputs = cmGeneratorExpression::Evaluate(
       *objectOutputs, this->LocalGenerator, config);
 
@@ -1582,13 +1601,13 @@
   std::string const objectFilePath =
     this->ConvertToNinjaPath(this->GetObjectFilePath(source, config));
   std::string const swiftDepsPath = [source, objectFilePath]() -> std::string {
-    if (cmProp name = source->GetProperty("Swift_DEPENDENCIES_FILE")) {
+    if (cmValue name = source->GetProperty("Swift_DEPENDENCIES_FILE")) {
       return *name;
     }
     return cmStrCat(objectFilePath, ".swiftdeps");
   }();
   std::string const swiftDiaPath = [source, objectFilePath]() -> std::string {
-    if (cmProp name = source->GetProperty("Swift_DIAGNOSTICS_FILE")) {
+    if (cmValue name = source->GetProperty("Swift_DIAGNOSTICS_FILE")) {
       return *name;
     }
     return cmStrCat(objectFilePath, ".dia");
@@ -1696,7 +1715,7 @@
 
 void cmNinjaTargetGenerator::AdditionalCleanFiles(const std::string& config)
 {
-  if (cmProp prop_value =
+  if (cmValue prop_value =
         this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
     cmLocalNinjaGenerator* lg = this->LocalGenerator;
     std::vector<std::string> cleanFiles;
@@ -1730,7 +1749,7 @@
   } else {
     cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
     std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory();
-    // Also ensures their is a trailing slash.
+    // Also ensures there is a trailing slash.
     gg->StripNinjaOutputPathPrefixAsSuffix(fullPath);
     fullPath += path;
     cmSystemTools::MakeDirectory(fullPath);
@@ -1786,7 +1805,7 @@
   const std::string& pool_property, cmGeneratorTarget* target,
   cmNinjaVars& vars)
 {
-  cmProp pool = target->GetProperty(pool_property);
+  cmValue pool = target->GetProperty(pool_property);
   if (pool) {
     vars["pool"] = *pool;
   }
diff --git a/Source/cmNinjaUtilityTargetGenerator.cxx b/Source/cmNinjaUtilityTargetGenerator.cxx
index 1f5a7ff..15197ba 100644
--- a/Source/cmNinjaUtilityTargetGenerator.cxx
+++ b/Source/cmNinjaUtilityTargetGenerator.cxx
@@ -18,12 +18,12 @@
 #include "cmLocalNinjaGenerator.h"
 #include "cmNinjaTypes.h"
 #include "cmOutputConverter.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 cmNinjaUtilityTargetGenerator::cmNinjaUtilityTargetGenerator(
   cmGeneratorTarget* target)
@@ -141,7 +141,7 @@
     std::string command = lg->BuildCommandLine(
       commands, config, fileConfig, "utility", this->GeneratorTarget);
     std::string desc;
-    cmProp echoStr = genTarget->GetProperty("EchoString");
+    cmValue echoStr = genTarget->GetProperty("EchoString");
     if (echoStr) {
       desc = *echoStr;
     } else {
diff --git a/Source/cmOptionCommand.cxx b/Source/cmOptionCommand.cxx
index bae67e0..ec54fc5 100644
--- a/Source/cmOptionCommand.cxx
+++ b/Source/cmOptionCommand.cxx
@@ -6,11 +6,11 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 // cmOptionCommand
 bool cmOptionCommand(std::vector<std::string> const& args,
@@ -29,12 +29,12 @@
   {
     auto policyStatus =
       status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0077);
-    const auto* existsBeforeSet =
+    const auto& existsBeforeSet =
       status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]);
     switch (policyStatus) {
       case cmPolicies::WARN:
         checkAndWarn = (existsBeforeSet != nullptr);
-        break;
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         // OLD behavior does not warn.
         break;
@@ -53,7 +53,7 @@
   // See if a cache variable with this name already exists
   // If so just make sure the doc state is correct
   cmState* state = status.GetMakefile().GetState();
-  cmProp existingValue = state->GetCacheEntryValue(args[0]);
+  cmValue existingValue = state->GetCacheEntryValue(args[0]);
   if (existingValue &&
       (state->GetCacheEntryType(args[0]) != cmStateEnums::UNINITIALIZED)) {
     state->SetCacheEntryProperty(args[0], "HELPSTRING", args[1]);
@@ -77,7 +77,7 @@
   }
 
   if (checkAndWarn) {
-    const auto* existsAfterSet =
+    const auto& existsAfterSet =
       status.GetMakefile().GetStateSnapshot().GetDefinition(args[0]);
     if (!existsAfterSet) {
       status.GetMakefile().IssueMessage(
diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx
index 840fdb9..2b785e1 100644
--- a/Source/cmOutputConverter.cxx
+++ b/Source/cmOutputConverter.cxx
@@ -12,13 +12,14 @@
 #include "cmStateDirectory.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 namespace {
 bool PathEqOrSubDir(std::string const& a, std::string const& b)
 {
   return (cmSystemTools::ComparePath(a, b) ||
           cmSystemTools::IsSubDirectory(a, b));
-};
+}
 }
 
 cmOutputConverter::cmOutputConverter(cmStateSnapshot const& snapshot)
diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx
index d589614..ad276d2 100644
--- a/Source/cmOutputRequiredFilesCommand.cxx
+++ b/Source/cmOutputRequiredFilesCommand.cxx
@@ -15,11 +15,11 @@
 #include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmSourceFile.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 namespace {
 /** \class cmDependInformation
@@ -118,7 +118,7 @@
     std::set<std::string> uniqueIncludes;
     std::vector<std::string> orderedAndUniqueIncludes;
     for (auto const& target : this->Makefile->GetTargets()) {
-      cmProp incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
+      cmValue incDirProp = target.second.GetProperty("INCLUDE_DIRECTORIES");
       if (!incDirProp) {
         continue;
       }
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index 2465069..9f3fd00 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -13,10 +13,10 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 static std::string EscapeArg(const std::string& arg)
 {
@@ -196,7 +196,7 @@
     for (unsigned long i = argvStart; i < count; ++i) {
       std::ostringstream argName;
       argName << "ARGV" << i;
-      cmProp arg = status.GetMakefile().GetDefinition(argName.str());
+      cmValue arg = status.GetMakefile().GetDefinition(argName.str());
       if (!arg) {
         status.GetMakefile().IssueMessage(MessageType::FATAL_ERROR,
                                           "PARSE_ARGV called with " +
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index 01e8c04..23000fa 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -15,6 +15,7 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 
 static bool stringToId(const char* input, cmPolicies::PolicyID& pid)
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index f7c0d25..ce04117 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -379,7 +379,13 @@
          3, 21, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0126,                                                     \
          "set(CACHE) does not remove a normal variable of the same name.", 3, \
-         21, 0, cmPolicies::WARN)
+         21, 0, cmPolicies::WARN)                                             \
+  SELECT(POLICY, CMP0127,                                                     \
+         "cmake_dependent_option() supports full Condition Syntax.", 3, 22,   \
+         0, cmPolicies::WARN)                                                 \
+  SELECT(POLICY, CMP0128,                                                     \
+         "Selection of language standard and extension flags improved.", 3,   \
+         22, 0, cmPolicies::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/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index 6950c19..20fcdbe 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -15,10 +15,10 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 static bool IncludeByVariable(cmExecutionStatus& status,
                               const std::string& variable);
@@ -308,7 +308,7 @@
     }
     std::string vw;
     for (std::string const& i : vv) {
-      cmProp v = mf.GetDefinition(i);
+      cmValue v = mf.GetDefinition(i);
       if (cmNonempty(v)) {
         if (cmp0048 == cmPolicies::WARN) {
           if (!injectedProjectCommand) {
@@ -358,7 +358,7 @@
                               const std::string& variable)
 {
   cmMakefile& mf = status.GetMakefile();
-  cmProp include = mf.GetDefinition(variable);
+  cmValue include = mf.GetDefinition(variable);
   if (!include) {
     return true;
   }
diff --git a/Source/cmProperty.h b/Source/cmProperty.h
index 1e03c3f..0781109 100644
--- a/Source/cmProperty.h
+++ b/Source/cmProperty.h
@@ -4,8 +4,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <string>
-
 class cmProperty
 {
 public:
@@ -22,15 +20,3 @@
     INSTALL
   };
 };
-
-using cmProp = const std::string*;
-
-inline const char* cmToCStr(cmProp p)
-{
-  return p ? p->c_str() : nullptr;
-}
-
-inline const char* cmToCStrSafe(cmProp p)
-{
-  return p ? p->c_str() : "";
-}
diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx
index 06e151a..b15000f 100644
--- a/Source/cmPropertyMap.cxx
+++ b/Source/cmPropertyMap.cxx
@@ -19,6 +19,15 @@
 
   this->Map_[name] = value;
 }
+void cmPropertyMap::SetProperty(const std::string& name, cmValue value)
+{
+  if (!value) {
+    this->Map_.erase(name);
+    return;
+  }
+
+  this->Map_[name] = *value;
+}
 
 void cmPropertyMap::AppendProperty(const std::string& name,
                                    const std::string& value, bool asString)
@@ -42,11 +51,11 @@
   this->Map_.erase(name);
 }
 
-cmProp cmPropertyMap::GetPropertyValue(const std::string& name) const
+cmValue cmPropertyMap::GetPropertyValue(const std::string& name) const
 {
   auto it = this->Map_.find(name);
   if (it != this->Map_.end()) {
-    return &it->second;
+    return cmValue(it->second);
   }
   return nullptr;
 }
diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h
index cda585a..f50b65e 100644
--- a/Source/cmPropertyMap.h
+++ b/Source/cmPropertyMap.h
@@ -9,7 +9,7 @@
 #include <utility>
 #include <vector>
 
-#include "cmProperty.h"
+#include "cmValue.h"
 
 /** \class cmPropertyMap
  * \brief String property map.
@@ -26,13 +26,18 @@
 
   //! Set the property value
   void SetProperty(const std::string& name, const char* value);
+  void SetProperty(const std::string& name, cmValue value);
+  void SetProperty(const std::string& name, const std::string& value)
+  {
+    this->SetProperty(name, cmValue(value));
+  }
 
   //! Append to the property value
   void AppendProperty(const std::string& name, const std::string& value,
                       bool asString = false);
 
   //! Get the property value
-  cmProp GetPropertyValue(const std::string& name) const;
+  cmValue GetPropertyValue(const std::string& name) const;
 
   //! Remove the property @a name from the map
   void RemoveProperty(const std::string& name);
diff --git a/Source/cmQTWrapCPPCommand.cxx b/Source/cmQTWrapCPPCommand.cxx
index e9670f9..ca0b259 100644
--- a/Source/cmQTWrapCPPCommand.cxx
+++ b/Source/cmQTWrapCPPCommand.cxx
@@ -6,7 +6,6 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmStringAlgorithms.h"
@@ -41,7 +40,7 @@
         cmStrCat(mf.GetCurrentBinaryDirectory(), "/moc_", srcName, ".cxx");
       cmSourceFile* sf = mf.GetOrCreateSource(newName, true);
       if (curr) {
-        sf->SetProperty("ABSTRACT", cmToCStr(curr->GetProperty("ABSTRACT")));
+        sf->SetProperty("ABSTRACT", curr->GetProperty("ABSTRACT"));
       }
 
       // Compute the name of the header from which to generate the file.
diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx
index 57fcd2d..9584e5c 100644
--- a/Source/cmQtAutoGen.cxx
+++ b/Source/cmQtAutoGen.cxx
@@ -24,7 +24,8 @@
 /// @arg valueOpts list of options that accept a value
 void MergeOptions(std::vector<std::string>& baseOpts,
                   std::vector<std::string> const& newOpts,
-                  std::initializer_list<cm::string_view> valueOpts, bool isQt5)
+                  std::initializer_list<cm::string_view> valueOpts,
+                  bool isQt5OrLater)
 {
   if (newOpts.empty()) {
     return;
@@ -47,7 +48,7 @@
           auto oit = newOpt.begin();
           if (*oit == '-') {
             ++oit;
-            if (isQt5 && (*oit == '-')) {
+            if (isQt5OrLater && (*oit == '-')) {
               ++oit;
             }
             optName.assign(oit, newOpt.end());
@@ -204,24 +205,24 @@
 
 void cmQtAutoGen::UicMergeOptions(std::vector<std::string>& baseOpts,
                                   std::vector<std::string> const& newOpts,
-                                  bool isQt5)
+                                  bool isQt5OrLater)
 {
   static std::initializer_list<cm::string_view> const valueOpts = {
     "tr",      "translate", "postfix", "generator",
     "include", // Since Qt 5.3
     "g"
   };
-  MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
+  MergeOptions(baseOpts, newOpts, valueOpts, isQt5OrLater);
 }
 
 void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
                                   std::vector<std::string> const& newOpts,
-                                  bool isQt5)
+                                  bool isQt5OrLater)
 {
   static std::initializer_list<cm::string_view> const valueOpts = {
     "name", "root", "compress", "threshold"
   };
-  MergeOptions(baseOpts, newOpts, valueOpts, isQt5);
+  MergeOptions(baseOpts, newOpts, valueOpts, isQt5OrLater);
 }
 
 static void RccListParseContent(std::string const& content,
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index 466a954..5a23ae9 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -93,12 +93,12 @@
   /// @brief Merges newOpts into baseOpts
   static void UicMergeOptions(std::vector<std::string>& baseOpts,
                               std::vector<std::string> const& newOpts,
-                              bool isQt5);
+                              bool isQt5OrLater);
 
   /// @brief Merges newOpts into baseOpts
   static void RccMergeOptions(std::vector<std::string>& baseOpts,
                               std::vector<std::string> const& newOpts,
-                              bool isQt5);
+                              bool isQt5OrLater);
 
   /** @class RccLister
    * @brief Lists files in qrc resource files
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index 77fe87e..f9e889a 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -15,7 +15,6 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmProcessOutput.h"
-#include "cmProperty.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenInitializer.h"
 #include "cmState.h"
@@ -23,6 +22,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 cmQtAutoGenGlobalInitializer::Keywords::Keywords()
   : AUTOMOC("AUTOMOC")
@@ -183,10 +183,10 @@
 
     // Set FOLDER property in the target
     {
-      cmProp folder =
+      cmValue folder =
         makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
       if (folder) {
-        target->SetProperty("FOLDER", *folder);
+        target->SetProperty("FOLDER", folder);
       }
     }
   }
diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h
index afcb4a2..3de5c1a 100644
--- a/Source/cmQtAutoGenGlobalInitializer.h
+++ b/Source/cmQtAutoGenGlobalInitializer.h
@@ -49,7 +49,7 @@
     std::vector<std::unique_ptr<cmLocalGenerator>> const& localGenerators);
   ~cmQtAutoGenGlobalInitializer();
 
-  Keywords const& kw() const { return this->Keywords_; };
+  Keywords const& kw() const { return this->Keywords_; }
 
   bool generate();
 
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index 4e3c584..c49cafe 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -24,6 +24,7 @@
 
 #include "cmsys/SystemInformation.hxx"
 
+#include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
 #include "cmGeneratedFileStream.h"
@@ -36,7 +37,6 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenGlobalInitializer.h"
 #include "cmSourceFile.h"
@@ -47,6 +47,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -355,7 +356,7 @@
 
   // Targets FOLDER
   {
-    cmProp folder =
+    cmValue folder =
       this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
     if (!folder) {
       folder = this->Makefile->GetState()->GetGlobalProperty(
@@ -522,6 +523,8 @@
       // Filters
       cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"),
                    this->Moc.MacroNames);
+      this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
+                                 this->Moc.MacroNames.end());
       {
         auto filterList = cmExpandedList(
           this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS"));
@@ -1796,7 +1799,7 @@
         cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP"
       };
       for (std::string const& prop : props) {
-        cmProp propName = this->Makefile->GetState()->GetGlobalProperty(prop);
+        cmValue propName = this->Makefile->GetState()->GetGlobalProperty(prop);
         if (cmNonempty(propName)) {
           groupName = *propName;
           property = prop;
@@ -1926,7 +1929,7 @@
     }
     return 0u;
   };
-  auto toUInt2 = [](cmProp input) -> unsigned int {
+  auto toUInt2 = [](cmValue input) -> unsigned int {
     unsigned long tmp = 0;
     if (input && cmStrToULong(*input, &tmp)) {
       return static_cast<unsigned int>(tmp);
@@ -1954,8 +1957,8 @@
     knownQtVersions.reserve(keys.size() * 2);
 
     // Adds a version to the result (nullptr safe)
-    auto addVersion = [&knownQtVersions, &toUInt2](cmProp major,
-                                                   cmProp minor) {
+    auto addVersion = [&knownQtVersions, &toUInt2](cmValue major,
+                                                   cmValue minor) {
       cmQtAutoGen::IntegerVersion ver(toUInt2(major), toUInt2(minor));
       if (ver.Major != 0) {
         knownQtVersions.emplace_back(ver);
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index e76817b..603c537 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -95,7 +95,9 @@
 
     GenVarsT(GenT gen)
       : Gen(gen)
-      , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){};
+      , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen))
+    {
+    }
   };
 
   /** @param mocExecutable The file path to the moc executable. Will be used as
@@ -209,7 +211,9 @@
   struct MocT : public GenVarsT
   {
     MocT()
-      : GenVarsT(GenT::MOC){};
+      : GenVarsT(GenT::MOC)
+    {
+    }
 
     bool RelaxedMode = false;
     bool PathPrefix = false;
@@ -237,7 +241,9 @@
     using UiFileT = std::pair<std::string, std::vector<std::string>>;
 
     UicT()
-      : GenVarsT(GenT::UIC){};
+      : GenVarsT(GenT::UIC)
+    {
+    }
 
     std::set<std::string> SkipUi;
     std::vector<std::string> UiFilesNoOptions;
@@ -252,7 +258,9 @@
   struct RccT : public GenVarsT
   {
     RccT()
-      : GenVarsT(GenT::RCC){};
+      : GenVarsT(GenT::RCC)
+    {
+    }
 
     bool GlobalTarget = false;
     std::vector<Qrc> Qrcs;
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index 568926e..e5c7bda 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -9,6 +9,7 @@
 #include "cmQtAutoGen.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmQtAutoGenerator::Logger::Logger()
 {
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index 2753fd5..4ed728e 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -310,7 +310,7 @@
     cmQtAutoMocUicT* Gen() const
     {
       return static_cast<cmQtAutoMocUicT*>(this->UserData());
-    };
+    }
 
     // -- Accessors. Only valid during Process() call!
     Logger const& Log() const { return this->Gen()->Log(); }
@@ -346,7 +346,7 @@
       : JobT(true)
     {
     }
-    void Process() override{};
+    void Process() override {}
   };
 
   /** Generate moc_predefs.h.  */
@@ -2083,7 +2083,7 @@
     auto optionIt = this->UicConst().UiFiles.find(sourceFile);
     if (optionIt != this->UicConst().UiFiles.end()) {
       UicMergeOptions(allOpts, optionIt->second.Options,
-                      (this->BaseConst().QtVersion.Major == 5));
+                      (this->BaseConst().QtVersion.Major >= 5));
     }
     cm::append(cmd, allOpts);
   }
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index fce6e80..1e4dedd 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -35,6 +35,7 @@
   , TocTreeDirective("^.. toctree::[ \t]*(.*)$")
   , ProductionListDirective("^.. productionlist::[ \t]*(.*)$")
   , NoteDirective("^.. note::[ \t]*(.*)$")
+  , VersionDirective("^.. version(added|changed)::[ \t]*(.*)$")
   , ModuleRST(R"(^#\[(=*)\[\.rst:$)")
   , CMakeRole("(:cmake)?:("
               "command|cpack_gen|generator|genex|"
@@ -209,6 +210,10 @@
     } else if (this->NoteDirective.find(line)) {
       // Output note directives and their content normally.
       this->NormalLine(line);
+    } else if (this->VersionDirective.find(line)) {
+      // Output versionadded and versionchanged directives and their content
+      // normally.
+      this->NormalLine(line);
     }
   }
   // An explicit markup start followed nothing but whitespace and a
diff --git a/Source/cmRST.h b/Source/cmRST.h
index 17cdfe8..156b20a 100644
--- a/Source/cmRST.h
+++ b/Source/cmRST.h
@@ -83,6 +83,7 @@
   cmsys::RegularExpression TocTreeDirective;
   cmsys::RegularExpression ProductionListDirective;
   cmsys::RegularExpression NoteDirective;
+  cmsys::RegularExpression VersionDirective;
   cmsys::RegularExpression ModuleRST;
   cmsys::RegularExpression CMakeRole;
   cmsys::RegularExpression InlineLink;
diff --git a/Source/cmRemoveCommand.cxx b/Source/cmRemoveCommand.cxx
index 1345588..8af13ae 100644
--- a/Source/cmRemoveCommand.cxx
+++ b/Source/cmRemoveCommand.cxx
@@ -4,8 +4,8 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 // cmRemoveCommand
 bool cmRemoveCommand(std::vector<std::string> const& args,
@@ -17,7 +17,7 @@
 
   std::string const& variable = args[0]; // VAR is always first
   // get the old value
-  cmProp cacheValue = status.GetMakefile().GetDefinition(variable);
+  cmValue cacheValue = status.GetMakefile().GetDefinition(variable);
 
   // if there is no old value then return
   if (!cacheValue) {
diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx
index a58be62..1bb459c 100644
--- a/Source/cmSearchPath.cxx
+++ b/Source/cmSearchPath.cxx
@@ -8,9 +8,9 @@
 
 #include "cmFindCommon.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 cmSearchPath::cmSearchPath(cmFindCommon* findCmd)
   : FC(findCmd)
@@ -78,7 +78,7 @@
   assert(this->FC != nullptr);
 
   // Get a path from a CMake variable.
-  if (cmProp value = this->FC->Makefile->GetDefinition(variable)) {
+  if (cmValue value = this->FC->Makefile->GetDefinition(variable)) {
     std::vector<std::string> expanded = cmExpandedList(*value);
 
     for (std::string const& p : expanded) {
@@ -102,7 +102,7 @@
   assert(this->FC != nullptr);
 
   // Get a path from a CMake variable.
-  if (cmProp value = this->FC->Makefile->GetDefinition(variable)) {
+  if (cmValue value = this->FC->Makefile->GetDefinition(variable)) {
     std::vector<std::string> expanded = cmExpandedList(*value);
 
     this->AddPrefixPaths(
@@ -179,7 +179,7 @@
       dir += "/";
     }
     if (subdir == "include" || subdir == "lib") {
-      cmProp arch =
+      cmValue arch =
         this->FC->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE");
       if (cmNonempty(arch)) {
         if (this->FC->Makefile->IsDefinitionSet("CMAKE_SYSROOT") &&
diff --git a/Source/cmSeparateArgumentsCommand.cxx b/Source/cmSeparateArgumentsCommand.cxx
index 52b1a44..17285e7 100644
--- a/Source/cmSeparateArgumentsCommand.cxx
+++ b/Source/cmSeparateArgumentsCommand.cxx
@@ -4,15 +4,16 @@
 
 #include <algorithm>
 
+#include <cm/string_view>
 #include <cmext/string_view>
 
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // cmSeparateArgumentsCommand
 bool cmSeparateArgumentsCommand(std::vector<std::string> const& args,
@@ -27,7 +28,7 @@
 
   if (args.size() == 1) {
     // Original space-replacement version of command.
-    if (cmProp def = status.GetMakefile().GetDefinition(var)) {
+    if (cmValue def = status.GetMakefile().GetDefinition(var)) {
       std::string value = *def;
       std::replace(value.begin(), value.end(), ' ', ';');
       status.GetMakefile().AddDefinition(var, value);
@@ -81,7 +82,7 @@
   }
 
   if (unparsedArguments.empty()) {
-    status.GetMakefile().AddDefinition(var, {});
+    status.GetMakefile().AddDefinition(var, cm::string_view{});
     return true;
   }
 
diff --git a/Source/cmSetCommand.cxx b/Source/cmSetCommand.cxx
index 354b4c3..ce0cb25 100644
--- a/Source/cmSetCommand.cxx
+++ b/Source/cmSetCommand.cxx
@@ -5,12 +5,12 @@
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // cmSetCommand
 bool cmSetCommand(std::vector<std::string> const& args,
@@ -136,7 +136,7 @@
 
   // see if this is already in the cache
   cmState* state = status.GetMakefile().GetState();
-  cmProp existingValue = state->GetCacheEntryValue(variable);
+  cmValue existingValue = state->GetCacheEntryValue(variable);
   if (existingValue &&
       (state->GetCacheEntryType(variable) != cmStateEnums::UNINITIALIZED)) {
     // if the set is trying to CACHE the value but the value
diff --git a/Source/cmSetDirectoryPropertiesCommand.cxx b/Source/cmSetDirectoryPropertiesCommand.cxx
index 07ad246..9adf537 100644
--- a/Source/cmSetDirectoryPropertiesCommand.cxx
+++ b/Source/cmSetDirectoryPropertiesCommand.cxx
@@ -32,7 +32,7 @@
         "Commands and macros cannot be set using SET_CMAKE_PROPERTIES");
       return false;
     }
-    status.GetMakefile().SetProperty(prop, (iter + 1)->c_str());
+    status.GetMakefile().SetProperty(prop, *(iter + 1));
   }
 
   return true;
diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx
index c899053..db10cd4 100644
--- a/Source/cmSetPropertyCommand.cxx
+++ b/Source/cmSetPropertyCommand.cxx
@@ -21,6 +21,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTest.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -116,7 +117,7 @@
           "given non-existent target for TARGET_DIRECTORY ", target_name));
         return false;
       }
-      cmProp target_source_dir = target->GetProperty("BINARY_DIR");
+      cmValue target_source_dir = target->GetProperty("BINARY_DIR");
       cmMakefile* target_dir_mf =
         status.GetMakefile().GetGlobalGenerator()->FindMakefile(
           *target_source_dir);
@@ -294,7 +295,7 @@
         sf->SetProperty("GENERATED", nullptr);
         break;
       case PropertyOp::Set:
-        sf->SetProperty("GENERATED", propertyValue.c_str());
+        sf->SetProperty("GENERATED", propertyValue);
         break;
     }
   } else {
@@ -474,7 +475,7 @@
     if (remove) {
       cm->SetProperty(propertyName, nullptr);
     } else {
-      cm->SetProperty(propertyName, propertyValue.c_str());
+      cm->SetProperty(propertyName, propertyValue);
     }
   }
 
@@ -520,7 +521,7 @@
     if (remove) {
       mf->SetProperty(propertyName, nullptr);
     } else {
-      mf->SetProperty(propertyName, propertyValue.c_str());
+      mf->SetProperty(propertyName, propertyValue);
     }
   }
 
@@ -631,7 +632,7 @@
     if (remove) {
       sf->SetProperty(propertyName, nullptr);
     } else {
-      sf->SetProperty(propertyName, propertyValue.c_str());
+      sf->SetProperty(propertyName, propertyValue);
     }
   }
   return true;
@@ -681,7 +682,7 @@
     if (remove) {
       test->SetProperty(propertyName, nullptr);
     } else {
-      test->SetProperty(propertyName, propertyValue.c_str());
+      test->SetProperty(propertyName, propertyValue);
     }
   }
 
@@ -719,7 +720,7 @@
   for (std::string const& name : names) {
     // Get the source file.
     cmake* cm = status.GetMakefile().GetCMakeInstance();
-    cmProp existingValue = cm->GetState()->GetCacheEntryValue(name);
+    cmValue existingValue = cm->GetState()->GetCacheEntryValue(name);
     if (existingValue) {
       if (!HandleCacheEntry(name, status.GetMakefile(), propertyName,
                             propertyValue, appendAsString, appendMode,
diff --git a/Source/cmSetSourceFilesPropertiesCommand.cxx b/Source/cmSetSourceFilesPropertiesCommand.cxx
index 237b67f..ab93ddb 100644
--- a/Source/cmSetSourceFilesPropertiesCommand.cxx
+++ b/Source/cmSetSourceFilesPropertiesCommand.cxx
@@ -173,7 +173,7 @@
           SetPropertyCommand::HandleAndValidateSourceFilePropertyGENERATED(
             sf, *(k + 1));
         } else {
-          sf->SetProperty(*k, (k + 1)->c_str());
+          sf->SetProperty(*k, *(k + 1));
         }
       }
     }
diff --git a/Source/cmSetTestsPropertiesCommand.cxx b/Source/cmSetTestsPropertiesCommand.cxx
index c4bff76..a17c964 100644
--- a/Source/cmSetTestsPropertiesCommand.cxx
+++ b/Source/cmSetTestsPropertiesCommand.cxx
@@ -37,7 +37,7 @@
       // loop through all the props and set them
       for (auto k = propsIter + 1; k != args.end(); k += 2) {
         if (!k->empty()) {
-          test->SetProperty(*k, (k + 1)->c_str());
+          test->SetProperty(*k, *(k + 1));
         }
       }
     } else {
diff --git a/Source/cmSiteNameCommand.cxx b/Source/cmSiteNameCommand.cxx
index 58af8f0..61d1c30 100644
--- a/Source/cmSiteNameCommand.cxx
+++ b/Source/cmSiteNameCommand.cxx
@@ -6,10 +6,9 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
-#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // cmSiteNameCommand
 bool cmSiteNameCommand(std::vector<std::string> const& args,
@@ -27,12 +26,12 @@
   paths.emplace_back("/sbin");
   paths.emplace_back("/usr/local/bin");
 
-  cmProp cacheValue = status.GetMakefile().GetDefinition(args[0]);
+  cmValue cacheValue = status.GetMakefile().GetDefinition(args[0]);
   if (cacheValue) {
     return true;
   }
 
-  cmProp temp = status.GetMakefile().GetDefinition("HOSTNAME");
+  cmValue temp = status.GetMakefile().GetDefinition("HOSTNAME");
   std::string hostname_cmd;
   if (temp) {
     hostname_cmd = *temp;
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index 3f3c8d5..3fa0051 100644
--- a/Source/cmSourceFile.cxx
+++ b/Source/cmSourceFile.cxx
@@ -13,6 +13,7 @@
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name,
@@ -53,7 +54,7 @@
 std::string const& cmSourceFile::GetOrDetermineLanguage()
 {
   // If the language was set explicitly by the user then use it.
-  if (cmProp lang = this->GetProperty(propLANGUAGE)) {
+  if (cmValue lang = this->GetProperty(propLANGUAGE)) {
     // Assign to member in order to return a reference.
     this->Language = *lang;
     return this->Language;
@@ -84,7 +85,7 @@
 std::string cmSourceFile::GetLanguage() const
 {
   // If the language was set explicitly by the user then use it.
-  if (cmProp lang = this->GetProperty(propLANGUAGE)) {
+  if (cmValue lang = this->GetProperty(propLANGUAGE)) {
     return *lang;
   }
 
@@ -269,7 +270,8 @@
   return this->Location.Matches(loc);
 }
 
-void cmSourceFile::SetProperty(const std::string& prop, const char* value)
+template <typename ValueType>
+void cmSourceFile::StoreProperty(const std::string& prop, ValueType value)
 {
   if (prop == propINCLUDE_DIRECTORIES) {
     this->IncludeDirectories.clear();
@@ -294,6 +296,15 @@
   }
 }
 
+void cmSourceFile::SetProperty(const std::string& prop, const char* value)
+{
+  this->StoreProperty(prop, value);
+}
+void cmSourceFile::SetProperty(const std::string& prop, cmValue value)
+{
+  this->StoreProperty(prop, value);
+}
+
 void cmSourceFile::AppendProperty(const std::string& prop,
                                   const std::string& value, bool asString)
 {
@@ -317,7 +328,7 @@
   }
 }
 
-cmProp cmSourceFile::GetPropertyForUser(const std::string& prop)
+cmValue cmSourceFile::GetPropertyForUser(const std::string& prop)
 {
   // This method is a consequence of design history and backwards
   // compatibility.  GetProperty is (and should be) a const method.
@@ -342,7 +353,7 @@
   // if it is requested by the user.
   if (prop == propLANGUAGE) {
     // The pointer is valid until `this->Language` is modified.
-    return &this->GetOrDetermineLanguage();
+    return cmValue(this->GetOrDetermineLanguage());
   }
 
   // Special handling for GENERATED property.
@@ -355,23 +366,23 @@
           (policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD)
             ? CheckScope::GlobalAndLocal
             : CheckScope::Global)) {
-      return &propTRUE;
+      return cmValue(propTRUE);
     }
-    return &propFALSE;
+    return cmValue(propFALSE);
   }
 
   // Perform the normal property lookup.
   return this->GetProperty(prop);
 }
 
-cmProp cmSourceFile::GetProperty(const std::string& prop) const
+cmValue cmSourceFile::GetProperty(const std::string& prop) const
 {
   // Check for computed properties.
   if (prop == propLOCATION) {
     if (this->FullPath.empty()) {
       return nullptr;
     }
-    return &this->FullPath;
+    return cmValue(this->FullPath);
   }
 
   // Check for the properties with backtraces.
@@ -382,7 +393,7 @@
 
     static std::string output;
     output = cmJoin(this->IncludeDirectories, ";");
-    return &output;
+    return cmValue(output);
   }
 
   if (prop == propCOMPILE_OPTIONS) {
@@ -392,7 +403,7 @@
 
     static std::string output;
     output = cmJoin(this->CompileOptions, ";");
-    return &output;
+    return cmValue(output);
   }
 
   if (prop == propCOMPILE_DEFINITIONS) {
@@ -402,10 +413,10 @@
 
     static std::string output;
     output = cmJoin(this->CompileDefinitions, ";");
-    return &output;
+    return cmValue(output);
   }
 
-  cmProp retVal = this->Properties.GetPropertyValue(prop);
+  cmValue retVal = this->Properties.GetPropertyValue(prop);
   if (!retVal) {
     cmMakefile const* mf = this->Location.GetMakefile();
     const bool chain =
@@ -421,7 +432,7 @@
 
 const std::string& cmSourceFile::GetSafeProperty(const std::string& prop) const
 {
-  cmProp ret = this->GetProperty(prop);
+  cmValue ret = this->GetProperty(prop);
   if (ret) {
     return *ret;
   }
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index 32ed687..c1c5201 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -10,10 +10,10 @@
 
 #include "cmCustomCommand.h"
 #include "cmListFileCache.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmSourceFileLocation.h"
 #include "cmSourceFileLocationKind.h"
+#include "cmValue.h"
 
 class cmMakefile;
 
@@ -42,17 +42,22 @@
 
   //! Set/Get a property of this source file
   void SetProperty(const std::string& prop, const char* value);
+  void SetProperty(const std::string& prop, cmValue value);
+  void SetProperty(const std::string& prop, const std::string& value)
+  {
+    this->SetProperty(prop, cmValue(value));
+  }
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString = false);
   //! Might return a nullptr if the property is not set or invalid
-  cmProp GetProperty(const std::string& prop) const;
+  cmValue GetProperty(const std::string& prop) const;
   //! Always returns a valid pointer
   const std::string& GetSafeProperty(const std::string& prop) const;
   bool GetPropertyAsBool(const std::string& prop) const;
 
   /** Implement getting a property when called from a CMake language
       command like get_property or get_source_file_property.  */
-  cmProp GetPropertyForUser(const std::string& prop);
+  cmValue GetPropertyForUser(const std::string& prop);
 
   /// Marks this file as generated
   /**
@@ -145,6 +150,9 @@
   std::string GetObjectLibrary() const;
 
 private:
+  template <typename ValueType>
+  void StoreProperty(const std::string& prop, ValueType value);
+
   cmSourceFileLocation Location;
   cmPropertyMap Properties;
   std::unique_ptr<cmCustomCommand> CustomCommand;
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx
index 37ed4c1..acc2ed2 100644
--- a/Source/cmStandardLevelResolver.cxx
+++ b/Source/cmStandardLevelResolver.cxx
@@ -20,9 +20,10 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
+#include "cmPolicies.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -57,10 +58,10 @@
   return -1;
 }
 
-struct StanardLevelComputer
+struct StandardLevelComputer
 {
-  explicit StanardLevelComputer(std::string lang, std::vector<int> levels,
-                                std::vector<std::string> levelsStr)
+  explicit StandardLevelComputer(std::string lang, std::vector<int> levels,
+                                 std::vector<std::string> levelsStr)
     : Language(std::move(lang))
     , Levels(std::move(levels))
     , LevelsAsStrings(std::move(levelsStr))
@@ -76,37 +77,74 @@
     const auto& stds = this->Levels;
     const auto& stdsStrings = this->LevelsAsStrings;
 
-    cmProp defaultStd = makefile->GetDefinition(
+    cmValue defaultStd = makefile->GetDefinition(
       cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
     if (!cmNonempty(defaultStd)) {
       // this compiler has no notion of language standard levels
       return std::string{};
     }
 
+    cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus(
+      cmPolicies::CMP0128) };
+    bool const defaultExt{ cmIsOn(*makefile->GetDefinition(
+      cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) };
     bool ext = true;
-    if (cmProp extPropValue = target->GetLanguageExtensions(this->Language)) {
-      if (cmIsOff(*extPropValue)) {
-        ext = false;
-      }
+
+    if (cmp0128 == cmPolicies::NEW) {
+      ext = defaultExt;
     }
 
-    cmProp standardProp = target->GetLanguageStandard(this->Language, config);
+    if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
+      ext = cmIsOn(*extPropValue);
+    }
+
+    std::string const type{ ext ? "EXTENSION" : "STANDARD" };
+
+    cmValue standardProp = target->GetLanguageStandard(this->Language, config);
     if (!standardProp) {
-      if (ext) {
-        // No language standard is specified and extensions are not disabled.
-        // Check if this compiler needs a flag to enable extensions.
-        return cmStrCat("CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION");
+      if (cmp0128 == cmPolicies::NEW) {
+        // Add extension flag if compiler's default doesn't match.
+        if (ext != defaultExt) {
+          return cmStrCat("CMAKE_", this->Language, *defaultStd, "_", type,
+                          "_COMPILE_OPTION");
+        }
+      } else {
+        if (cmp0128 == cmPolicies::WARN &&
+            makefile->PolicyOptionalWarningEnabled(
+              "CMAKE_POLICY_WARNING_CMP0128") &&
+            ext != defaultExt) {
+          const char* state{};
+          if (ext) {
+            if (!makefile->GetDefinition(cmStrCat(
+                  "CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION"))) {
+              state = "enabled";
+            }
+          } else {
+            state = "disabled";
+          }
+          if (state) {
+            makefile->IssueMessage(
+              MessageType::AUTHOR_WARNING,
+              cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128),
+                       "\nFor compatibility with older versions of CMake, "
+                       "compiler extensions won't be ",
+                       state, "."));
+          }
+        }
+
+        if (ext) {
+          return cmStrCat("CMAKE_", this->Language,
+                          "_EXTENSION_COMPILE_OPTION");
+        }
       }
       return std::string{};
     }
 
-    std::string const type = ext ? "EXTENSION" : "STANDARD";
-
     if (target->GetLanguageStandardRequired(this->Language)) {
       std::string option_flag = cmStrCat(
         "CMAKE_", this->Language, *standardProp, "_", type, "_COMPILE_OPTION");
 
-      cmProp opt = target->Target->GetMakefile()->GetDefinition(option_flag);
+      cmValue opt = target->Target->GetMakefile()->GetDefinition(option_flag);
       if (!opt) {
         std::ostringstream e;
         e << "Target \"" << target->GetName()
@@ -121,6 +159,25 @@
       return option_flag;
     }
 
+    // If the request matches the compiler's defaults we don't need to add
+    // anything.
+    if (*standardProp == *defaultStd && ext == defaultExt) {
+      if (cmp0128 == cmPolicies::NEW) {
+        return std::string{};
+      }
+
+      if (cmp0128 == cmPolicies::WARN &&
+          makefile->PolicyOptionalWarningEnabled(
+            "CMAKE_POLICY_WARNING_CMP0128")) {
+        makefile->IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128),
+                   "\nFor compatibility with older versions of CMake, "
+                   "unnecessary flags for language standard or compiler "
+                   "extensions may be added."));
+      }
+    }
+
     std::string standardStr(*standardProp);
     if (this->Language == "CUDA" && standardStr == "98") {
       standardStr = "03";
@@ -147,17 +204,19 @@
       return std::string{};
     }
 
-    // If the standard requested is older than the compiler's default
-    // then we need to use a flag to change it.
-    if (stdIt <= defaultStdIt) {
+    // If the standard requested is older than the compiler's default or the
+    // extension mode doesn't match then we need to use a flag.
+    if ((cmp0128 != cmPolicies::NEW && stdIt <= defaultStdIt) ||
+        (cmp0128 == cmPolicies::NEW &&
+         (stdIt < defaultStdIt || ext != defaultExt))) {
       auto offset = std::distance(cm::cbegin(stds), stdIt);
       return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type,
                       "_COMPILE_OPTION");
     }
 
-    // The standard requested is at least as new as the compiler's default,
-    // and the standard request is not required.  Decay to the newest standard
-    // for which a flag is defined.
+    // The compiler's default is at least as new as the requested standard,
+    // and the requested standard is not required.  Decay to the newest
+    // standard for which a flag is defined.
     for (; defaultStdIt < stdIt; --stdIt) {
       auto offset = std::distance(cm::cbegin(stds), stdIt);
       std::string option_flag =
@@ -174,7 +233,7 @@
   bool GetNewRequiredStandard(cmMakefile* makefile,
                               std::string const& targetName,
                               const std::string& feature,
-                              cmProp currentLangStandardValue,
+                              cmValue currentLangStandardValue,
                               std::string& newRequiredStandard,
                               std::string* error) const
   {
@@ -186,9 +245,9 @@
 
     auto needed = this->HighestStandardNeeded(makefile, feature);
 
-    cmProp existingStandard = currentLangStandardValue;
+    cmValue existingStandard = currentLangStandardValue;
     if (!existingStandard) {
-      cmProp defaultStandard = makefile->GetDefinition(
+      cmValue defaultStandard = makefile->GetDefinition(
         cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
       if (cmNonempty(defaultStandard)) {
         existingStandard = defaultStandard;
@@ -231,7 +290,7 @@
                              std::string const& config,
                              std::string const& feature) const
   {
-    cmProp defaultStandard = makefile->GetDefinition(
+    cmValue defaultStandard = makefile->GetDefinition(
       cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
     if (!defaultStandard) {
       makefile->IssueMessage(
@@ -253,7 +312,7 @@
       return false;
     }
 
-    cmProp existingStandard =
+    cmValue existingStandard =
       target->GetLanguageStandard(this->Language, config);
     if (!existingStandard) {
       existingStandard = defaultStandard;
@@ -283,7 +342,7 @@
     std::string prefix = cmStrCat("CMAKE_", this->Language);
     StandardNeeded maxLevel = { -1, -1 };
     for (size_t i = 0; i < this->Levels.size(); ++i) {
-      if (cmProp prop = makefile->GetDefinition(
+      if (cmValue prop = makefile->GetDefinition(
             cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) {
         std::vector<std::string> props = cmExpandedList(*prop);
         if (cm::contains(props, feature)) {
@@ -308,31 +367,33 @@
   std::vector<std::string> LevelsAsStrings;
 };
 
-std::unordered_map<std::string, StanardLevelComputer> StandardComputerMapping =
-  { { "C",
-      StanardLevelComputer{
+std::unordered_map<std::string, StandardLevelComputer>
+  StandardComputerMapping = {
+    { "C",
+      StandardLevelComputer{
         "C", std::vector<int>{ 90, 99, 11, 17, 23 },
         std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
     { "CXX",
-      StanardLevelComputer{
+      StandardLevelComputer{
         "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
         std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
     { "CUDA",
-      StanardLevelComputer{
+      StandardLevelComputer{
         "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23 },
         std::vector<std::string>{ "03", "11", "14", "17", "20", "23" } } },
     { "OBJC",
-      StanardLevelComputer{
+      StandardLevelComputer{
         "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
         std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
     { "OBJCXX",
-      StanardLevelComputer{
+      StandardLevelComputer{
         "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
         std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
     { "HIP",
-      StanardLevelComputer{
+      StandardLevelComputer{
         "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
-        std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } } };
+        std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } }
+  };
 }
 
 std::string cmStandardLevelResolver::GetCompileOptionDef(
@@ -387,7 +448,11 @@
     return false;
   }
 
-  const char* features = this->CompileFeaturesAvailable(lang, error);
+  if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
+    return true;
+  }
+
+  cmValue features = this->CompileFeaturesAvailable(lang, error);
   if (!features) {
     return false;
   }
@@ -465,7 +530,7 @@
   return false;
 }
 
-const char* cmStandardLevelResolver::CompileFeaturesAvailable(
+cmValue cmStandardLevelResolver::CompileFeaturesAvailable(
   const std::string& lang, std::string* error) const
 {
   if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
@@ -484,7 +549,7 @@
     return nullptr;
   }
 
-  cmProp featuresKnown =
+  cmValue featuresKnown =
     this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
 
   if (!cmNonempty(featuresKnown)) {
@@ -507,12 +572,12 @@
     }
     return nullptr;
   }
-  return cmToCStr(featuresKnown);
+  return featuresKnown;
 }
 
 bool cmStandardLevelResolver::GetNewRequiredStandard(
   const std::string& targetName, const std::string& feature,
-  cmProp currentLangStandardValue, std::string& newRequiredStandard,
+  cmValue currentLangStandardValue, std::string& newRequiredStandard,
   std::string* error) const
 {
   std::string lang;
diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h
index d84fbcb..4226456 100644
--- a/Source/cmStandardLevelResolver.h
+++ b/Source/cmStandardLevelResolver.h
@@ -4,7 +4,7 @@
 
 #include <string>
 
-#include "cmProperty.h"
+#include "cmValue.h"
 
 class cmMakefile;
 class cmGeneratorTarget;
@@ -30,12 +30,12 @@
                            const std::string& feature, std::string& lang,
                            std::string* error) const;
 
-  const char* CompileFeaturesAvailable(const std::string& lang,
-                                       std::string* error) const;
+  cmValue CompileFeaturesAvailable(const std::string& lang,
+                                   std::string* error) const;
 
   bool GetNewRequiredStandard(const std::string& targetName,
                               const std::string& feature,
-                              cmProp currentLangStandardValue,
+                              cmValue currentLangStandardValue,
                               std::string& newRequiredStandard,
                               std::string* error = nullptr) const;
 
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index ce6eb31..e79949d 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -26,7 +26,9 @@
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-cmState::cmState()
+cmState::cmState(Mode mode, ProjectKind projectKind)
+  : StateMode(mode)
+  , StateProjectKind(projectKind)
 {
   this->CacheManager = cm::make_unique<cmCacheManager>();
   this->GlobVerificationManager = cm::make_unique<cmGlobVerificationManager>();
@@ -142,20 +144,20 @@
   return this->CacheManager->GetCacheEntryKeys();
 }
 
-cmProp cmState::GetCacheEntryValue(std::string const& key) const
+cmValue cmState::GetCacheEntryValue(std::string const& key) const
 {
   return this->CacheManager->GetCacheEntryValue(key);
 }
 
 std::string cmState::GetSafeCacheEntryValue(std::string const& key) const
 {
-  if (cmProp val = this->GetCacheEntryValue(key)) {
+  if (cmValue val = this->GetCacheEntryValue(key)) {
     return *val;
   }
   return std::string();
 }
 
-cmProp cmState::GetInitializedCacheValue(std::string const& key) const
+cmValue cmState::GetInitializedCacheValue(std::string const& key) const
 {
   return this->CacheManager->GetInitializedCacheValue(key);
 }
@@ -192,8 +194,8 @@
   return this->CacheManager->GetCacheEntryPropertyList(key);
 }
 
-cmProp cmState::GetCacheEntryProperty(std::string const& key,
-                                      std::string const& propertyName)
+cmValue cmState::GetCacheEntryProperty(std::string const& key,
+                                       std::string const& propertyName)
 {
   return this->CacheManager->GetCacheEntryProperty(key, propertyName);
 }
@@ -204,7 +206,7 @@
   return this->CacheManager->GetCacheEntryPropertyAsBool(key, propertyName);
 }
 
-void cmState::AddCacheEntry(const std::string& key, const char* value,
+void cmState::AddCacheEntry(const std::string& key, cmValue value,
                             const char* helpString,
                             cmStateEnums::CacheEntryType type)
 {
@@ -275,15 +277,10 @@
     cmLinkedTree<cmStateDetail::BuildsystemDirectoryStateType>::iterator it =
       this->BuildsystemDirectory.Truncate();
     it->IncludeDirectories.clear();
-    it->IncludeDirectoryBacktraces.clear();
     it->CompileDefinitions.clear();
-    it->CompileDefinitionsBacktraces.clear();
     it->CompileOptions.clear();
-    it->CompileOptionsBacktraces.clear();
     it->LinkOptions.clear();
-    it->LinkOptionsBacktraces.clear();
     it->LinkDirectories.clear();
-    it->LinkDirectoriesBacktraces.clear();
     it->DirectoryEnd = pos;
     it->NormalTargetNames.clear();
     it->ImportedTargetNames.clear();
@@ -381,16 +378,6 @@
   this->EnabledLanguages.clear();
 }
 
-bool cmState::GetIsInTryCompile() const
-{
-  return this->IsInTryCompile;
-}
-
-void cmState::SetIsInTryCompile(bool b)
-{
-  this->IsInTryCompile = b;
-}
-
 bool cmState::GetIsGeneratorMultiConfig() const
 {
   return this->IsGeneratorMultiConfig;
@@ -466,7 +453,7 @@
         case cmPolicies::WARN:
           mf.IssueMessage(MessageType::AUTHOR_WARNING,
                           cmPolicies::GetPolicyWarning(policy));
-          break;
+          CM_FALLTHROUGH;
         case cmPolicies::OLD:
           break;
         case cmPolicies::REQUIRED_IF_USED:
@@ -485,7 +472,7 @@
     name,
     [name, error](std::vector<cmListFileArgument> const&,
                   cmExecutionStatus& status) -> bool {
-      cmProp versionValue =
+      cmValue versionValue =
         status.GetMakefile().GetDefinition("CMAKE_MINIMUM_REQUIRED_VERSION");
       if (name == "endif" &&
           (!versionValue || atof(versionValue->c_str()) <= 1.4)) {
@@ -577,6 +564,10 @@
 {
   this->GlobalProperties.SetProperty(prop, value);
 }
+void cmState::SetGlobalProperty(const std::string& prop, cmValue value)
+{
+  this->GlobalProperties.SetProperty(prop, value);
+}
 
 void cmState::AppendGlobalProperty(const std::string& prop,
                                    const std::string& value, bool asString)
@@ -584,7 +575,7 @@
   this->GlobalProperties.AppendProperty(prop, value, asString);
 }
 
-cmProp cmState::GetGlobalProperty(const std::string& prop)
+cmValue cmState::GetGlobalProperty(const std::string& prop)
 {
   if (prop == "CACHE_VARIABLES") {
     std::vector<std::string> cacheKeys = this->GetCacheEntryKeys();
@@ -593,8 +584,9 @@
     std::vector<std::string> commands = this->GetCommandNames();
     this->SetGlobalProperty("COMMANDS", cmJoin(commands, ";").c_str());
   } else if (prop == "IN_TRY_COMPILE") {
-    this->SetGlobalProperty("IN_TRY_COMPILE",
-                            this->IsInTryCompile ? "1" : "0");
+    this->SetGlobalProperty(
+      "IN_TRY_COMPILE",
+      this->StateProjectKind == ProjectKind::TryCompile ? "1" : "0");
   } else if (prop == "GENERATOR_IS_MULTI_CONFIG") {
     this->SetGlobalProperty("GENERATOR_IS_MULTI_CONFIG",
                             this->IsGeneratorMultiConfig ? "1" : "0");
@@ -610,47 +602,47 @@
   if (prop == "CMAKE_C_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_C_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_C90_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_C90_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_C99_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_C99_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_C11_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_C11_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_CXX_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_CXX_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_CXX98_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_CXX98_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_CXX11_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_CXX11_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_CXX14_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_CXX14_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
   if (prop == "CMAKE_CUDA_KNOWN_FEATURES") {
     static const std::string s_out(
       &FOR_EACH_CUDA_FEATURE(STRING_LIST_ELEMENT)[1]);
-    return &s_out;
+    return cmValue(s_out);
   }
 
 #undef STRING_LIST_ELEMENT
@@ -771,17 +763,12 @@
 
 cmState::Mode cmState::GetMode() const
 {
-  return this->CurrentMode;
+  return this->StateMode;
 }
 
 std::string cmState::GetModeString() const
 {
-  return ModeToString(this->CurrentMode);
-}
-
-void cmState::SetMode(cmState::Mode mode)
-{
-  this->CurrentMode = mode;
+  return ModeToString(this->StateMode);
 }
 
 std::string cmState::ModeToString(cmState::Mode mode)
@@ -803,6 +790,11 @@
   return "UNKNOWN";
 }
 
+cmState::ProjectKind cmState::GetProjectKind() const
+{
+  return this->StateProjectKind;
+}
+
 std::string const& cmState::GetBinaryDirectory() const
 {
   return this->BinaryDirectory;
diff --git a/Source/cmState.h b/Source/cmState.h
index 9951b9a..a1666ca 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -21,6 +21,7 @@
 #include "cmPropertyMap.h"
 #include "cmStatePrivate.h"
 #include "cmStateTypes.h"
+#include "cmValue.h"
 
 class cmCacheManager;
 class cmCommand;
@@ -35,12 +36,6 @@
   friend class cmStateSnapshot;
 
 public:
-  cmState();
-  ~cmState();
-
-  cmState(const cmState&) = delete;
-  cmState& operator=(const cmState&) = delete;
-
   enum Mode
   {
     Unknown,
@@ -51,6 +46,18 @@
     CPack,
   };
 
+  enum class ProjectKind
+  {
+    Normal,
+    TryCompile,
+  };
+
+  cmState(Mode mode, ProjectKind projectKind = ProjectKind::Normal);
+  ~cmState();
+
+  cmState(const cmState&) = delete;
+  cmState& operator=(const cmState&) = delete;
+
   static const std::string& GetTargetTypeName(
     cmStateEnums::TargetType targetType);
 
@@ -92,9 +99,9 @@
   bool IsCacheLoaded() const;
 
   std::vector<std::string> GetCacheEntryKeys() const;
-  cmProp GetCacheEntryValue(std::string const& key) const;
+  cmValue GetCacheEntryValue(std::string const& key) const;
   std::string GetSafeCacheEntryValue(std::string const& key) const;
-  cmProp GetInitializedCacheValue(std::string const& key) const;
+  cmValue GetInitializedCacheValue(std::string const& key) const;
   cmStateEnums::CacheEntryType GetCacheEntryType(std::string const& key) const;
   void SetCacheEntryValue(std::string const& key, std::string const& value);
 
@@ -106,8 +113,8 @@
   void SetCacheEntryBoolProperty(std::string const& key,
                                  std::string const& propertyName, bool value);
   std::vector<std::string> GetCacheEntryPropertyList(std::string const& key);
-  cmProp GetCacheEntryProperty(std::string const& key,
-                               std::string const& propertyName);
+  cmValue GetCacheEntryProperty(std::string const& key,
+                                std::string const& propertyName);
   bool GetCacheEntryPropertyAsBool(std::string const& key,
                                    std::string const& propertyName);
   void AppendCacheEntryProperty(std::string const& key,
@@ -141,9 +148,6 @@
   void SetEnabledLanguages(std::vector<std::string> const& langs);
   void ClearEnabledLanguages();
 
-  bool GetIsInTryCompile() const;
-  void SetIsInTryCompile(bool b);
-
   bool GetIsGeneratorMultiConfig() const;
   void SetIsGeneratorMultiConfig(bool b);
 
@@ -175,9 +179,10 @@
   std::vector<std::string> GetCommandNames() const;
 
   void SetGlobalProperty(const std::string& prop, const char* value);
+  void SetGlobalProperty(const std::string& prop, cmValue value);
   void AppendGlobalProperty(const std::string& prop, const std::string& value,
                             bool asString = false);
-  cmProp GetGlobalProperty(const std::string& prop);
+  cmValue GetGlobalProperty(const std::string& prop);
   bool GetGlobalPropertyAsBool(const std::string& prop);
 
   std::string const& GetSourceDirectory() const;
@@ -207,13 +212,26 @@
 
   Mode GetMode() const;
   std::string GetModeString() const;
-  void SetMode(Mode mode);
 
   static std::string ModeToString(Mode mode);
 
+  ProjectKind GetProjectKind() const;
+
 private:
   friend class cmake;
   void AddCacheEntry(const std::string& key, const char* value,
+                     const char* helpString, cmStateEnums::CacheEntryType type)
+  {
+    this->AddCacheEntry(key,
+                        value ? cmValue(std::string(value)) : cmValue(nullptr),
+                        helpString, type);
+  }
+  void AddCacheEntry(const std::string& key, const std::string& value,
+                     const char* helpString, cmStateEnums::CacheEntryType type)
+  {
+    this->AddCacheEntry(key, cmValue(value), helpString, type);
+  }
+  void AddCacheEntry(const std::string& key, cmValue value,
                      const char* helpString,
                      cmStateEnums::CacheEntryType type);
 
@@ -248,7 +266,6 @@
 
   std::string SourceDirectory;
   std::string BinaryDirectory;
-  bool IsInTryCompile = false;
   bool IsGeneratorMultiConfig = false;
   bool WindowsShell = false;
   bool WindowsVSIDE = false;
@@ -258,5 +275,6 @@
   bool NMake = false;
   bool MSYSShell = false;
   bool NinjaMulti = false;
-  Mode CurrentMode = Unknown;
+  Mode StateMode = Unknown;
+  ProjectKind StateProjectKind = ProjectKind::Normal;
 };
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index c898dd4..b42e5c3 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -19,7 +19,9 @@
 #include "cmState.h"
 #include "cmStatePrivate.h"
 #include "cmStateTypes.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 static std::string const kBINARY_DIR = "BINARY_DIR";
 static std::string const kBUILDSYSTEM_TARGETS = "BUILDSYSTEM_TARGETS";
@@ -63,7 +65,7 @@
 }
 
 template <typename T, typename U>
-cmStringRange GetPropertyContent(T const& content, U contentEndPosition)
+cmBTStringRange GetPropertyContent(T const& content, U contentEndPosition)
 {
   auto end = content.begin() + contentEndPosition;
 
@@ -73,88 +75,59 @@
   return cmMakeRange(rbegin.base(), end);
 }
 
-template <typename T, typename U, typename V>
-cmBacktraceRange GetPropertyBacktraces(T const& content, U const& backtraces,
-                                       V contentEndPosition)
+template <typename T, typename U>
+void AppendEntry(T& content, U& endContentPosition,
+                 const BT<std::string>& value)
 {
-  auto entryEnd = content.begin() + contentEndPosition;
-
-  auto rbegin = cm::make_reverse_iterator(entryEnd);
-  rbegin = std::find(rbegin, content.rend(), cmPropertySentinal);
-
-  auto it = backtraces.begin() + std::distance(content.begin(), rbegin.base());
-
-  auto end = backtraces.end();
-  return cmMakeRange(it, end);
-}
-
-template <typename T, typename U, typename V>
-void AppendEntry(T& content, U& backtraces, V& endContentPosition,
-                 const std::string& value, const cmListFileBacktrace& lfbt)
-{
-  if (value.empty()) {
+  if (value.Value.empty()) {
     return;
   }
 
   assert(endContentPosition == content.size());
 
   content.push_back(value);
-  backtraces.push_back(lfbt);
 
   endContentPosition = content.size();
 }
 
-template <typename T, typename U, typename V>
-void SetContent(T& content, U& backtraces, V& endContentPosition,
-                const std::string& vec, const cmListFileBacktrace& lfbt)
+template <typename T, typename U>
+void SetContent(T& content, U& endContentPosition, const BT<std::string>& vec)
 {
   assert(endContentPosition == content.size());
 
   content.resize(content.size() + 2);
-  backtraces.resize(backtraces.size() + 2);
 
   content.back() = vec;
-  backtraces.back() = lfbt;
 
   endContentPosition = content.size();
 }
 
-template <typename T, typename U, typename V>
-void ClearContent(T& content, U& backtraces, V& endContentPosition)
+template <typename T, typename U>
+void ClearContent(T& content, U& endContentPosition)
 {
   assert(endContentPosition == content.size());
 
   content.resize(content.size() + 1);
-  backtraces.resize(backtraces.size() + 1);
 
   endContentPosition = content.size();
 }
 
-cmStringRange cmStateDirectory::GetIncludeDirectoriesEntries() const
+cmBTStringRange cmStateDirectory::GetIncludeDirectoriesEntries() const
 {
   return GetPropertyContent(
     this->DirectoryState->IncludeDirectories,
     this->Snapshot_.Position->IncludeDirectoryPosition);
 }
 
-cmBacktraceRange cmStateDirectory::GetIncludeDirectoriesEntryBacktraces() const
-{
-  return GetPropertyBacktraces(
-    this->DirectoryState->IncludeDirectories,
-    this->DirectoryState->IncludeDirectoryBacktraces,
-    this->Snapshot_.Position->IncludeDirectoryPosition);
-}
-
 void cmStateDirectory::AppendIncludeDirectoriesEntry(
-  const std::string& vec, const cmListFileBacktrace& lfbt)
+  const BT<std::string>& vec)
 {
   AppendEntry(this->DirectoryState->IncludeDirectories,
-              this->DirectoryState->IncludeDirectoryBacktraces,
-              this->Snapshot_.Position->IncludeDirectoryPosition, vec, lfbt);
+              this->Snapshot_.Position->IncludeDirectoryPosition, vec);
 }
 
 void cmStateDirectory::PrependIncludeDirectoriesEntry(
-  const std::string& vec, const cmListFileBacktrace& lfbt)
+  const BT<std::string>& vec)
 {
   auto entryEnd = this->DirectoryState->IncludeDirectories.begin() +
     this->Snapshot_.Position->IncludeDirectoryPosition;
@@ -164,167 +137,111 @@
   rbegin = std::find(rbegin, rend, cmPropertySentinal);
 
   auto entryIt = rbegin.base();
-  auto entryBegin = this->DirectoryState->IncludeDirectories.begin();
-
-  auto btIt = this->DirectoryState->IncludeDirectoryBacktraces.begin() +
-    std::distance(entryBegin, entryIt);
 
   this->DirectoryState->IncludeDirectories.insert(entryIt, vec);
-  this->DirectoryState->IncludeDirectoryBacktraces.insert(btIt, lfbt);
 
   this->Snapshot_.Position->IncludeDirectoryPosition =
     this->DirectoryState->IncludeDirectories.size();
 }
 
-void cmStateDirectory::SetIncludeDirectories(const std::string& vec,
-                                             const cmListFileBacktrace& lfbt)
+void cmStateDirectory::SetIncludeDirectories(const BT<std::string>& vec)
 {
   SetContent(this->DirectoryState->IncludeDirectories,
-             this->DirectoryState->IncludeDirectoryBacktraces,
-             this->Snapshot_.Position->IncludeDirectoryPosition, vec, lfbt);
+             this->Snapshot_.Position->IncludeDirectoryPosition, vec);
 }
 
 void cmStateDirectory::ClearIncludeDirectories()
 {
   ClearContent(this->DirectoryState->IncludeDirectories,
-               this->DirectoryState->IncludeDirectoryBacktraces,
                this->Snapshot_.Position->IncludeDirectoryPosition);
 }
 
-cmStringRange cmStateDirectory::GetCompileDefinitionsEntries() const
+cmBTStringRange cmStateDirectory::GetCompileDefinitionsEntries() const
 {
   return GetPropertyContent(
     this->DirectoryState->CompileDefinitions,
     this->Snapshot_.Position->CompileDefinitionsPosition);
 }
 
-cmBacktraceRange cmStateDirectory::GetCompileDefinitionsEntryBacktraces() const
-{
-  return GetPropertyBacktraces(
-    this->DirectoryState->CompileDefinitions,
-    this->DirectoryState->CompileDefinitionsBacktraces,
-    this->Snapshot_.Position->CompileDefinitionsPosition);
-}
-
 void cmStateDirectory::AppendCompileDefinitionsEntry(
-  const std::string& vec, const cmListFileBacktrace& lfbt)
+  const BT<std::string>& vec)
 {
   AppendEntry(this->DirectoryState->CompileDefinitions,
-              this->DirectoryState->CompileDefinitionsBacktraces,
-              this->Snapshot_.Position->CompileDefinitionsPosition, vec, lfbt);
+              this->Snapshot_.Position->CompileDefinitionsPosition, vec);
 }
 
-void cmStateDirectory::SetCompileDefinitions(const std::string& vec,
-                                             const cmListFileBacktrace& lfbt)
+void cmStateDirectory::SetCompileDefinitions(const BT<std::string>& vec)
 {
   SetContent(this->DirectoryState->CompileDefinitions,
-             this->DirectoryState->CompileDefinitionsBacktraces,
-             this->Snapshot_.Position->CompileDefinitionsPosition, vec, lfbt);
+             this->Snapshot_.Position->CompileDefinitionsPosition, vec);
 }
 
 void cmStateDirectory::ClearCompileDefinitions()
 {
   ClearContent(this->DirectoryState->CompileDefinitions,
-               this->DirectoryState->CompileDefinitionsBacktraces,
                this->Snapshot_.Position->CompileDefinitionsPosition);
 }
 
-cmStringRange cmStateDirectory::GetCompileOptionsEntries() const
+cmBTStringRange cmStateDirectory::GetCompileOptionsEntries() const
 {
   return GetPropertyContent(this->DirectoryState->CompileOptions,
                             this->Snapshot_.Position->CompileOptionsPosition);
 }
 
-cmBacktraceRange cmStateDirectory::GetCompileOptionsEntryBacktraces() const
-{
-  return GetPropertyBacktraces(
-    this->DirectoryState->CompileOptions,
-    this->DirectoryState->CompileOptionsBacktraces,
-    this->Snapshot_.Position->CompileOptionsPosition);
-}
-
-void cmStateDirectory::AppendCompileOptionsEntry(
-  const std::string& vec, const cmListFileBacktrace& lfbt)
+void cmStateDirectory::AppendCompileOptionsEntry(const BT<std::string>& vec)
 {
   AppendEntry(this->DirectoryState->CompileOptions,
-              this->DirectoryState->CompileOptionsBacktraces,
-              this->Snapshot_.Position->CompileOptionsPosition, vec, lfbt);
+              this->Snapshot_.Position->CompileOptionsPosition, vec);
 }
 
-void cmStateDirectory::SetCompileOptions(const std::string& vec,
-                                         const cmListFileBacktrace& lfbt)
+void cmStateDirectory::SetCompileOptions(const BT<std::string>& vec)
 {
   SetContent(this->DirectoryState->CompileOptions,
-             this->DirectoryState->CompileOptionsBacktraces,
-             this->Snapshot_.Position->CompileOptionsPosition, vec, lfbt);
+             this->Snapshot_.Position->CompileOptionsPosition, vec);
 }
 
 void cmStateDirectory::ClearCompileOptions()
 {
   ClearContent(this->DirectoryState->CompileOptions,
-               this->DirectoryState->CompileOptionsBacktraces,
                this->Snapshot_.Position->CompileOptionsPosition);
 }
 
-cmStringRange cmStateDirectory::GetLinkOptionsEntries() const
+cmBTStringRange cmStateDirectory::GetLinkOptionsEntries() const
 {
   return GetPropertyContent(this->DirectoryState->LinkOptions,
                             this->Snapshot_.Position->LinkOptionsPosition);
 }
 
-cmBacktraceRange cmStateDirectory::GetLinkOptionsEntryBacktraces() const
-{
-  return GetPropertyBacktraces(this->DirectoryState->LinkOptions,
-                               this->DirectoryState->LinkOptionsBacktraces,
-                               this->Snapshot_.Position->LinkOptionsPosition);
-}
-
-void cmStateDirectory::AppendLinkOptionsEntry(const std::string& vec,
-                                              const cmListFileBacktrace& lfbt)
+void cmStateDirectory::AppendLinkOptionsEntry(const BT<std::string>& vec)
 {
   AppendEntry(this->DirectoryState->LinkOptions,
-              this->DirectoryState->LinkOptionsBacktraces,
-              this->Snapshot_.Position->LinkOptionsPosition, vec, lfbt);
+              this->Snapshot_.Position->LinkOptionsPosition, vec);
 }
 
-void cmStateDirectory::SetLinkOptions(const std::string& vec,
-                                      const cmListFileBacktrace& lfbt)
+void cmStateDirectory::SetLinkOptions(const BT<std::string>& vec)
 {
   SetContent(this->DirectoryState->LinkOptions,
-             this->DirectoryState->LinkOptionsBacktraces,
-             this->Snapshot_.Position->LinkOptionsPosition, vec, lfbt);
+             this->Snapshot_.Position->LinkOptionsPosition, vec);
 }
 
 void cmStateDirectory::ClearLinkOptions()
 {
   ClearContent(this->DirectoryState->LinkOptions,
-               this->DirectoryState->LinkOptionsBacktraces,
                this->Snapshot_.Position->LinkOptionsPosition);
 }
 
-cmStringRange cmStateDirectory::GetLinkDirectoriesEntries() const
+cmBTStringRange cmStateDirectory::GetLinkDirectoriesEntries() const
 {
   return GetPropertyContent(this->DirectoryState->LinkDirectories,
                             this->Snapshot_.Position->LinkDirectoriesPosition);
 }
 
-cmBacktraceRange cmStateDirectory::GetLinkDirectoriesEntryBacktraces() const
-{
-  return GetPropertyBacktraces(
-    this->DirectoryState->LinkDirectories,
-    this->DirectoryState->LinkDirectoriesBacktraces,
-    this->Snapshot_.Position->LinkDirectoriesPosition);
-}
-
-void cmStateDirectory::AppendLinkDirectoriesEntry(
-  const std::string& vec, const cmListFileBacktrace& lfbt)
+void cmStateDirectory::AppendLinkDirectoriesEntry(const BT<std::string>& vec)
 {
   AppendEntry(this->DirectoryState->LinkDirectories,
-              this->DirectoryState->LinkDirectoriesBacktraces,
-              this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt);
+              this->Snapshot_.Position->LinkDirectoriesPosition, vec);
 }
-void cmStateDirectory::PrependLinkDirectoriesEntry(
-  const std::string& vec, const cmListFileBacktrace& lfbt)
+void cmStateDirectory::PrependLinkDirectoriesEntry(const BT<std::string>& vec)
 {
   auto entryEnd = this->DirectoryState->LinkDirectories.begin() +
     this->Snapshot_.Position->LinkDirectoriesPosition;
@@ -334,42 +251,35 @@
   rbegin = std::find(rbegin, rend, cmPropertySentinal);
 
   auto entryIt = rbegin.base();
-  auto entryBegin = this->DirectoryState->LinkDirectories.begin();
-
-  auto btIt = this->DirectoryState->LinkDirectoriesBacktraces.begin() +
-    std::distance(entryBegin, entryIt);
 
   this->DirectoryState->LinkDirectories.insert(entryIt, vec);
-  this->DirectoryState->LinkDirectoriesBacktraces.insert(btIt, lfbt);
 
   this->Snapshot_.Position->LinkDirectoriesPosition =
     this->DirectoryState->LinkDirectories.size();
 }
 
-void cmStateDirectory::SetLinkDirectories(const std::string& vec,
-                                          const cmListFileBacktrace& lfbt)
+void cmStateDirectory::SetLinkDirectories(const BT<std::string>& vec)
 {
   SetContent(this->DirectoryState->LinkDirectories,
-             this->DirectoryState->LinkDirectoriesBacktraces,
-             this->Snapshot_.Position->LinkDirectoriesPosition, vec, lfbt);
+             this->Snapshot_.Position->LinkDirectoriesPosition, vec);
 }
 
 void cmStateDirectory::ClearLinkDirectories()
 {
   ClearContent(this->DirectoryState->LinkDirectories,
-               this->DirectoryState->LinkDirectoriesBacktraces,
                this->Snapshot_.Position->LinkDirectoriesPosition);
 }
 
-void cmStateDirectory::SetProperty(const std::string& prop, const char* value,
-                                   cmListFileBacktrace const& lfbt)
+template <typename ValueType>
+void cmStateDirectory::StoreProperty(const std::string& prop, ValueType value,
+                                     cmListFileBacktrace const& lfbt)
 {
   if (prop == "INCLUDE_DIRECTORIES") {
     if (!value) {
       this->ClearIncludeDirectories();
       return;
     }
-    this->SetIncludeDirectories(value, lfbt);
+    this->SetIncludeDirectories(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "COMPILE_OPTIONS") {
@@ -377,7 +287,7 @@
       this->ClearCompileOptions();
       return;
     }
-    this->SetCompileOptions(value, lfbt);
+    this->SetCompileOptions(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "COMPILE_DEFINITIONS") {
@@ -385,7 +295,7 @@
       this->ClearCompileDefinitions();
       return;
     }
-    this->SetCompileDefinitions(value, lfbt);
+    this->SetCompileDefinitions(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "LINK_OPTIONS") {
@@ -393,7 +303,7 @@
       this->ClearLinkOptions();
       return;
     }
-    this->SetLinkOptions(value, lfbt);
+    this->SetLinkOptions(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "LINK_DIRECTORIES") {
@@ -401,66 +311,78 @@
       this->ClearLinkDirectories();
       return;
     }
-    this->SetLinkDirectories(value, lfbt);
+    this->SetLinkDirectories(BT<std::string>(value, lfbt));
     return;
   }
 
   this->DirectoryState->Properties.SetProperty(prop, value);
 }
 
+void cmStateDirectory::SetProperty(const std::string& prop, const char* value,
+                                   cmListFileBacktrace const& lfbt)
+{
+  this->StoreProperty(prop, value, lfbt);
+}
+void cmStateDirectory::SetProperty(const std::string& prop, cmValue value,
+                                   cmListFileBacktrace const& lfbt)
+{
+  this->StoreProperty(prop, value, lfbt);
+}
+
 void cmStateDirectory::AppendProperty(const std::string& prop,
                                       const std::string& value, bool asString,
                                       cmListFileBacktrace const& lfbt)
 {
   if (prop == "INCLUDE_DIRECTORIES") {
-    this->AppendIncludeDirectoriesEntry(value, lfbt);
+    this->AppendIncludeDirectoriesEntry(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "COMPILE_OPTIONS") {
-    this->AppendCompileOptionsEntry(value, lfbt);
+    this->AppendCompileOptionsEntry(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "COMPILE_DEFINITIONS") {
-    this->AppendCompileDefinitionsEntry(value, lfbt);
+    this->AppendCompileDefinitionsEntry(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "LINK_OPTIONS") {
-    this->AppendLinkOptionsEntry(value, lfbt);
+    this->AppendLinkOptionsEntry(BT<std::string>(value, lfbt));
     return;
   }
   if (prop == "LINK_DIRECTORIES") {
-    this->AppendLinkDirectoriesEntry(value, lfbt);
+    this->AppendLinkDirectoriesEntry(BT<std::string>(value, lfbt));
     return;
   }
 
   this->DirectoryState->Properties.AppendProperty(prop, value, asString);
 }
 
-cmProp cmStateDirectory::GetProperty(const std::string& prop) const
+cmValue cmStateDirectory::GetProperty(const std::string& prop) const
 {
   const bool chain =
     this->Snapshot_.State->IsPropertyChained(prop, cmProperty::DIRECTORY);
   return this->GetProperty(prop, chain);
 }
 
-cmProp cmStateDirectory::GetProperty(const std::string& prop, bool chain) const
+cmValue cmStateDirectory::GetProperty(const std::string& prop,
+                                      bool chain) const
 {
   static std::string output;
   output.clear();
   if (prop == "PARENT_DIRECTORY") {
     cmStateSnapshot parent = this->Snapshot_.GetBuildsystemDirectoryParent();
     if (parent.IsValid()) {
-      return &parent.GetDirectory().GetCurrentSource();
+      return cmValue(parent.GetDirectory().GetCurrentSource());
     }
-    return &output;
+    return cmValue(output);
   }
   if (prop == kBINARY_DIR) {
     output = this->GetCurrentBinary();
-    return &output;
+    return cmValue(output);
   }
   if (prop == kSOURCE_DIR) {
     output = this->GetCurrentSource();
-    return &output;
+    return cmValue(output);
   }
   if (prop == kSUBDIRECTORIES) {
     std::vector<std::string> child_dirs;
@@ -471,15 +393,15 @@
       child_dirs.push_back(ci.GetDirectory().GetCurrentSource());
     }
     output = cmJoin(child_dirs, ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == kBUILDSYSTEM_TARGETS) {
     output = cmJoin(this->DirectoryState->NormalTargetNames, ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "IMPORTED_TARGETS"_s) {
     output = cmJoin(this->DirectoryState->ImportedTargetNames, ";");
-    return &output;
+    return cmValue(output);
   }
 
   if (prop == "LISTFILE_STACK") {
@@ -491,41 +413,41 @@
     }
     std::reverse(listFiles.begin(), listFiles.end());
     output = cmJoin(listFiles, ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "CACHE_VARIABLES") {
     output = cmJoin(this->Snapshot_.State->GetCacheEntryKeys(), ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "VARIABLES") {
     std::vector<std::string> res = this->Snapshot_.ClosureKeys();
     cm::append(res, this->Snapshot_.State->GetCacheEntryKeys());
     std::sort(res.begin(), res.end());
     output = cmJoin(res, ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "INCLUDE_DIRECTORIES") {
     output = cmJoin(this->GetIncludeDirectoriesEntries(), ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "COMPILE_OPTIONS") {
     output = cmJoin(this->GetCompileOptionsEntries(), ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "COMPILE_DEFINITIONS") {
     output = cmJoin(this->GetCompileDefinitionsEntries(), ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "LINK_OPTIONS") {
     output = cmJoin(this->GetLinkOptionsEntries(), ";");
-    return &output;
+    return cmValue(output);
   }
   if (prop == "LINK_DIRECTORIES") {
     output = cmJoin(this->GetLinkDirectoriesEntries(), ";");
-    return &output;
+    return cmValue(output);
   }
 
-  cmProp retVal = this->DirectoryState->Properties.GetPropertyValue(prop);
+  cmValue retVal = this->DirectoryState->Properties.GetPropertyValue(prop);
   if (!retVal && chain) {
     cmStateSnapshot parentSnapshot =
       this->Snapshot_.GetBuildsystemDirectoryParent();
diff --git a/Source/cmStateDirectory.h b/Source/cmStateDirectory.h
index b8abccb..6429f32 100644
--- a/Source/cmStateDirectory.h
+++ b/Source/cmStateDirectory.h
@@ -11,10 +11,9 @@
 #include "cmAlgorithms.h"
 #include "cmLinkedTree.h"
 #include "cmListFileCache.h"
-#include "cmProperty.h"
 #include "cmStatePrivate.h"
 #include "cmStateSnapshot.h"
-#include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 class cmStateDirectory
 {
@@ -28,55 +27,41 @@
   std::string const& GetCurrentBinary() const;
   void SetCurrentBinary(std::string const& dir);
 
-  cmStringRange GetIncludeDirectoriesEntries() const;
-  cmBacktraceRange GetIncludeDirectoriesEntryBacktraces() const;
-  void AppendIncludeDirectoriesEntry(std::string const& vec,
-                                     cmListFileBacktrace const& lfbt);
-  void PrependIncludeDirectoriesEntry(std::string const& vec,
-                                      cmListFileBacktrace const& lfbt);
-  void SetIncludeDirectories(std::string const& vec,
-                             cmListFileBacktrace const& lfbt);
+  cmBTStringRange GetIncludeDirectoriesEntries() const;
+  void AppendIncludeDirectoriesEntry(BT<std::string> const& vec);
+  void PrependIncludeDirectoriesEntry(BT<std::string> const& vec);
+  void SetIncludeDirectories(BT<std::string> const& vec);
   void ClearIncludeDirectories();
 
-  cmStringRange GetCompileDefinitionsEntries() const;
-  cmBacktraceRange GetCompileDefinitionsEntryBacktraces() const;
-  void AppendCompileDefinitionsEntry(std::string const& vec,
-                                     cmListFileBacktrace const& lfbt);
-  void SetCompileDefinitions(std::string const& vec,
-                             cmListFileBacktrace const& lfbt);
+  cmBTStringRange GetCompileDefinitionsEntries() const;
+  void AppendCompileDefinitionsEntry(BT<std::string> const& vec);
+  void SetCompileDefinitions(BT<std::string> const& vec);
   void ClearCompileDefinitions();
 
-  cmStringRange GetCompileOptionsEntries() const;
-  cmBacktraceRange GetCompileOptionsEntryBacktraces() const;
-  void AppendCompileOptionsEntry(std::string const& vec,
-                                 cmListFileBacktrace const& lfbt);
-  void SetCompileOptions(std::string const& vec,
-                         cmListFileBacktrace const& lfbt);
+  cmBTStringRange GetCompileOptionsEntries() const;
+  void AppendCompileOptionsEntry(BT<std::string> const& vec);
+  void SetCompileOptions(BT<std::string> const& vec);
   void ClearCompileOptions();
 
-  cmStringRange GetLinkOptionsEntries() const;
-  cmBacktraceRange GetLinkOptionsEntryBacktraces() const;
-  void AppendLinkOptionsEntry(std::string const& vec,
-                              cmListFileBacktrace const& lfbt);
-  void PrependLinkDirectoriesEntry(std::string const& vec,
-                                   cmListFileBacktrace const& lfbt);
-  void SetLinkOptions(std::string const& vec, cmListFileBacktrace const& lfbt);
+  cmBTStringRange GetLinkOptionsEntries() const;
+  void AppendLinkOptionsEntry(BT<std::string> const& vec);
+  void PrependLinkDirectoriesEntry(BT<std::string> const& vec);
+  void SetLinkOptions(BT<std::string> const& vec);
   void ClearLinkOptions();
 
-  cmStringRange GetLinkDirectoriesEntries() const;
-  cmBacktraceRange GetLinkDirectoriesEntryBacktraces() const;
-  void AppendLinkDirectoriesEntry(std::string const& vec,
-                                  cmListFileBacktrace const& lfbt);
-  void SetLinkDirectories(std::string const& vec,
-                          cmListFileBacktrace const& lfbt);
+  cmBTStringRange GetLinkDirectoriesEntries() const;
+  void AppendLinkDirectoriesEntry(BT<std::string> const& vec);
+  void SetLinkDirectories(BT<std::string> const& vecs);
   void ClearLinkDirectories();
 
   void SetProperty(const std::string& prop, const char* value,
                    cmListFileBacktrace const& lfbt);
+  void SetProperty(const std::string& prop, cmValue value,
+                   cmListFileBacktrace const& lfbt);
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString, cmListFileBacktrace const& lfbt);
-  cmProp GetProperty(const std::string& prop) const;
-  cmProp GetProperty(const std::string& prop, bool chain) const;
+  cmValue GetProperty(const std::string& prop) const;
+  cmValue GetProperty(const std::string& prop, bool chain) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   std::vector<std::string> GetPropertyKeys() const;
 
@@ -84,6 +69,10 @@
   void AddImportedTargetName(std::string const& name);
 
 private:
+  template <typename ValueType>
+  void StoreProperty(const std::string& prop, ValueType value,
+                     cmListFileBacktrace const& lfbt);
+
   cmLinkedTree<cmStateDetail::BuildsystemDirectoryStateType>::iterator
     DirectoryState;
   cmStateSnapshot Snapshot_;
diff --git a/Source/cmStatePrivate.h b/Source/cmStatePrivate.h
index 6f475f2..fd46eed 100644
--- a/Source/cmStatePrivate.h
+++ b/Source/cmStatePrivate.h
@@ -67,20 +67,15 @@
   std::string Location;
   std::string OutputLocation;
 
-  std::vector<std::string> IncludeDirectories;
-  std::vector<cmListFileBacktrace> IncludeDirectoryBacktraces;
+  std::vector<BT<std::string>> IncludeDirectories;
 
-  std::vector<std::string> CompileDefinitions;
-  std::vector<cmListFileBacktrace> CompileDefinitionsBacktraces;
+  std::vector<BT<std::string>> CompileDefinitions;
 
-  std::vector<std::string> CompileOptions;
-  std::vector<cmListFileBacktrace> CompileOptionsBacktraces;
+  std::vector<BT<std::string>> CompileOptions;
 
-  std::vector<std::string> LinkOptions;
-  std::vector<cmListFileBacktrace> LinkOptionsBacktraces;
+  std::vector<BT<std::string>> LinkOptions;
 
-  std::vector<std::string> LinkDirectories;
-  std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces;
+  std::vector<BT<std::string>> LinkDirectories;
 
   std::vector<std::string> NormalTargetNames;
   std::vector<std::string> ImportedTargetNames;
diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx
index fbf47ef..f73df8f 100644
--- a/Source/cmStateSnapshot.cxx
+++ b/Source/cmStateSnapshot.cxx
@@ -11,14 +11,18 @@
 
 #include "cmDefinitions.h"
 #include "cmListFileCache.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStatePrivate.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVersion.h"
 
+#if defined(__CYGWIN__)
+#  include "cmStringAlgorithms.h"
+#endif
+
 cmStateSnapshot::cmStateSnapshot(cmState* state)
   : State(state)
 {
@@ -201,8 +205,7 @@
   return !this->Position->Policies->IsEmpty();
 }
 
-std::string const* cmStateSnapshot::GetDefinition(
-  std::string const& name) const
+cmValue cmStateSnapshot::GetDefinition(std::string const& name) const
 {
   assert(this->Position->Vars.IsValid());
   return cmDefinitions::Get(name, this->Position->Vars, this->Position->Root);
@@ -260,12 +263,10 @@
   return true;
 }
 
-template <typename T, typename U, typename V>
+template <typename T, typename U>
 void InitializeContentFromParent(T& parentContent, T& thisContent,
-                                 U& parentBacktraces, U& thisBacktraces,
-                                 V& contentEndPosition)
+                                 U& contentEndPosition)
 {
-  auto parentBegin = parentContent.begin();
   auto parentEnd = parentContent.end();
 
   auto parentRbegin = cm::make_reverse_iterator(parentEnd);
@@ -273,12 +274,7 @@
   parentRbegin = std::find(parentRbegin, parentRend, cmPropertySentinal);
   auto parentIt = parentRbegin.base();
 
-  thisContent = std::vector<std::string>(parentIt, parentEnd);
-
-  auto btIt = parentBacktraces.begin() + std::distance(parentBegin, parentIt);
-  auto btEnd = parentBacktraces.end();
-
-  thisBacktraces = std::vector<cmListFileBacktrace>(btIt, btEnd);
+  thisContent = std::vector<BT<std::string>>(parentIt, parentEnd);
 
   contentEndPosition = thisContent.size();
 }
@@ -360,43 +356,33 @@
   InitializeContentFromParent(
     parent->BuildSystemDirectory->IncludeDirectories,
     this->Position->BuildSystemDirectory->IncludeDirectories,
-    parent->BuildSystemDirectory->IncludeDirectoryBacktraces,
-    this->Position->BuildSystemDirectory->IncludeDirectoryBacktraces,
     this->Position->IncludeDirectoryPosition);
 
   InitializeContentFromParent(
     parent->BuildSystemDirectory->CompileDefinitions,
     this->Position->BuildSystemDirectory->CompileDefinitions,
-    parent->BuildSystemDirectory->CompileDefinitionsBacktraces,
-    this->Position->BuildSystemDirectory->CompileDefinitionsBacktraces,
     this->Position->CompileDefinitionsPosition);
 
   InitializeContentFromParent(
     parent->BuildSystemDirectory->CompileOptions,
     this->Position->BuildSystemDirectory->CompileOptions,
-    parent->BuildSystemDirectory->CompileOptionsBacktraces,
-    this->Position->BuildSystemDirectory->CompileOptionsBacktraces,
     this->Position->CompileOptionsPosition);
 
   InitializeContentFromParent(
     parent->BuildSystemDirectory->LinkOptions,
     this->Position->BuildSystemDirectory->LinkOptions,
-    parent->BuildSystemDirectory->LinkOptionsBacktraces,
-    this->Position->BuildSystemDirectory->LinkOptionsBacktraces,
     this->Position->LinkOptionsPosition);
 
   InitializeContentFromParent(
     parent->BuildSystemDirectory->LinkDirectories,
     this->Position->BuildSystemDirectory->LinkDirectories,
-    parent->BuildSystemDirectory->LinkDirectoriesBacktraces,
-    this->Position->BuildSystemDirectory->LinkDirectoriesBacktraces,
     this->Position->LinkDirectoriesPosition);
 
-  cmProp include_regex =
+  cmValue include_regex =
     parent->BuildSystemDirectory->Properties.GetPropertyValue(
       "INCLUDE_REGULAR_EXPRESSION");
   this->Position->BuildSystemDirectory->Properties.SetProperty(
-    "INCLUDE_REGULAR_EXPRESSION", cmToCStr(include_regex));
+    "INCLUDE_REGULAR_EXPRESSION", include_regex);
 }
 
 cmState* cmStateSnapshot::GetState() const
diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h
index d06cba3..a61ec83 100644
--- a/Source/cmStateSnapshot.h
+++ b/Source/cmStateSnapshot.h
@@ -13,6 +13,7 @@
 #include "cmLinkedTree.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
+#include "cmValue.h"
 
 class cmState;
 class cmStateDirectory;
@@ -23,7 +24,7 @@
   cmStateSnapshot(cmState* state = nullptr);
   cmStateSnapshot(cmState* state, cmStateDetail::PositionType position);
 
-  std::string const* GetDefinition(std::string const& name) const;
+  cmValue GetDefinition(std::string const& name) const;
   bool IsInitialized(std::string const& name) const;
   void SetDefinition(std::string const& name, cm::string_view value);
   void RemoveDefinition(std::string const& name);
diff --git a/Source/cmStringAlgorithms.cxx b/Source/cmStringAlgorithms.cxx
index 5bb6e7b..1bb6808 100644
--- a/Source/cmStringAlgorithms.cxx
+++ b/Source/cmStringAlgorithms.cxx
@@ -218,85 +218,6 @@
   return result;
 }
 
-bool cmIsInternallyOn(cm::string_view val)
-{
-  return (val.size() == 4) &&           //
-    (val[0] == 'I' || val[0] == 'i') && //
-    (val[1] == '_') &&                  //
-    (val[2] == 'O' || val[2] == 'o') && //
-    (val[3] == 'N' || val[3] == 'n');
-}
-
-bool cmIsNOTFOUND(cm::string_view val)
-{
-  return (val == "NOTFOUND") || cmHasLiteralSuffix(val, "-NOTFOUND");
-}
-
-bool cmIsOn(cm::string_view val)
-{
-  switch (val.size()) {
-    case 1:
-      return val[0] == '1' || val[0] == 'Y' || val[0] == 'y';
-    case 2:
-      return                                //
-        (val[0] == 'O' || val[0] == 'o') && //
-        (val[1] == 'N' || val[1] == 'n');
-    case 3:
-      return                                //
-        (val[0] == 'Y' || val[0] == 'y') && //
-        (val[1] == 'E' || val[1] == 'e') && //
-        (val[2] == 'S' || val[2] == 's');
-    case 4:
-      return                                //
-        (val[0] == 'T' || val[0] == 't') && //
-        (val[1] == 'R' || val[1] == 'r') && //
-        (val[2] == 'U' || val[2] == 'u') && //
-        (val[3] == 'E' || val[3] == 'e');
-    default:
-      break;
-  }
-
-  return false;
-}
-
-bool cmIsOff(cm::string_view val)
-{
-  switch (val.size()) {
-    case 0:
-      return true;
-    case 1:
-      return val[0] == '0' || val[0] == 'N' || val[0] == 'n';
-    case 2:
-      return                                //
-        (val[0] == 'N' || val[0] == 'n') && //
-        (val[1] == 'O' || val[1] == 'o');
-    case 3:
-      return                                //
-        (val[0] == 'O' || val[0] == 'o') && //
-        (val[1] == 'F' || val[1] == 'f') && //
-        (val[2] == 'F' || val[2] == 'f');
-    case 5:
-      return                                //
-        (val[0] == 'F' || val[0] == 'f') && //
-        (val[1] == 'A' || val[1] == 'a') && //
-        (val[2] == 'L' || val[2] == 'l') && //
-        (val[3] == 'S' || val[3] == 's') && //
-        (val[4] == 'E' || val[4] == 'e');
-    case 6:
-      return                                //
-        (val[0] == 'I' || val[0] == 'i') && //
-        (val[1] == 'G' || val[1] == 'g') && //
-        (val[2] == 'N' || val[2] == 'n') && //
-        (val[3] == 'O' || val[3] == 'o') && //
-        (val[4] == 'R' || val[4] == 'r') && //
-        (val[5] == 'E' || val[5] == 'e');
-    default:
-      break;
-  }
-
-  return cmIsNOTFOUND(val);
-}
-
 bool cmStrToLong(const char* str, long* value)
 {
   errno = 0;
diff --git a/Source/cmStringAlgorithms.h b/Source/cmStringAlgorithms.h
index 6b458ec..492e588 100644
--- a/Source/cmStringAlgorithms.h
+++ b/Source/cmStringAlgorithms.h
@@ -15,24 +15,11 @@
 #include <cm/string_view>
 
 #include "cmRange.h"
+#include "cmValue.h"
 
 /** String range type.  */
 using cmStringRange = cmRange<std::vector<std::string>::const_iterator>;
 
-/** Check for non-empty string.  */
-inline bool cmNonempty(const char* str)
-{
-  return str && *str;
-}
-inline bool cmNonempty(cm::string_view str)
-{
-  return !str.empty();
-}
-inline bool cmNonempty(std::string const* str)
-{
-  return str && !str->empty();
-}
-
 /** Returns length of a literal string.  */
 template <size_t N>
 constexpr size_t cmStrLen(const char (&/*str*/)[N])
@@ -107,6 +94,13 @@
  */
 void cmExpandList(cm::string_view arg, std::vector<std::string>& argsOut,
                   bool emptyArgs = false);
+inline void cmExpandList(cmValue arg, std::vector<std::string>& argsOut,
+                         bool emptyArgs = false)
+{
+  if (arg) {
+    cmExpandList(*arg, argsOut, emptyArgs);
+  }
+}
 
 /**
  * Expand out any arguments in the string range [@a first, @a last) that have
@@ -128,6 +122,14 @@
  */
 std::vector<std::string> cmExpandedList(cm::string_view arg,
                                         bool emptyArgs = false);
+inline std::vector<std::string> cmExpandedList(cmValue arg,
+                                               bool emptyArgs = false)
+{
+  if (!arg) {
+    return {};
+  }
+  return cmExpandedList(*arg, emptyArgs);
+}
 
 /**
  * Same as cmExpandList but a new vector is created containing the expanded
@@ -175,6 +177,10 @@
   cmAlphaNum(unsigned long long int val);
   cmAlphaNum(float val);
   cmAlphaNum(double val);
+  cmAlphaNum(cmValue value)
+    : View_(*value)
+  {
+  }
 
   cm::string_view View() const { return this->View_; }
 
@@ -213,53 +219,6 @@
                 sep);
 }
 
-/**
- * Does a string indicates that CMake/CPack/CTest internally
- * forced this value. This is not the same as On, but this
- * may be considered as "internally switched on".
- */
-bool cmIsInternallyOn(cm::string_view val);
-inline bool cmIsInternallyOn(const char* val)
-{
-  if (!val) {
-    return false;
-  }
-  return cmIsInternallyOn(cm::string_view(val));
-}
-
-/** Return true if value is NOTFOUND or ends in -NOTFOUND.  */
-bool cmIsNOTFOUND(cm::string_view val);
-
-/**
- * Does a string indicate a true or ON value? This is not the same as ifdef.
- */
-bool cmIsOn(cm::string_view val);
-inline bool cmIsOn(const char* val)
-{
-  return val && cmIsOn(cm::string_view(val));
-}
-inline bool cmIsOn(std::string const* val)
-{
-  return val && cmIsOn(*val);
-}
-
-/**
- * Does a string indicate a false or off value ? Note that this is
- * not the same as !IsOn(...) because there are a number of
- * ambiguous values such as "/usr/local/bin" a path will result in
- * IsON and IsOff both returning false. Note that the special path
- * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
- */
-bool cmIsOff(cm::string_view val);
-inline bool cmIsOff(const char* val)
-{
-  return !val || cmIsOff(cm::string_view(val));
-}
-inline bool cmIsOff(std::string const* val)
-{
-  return !val || cmIsOff(*val);
-}
-
 /** Returns true if string @a str starts with the character @a prefix.  */
 inline bool cmHasPrefix(cm::string_view str, char prefix)
 {
@@ -273,6 +232,16 @@
 }
 
 /** Returns true if string @a str starts with string @a prefix.  */
+inline bool cmHasPrefix(cm::string_view str, cmValue prefix)
+{
+  if (!prefix) {
+    return false;
+  }
+
+  return str.compare(0, prefix->size(), *prefix) == 0;
+}
+
+/** Returns true if string @a str starts with string @a prefix.  */
 template <size_t N>
 inline bool cmHasLiteralPrefix(cm::string_view str, const char (&prefix)[N])
 {
@@ -293,6 +262,17 @@
 }
 
 /** Returns true if string @a str ends with string @a suffix.  */
+inline bool cmHasSuffix(cm::string_view str, cmValue suffix)
+{
+  if (!suffix) {
+    return false;
+  }
+
+  return str.size() >= suffix->size() &&
+    str.compare(str.size() - suffix->size(), suffix->size(), *suffix) == 0;
+}
+
+/** Returns true if string @a str ends with string @a suffix.  */
 template <size_t N>
 inline bool cmHasLiteralSuffix(cm::string_view str, const char (&suffix)[N])
 {
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 5fa309d..f44fcf7 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -31,7 +31,6 @@
 #include "cmGeneratorExpression.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
@@ -39,6 +38,7 @@
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmUuid.h"
+#include "cmValue.h"
 
 namespace {
 
@@ -572,7 +572,7 @@
   const std::string& variable = args[1];
 
   std::string value = cmJoin(cmMakeRange(args).advance(2), std::string());
-  cmProp oldValue = status.GetMakefile().GetDefinition(variable);
+  cmValue oldValue = status.GetMakefile().GetDefinition(variable);
   if (oldValue) {
     value += *oldValue;
   }
@@ -1054,7 +1054,7 @@
     }
   }
   return *search;
-};
+}
 
 Json::Value ReadJson(const std::string& jsonstr)
 {
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 10d2e50..1934393 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -20,10 +20,12 @@
 #include <cm3p/uv.h>
 
 #include "cmDuration.h"
+#include "cmELF.h"
 #include "cmMessageMetadata.h"
 #include "cmProcessOutput.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
 #  include <cm3p/archive.h>
@@ -46,10 +48,6 @@
 #  include "cmCryptoHash.h"
 #endif
 
-#if defined(CMake_USE_ELF_PARSER)
-#  include "cmELF.h"
-#endif
-
 #if defined(CMake_USE_MACH_PARSER)
 #  include "cmMachO.h"
 #endif
@@ -1595,6 +1593,12 @@
           cmHasLiteralSuffix(path, ".framework"));
 }
 
+bool cmSystemTools::IsPathToMacOSSharedLibrary(const std::string& path)
+{
+  return (cmSystemTools::FileIsFullPath(path) &&
+          cmHasLiteralSuffix(path, ".dylib"));
+}
+
 bool cmSystemTools::CreateTar(const std::string& outFileName,
                               const std::vector<std::string>& files,
                               cmTarCompression compressType, bool verbose,
@@ -1632,7 +1636,10 @@
   cmArchiveWrite a(fout, compress, format.empty() ? "paxr" : format,
                    compressionLevel);
 
-  a.Open();
+  if (!a.Open()) {
+    cmSystemTools::Error(a.GetError());
+    return false;
+  }
   a.SetMTime(mtime);
   a.SetVerbose(verbose);
   bool tarCreatedSuccessfully = true;
@@ -2446,14 +2453,12 @@
 bool cmSystemTools::GuessLibrarySOName(std::string const& fullPath,
                                        std::string& soname)
 {
-// For ELF shared libraries use a real parser to get the correct
-// soname.
-#if defined(CMake_USE_ELF_PARSER)
+  // For ELF shared libraries use a real parser to get the correct
+  // soname.
   cmELF elf(fullPath.c_str());
   if (elf) {
     return elf.GetSOName(soname);
   }
-#endif
 
   // If the file is not a symlink we have no guess for its soname.
   if (!cmSystemTools::FileIsSymlink(fullPath)) {
@@ -2491,7 +2496,6 @@
   return false;
 }
 
-#if defined(CMake_USE_ELF_PARSER) || defined(CMake_USE_XCOFF_PARSER)
 std::string::size_type cmSystemToolsFindRPath(cm::string_view const& have,
                                               cm::string_view const& want)
 {
@@ -2523,9 +2527,7 @@
   // The desired rpath was not found.
   return std::string::npos;
 }
-#endif
 
-#if defined(CMake_USE_ELF_PARSER)
 namespace {
 struct cmSystemToolsRPathInfo
 {
@@ -2539,10 +2541,10 @@
 using AdjustCallback = std::function<bool(
   cm::optional<std::string>&, const std::string&, const char*, std::string*)>;
 
-// FIXME: Dispatch if multiple formats are supported.
-bool AdjustRPath(std::string const& file, const EmptyCallback& emptyCallback,
-                 const AdjustCallback& adjustCallback, std::string* emsg,
-                 bool* changed)
+cm::optional<bool> AdjustRPathELF(std::string const& file,
+                                  const EmptyCallback& emptyCallback,
+                                  const AdjustCallback& adjustCallback,
+                                  std::string* emsg, bool* changed)
 {
   if (changed) {
     *changed = false;
@@ -2553,6 +2555,9 @@
   {
     // Parse the ELF binary.
     cmELF elf(file.c_str());
+    if (!elf) {
+      return cm::nullopt; // Not a valid ELF file.
+    }
 
     // Get the RPATH and RUNPATH entries from it.
     int se_count = 0;
@@ -2686,14 +2691,14 @@
     }
     return false;
   };
-};
+}
 }
 
-bool cmSystemTools::ChangeRPath(std::string const& file,
-                                std::string const& oldRPath,
-                                std::string const& newRPath,
-                                bool removeEnvironmentRPath, std::string* emsg,
-                                bool* changed)
+cm::optional<bool> ChangeRPathELF(std::string const& file,
+                                  std::string const& oldRPath,
+                                  std::string const& newRPath,
+                                  bool removeEnvironmentRPath,
+                                  std::string* emsg, bool* changed)
 {
   auto adjustCallback = [oldRPath, newRPath, removeEnvironmentRPath](
                           cm::optional<std::string>& outRPath,
@@ -2741,13 +2746,13 @@
     return true;
   };
 
-  return AdjustRPath(file, MakeEmptyCallback(newRPath), adjustCallback, emsg,
-                     changed);
+  return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
+                        emsg, changed);
 }
 
-bool cmSystemTools::SetRPath(std::string const& file,
-                             std::string const& newRPath, std::string* emsg,
-                             bool* changed)
+static cm::optional<bool> SetRPathELF(std::string const& file,
+                                      std::string const& newRPath,
+                                      std::string* emsg, bool* changed)
 {
   auto adjustCallback = [newRPath](cm::optional<std::string>& outRPath,
                                    const std::string& inRPath,
@@ -2759,22 +2764,31 @@
     return true;
   };
 
-  return AdjustRPath(file, MakeEmptyCallback(newRPath), adjustCallback, emsg,
-                     changed);
+  return AdjustRPathELF(file, MakeEmptyCallback(newRPath), adjustCallback,
+                        emsg, changed);
 }
-#elif defined(CMake_USE_XCOFF_PARSER)
-bool cmSystemTools::ChangeRPath(std::string const& file,
-                                std::string const& oldRPath,
-                                std::string const& newRPath,
-                                bool removeEnvironmentRPath, std::string* emsg,
-                                bool* changed)
+static cm::optional<bool> ChangeRPathXCOFF(std::string const& file,
+                                           std::string const& oldRPath,
+                                           std::string const& newRPath,
+                                           bool removeEnvironmentRPath,
+                                           std::string* emsg, bool* changed)
 {
   if (changed) {
     *changed = false;
   }
-
+#if !defined(CMake_USE_XCOFF_PARSER)
+  (void)file;
+  (void)oldRPath;
+  (void)newRPath;
+  (void)removeEnvironmentRPath;
+  (void)emsg;
+  return cm::nullopt;
+#else
   bool chg = false;
   cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
+  if (!xcoff) {
+    return cm::nullopt; // Not a valid XCOFF file
+  }
   if (cm::optional<cm::string_view> maybeLibPath = xcoff.GetLibPath()) {
     cm::string_view libPath = *maybeLibPath;
     // Make sure the current rpath contains the old rpath.
@@ -2830,34 +2844,67 @@
     *changed = chg;
   }
   return true;
-}
-
-bool cmSystemTools::SetRPath(std::string const& /*file*/,
-                             std::string const& /*newRPath*/,
-                             std::string* /*emsg*/, bool* /*changed*/)
-{
-  return false;
-}
-#else
-bool cmSystemTools::ChangeRPath(std::string const& /*file*/,
-                                std::string const& /*oldRPath*/,
-                                std::string const& /*newRPath*/,
-                                bool /*removeEnvironmentRPath*/,
-                                std::string* /*emsg*/, bool* /*changed*/)
-{
-  return false;
-}
-
-bool cmSystemTools::SetRPath(std::string const& /*file*/,
-                             std::string const& /*newRPath*/,
-                             std::string* /*emsg*/, bool* /*changed*/)
-{
-  return false;
-}
 #endif
+}
 
-bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
-                                   const char* lhss, const char* rhss)
+static cm::optional<bool> SetRPathXCOFF(std::string const& /*file*/,
+                                        std::string const& /*newRPath*/,
+                                        std::string* /*emsg*/,
+                                        bool* /*changed*/)
+{
+  return cm::nullopt; // Not implemented.
+}
+
+bool cmSystemTools::ChangeRPath(std::string const& file,
+                                std::string const& oldRPath,
+                                std::string const& newRPath,
+                                bool removeEnvironmentRPath, std::string* emsg,
+                                bool* changed)
+{
+  if (cm::optional<bool> result = ChangeRPathELF(
+        file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
+    return result.value();
+  }
+  if (cm::optional<bool> result = ChangeRPathXCOFF(
+        file, oldRPath, newRPath, removeEnvironmentRPath, emsg, changed)) {
+    return result.value();
+  }
+  // The file format is not recognized.  Assume it has no RPATH.
+  if (newRPath.empty()) {
+    // The caller wanted no RPATH anyway.
+    return true;
+  }
+  if (emsg) {
+    *emsg = "The file format is not recognized.";
+  }
+  return false;
+}
+
+bool cmSystemTools::SetRPath(std::string const& file,
+                             std::string const& newRPath, std::string* emsg,
+                             bool* changed)
+{
+  if (cm::optional<bool> result = SetRPathELF(file, newRPath, emsg, changed)) {
+    return result.value();
+  }
+  if (cm::optional<bool> result =
+        SetRPathXCOFF(file, newRPath, emsg, changed)) {
+    return result.value();
+  }
+  // The file format is not recognized.  Assume it has no RPATH.
+  if (newRPath.empty()) {
+    // The caller wanted no RPATH anyway.
+    return true;
+  }
+  if (emsg) {
+    *emsg = "The file format is not recognized.";
+  }
+  return false;
+}
+
+namespace {
+bool VersionCompare(cmSystemTools::CompareOp op, const char* lhss,
+                    const char* rhss)
 {
   const char* endl = lhss;
   const char* endr = rhss;
@@ -2890,26 +2937,37 @@
   // lhs == rhs, so true if operation is EQUAL
   return (op & cmSystemTools::OP_EQUAL) != 0;
 }
+}
+
+bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
+                                   const std::string& lhs,
+                                   const std::string& rhs)
+{
+  return ::VersionCompare(op, lhs.c_str(), rhs.c_str());
+}
+bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op,
+                                   const std::string& lhs, const char rhs[])
+{
+  return ::VersionCompare(op, lhs.c_str(), rhs);
+}
 
 bool cmSystemTools::VersionCompareEqual(std::string const& lhs,
                                         std::string const& rhs)
 {
-  return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, lhs.c_str(),
-                                       rhs.c_str());
+  return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, lhs, rhs);
 }
 
 bool cmSystemTools::VersionCompareGreater(std::string const& lhs,
                                           std::string const& rhs)
 {
-  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, lhs.c_str(),
-                                       rhs.c_str());
+  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, lhs, rhs);
 }
 
 bool cmSystemTools::VersionCompareGreaterEq(std::string const& lhs,
                                             std::string const& rhs)
 {
-  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
-                                       lhs.c_str(), rhs.c_str());
+  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL, lhs,
+                                       rhs);
 }
 
 static size_t cm_strverscmp_find_first_difference_or_end(const char* lhs,
@@ -2989,10 +3047,8 @@
   return cm_strverscmp(lhs.c_str(), rhs.c_str());
 }
 
-// FIXME: Dispatch if multiple formats are supported.
-#if defined(CMake_USE_ELF_PARSER)
-bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
-                                bool* removed)
+static cm::optional<bool> RemoveRPathELF(std::string const& file,
+                                         std::string* emsg, bool* removed)
 {
   if (removed) {
     *removed = false;
@@ -3005,6 +3061,9 @@
   {
     // Parse the ELF binary.
     cmELF elf(file.c_str());
+    if (!elf) {
+      return cm::nullopt; // Not a valid ELF file.
+    }
 
     // Get the RPATH and RUNPATH entries from it and sort them by index
     // in the dynamic section header.
@@ -3054,8 +3113,7 @@
         entriesErased++;
         continue;
       }
-      if (cmELF::TagMipsRldMapRel != 0 &&
-          it->first == cmELF::TagMipsRldMapRel) {
+      if (it->first == cmELF::TagMipsRldMapRel && elf.IsMIPS()) {
         // Background: debuggers need to know the "linker map" which contains
         // the addresses each dynamic object is loaded at. Most arches use
         // the DT_DEBUG tag which the dynamic linker writes to (directly) and
@@ -3131,15 +3189,22 @@
   }
   return true;
 }
-#elif defined(CMake_USE_XCOFF_PARSER)
-bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
-                                bool* removed)
+
+static cm::optional<bool> RemoveRPathXCOFF(std::string const& file,
+                                           std::string* emsg, bool* removed)
 {
   if (removed) {
     *removed = false;
   }
-
+#if !defined(CMake_USE_XCOFF_PARSER)
+  (void)file;
+  (void)emsg;
+  return cm::nullopt; // Cannot handle XCOFF files.
+#else
   cmXCOFF xcoff(file.c_str(), cmXCOFF::Mode::ReadWrite);
+  if (!xcoff) {
+    return cm::nullopt; // Not a valid XCOFF file.
+  }
   bool rm = xcoff.RemoveLibPath();
   if (!xcoff) {
     if (emsg) {
@@ -3152,55 +3217,61 @@
     *removed = rm;
   }
   return true;
-}
-#else
-bool cmSystemTools::RemoveRPath(std::string const& /*file*/,
-                                std::string* /*emsg*/, bool* /*removed*/)
-{
-  return false;
-}
 #endif
+}
+bool cmSystemTools::RemoveRPath(std::string const& file, std::string* emsg,
+                                bool* removed)
+{
+  if (cm::optional<bool> result = RemoveRPathELF(file, emsg, removed)) {
+    return result.value();
+  }
+  if (cm::optional<bool> result = RemoveRPathXCOFF(file, emsg, removed)) {
+    return result.value();
+  }
+  // The file format is not recognized.  Assume it has no RPATH.
+  return true;
+}
 
-// FIXME: Dispatch if multiple formats are supported.
 bool cmSystemTools::CheckRPath(std::string const& file,
                                std::string const& newRPath)
 {
-#if defined(CMake_USE_ELF_PARSER)
   // Parse the ELF binary.
   cmELF elf(file.c_str());
-
-  // Get the RPATH or RUNPATH entry from it.
-  cmELF::StringEntry const* se = elf.GetRPath();
-  if (!se) {
-    se = elf.GetRunPath();
-  }
-
-  // Make sure the current rpath contains the new rpath.
-  if (newRPath.empty()) {
+  if (elf) {
+    // Get the RPATH or RUNPATH entry from it.
+    cmELF::StringEntry const* se = elf.GetRPath();
     if (!se) {
-      return true;
+      se = elf.GetRunPath();
     }
-  } else {
-    if (se &&
-        cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos) {
-      return true;
+
+    // Make sure the current rpath contains the new rpath.
+    if (newRPath.empty()) {
+      if (!se) {
+        return true;
+      }
+    } else {
+      if (se &&
+          cmSystemToolsFindRPath(se->Value, newRPath) != std::string::npos) {
+        return true;
+      }
     }
+    return false;
   }
-  return false;
-#elif defined(CMake_USE_XCOFF_PARSER)
+#if defined(CMake_USE_XCOFF_PARSER)
   // Parse the XCOFF binary.
   cmXCOFF xcoff(file.c_str());
-  if (cm::optional<cm::string_view> libPath = xcoff.GetLibPath()) {
-    if (cmSystemToolsFindRPath(*libPath, newRPath) != std::string::npos) {
-      return true;
+  if (xcoff) {
+    if (cm::optional<cm::string_view> libPath = xcoff.GetLibPath()) {
+      if (cmSystemToolsFindRPath(*libPath, newRPath) != std::string::npos) {
+        return true;
+      }
     }
+    return false;
   }
-  return false;
-#else
-  (void)file;
-  (void)newRPath;
-  return false;
 #endif
+  // The file format is not recognized.  Assume it has no RPATH.
+  // Therefore we succeed if the new rpath is empty anyway.
+  return newRPath.empty();
 }
 
 bool cmSystemTools::RepeatedRemoveDirectory(const std::string& dir)
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 44ccbf7..715724c 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -102,7 +102,11 @@
   }
 
   //! Return true if the path is a framework
-  static bool IsPathToFramework(const std::string& value);
+  static bool IsPathToFramework(const std::string& path);
+
+  //! Return true if the path is a macOS non-framework shared library (aka
+  //! .dylib)
+  static bool IsPathToMacOSSharedLibrary(const std::string& path);
 
   static bool DoesFileExistWithExtensions(
     const std::string& name, const std::vector<std::string>& sourceExts);
@@ -280,7 +284,10 @@
   /**
    * Compare versions
    */
-  static bool VersionCompare(CompareOp op, const char* lhs, const char* rhs);
+  static bool VersionCompare(CompareOp op, const std::string& lhs,
+                             const std::string& rhs);
+  static bool VersionCompare(CompareOp op, const std::string& lhs,
+                             const char rhs[]);
   static bool VersionCompareEqual(std::string const& lhs,
                                   std::string const& rhs);
   static bool VersionCompareGreater(std::string const& lhs,
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 7622700..45e23d3 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -37,6 +37,7 @@
 #include "cmStateSnapshot.h"
 #include "cmSystemTools.h"
 #include "cmTargetPropertyComputer.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 template <>
@@ -79,19 +80,19 @@
 }
 
 template <>
-cmProp cmTargetPropertyComputer::GetSources<cmTarget>(
+cmValue cmTargetPropertyComputer::GetSources<cmTarget>(
   cmTarget const* tgt, cmMessenger* messenger,
   cmListFileBacktrace const& context)
 {
-  cmStringRange entries = tgt->GetSourceEntries();
+  cmBTStringRange entries = tgt->GetSourceEntries();
   if (entries.empty()) {
     return nullptr;
   }
 
   std::ostringstream ss;
   const char* sep = "";
-  for (std::string const& entry : entries) {
-    std::vector<std::string> files = cmExpandedList(entry);
+  for (auto const& entry : entries) {
+    std::vector<std::string> files = cmExpandedList(entry.Value);
     for (std::string const& file : files) {
       if (cmHasLiteralPrefix(file, "$<TARGET_OBJECTS:") &&
           file.back() == '>') {
@@ -112,12 +113,14 @@
           case cmPolicies::WARN:
             e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0051) << "\n";
             noMessage = false;
+            CM_FALLTHROUGH;
           case cmPolicies::OLD:
             break;
           case cmPolicies::REQUIRED_ALWAYS:
           case cmPolicies::REQUIRED_IF_USED:
           case cmPolicies::NEW:
             addContent = true;
+            break;
         }
         if (!noMessage) {
           e << "Target \"" << tgt->GetName()
@@ -157,7 +160,7 @@
   }
   static std::string srcs;
   srcs = ss.str();
-  return &srcs;
+  return cmValue(srcs);
 }
 
 class cmTargetInternals
@@ -187,24 +190,17 @@
   std::set<std::string> SystemIncludeDirectories;
   cmTarget::LinkLibraryVectorType OriginalLinkLibraries;
   std::map<std::string, BTs<std::string>> LanguageStandardProperties;
-  std::vector<std::string> IncludeDirectoriesEntries;
-  std::vector<cmListFileBacktrace> IncludeDirectoriesBacktraces;
-  std::vector<std::string> CompileOptionsEntries;
-  std::vector<cmListFileBacktrace> CompileOptionsBacktraces;
-  std::vector<std::string> CompileFeaturesEntries;
-  std::vector<cmListFileBacktrace> CompileFeaturesBacktraces;
-  std::vector<std::string> CompileDefinitionsEntries;
-  std::vector<cmListFileBacktrace> CompileDefinitionsBacktraces;
-  std::vector<std::string> PrecompileHeadersEntries;
-  std::vector<cmListFileBacktrace> PrecompileHeadersBacktraces;
-  std::vector<std::string> SourceEntries;
-  std::vector<cmListFileBacktrace> SourceBacktraces;
-  std::vector<std::string> LinkOptionsEntries;
-  std::vector<cmListFileBacktrace> LinkOptionsBacktraces;
-  std::vector<std::string> LinkDirectoriesEntries;
-  std::vector<cmListFileBacktrace> LinkDirectoriesBacktraces;
-  std::vector<std::string> LinkImplementationPropertyEntries;
-  std::vector<cmListFileBacktrace> LinkImplementationPropertyBacktraces;
+  std::vector<BT<std::string>> IncludeDirectoriesEntries;
+  std::map<cmTargetExport const*, std::vector<std::string>>
+    InstallIncludeDirectoriesEntries;
+  std::vector<BT<std::string>> CompileOptionsEntries;
+  std::vector<BT<std::string>> CompileFeaturesEntries;
+  std::vector<BT<std::string>> CompileDefinitionsEntries;
+  std::vector<BT<std::string>> PrecompileHeadersEntries;
+  std::vector<BT<std::string>> SourceEntries;
+  std::vector<BT<std::string>> LinkOptionsEntries;
+  std::vector<BT<std::string>> LinkDirectoriesEntries;
+  std::vector<BT<std::string>> LinkImplementationPropertyEntries;
   std::vector<std::pair<cmTarget::TLLSignature, cmListFileContext>>
     TLLCommands;
   cmListFileBacktrace Backtrace;
@@ -265,16 +261,16 @@
   auto initProp = [this, mf, &defKey](const std::string& property) {
     // Replace everything after "CMAKE_"
     defKey.replace(defKey.begin() + 6, defKey.end(), property);
-    if (cmProp value = mf->GetDefinition(defKey)) {
-      this->SetProperty(property, *value);
+    if (cmValue value = mf->GetDefinition(defKey)) {
+      this->SetProperty(property, value);
     }
   };
   auto initPropValue = [this, mf, &defKey](const std::string& property,
                                            const char* default_value) {
     // Replace everything after "CMAKE_"
     defKey.replace(defKey.begin() + 6, defKey.end(), property);
-    if (cmProp value = mf->GetDefinition(defKey)) {
-      this->SetProperty(property, *value);
+    if (cmValue value = mf->GetDefinition(defKey)) {
+      this->SetProperty(property, value);
     } else if (default_value) {
       this->SetProperty(property, default_value);
     }
@@ -481,8 +477,6 @@
     // of the same directory property:
     cm::append(this->impl->IncludeDirectoriesEntries,
                this->impl->Makefile->GetIncludeDirectoriesEntries());
-    cm::append(this->impl->IncludeDirectoriesBacktraces,
-               this->impl->Makefile->GetIncludeDirectoriesBacktraces());
 
     {
       auto const& sysInc = this->impl->Makefile->GetSystemIncludeDirectories();
@@ -492,18 +486,12 @@
 
     cm::append(this->impl->CompileOptionsEntries,
                this->impl->Makefile->GetCompileOptionsEntries());
-    cm::append(this->impl->CompileOptionsBacktraces,
-               this->impl->Makefile->GetCompileOptionsBacktraces());
 
     cm::append(this->impl->LinkOptionsEntries,
                this->impl->Makefile->GetLinkOptionsEntries());
-    cm::append(this->impl->LinkOptionsBacktraces,
-               this->impl->Makefile->GetLinkOptionsBacktraces());
 
     cm::append(this->impl->LinkDirectoriesEntries,
                this->impl->Makefile->GetLinkDirectoriesEntries());
-    cm::append(this->impl->LinkDirectoriesBacktraces,
-               this->impl->Makefile->GetLinkDirectoriesBacktraces());
   }
 
   if (this->impl->TargetType == cmStateEnums::EXECUTABLE) {
@@ -541,7 +529,7 @@
 
   // check for "CMAKE_VS_GLOBALS" variable and set up target properties
   // if any
-  cmProp globals = mf->GetDefinition("CMAKE_VS_GLOBALS");
+  cmValue globals = mf->GetDefinition("CMAKE_VS_GLOBALS");
   if (globals) {
     const std::string genName = mf->GetGlobalGenerator()->GetName();
     if (cmHasLiteralPrefix(genName, "Visual Studio")) {
@@ -612,11 +600,9 @@
                                            const std::string& feature)
 {
   cmListFileBacktrace featureBacktrace;
-  for (size_t i = 0; i < this->impl->CompileFeaturesEntries.size(); i++) {
-    if (this->impl->CompileFeaturesEntries[i] == feature) {
-      if (i < this->impl->CompileFeaturesBacktraces.size()) {
-        featureBacktrace = this->impl->CompileFeaturesBacktraces[i];
-      }
+  for (auto const& entry : this->impl->CompileFeaturesEntries) {
+    if (entry.Value == feature) {
+      featureBacktrace = entry.Backtrace;
       break;
     }
   }
@@ -728,8 +714,7 @@
 {
   if (!srcs.empty()) {
     cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-    this->impl->SourceEntries.push_back(cmJoin(srcs, ";"));
-    this->impl->SourceBacktraces.push_back(lfbt);
+    this->impl->SourceEntries.emplace_back(cmJoin(srcs, ";"), lfbt);
   }
 }
 
@@ -753,8 +738,7 @@
   }
   if (!srcFiles.empty()) {
     cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-    this->impl->SourceEntries.push_back(std::move(srcFiles));
-    this->impl->SourceBacktraces.push_back(lfbt);
+    this->impl->SourceEntries.emplace_back(std::move(srcFiles), lfbt);
   }
 }
 
@@ -842,9 +826,9 @@
   {
   }
 
-  bool operator()(std::string const& entry)
+  bool operator()(BT<std::string> const& entry)
   {
-    std::vector<std::string> files = cmExpandedList(entry);
+    std::vector<std::string> files = cmExpandedList(entry.Value);
     std::vector<cmSourceFileLocation> locations;
     locations.reserve(files.size());
     std::transform(files.begin(), files.end(), std::back_inserter(locations),
@@ -865,11 +849,7 @@
     cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
     this->impl->SourceEntries.insert(before ? this->impl->SourceEntries.begin()
                                             : this->impl->SourceEntries.end(),
-                                     src);
-    this->impl->SourceBacktraces.insert(
-      before ? this->impl->SourceBacktraces.begin()
-             : this->impl->SourceBacktraces.end(),
-      lfbt);
+                                     BT<std::string>(src, lfbt));
   }
   if (cmGeneratorExpression::Find(src) != std::string::npos) {
     return nullptr;
@@ -1042,7 +1022,7 @@
        this->GetPolicyStatusCMP0073() == cmPolicies::WARN)) {
     std::string targetEntry = cmStrCat(this->impl->Name, "_LIB_DEPENDS");
     std::string dependencies;
-    cmProp old_val = mf.GetDefinition(targetEntry);
+    cmValue old_val = mf.GetDefinition(targetEntry);
     if (old_val) {
       dependencies += *old_val;
     }
@@ -1075,122 +1055,118 @@
   return this->impl->SystemIncludeDirectories;
 }
 
-cmStringRange cmTarget::GetIncludeDirectoriesEntries() const
+void cmTarget::AddInstallIncludeDirectories(cmTargetExport const& te,
+                                            cmStringRange const& incs)
+{
+  std::copy(
+    incs.begin(), incs.end(),
+    std::back_inserter(this->impl->InstallIncludeDirectoriesEntries[&te]));
+}
+
+cmStringRange cmTarget::GetInstallIncludeDirectoriesEntries(
+  cmTargetExport const& te) const
+{
+  return cmMakeRange(this->impl->InstallIncludeDirectoriesEntries[&te]);
+}
+
+cmBTStringRange cmTarget::GetIncludeDirectoriesEntries() const
 {
   return cmMakeRange(this->impl->IncludeDirectoriesEntries);
 }
 
-cmBacktraceRange cmTarget::GetIncludeDirectoriesBacktraces() const
-{
-  return cmMakeRange(this->impl->IncludeDirectoriesBacktraces);
-}
-
-cmStringRange cmTarget::GetCompileOptionsEntries() const
+cmBTStringRange cmTarget::GetCompileOptionsEntries() const
 {
   return cmMakeRange(this->impl->CompileOptionsEntries);
 }
 
-cmBacktraceRange cmTarget::GetCompileOptionsBacktraces() const
-{
-  return cmMakeRange(this->impl->CompileOptionsBacktraces);
-}
-
-cmStringRange cmTarget::GetCompileFeaturesEntries() const
+cmBTStringRange cmTarget::GetCompileFeaturesEntries() const
 {
   return cmMakeRange(this->impl->CompileFeaturesEntries);
 }
 
-cmBacktraceRange cmTarget::GetCompileFeaturesBacktraces() const
-{
-  return cmMakeRange(this->impl->CompileFeaturesBacktraces);
-}
-
-cmStringRange cmTarget::GetCompileDefinitionsEntries() const
+cmBTStringRange cmTarget::GetCompileDefinitionsEntries() const
 {
   return cmMakeRange(this->impl->CompileDefinitionsEntries);
 }
 
-cmBacktraceRange cmTarget::GetCompileDefinitionsBacktraces() const
-{
-  return cmMakeRange(this->impl->CompileDefinitionsBacktraces);
-}
-
-cmStringRange cmTarget::GetPrecompileHeadersEntries() const
+cmBTStringRange cmTarget::GetPrecompileHeadersEntries() const
 {
   return cmMakeRange(this->impl->PrecompileHeadersEntries);
 }
 
-cmBacktraceRange cmTarget::GetPrecompileHeadersBacktraces() const
-{
-  return cmMakeRange(this->impl->PrecompileHeadersBacktraces);
-}
-
-cmStringRange cmTarget::GetSourceEntries() const
+cmBTStringRange cmTarget::GetSourceEntries() const
 {
   return cmMakeRange(this->impl->SourceEntries);
 }
 
-cmBacktraceRange cmTarget::GetSourceBacktraces() const
-{
-  return cmMakeRange(this->impl->SourceBacktraces);
-}
-
-cmStringRange cmTarget::GetLinkOptionsEntries() const
+cmBTStringRange cmTarget::GetLinkOptionsEntries() const
 {
   return cmMakeRange(this->impl->LinkOptionsEntries);
 }
 
-cmBacktraceRange cmTarget::GetLinkOptionsBacktraces() const
-{
-  return cmMakeRange(this->impl->LinkOptionsBacktraces);
-}
-
-cmStringRange cmTarget::GetLinkDirectoriesEntries() const
+cmBTStringRange cmTarget::GetLinkDirectoriesEntries() const
 {
   return cmMakeRange(this->impl->LinkDirectoriesEntries);
 }
 
-cmBacktraceRange cmTarget::GetLinkDirectoriesBacktraces() const
-{
-  return cmMakeRange(this->impl->LinkDirectoriesBacktraces);
-}
-
-cmStringRange cmTarget::GetLinkImplementationEntries() const
+cmBTStringRange cmTarget::GetLinkImplementationEntries() const
 {
   return cmMakeRange(this->impl->LinkImplementationPropertyEntries);
 }
 
-cmBacktraceRange cmTarget::GetLinkImplementationBacktraces() const
-{
-  return cmMakeRange(this->impl->LinkImplementationPropertyBacktraces);
+namespace {
+#define MAKE_PROP(PROP) const std::string prop##PROP = #PROP
+MAKE_PROP(C_STANDARD);
+MAKE_PROP(CXX_STANDARD);
+MAKE_PROP(CUDA_STANDARD);
+MAKE_PROP(HIP_STANDARD);
+MAKE_PROP(OBJC_STANDARD);
+MAKE_PROP(OBJCXX_STANDARD);
+MAKE_PROP(COMPILE_DEFINITIONS);
+MAKE_PROP(COMPILE_FEATURES);
+MAKE_PROP(COMPILE_OPTIONS);
+MAKE_PROP(PRECOMPILE_HEADERS);
+MAKE_PROP(PRECOMPILE_HEADERS_REUSE_FROM);
+MAKE_PROP(CUDA_PTX_COMPILATION);
+MAKE_PROP(EXPORT_NAME);
+MAKE_PROP(IMPORTED);
+MAKE_PROP(IMPORTED_GLOBAL);
+MAKE_PROP(INCLUDE_DIRECTORIES);
+MAKE_PROP(LINK_OPTIONS);
+MAKE_PROP(LINK_DIRECTORIES);
+MAKE_PROP(LINK_LIBRARIES);
+MAKE_PROP(MANUALLY_ADDED_DEPENDENCIES);
+MAKE_PROP(NAME);
+MAKE_PROP(SOURCES);
+MAKE_PROP(TYPE);
+MAKE_PROP(BINARY_DIR);
+MAKE_PROP(SOURCE_DIR);
+MAKE_PROP(FALSE);
+MAKE_PROP(TRUE);
+#undef MAKE_PROP
 }
 
-void cmTarget::SetProperty(const std::string& prop, const char* value)
+namespace {
+// to workaround bug on GCC/AIX
+// Define a template to force conversion to std::string
+template <typename ValueType>
+std::string ConvertToString(ValueType value);
+
+template <>
+std::string ConvertToString<const char*>(const char* value)
 {
-#define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
-  MAKE_STATIC_PROP(C_STANDARD);
-  MAKE_STATIC_PROP(CXX_STANDARD);
-  MAKE_STATIC_PROP(CUDA_STANDARD);
-  MAKE_STATIC_PROP(HIP_STANDARD);
-  MAKE_STATIC_PROP(OBJC_STANDARD);
-  MAKE_STATIC_PROP(OBJCXX_STANDARD);
-  MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
-  MAKE_STATIC_PROP(COMPILE_FEATURES);
-  MAKE_STATIC_PROP(COMPILE_OPTIONS);
-  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
-  MAKE_STATIC_PROP(PRECOMPILE_HEADERS_REUSE_FROM);
-  MAKE_STATIC_PROP(CUDA_PTX_COMPILATION);
-  MAKE_STATIC_PROP(EXPORT_NAME);
-  MAKE_STATIC_PROP(IMPORTED_GLOBAL);
-  MAKE_STATIC_PROP(INCLUDE_DIRECTORIES);
-  MAKE_STATIC_PROP(LINK_OPTIONS);
-  MAKE_STATIC_PROP(LINK_DIRECTORIES);
-  MAKE_STATIC_PROP(LINK_LIBRARIES);
-  MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
-  MAKE_STATIC_PROP(NAME);
-  MAKE_STATIC_PROP(SOURCES);
-  MAKE_STATIC_PROP(TYPE);
-#undef MAKE_STATIC_PROP
+  return std::string(value);
+}
+template <>
+std::string ConvertToString<cmValue>(cmValue value)
+{
+  return std::string(*value);
+}
+}
+
+template <typename ValueType>
+void cmTarget::StoreProperty(const std::string& prop, ValueType value)
+{
   if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
     this->impl->Makefile->IssueMessage(
       MessageType::FATAL_ERROR,
@@ -1231,75 +1207,57 @@
 
   if (prop == propINCLUDE_DIRECTORIES) {
     this->impl->IncludeDirectoriesEntries.clear();
-    this->impl->IncludeDirectoriesBacktraces.clear();
     if (value) {
-      this->impl->IncludeDirectoriesEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->IncludeDirectoriesBacktraces.push_back(lfbt);
+      this->impl->IncludeDirectoriesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propCOMPILE_OPTIONS) {
     this->impl->CompileOptionsEntries.clear();
-    this->impl->CompileOptionsBacktraces.clear();
     if (value) {
-      this->impl->CompileOptionsEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->CompileOptionsBacktraces.push_back(lfbt);
+      this->impl->CompileOptionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propCOMPILE_FEATURES) {
     this->impl->CompileFeaturesEntries.clear();
-    this->impl->CompileFeaturesBacktraces.clear();
     if (value) {
-      this->impl->CompileFeaturesEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->CompileFeaturesBacktraces.push_back(lfbt);
+      this->impl->CompileFeaturesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propCOMPILE_DEFINITIONS) {
     this->impl->CompileDefinitionsEntries.clear();
-    this->impl->CompileDefinitionsBacktraces.clear();
     if (value) {
-      this->impl->CompileDefinitionsEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->CompileDefinitionsBacktraces.push_back(lfbt);
+      this->impl->CompileDefinitionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propLINK_OPTIONS) {
     this->impl->LinkOptionsEntries.clear();
-    this->impl->LinkOptionsBacktraces.clear();
     if (value) {
-      this->impl->LinkOptionsEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->LinkOptionsBacktraces.push_back(lfbt);
+      this->impl->LinkOptionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propLINK_DIRECTORIES) {
     this->impl->LinkDirectoriesEntries.clear();
-    this->impl->LinkDirectoriesBacktraces.clear();
     if (value) {
-      this->impl->LinkDirectoriesEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->LinkDirectoriesBacktraces.push_back(lfbt);
+      this->impl->LinkDirectoriesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propPRECOMPILE_HEADERS) {
     this->impl->PrecompileHeadersEntries.clear();
-    this->impl->PrecompileHeadersBacktraces.clear();
     if (value) {
-      this->impl->PrecompileHeadersEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->PrecompileHeadersBacktraces.push_back(lfbt);
+      this->impl->PrecompileHeadersEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propLINK_LIBRARIES) {
     this->impl->LinkImplementationPropertyEntries.clear();
-    this->impl->LinkImplementationPropertyBacktraces.clear();
     if (value) {
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->LinkImplementationPropertyEntries.emplace_back(value);
-      this->impl->LinkImplementationPropertyBacktraces.push_back(lfbt);
+      this->impl->LinkImplementationPropertyEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propSOURCES) {
     this->impl->SourceEntries.clear();
-    this->impl->SourceBacktraces.clear();
     if (value) {
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->SourceEntries.emplace_back(value);
-      this->impl->SourceBacktraces.push_back(lfbt);
+      this->impl->SourceEntries.emplace_back(value, lfbt);
     }
   } else if (prop == propIMPORTED_GLOBAL) {
     if (!cmIsOn(value)) {
@@ -1315,7 +1273,8 @@
       this->GetGlobalGenerator()->IndexTarget(this);
     }
   } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME") &&
-             !this->impl->CheckImportedLibName(prop, value ? value : "")) {
+             !this->impl->CheckImportedLibName(
+               prop, value ? value : std::string{})) {
     /* error was reported by check method */
   } else if (prop == propCUDA_PTX_COMPILATION &&
              this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
@@ -1345,17 +1304,17 @@
 
     std::string reusedFrom = reusedTarget->GetSafeProperty(prop);
     if (reusedFrom.empty()) {
-      reusedFrom = value;
+      reusedFrom = ConvertToString(value);
     }
 
-    this->impl->Properties.SetProperty(prop, reusedFrom.c_str());
+    this->impl->Properties.SetProperty(prop, reusedFrom);
 
     reusedTarget->SetProperty("COMPILE_PDB_NAME", reusedFrom);
     reusedTarget->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
                               cmStrCat(reusedFrom, ".dir/"));
 
-    cmProp tmp = reusedTarget->GetProperty("COMPILE_PDB_NAME");
-    this->SetProperty("COMPILE_PDB_NAME", cmToCStr(tmp));
+    cmValue tmp = reusedTarget->GetProperty("COMPILE_PDB_NAME");
+    this->SetProperty("COMPILE_PDB_NAME", tmp);
     this->AddUtility(reusedFrom, false, this->impl->Makefile);
   } else if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
              prop == propCUDA_STANDARD || prop == propHIP_STANDARD ||
@@ -1403,39 +1362,33 @@
   }
   if (prop == "INCLUDE_DIRECTORIES") {
     if (!value.empty()) {
-      this->impl->IncludeDirectoriesEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->IncludeDirectoriesBacktraces.push_back(lfbt);
+      this->impl->IncludeDirectoriesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "COMPILE_OPTIONS") {
     if (!value.empty()) {
-      this->impl->CompileOptionsEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->CompileOptionsBacktraces.push_back(lfbt);
+      this->impl->CompileOptionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "COMPILE_FEATURES") {
     if (!value.empty()) {
-      this->impl->CompileFeaturesEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->CompileFeaturesBacktraces.push_back(lfbt);
+      this->impl->CompileFeaturesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "COMPILE_DEFINITIONS") {
     if (!value.empty()) {
-      this->impl->CompileDefinitionsEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->CompileDefinitionsBacktraces.push_back(lfbt);
+      this->impl->CompileDefinitionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "LINK_OPTIONS") {
     if (!value.empty()) {
-      this->impl->LinkOptionsEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->LinkOptionsBacktraces.push_back(lfbt);
+      this->impl->LinkOptionsEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "LINK_DIRECTORIES") {
     if (!value.empty()) {
-      this->impl->LinkDirectoriesEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->LinkDirectoriesBacktraces.push_back(lfbt);
+      this->impl->LinkDirectoriesEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "PRECOMPILE_HEADERS") {
     if (this->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM")) {
@@ -1447,20 +1400,17 @@
       return;
     }
     if (!value.empty()) {
-      this->impl->PrecompileHeadersEntries.emplace_back(value);
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->PrecompileHeadersBacktraces.push_back(lfbt);
+      this->impl->PrecompileHeadersEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "LINK_LIBRARIES") {
     if (!value.empty()) {
       cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-      this->impl->LinkImplementationPropertyEntries.emplace_back(value);
-      this->impl->LinkImplementationPropertyBacktraces.push_back(lfbt);
+      this->impl->LinkImplementationPropertyEntries.emplace_back(value, lfbt);
     }
   } else if (prop == "SOURCES") {
     cmListFileBacktrace lfbt = this->impl->Makefile->GetBacktrace();
-    this->impl->SourceEntries.emplace_back(value);
-    this->impl->SourceBacktraces.push_back(lfbt);
+    this->impl->SourceEntries.emplace_back(value, lfbt);
   } else if (cmHasLiteralPrefix(prop, "IMPORTED_LIBNAME")) {
     this->impl->Makefile->IssueMessage(
       MessageType::FATAL_ERROR, prop + " property may not be APPENDed.");
@@ -1474,6 +1424,15 @@
   }
 }
 
+void cmTarget::SetProperty(const std::string& prop, const char* value)
+{
+  this->StoreProperty(prop, value);
+}
+void cmTarget::SetProperty(const std::string& prop, cmValue value)
+{
+  this->StoreProperty(prop, value);
+}
+
 void cmTarget::AppendBuildInterfaceIncludes()
 {
   if (this->GetType() != cmStateEnums::SHARED_LIBRARY &&
@@ -1501,70 +1460,46 @@
   }
 }
 
-void cmTarget::InsertInclude(std::string const& entry,
-                             cmListFileBacktrace const& bt, bool before)
+void cmTarget::InsertInclude(BT<std::string> const& entry, bool before)
 {
   auto position = before ? this->impl->IncludeDirectoriesEntries.begin()
                          : this->impl->IncludeDirectoriesEntries.end();
 
-  auto btPosition = before ? this->impl->IncludeDirectoriesBacktraces.begin()
-                           : this->impl->IncludeDirectoriesBacktraces.end();
-
   this->impl->IncludeDirectoriesEntries.insert(position, entry);
-  this->impl->IncludeDirectoriesBacktraces.insert(btPosition, bt);
 }
 
-void cmTarget::InsertCompileOption(std::string const& entry,
-                                   cmListFileBacktrace const& bt, bool before)
+void cmTarget::InsertCompileOption(BT<std::string> const& entry, bool before)
 {
   auto position = before ? this->impl->CompileOptionsEntries.begin()
                          : this->impl->CompileOptionsEntries.end();
 
-  auto btPosition = before ? this->impl->CompileOptionsBacktraces.begin()
-                           : this->impl->CompileOptionsBacktraces.end();
-
   this->impl->CompileOptionsEntries.insert(position, entry);
-  this->impl->CompileOptionsBacktraces.insert(btPosition, bt);
 }
 
-void cmTarget::InsertCompileDefinition(std::string const& entry,
-                                       cmListFileBacktrace const& bt)
+void cmTarget::InsertCompileDefinition(BT<std::string> const& entry)
 {
   this->impl->CompileDefinitionsEntries.push_back(entry);
-  this->impl->CompileDefinitionsBacktraces.push_back(bt);
 }
 
-void cmTarget::InsertLinkOption(std::string const& entry,
-                                cmListFileBacktrace const& bt, bool before)
+void cmTarget::InsertLinkOption(BT<std::string> const& entry, bool before)
 {
   auto position = before ? this->impl->LinkOptionsEntries.begin()
                          : this->impl->LinkOptionsEntries.end();
 
-  auto btPosition = before ? this->impl->LinkOptionsBacktraces.begin()
-                           : this->impl->LinkOptionsBacktraces.end();
-
   this->impl->LinkOptionsEntries.insert(position, entry);
-  this->impl->LinkOptionsBacktraces.insert(btPosition, bt);
 }
 
-void cmTarget::InsertLinkDirectory(std::string const& entry,
-                                   cmListFileBacktrace const& bt, bool before)
+void cmTarget::InsertLinkDirectory(BT<std::string> const& entry, bool before)
 {
   auto position = before ? this->impl->LinkDirectoriesEntries.begin()
                          : this->impl->LinkDirectoriesEntries.end();
 
-  auto btPosition = before ? this->impl->LinkDirectoriesBacktraces.begin()
-                           : this->impl->LinkDirectoriesBacktraces.end();
-
   this->impl->LinkDirectoriesEntries.insert(position, entry);
-  this->impl->LinkDirectoriesBacktraces.insert(btPosition, bt);
 }
 
-void cmTarget::InsertPrecompileHeader(std::string const& entry,
-                                      cmListFileBacktrace const& bt)
+void cmTarget::InsertPrecompileHeader(BT<std::string> const& entry)
 {
   this->impl->PrecompileHeadersEntries.push_back(entry);
-  this->impl->PrecompileHeadersBacktraces.push_back(bt);
 }
 
 static void cmTargetCheckLINK_INTERFACE_LIBRARIES(const std::string& prop,
@@ -1651,17 +1586,17 @@
 {
   // Certain properties need checking.
   if (cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES")) {
-    if (cmProp value = this->GetProperty(prop)) {
+    if (cmValue value = this->GetProperty(prop)) {
       cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, false);
     }
   }
   if (cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES")) {
-    if (cmProp value = this->GetProperty(prop)) {
+    if (cmValue value = this->GetProperty(prop)) {
       cmTargetCheckLINK_INTERFACE_LIBRARIES(prop, *value, context, true);
     }
   }
   if (prop == "INTERFACE_LINK_LIBRARIES") {
-    if (cmProp value = this->GetProperty(prop)) {
+    if (cmValue value = this->GetProperty(prop)) {
       cmTargetCheckINTERFACE_LINK_LIBRARIES(*value, context);
     }
   }
@@ -1672,40 +1607,15 @@
   }
 }
 
-cmProp cmTarget::GetComputedProperty(const std::string& prop,
-                                     cmMessenger* messenger,
-                                     cmListFileBacktrace const& context) const
+cmValue cmTarget::GetComputedProperty(const std::string& prop,
+                                      cmMessenger* messenger,
+                                      cmListFileBacktrace const& context) const
 {
   return cmTargetPropertyComputer::GetProperty(this, prop, messenger, context);
 }
 
-cmProp cmTarget::GetProperty(const std::string& prop) const
+cmValue cmTarget::GetProperty(const std::string& prop) const
 {
-#define MAKE_STATIC_PROP(PROP) static const std::string prop##PROP = #PROP
-  MAKE_STATIC_PROP(C_STANDARD);
-  MAKE_STATIC_PROP(CXX_STANDARD);
-  MAKE_STATIC_PROP(CUDA_STANDARD);
-  MAKE_STATIC_PROP(OBJC_STANDARD);
-  MAKE_STATIC_PROP(OBJCXX_STANDARD);
-  MAKE_STATIC_PROP(LINK_LIBRARIES);
-  MAKE_STATIC_PROP(TYPE);
-  MAKE_STATIC_PROP(INCLUDE_DIRECTORIES);
-  MAKE_STATIC_PROP(COMPILE_FEATURES);
-  MAKE_STATIC_PROP(COMPILE_OPTIONS);
-  MAKE_STATIC_PROP(COMPILE_DEFINITIONS);
-  MAKE_STATIC_PROP(LINK_OPTIONS);
-  MAKE_STATIC_PROP(LINK_DIRECTORIES);
-  MAKE_STATIC_PROP(PRECOMPILE_HEADERS);
-  MAKE_STATIC_PROP(IMPORTED);
-  MAKE_STATIC_PROP(IMPORTED_GLOBAL);
-  MAKE_STATIC_PROP(MANUALLY_ADDED_DEPENDENCIES);
-  MAKE_STATIC_PROP(NAME);
-  MAKE_STATIC_PROP(BINARY_DIR);
-  MAKE_STATIC_PROP(SOURCE_DIR);
-  MAKE_STATIC_PROP(SOURCES);
-  MAKE_STATIC_PROP(FALSE);
-  MAKE_STATIC_PROP(TRUE);
-#undef MAKE_STATIC_PROP
   static std::unordered_set<std::string> const specialProps{
     propC_STANDARD,
     propCXX_STANDARD,
@@ -1737,7 +1647,7 @@
       if (propertyIter == this->impl->LanguageStandardProperties.end()) {
         return nullptr;
       }
-      return &(propertyIter->second.Value);
+      return cmValue(propertyIter->second.Value);
     }
     if (prop == propLINK_LIBRARIES) {
       if (this->impl->LinkImplementationPropertyEntries.empty()) {
@@ -1746,11 +1656,11 @@
 
       static std::string output;
       output = cmJoin(this->impl->LinkImplementationPropertyEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     // the type property returns what type the target is
     if (prop == propTYPE) {
-      return &cmState::GetTargetTypeName(this->GetType());
+      return cmValue(cmState::GetTargetTypeName(this->GetType()));
     }
     if (prop == propINCLUDE_DIRECTORIES) {
       if (this->impl->IncludeDirectoriesEntries.empty()) {
@@ -1759,7 +1669,7 @@
 
       static std::string output;
       output = cmJoin(this->impl->IncludeDirectoriesEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propCOMPILE_FEATURES) {
       if (this->impl->CompileFeaturesEntries.empty()) {
@@ -1768,7 +1678,7 @@
 
       static std::string output;
       output = cmJoin(this->impl->CompileFeaturesEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propCOMPILE_OPTIONS) {
       if (this->impl->CompileOptionsEntries.empty()) {
@@ -1777,7 +1687,7 @@
 
       static std::string output;
       output = cmJoin(this->impl->CompileOptionsEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propCOMPILE_DEFINITIONS) {
       if (this->impl->CompileDefinitionsEntries.empty()) {
@@ -1786,7 +1696,7 @@
 
       static std::string output;
       output = cmJoin(this->impl->CompileDefinitionsEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propLINK_OPTIONS) {
       if (this->impl->LinkOptionsEntries.empty()) {
@@ -1795,7 +1705,7 @@
 
       static std::string output;
       output = cmJoin(this->impl->LinkOptionsEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propLINK_DIRECTORIES) {
       if (this->impl->LinkDirectoriesEntries.empty()) {
@@ -1805,7 +1715,7 @@
       static std::string output;
       output = cmJoin(this->impl->LinkDirectoriesEntries, ";");
 
-      return &output;
+      return cmValue(output);
     }
     if (prop == propMANUALLY_ADDED_DEPENDENCIES) {
       if (this->impl->Utilities.empty()) {
@@ -1822,7 +1732,7 @@
           return item.Value.first;
         });
       output = cmJoin(utilities, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propPRECOMPILE_HEADERS) {
       if (this->impl->PrecompileHeadersEntries.empty()) {
@@ -1831,30 +1741,31 @@
 
       static std::string output;
       output = cmJoin(this->impl->PrecompileHeadersEntries, ";");
-      return &output;
+      return cmValue(output);
     }
     if (prop == propIMPORTED) {
-      return this->IsImported() ? &propTRUE : &propFALSE;
+      return this->IsImported() ? cmValue(propTRUE) : cmValue(propFALSE);
     }
     if (prop == propIMPORTED_GLOBAL) {
-      return this->IsImportedGloballyVisible() ? &propTRUE : &propFALSE;
+      return this->IsImportedGloballyVisible() ? cmValue(propTRUE)
+                                               : cmValue(propFALSE);
     }
     if (prop == propNAME) {
-      return &this->GetName();
+      return cmValue(this->GetName());
     }
     if (prop == propBINARY_DIR) {
-      return &this->impl->Makefile->GetStateSnapshot()
-                .GetDirectory()
-                .GetCurrentBinary();
+      return cmValue(this->impl->Makefile->GetStateSnapshot()
+                       .GetDirectory()
+                       .GetCurrentBinary());
     }
     if (prop == propSOURCE_DIR) {
-      return &this->impl->Makefile->GetStateSnapshot()
-                .GetDirectory()
-                .GetCurrentSource();
+      return cmValue(this->impl->Makefile->GetStateSnapshot()
+                       .GetDirectory()
+                       .GetCurrentSource());
     }
   }
 
-  cmProp retVal = this->impl->Properties.GetPropertyValue(prop);
+  cmValue retVal = this->impl->Properties.GetPropertyValue(prop);
   if (!retVal) {
     const bool chain = this->impl->Makefile->GetState()->IsPropertyChained(
       prop, cmProperty::TARGET);
@@ -1870,7 +1781,7 @@
 
 std::string const& cmTarget::GetSafeProperty(std::string const& prop) const
 {
-  cmProp ret = this->GetProperty(prop);
+  cmValue ret = this->GetProperty(prop);
   if (ret) {
     return *ret;
   }
@@ -2031,8 +1942,8 @@
 
   std::string result;
 
-  cmProp loc = nullptr;
-  cmProp imp = nullptr;
+  cmValue loc = nullptr;
+  cmValue imp = nullptr;
   std::string suffix;
 
   if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
@@ -2043,9 +1954,9 @@
           result = *loc;
         } else {
           std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
-          if (cmProp config_location = this->GetProperty(impProp)) {
+          if (cmValue config_location = this->GetProperty(impProp)) {
             result = *config_location;
-          } else if (cmProp location =
+          } else if (cmValue location =
                        this->GetProperty("IMPORTED_LOCATION")) {
             result = *location;
           }
@@ -2058,9 +1969,9 @@
         } else if (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
                    this->IsExecutableWithExports()) {
           std::string impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
-          if (cmProp config_implib = this->GetProperty(impProp)) {
+          if (cmValue config_implib = this->GetProperty(impProp)) {
             result = *config_implib;
-          } else if (cmProp implib = this->GetProperty("IMPORTED_IMPLIB")) {
+          } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) {
             result = *implib;
           }
         }
@@ -2138,8 +2049,8 @@
   return true;
 }
 
-bool cmTarget::GetMappedConfig(std::string const& desired_config, cmProp& loc,
-                               cmProp& imp, std::string& suffix) const
+bool cmTarget::GetMappedConfig(std::string const& desired_config, cmValue& loc,
+                               cmValue& imp, std::string& suffix) const
 {
   std::string config_upper;
   if (!desired_config.empty()) {
@@ -2161,7 +2072,7 @@
   std::vector<std::string> mappedConfigs;
   {
     std::string mapProp = cmStrCat("MAP_IMPORTED_CONFIG_", config_upper);
-    if (cmProp mapValue = this->GetProperty(mapProp)) {
+    if (cmValue mapValue = this->GetProperty(mapProp)) {
       cmExpandList(*mapValue, mappedConfigs, true);
     }
   }
@@ -2243,7 +2154,7 @@
   // any available configuration.
   if (!loc && !imp) {
     std::vector<std::string> availableConfigs;
-    if (cmProp iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS")) {
+    if (cmValue iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS")) {
       cmExpandList(*iconfigs, availableConfigs);
     }
     for (auto aci = availableConfigs.begin();
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 30d9f5d..95aa4d3 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -14,10 +14,10 @@
 #include "cmAlgorithms.h"
 #include "cmListFileCache.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTargetLinkLibraryType.h"
+#include "cmValue.h"
 
 class cmCustomCommand;
 class cmGlobalGenerator;
@@ -26,6 +26,7 @@
 class cmMessenger;
 class cmPropertyMap;
 class cmSourceFile;
+class cmTargetExport;
 class cmTargetInternals;
 
 /** \class cmTarget
@@ -170,20 +171,21 @@
 
   //! Set/Get a property of this target file
   void SetProperty(const std::string& prop, const char* value);
+  void SetProperty(const std::string& prop, cmValue value);
   void SetProperty(const std::string& prop, const std::string& value)
   {
-    this->SetProperty(prop, value.c_str());
+    this->SetProperty(prop, cmValue(value));
   }
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString = false);
   //! Might return a nullptr if the property is not set or invalid
-  cmProp GetProperty(const std::string& prop) const;
+  cmValue GetProperty(const std::string& prop) const;
   //! Always returns a valid pointer
   std::string const& GetSafeProperty(std::string const& prop) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   void CheckProperty(const std::string& prop, cmMakefile* context) const;
-  cmProp GetComputedProperty(const std::string& prop, cmMessenger* messenger,
-                             cmListFileBacktrace const& context) const;
+  cmValue GetComputedProperty(const std::string& prop, cmMessenger* messenger,
+                              cmListFileBacktrace const& context) const;
   //! Get all properties
   cmPropertyMap const& GetProperties() const;
 
@@ -198,8 +200,8 @@
   bool IsPerConfig() const;
   bool CanCompileSources() const;
 
-  bool GetMappedConfig(std::string const& desired_config, cmProp& loc,
-                       cmProp& imp, std::string& suffix) const;
+  bool GetMappedConfig(std::string const& desired_config, cmValue& loc,
+                       cmValue& imp, std::string& suffix) const;
 
   //! Return whether this target is an executable with symbol exports enabled.
   bool IsExecutableWithExports() const;
@@ -216,18 +218,12 @@
   //! Get a backtrace from the creation of the target.
   cmListFileBacktrace const& GetBacktrace() const;
 
-  void InsertInclude(std::string const& entry, cmListFileBacktrace const& bt,
-                     bool before = false);
-  void InsertCompileOption(std::string const& entry,
-                           cmListFileBacktrace const& bt, bool before = false);
-  void InsertCompileDefinition(std::string const& entry,
-                               cmListFileBacktrace const& bt);
-  void InsertLinkOption(std::string const& entry,
-                        cmListFileBacktrace const& bt, bool before = false);
-  void InsertLinkDirectory(std::string const& entry,
-                           cmListFileBacktrace const& bt, bool before = false);
-  void InsertPrecompileHeader(std::string const& entry,
-                              cmListFileBacktrace const& bt);
+  void InsertInclude(BT<std::string> const& entry, bool before = false);
+  void InsertCompileOption(BT<std::string> const& entry, bool before = false);
+  void InsertCompileDefinition(BT<std::string> const& entry);
+  void InsertLinkOption(BT<std::string> const& entry, bool before = false);
+  void InsertLinkDirectory(BT<std::string> const& entry, bool before = false);
+  void InsertPrecompileHeader(BT<std::string> const& entry);
 
   void AppendBuildInterfaceIncludes();
 
@@ -237,6 +233,11 @@
   void AddSystemIncludeDirectories(std::set<std::string> const& incs);
   std::set<std::string> const& GetSystemIncludeDirectories() const;
 
+  void AddInstallIncludeDirectories(cmTargetExport const& te,
+                                    cmStringRange const& incs);
+  cmStringRange GetInstallIncludeDirectoriesEntries(
+    cmTargetExport const& te) const;
+
   BTs<std::string> const* GetLanguageStandardProperty(
     const std::string& propertyName) const;
 
@@ -244,32 +245,23 @@
                                    std::string const& value,
                                    const std::string& feature);
 
-  cmStringRange GetIncludeDirectoriesEntries() const;
-  cmBacktraceRange GetIncludeDirectoriesBacktraces() const;
+  cmBTStringRange GetIncludeDirectoriesEntries() const;
 
-  cmStringRange GetCompileOptionsEntries() const;
-  cmBacktraceRange GetCompileOptionsBacktraces() const;
+  cmBTStringRange GetCompileOptionsEntries() const;
 
-  cmStringRange GetCompileFeaturesEntries() const;
-  cmBacktraceRange GetCompileFeaturesBacktraces() const;
+  cmBTStringRange GetCompileFeaturesEntries() const;
 
-  cmStringRange GetCompileDefinitionsEntries() const;
-  cmBacktraceRange GetCompileDefinitionsBacktraces() const;
+  cmBTStringRange GetCompileDefinitionsEntries() const;
 
-  cmStringRange GetPrecompileHeadersEntries() const;
-  cmBacktraceRange GetPrecompileHeadersBacktraces() const;
+  cmBTStringRange GetPrecompileHeadersEntries() const;
 
-  cmStringRange GetSourceEntries() const;
-  cmBacktraceRange GetSourceBacktraces() const;
+  cmBTStringRange GetSourceEntries() const;
 
-  cmStringRange GetLinkOptionsEntries() const;
-  cmBacktraceRange GetLinkOptionsBacktraces() const;
+  cmBTStringRange GetLinkOptionsEntries() const;
 
-  cmStringRange GetLinkDirectoriesEntries() const;
-  cmBacktraceRange GetLinkDirectoriesBacktraces() const;
+  cmBTStringRange GetLinkDirectoriesEntries() const;
 
-  cmStringRange GetLinkImplementationEntries() const;
-  cmBacktraceRange GetLinkImplementationBacktraces() const;
+  cmBTStringRange GetLinkImplementationEntries() const;
 
   std::string ImportedGetFullPath(const std::string& config,
                                   cmStateEnums::ArtifactType artifact) const;
@@ -280,6 +272,9 @@
   };
 
 private:
+  template <typename ValueType>
+  void StoreProperty(const std::string& prop, ValueType value);
+
   // Internal representation details.
   friend class cmGeneratorTarget;
 
diff --git a/Source/cmTargetCompileOptionsCommand.cxx b/Source/cmTargetCompileOptionsCommand.cxx
index dee2c10..8ca3842 100644
--- a/Source/cmTargetCompileOptionsCommand.cxx
+++ b/Source/cmTargetCompileOptionsCommand.cxx
@@ -37,7 +37,8 @@
     }
 
     cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-    tgt->InsertCompileOption(this->Join(content), lfbt, prepend);
+    tgt->InsertCompileOption(BT<std::string>(this->Join(content), lfbt),
+                             prepend);
     return true; // Successfully handled.
   }
 
diff --git a/Source/cmTargetExport.h b/Source/cmTargetExport.h
index 1e38d84..19fc931 100644
--- a/Source/cmTargetExport.h
+++ b/Source/cmTargetExport.h
@@ -29,7 +29,6 @@
   cmInstallTargetGenerator* FrameworkGenerator;
   cmInstallTargetGenerator* BundleGenerator;
   cmInstallFilesGenerator* HeaderGenerator;
-  std::string InterfaceIncludeDirectories;
   ///@}
 
   bool NamelinkOnly = false;
diff --git a/Source/cmTargetIncludeDirectoriesCommand.cxx b/Source/cmTargetIncludeDirectoriesCommand.cxx
index 3897499..f31501e 100644
--- a/Source/cmTargetIncludeDirectoriesCommand.cxx
+++ b/Source/cmTargetIncludeDirectoriesCommand.cxx
@@ -63,7 +63,7 @@
   bool system)
 {
   cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-  tgt->InsertInclude(this->Join(content), lfbt, prepend);
+  tgt->InsertInclude(BT<std::string>(this->Join(content), lfbt), prepend);
   if (system) {
     std::string prefix = this->Makefile->GetCurrentSourceDirectory() + "/";
     std::set<std::string> sdirs;
diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx
index 0c68d60..3ba27a8 100644
--- a/Source/cmTargetLinkDirectoriesCommand.cxx
+++ b/Source/cmTargetLinkDirectoriesCommand.cxx
@@ -34,7 +34,8 @@
                            bool prepend, bool /*system*/) override
   {
     cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-    tgt->InsertLinkDirectory(this->Join(content), lfbt, prepend);
+    tgt->InsertLinkDirectory(BT<std::string>(this->Join(content), lfbt),
+                             prepend);
     return true; // Successfully handled.
   }
 };
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index 3423b30..e15c941 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -14,13 +14,13 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 namespace {
@@ -143,6 +143,7 @@
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0039) << "\n";
         modal = "should";
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::REQUIRED_ALWAYS:
@@ -150,6 +151,7 @@
       case cmPolicies::NEW:
         modal = "must";
         messageType = MessageType::FATAL_ERROR;
+        break;
     }
     if (modal) {
       e << "Utility target \"" << target->GetName() << "\" " << modal
@@ -278,7 +280,7 @@
       // with old versions of CMake and new)
       llt = GENERAL_LibraryType;
       std::string linkType = cmStrCat(args[0], "_LINK_TYPE");
-      cmProp linkTypeString = mf.GetDefinition(linkType);
+      cmValue linkTypeString = mf.GetDefinition(linkType);
       if (linkTypeString) {
         if (*linkTypeString == "debug") {
           llt = DEBUG_LibraryType;
@@ -395,6 +397,7 @@
       case cmPolicies::WARN:
         e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0023) << "\n";
         modal = "should";
+        CM_FALLTHROUGH;
       case cmPolicies::OLD:
         break;
       case cmPolicies::REQUIRED_ALWAYS:
@@ -402,6 +405,7 @@
       case cmPolicies::NEW:
         modal = "must";
         messageType = MessageType::FATAL_ERROR;
+        break;
     }
 
     if (modal) {
diff --git a/Source/cmTargetLinkOptionsCommand.cxx b/Source/cmTargetLinkOptionsCommand.cxx
index df9416f..3ea2d71 100644
--- a/Source/cmTargetLinkOptionsCommand.cxx
+++ b/Source/cmTargetLinkOptionsCommand.cxx
@@ -30,7 +30,7 @@
                            bool prepend, bool /*system*/) override
   {
     cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
-    tgt->InsertLinkOption(this->Join(content), lfbt, prepend);
+    tgt->InsertLinkOption(BT<std::string>(this->Join(content), lfbt), prepend);
     return true; // Successfully handled.
   }
 
diff --git a/Source/cmTargetPropCommandBase.cxx b/Source/cmTargetPropCommandBase.cxx
index e41714a..3bd1ea3 100644
--- a/Source/cmTargetPropCommandBase.cxx
+++ b/Source/cmTargetPropCommandBase.cxx
@@ -5,9 +5,9 @@
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 cmTargetPropCommandBase::cmTargetPropCommandBase(cmExecutionStatus& status)
@@ -181,7 +181,7 @@
 {
   if (prepend) {
     const std::string propName = std::string("INTERFACE_") + this->Property;
-    cmProp propValue = tgt->GetProperty(propName);
+    cmValue propValue = tgt->GetProperty(propName);
     const std::string totalContent =
       this->Join(content) + (propValue ? (";" + *propValue) : std::string());
     tgt->SetProperty(propName, totalContent);
diff --git a/Source/cmTargetPropertyComputer.cxx b/Source/cmTargetPropertyComputer.cxx
index b9c9365..9b94142 100644
--- a/Source/cmTargetPropertyComputer.cxx
+++ b/Source/cmTargetPropertyComputer.cxx
@@ -21,6 +21,7 @@
     case cmPolicies::WARN:
       e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0026) << "\n";
       modal = "should";
+      CM_FALLTHROUGH;
     case cmPolicies::OLD:
       break;
     case cmPolicies::REQUIRED_ALWAYS:
@@ -28,6 +29,7 @@
     case cmPolicies::NEW:
       modal = "may";
       messageType = MessageType::FATAL_ERROR;
+      break;
   }
 
   if (modal) {
diff --git a/Source/cmTargetPropertyComputer.h b/Source/cmTargetPropertyComputer.h
index f2be318..e61a1fc 100644
--- a/Source/cmTargetPropertyComputer.h
+++ b/Source/cmTargetPropertyComputer.h
@@ -7,10 +7,10 @@
 #include <string>
 
 #include "cmListFileCache.h"
-#include "cmProperty.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 class cmMessenger;
 
@@ -18,11 +18,11 @@
 {
 public:
   template <typename Target>
-  static cmProp GetProperty(Target const* tgt, const std::string& prop,
-                            cmMessenger* messenger,
-                            cmListFileBacktrace const& context)
+  static cmValue GetProperty(Target const* tgt, const std::string& prop,
+                             cmMessenger* messenger,
+                             cmListFileBacktrace const& context)
   {
-    if (cmProp loc = GetLocation(tgt, prop, messenger, context)) {
+    if (cmValue loc = GetLocation(tgt, prop, messenger, context)) {
       return loc;
     }
     if (cmSystemTools::GetFatalErrorOccured()) {
@@ -46,9 +46,9 @@
                                             std::string const& config);
 
   template <typename Target>
-  static cmProp GetLocation(Target const* tgt, std::string const& prop,
-                            cmMessenger* messenger,
-                            cmListFileBacktrace const& context)
+  static cmValue GetLocation(Target const* tgt, std::string const& prop,
+                             cmMessenger* messenger,
+                             cmListFileBacktrace const& context)
 
   {
     // Watch for special "computed" properties that are dependent on
@@ -65,7 +65,7 @@
                                           context)) {
           return nullptr;
         }
-        return &ComputeLocationForBuild(tgt);
+        return cmValue(ComputeLocationForBuild(tgt));
       }
 
       // Support "LOCATION_<CONFIG>".
@@ -76,7 +76,7 @@
           return nullptr;
         }
         std::string configName = prop.substr(9);
-        return &ComputeLocation(tgt, configName);
+        return cmValue(ComputeLocation(tgt, configName));
       }
 
       // Support "<CONFIG>_LOCATION".
@@ -89,7 +89,7 @@
                                             context)) {
             return nullptr;
           }
-          return &ComputeLocation(tgt, configName);
+          return cmValue(ComputeLocation(tgt, configName));
         }
       }
     }
@@ -97,6 +97,6 @@
   }
 
   template <typename Target>
-  static cmProp GetSources(Target const* tgt, cmMessenger* messenger,
-                           cmListFileBacktrace const& context);
+  static cmValue GetSources(Target const* tgt, cmMessenger* messenger,
+                            cmListFileBacktrace const& context);
 };
diff --git a/Source/cmTest.cxx b/Source/cmTest.cxx
index a26bef3..7c0f9e7 100644
--- a/Source/cmTest.cxx
+++ b/Source/cmTest.cxx
@@ -5,7 +5,7 @@
 #include "cmMakefile.h"
 #include "cmProperty.h"
 #include "cmState.h"
-#include "cmStringAlgorithms.h"
+#include "cmValue.h"
 
 cmTest::cmTest(cmMakefile* mf)
   : CommandExpandLists(false)
@@ -32,20 +32,20 @@
   this->Command = command;
 }
 
-const char* cmTest::GetProperty(const std::string& prop) const
+cmValue cmTest::GetProperty(const std::string& prop) const
 {
-  cmProp retVal = this->Properties.GetPropertyValue(prop);
+  cmValue retVal = this->Properties.GetPropertyValue(prop);
   if (!retVal) {
     const bool chain =
       this->Makefile->GetState()->IsPropertyChained(prop, cmProperty::TEST);
     if (chain) {
-      if (cmProp p = this->Makefile->GetProperty(prop, chain)) {
-        return p->c_str();
+      if (cmValue p = this->Makefile->GetProperty(prop, chain)) {
+        return p;
       }
     }
     return nullptr;
   }
-  return retVal->c_str();
+  return retVal;
 }
 
 bool cmTest::GetPropertyAsBool(const std::string& prop) const
@@ -57,6 +57,10 @@
 {
   this->Properties.SetProperty(prop, value);
 }
+void cmTest::SetProperty(const std::string& prop, cmValue value)
+{
+  this->Properties.SetProperty(prop, value);
+}
 
 void cmTest::AppendProperty(const std::string& prop, const std::string& value,
                             bool asString)
diff --git a/Source/cmTest.h b/Source/cmTest.h
index f33b7e2..85978da 100644
--- a/Source/cmTest.h
+++ b/Source/cmTest.h
@@ -9,6 +9,7 @@
 
 #include "cmListFileCache.h"
 #include "cmPropertyMap.h"
+#include "cmValue.h"
 
 class cmMakefile;
 
@@ -34,9 +35,14 @@
 
   //! Set/Get a property of this source file
   void SetProperty(const std::string& prop, const char* value);
+  void SetProperty(const std::string& prop, cmValue value);
+  void SetProperty(const std::string& prop, const std::string& value)
+  {
+    this->SetProperty(prop, cmValue(value));
+  }
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString = false);
-  const char* GetProperty(const std::string& prop) const;
+  cmValue GetProperty(const std::string& prop) const;
   bool GetPropertyAsBool(const std::string& prop) const;
   cmPropertyMap& GetProperties() { return this->Properties; }
 
diff --git a/Source/cmTestGenerator.cxx b/Source/cmTestGenerator.cxx
index 7022c4e..dbb0876 100644
--- a/Source/cmTestGenerator.cxx
+++ b/Source/cmTestGenerator.cxx
@@ -19,13 +19,13 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
-#include "cmProperty.h"
 #include "cmPropertyMap.h"
 #include "cmRange.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTest.h"
+#include "cmValue.h"
 
 namespace /* anonymous */
 {
@@ -167,7 +167,7 @@
     exe = target->GetFullPath(config);
 
     // Prepend with the emulator when cross compiling if required.
-    cmProp emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");
+    cmValue emulator = target->GetProperty("CROSSCOMPILING_EMULATOR");
     if (cmNonempty(emulator)) {
       std::vector<std::string> emulatorWithArgs = cmExpandedList(*emulator);
       std::string emulatorExe(emulatorWithArgs[0]);
diff --git a/Source/cmTimestamp.cxx b/Source/cmTimestamp.cxx
index 056696d..3826577 100644
--- a/Source/cmTimestamp.cxx
+++ b/Source/cmTimestamp.cxx
@@ -18,6 +18,10 @@
 #include <cstring>
 #include <sstream>
 
+#ifdef __MINGW32__
+#  include <libloaderapi.h>
+#endif
+
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
@@ -159,6 +163,7 @@
     case 'M':
     case 'S':
     case 'U':
+    case 'V':
     case 'w':
     case 'y':
     case 'Y':
@@ -189,6 +194,30 @@
 
   char buffer[16];
 
+#ifdef __MINGW32__
+  /* See a bug in MinGW: https://sourceforge.net/p/mingw-w64/bugs/793/. A work
+   * around is to try to use strftime() from ucrtbase.dll. */
+  using T = size_t(__cdecl*)(char*, size_t, const char*, const struct tm*);
+  auto loadUcrtStrftime = []() -> T {
+    auto handle =
+      LoadLibraryExA("ucrtbase.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+    if (handle) {
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wcast-function-type"
+      return reinterpret_cast<T>(GetProcAddress(handle, "strftime"));
+#  pragma GCC diagnostic pop
+    }
+    return nullptr;
+  };
+  static T ucrtStrftime = loadUcrtStrftime();
+
+  if (ucrtStrftime) {
+    size_t size =
+      ucrtStrftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
+    return std::string(buffer, size);
+  }
+#endif
+
   size_t size =
     strftime(buffer, sizeof(buffer), formatString.c_str(), &timeStruct);
 
diff --git a/Source/cmTryRunCommand.cxx b/Source/cmTryRunCommand.cxx
index 8cac74d..cc9e158 100644
--- a/Source/cmTryRunCommand.cxx
+++ b/Source/cmTryRunCommand.cxx
@@ -9,12 +9,12 @@
 #include "cmDuration.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 
 class cmExecutionStatus;
@@ -162,7 +162,7 @@
       if (!this->OutputVariable.empty()) {
         // if the TryCompileCore saved output in this outputVariable then
         // prepend that output to this output
-        cmProp compileOutput =
+        cmValue compileOutput =
           this->Makefile->GetDefinition(this->OutputVariable);
         if (compileOutput) {
           runOutputContents = *compileOutput + runOutputContents;
@@ -260,7 +260,7 @@
                                        comment.c_str(), cmStateEnums::STRING);
 
     cmState* state = this->Makefile->GetState();
-    cmProp existingValue = state->GetCacheEntryValue(this->RunResultVariable);
+    cmValue existingValue = state->GetCacheEntryValue(this->RunResultVariable);
     if (existingValue) {
       state->SetCacheEntryProperty(this->RunResultVariable, "ADVANCED", "1");
     }
@@ -282,7 +282,7 @@
         internalRunOutputName, "PLEASE_FILL_OUT-NOTFOUND", comment.c_str(),
         cmStateEnums::STRING);
       cmState* state = this->Makefile->GetState();
-      cmProp existing = state->GetCacheEntryValue(internalRunOutputName);
+      cmValue existing = state->GetCacheEntryValue(internalRunOutputName);
       if (existing) {
         state->SetCacheEntryProperty(internalRunOutputName, "ADVANCED", "1");
       }
diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h
index 8c5ad59..027d690 100644
--- a/Source/cmUVHandlePtr.h
+++ b/Source/cmUVHandlePtr.h
@@ -23,7 +23,7 @@
 
 #else
 
-#  define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base;
+#  define CM_INHERIT_CTOR(Class, Base, Tpl) using Base Tpl ::Base
 
 #endif
 
diff --git a/Source/cmUtilitySourceCommand.cxx b/Source/cmUtilitySourceCommand.cxx
index d276c8a..2805a33 100644
--- a/Source/cmUtilitySourceCommand.cxx
+++ b/Source/cmUtilitySourceCommand.cxx
@@ -6,11 +6,11 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // cmUtilitySourceCommand
 bool cmUtilitySourceCommand(std::vector<std::string> const& args,
@@ -25,7 +25,7 @@
 
   // The first argument is the cache entry name.
   std::string const& cacheEntry = *arg++;
-  cmProp cacheValue = status.GetMakefile().GetDefinition(cacheEntry);
+  cmValue cacheValue = status.GetMakefile().GetDefinition(cacheEntry);
   // If it exists already and appears up to date then we are done.  If
   // the string contains "(IntDir)" but that is not the
   // CMAKE_CFG_INTDIR setting then the value is out of date.
@@ -85,7 +85,7 @@
   std::string utilityDirectory =
     status.GetMakefile().GetCurrentBinaryDirectory();
   std::string exePath;
-  if (cmProp d =
+  if (cmValue d =
         status.GetMakefile().GetDefinition("EXECUTABLE_OUTPUT_PATH")) {
     exePath = *d;
   }
diff --git a/Source/cmValue.cxx b/Source/cmValue.cxx
new file mode 100644
index 0000000..044db29
--- /dev/null
+++ b/Source/cmValue.cxx
@@ -0,0 +1,123 @@
+
+#include "cmValue.h"
+
+#include <string>
+
+#include <cmext/string_view>
+
+#include "cmStringAlgorithms.h"
+
+std::string cmValue::Empty;
+
+bool cmValue::IsOn(cm::string_view value) noexcept
+{
+  switch (value.size()) {
+    case 1:
+      return value[0] == '1' || value[0] == 'Y' || value[0] == 'y';
+    case 2:
+      return                                    //
+        (value[0] == 'O' || value[0] == 'o') && //
+        (value[1] == 'N' || value[1] == 'n');
+    case 3:
+      return                                    //
+        (value[0] == 'Y' || value[0] == 'y') && //
+        (value[1] == 'E' || value[1] == 'e') && //
+        (value[2] == 'S' || value[2] == 's');
+    case 4:
+      return                                    //
+        (value[0] == 'T' || value[0] == 't') && //
+        (value[1] == 'R' || value[1] == 'r') && //
+        (value[2] == 'U' || value[2] == 'u') && //
+        (value[3] == 'E' || value[3] == 'e');
+    default:
+      break;
+  }
+
+  return false;
+}
+
+bool cmValue::IsOff(cm::string_view value) noexcept
+{
+  switch (value.size()) {
+    case 0:
+      return true;
+    case 1:
+      return value[0] == '0' || value[0] == 'N' || value[0] == 'n';
+    case 2:
+      return                                    //
+        (value[0] == 'N' || value[0] == 'n') && //
+        (value[1] == 'O' || value[1] == 'o');
+    case 3:
+      return                                    //
+        (value[0] == 'O' || value[0] == 'o') && //
+        (value[1] == 'F' || value[1] == 'f') && //
+        (value[2] == 'F' || value[2] == 'f');
+    case 5:
+      return                                    //
+        (value[0] == 'F' || value[0] == 'f') && //
+        (value[1] == 'A' || value[1] == 'a') && //
+        (value[2] == 'L' || value[2] == 'l') && //
+        (value[3] == 'S' || value[3] == 's') && //
+        (value[4] == 'E' || value[4] == 'e');
+    case 6:
+      return                                    //
+        (value[0] == 'I' || value[0] == 'i') && //
+        (value[1] == 'G' || value[1] == 'g') && //
+        (value[2] == 'N' || value[2] == 'n') && //
+        (value[3] == 'O' || value[3] == 'o') && //
+        (value[4] == 'R' || value[4] == 'r') && //
+        (value[5] == 'E' || value[5] == 'e');
+    default:
+      break;
+  }
+
+  return IsNOTFOUND(value);
+}
+
+bool cmValue::IsNOTFOUND(cm::string_view value) noexcept
+{
+  return (value == "NOTFOUND"_s) || cmHasSuffix(value, "-NOTFOUND"_s);
+}
+
+bool cmValue::IsInternallyOn(cm::string_view value) noexcept
+{
+  return (value.size() == 4) &&             //
+    (value[0] == 'I' || value[0] == 'i') && //
+    (value[1] == '_') &&                    //
+    (value[2] == 'O' || value[2] == 'o') && //
+    (value[3] == 'N' || value[3] == 'n');
+}
+
+int cmValue::Compare(cmValue value) const noexcept
+{
+  if (this->Value == nullptr && !value) {
+    return 0;
+  }
+  if (this->Value == nullptr) {
+    return -1;
+  }
+  if (!value) {
+    return 1;
+  }
+  return this->Value->compare(*value);
+}
+
+int cmValue::Compare(cm::string_view value) const noexcept
+{
+  if (this->Value == nullptr && value.data() == nullptr) {
+    return 0;
+  }
+  if (this->Value == nullptr) {
+    return -1;
+  }
+  if (value.data() == nullptr) {
+    return 1;
+  }
+  return cm::string_view(*this->Value).compare(value);
+}
+
+std::ostream& operator<<(std::ostream& o, cmValue v)
+{
+  o << *v;
+  return o;
+}
diff --git a/Source/cmValue.h b/Source/cmValue.h
new file mode 100644
index 0000000..f96d2f5
--- /dev/null
+++ b/Source/cmValue.h
@@ -0,0 +1,314 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <cstddef>
+#include <iosfwd>
+#include <string>
+
+#include <cm/string_view>
+
+class cmValue
+{
+public:
+  cmValue() noexcept = default;
+  cmValue(std::nullptr_t) noexcept {}
+  explicit cmValue(const std::string* value) noexcept
+    : Value(value)
+  {
+  }
+  explicit cmValue(const std::string& value) noexcept
+    : Value(&value)
+  {
+  }
+  cmValue(const cmValue& other) noexcept = default;
+
+  cmValue& operator=(const cmValue& other) noexcept = default;
+  cmValue& operator=(std::nullptr_t) noexcept
+  {
+    this->Value = nullptr;
+    return *this;
+  }
+
+  const std::string* Get() const noexcept { return this->Value; }
+  const char* GetCStr() const noexcept
+  {
+    return this->Value == nullptr ? nullptr : this->Value->c_str();
+  }
+
+  const std::string* operator->() const noexcept
+  {
+    return this->Value == nullptr ? &cmValue::Empty : this->Value;
+  }
+  const std::string& operator*() const noexcept
+  {
+    return this->Value == nullptr ? cmValue::Empty : *this->Value;
+  }
+
+  explicit operator bool() const noexcept { return this->Value != nullptr; }
+  operator const std::string&() const noexcept { return this->operator*(); }
+  explicit operator cm::string_view() const noexcept
+  {
+    return this->operator*();
+  }
+
+  /**
+   * Does the value indicate a true or ON value?
+   */
+  bool IsOn() const noexcept
+  {
+    return this->Value != nullptr &&
+      cmValue::IsOn(cm::string_view(*this->Value));
+  }
+  /**
+   * Does the value indicate a false or off value ? Note that this is
+   * not the same as !IsOn(...) because there are a number of
+   * ambiguous values such as "/usr/local/bin" a path will result in
+   * IsOn and IsOff both returning false. Note that the special path
+   * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
+   */
+  bool IsOff() const noexcept
+  {
+    return this->Value == nullptr ||
+      cmValue::IsOff(cm::string_view(*this->Value));
+  }
+  /** Return true if value is NOTFOUND or ends in -NOTFOUND.  */
+  bool IsNOTFOUND() const noexcept
+  {
+    return this->Value != nullptr &&
+      cmValue::IsNOTFOUND(cm::string_view(*this->Value));
+  }
+  bool IsEmpty() const noexcept
+  {
+    return this->Value == nullptr || this->Value->empty();
+  }
+
+  /**
+   * Does a string indicates that CMake/CPack/CTest internally
+   *  forced this value. This is not the same as On, but this
+   * may be considered as "internally switched on".
+   */
+  bool IsInternallyOn() const noexcept
+  {
+    return this->Value != nullptr &&
+      cmValue::IsInternallyOn(cm::string_view(*this->Value));
+  }
+
+  bool IsSet() const noexcept
+  {
+    return !this->IsEmpty() && !this->IsNOTFOUND();
+  }
+
+  /**
+   * Does a string indicate a true or ON value?
+   */
+  static bool IsOn(const char* value) noexcept
+  {
+    return value != nullptr && IsOn(cm::string_view(value));
+  }
+  static bool IsOn(cm::string_view) noexcept;
+
+  /**
+   * Compare method has same semantic as std::optional::compare
+   */
+  int Compare(cmValue value) const noexcept;
+  int Compare(cm::string_view value) const noexcept;
+
+  /**
+   * Does a string indicate a false or off value ? Note that this is
+   * not the same as !IsOn(...) because there are a number of
+   * ambiguous values such as "/usr/local/bin" a path will result in
+   * IsOn and IsOff both returning false. Note that the special path
+   * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
+   */
+  static bool IsOff(const char* value) noexcept
+  {
+    return value == nullptr || IsOff(cm::string_view(value));
+  }
+  static bool IsOff(cm::string_view) noexcept;
+
+  /** Return true if value is NOTFOUND or ends in -NOTFOUND.  */
+  static bool IsNOTFOUND(const char* value) noexcept
+  {
+    return value == nullptr || IsNOTFOUND(cm::string_view(value));
+  }
+  static bool IsNOTFOUND(cm::string_view) noexcept;
+
+  static bool IsEmpty(const char* value) noexcept
+  {
+    return value == nullptr || *value == '\0';
+  }
+  static bool IsEmpty(cm::string_view value) noexcept { return value.empty(); }
+
+  /**
+   * Does a string indicates that CMake/CPack/CTest internally
+   * forced this value. This is not the same as On, but this
+   * may be considered as "internally switched on".
+   */
+  static bool IsInternallyOn(const char* value) noexcept
+  {
+    return value != nullptr && IsInternallyOn(cm::string_view(value));
+  }
+  static bool IsInternallyOn(cm::string_view) noexcept;
+
+private:
+  static std::string Empty;
+  const std::string* Value = nullptr;
+};
+
+std::ostream& operator<<(std::ostream& o, cmValue v);
+
+inline bool operator==(cmValue l, cmValue r) noexcept
+{
+  return l.Compare(r) == 0;
+}
+inline bool operator!=(cmValue l, cmValue r) noexcept
+{
+  return l.Compare(r) != 0;
+}
+inline bool operator<(cmValue l, cmValue r) noexcept
+{
+  return l.Compare(r) < 0;
+}
+inline bool operator<=(cmValue l, cmValue r) noexcept
+{
+  return l.Compare(r) <= 0;
+}
+inline bool operator>(cmValue l, cmValue r) noexcept
+{
+  return l.Compare(r) > 0;
+}
+inline bool operator>=(cmValue l, cmValue r) noexcept
+{
+  return l.Compare(r) >= 0;
+}
+
+inline bool operator==(cmValue l, cm::string_view r) noexcept
+{
+  return l.Compare(r) == 0;
+}
+inline bool operator!=(cmValue l, cm::string_view r) noexcept
+{
+  return l.Compare(r) != 0;
+}
+inline bool operator<(cmValue l, cm::string_view r) noexcept
+{
+  return l.Compare(r) < 0;
+}
+inline bool operator<=(cmValue l, cm::string_view r) noexcept
+{
+  return l.Compare(r) <= 0;
+}
+inline bool operator>(cmValue l, cm::string_view r) noexcept
+{
+  return l.Compare(r) > 0;
+}
+inline bool operator>=(cmValue l, cm::string_view r) noexcept
+{
+  return l.Compare(r) >= 0;
+}
+
+inline bool operator==(cmValue l, std::nullptr_t) noexcept
+{
+  return l.Compare(cmValue{}) == 0;
+}
+inline bool operator!=(cmValue l, std::nullptr_t) noexcept
+{
+  return l.Compare(cmValue{}) != 0;
+}
+inline bool operator<(cmValue l, std::nullptr_t) noexcept
+{
+  return l.Compare(cmValue{}) < 0;
+}
+inline bool operator<=(cmValue l, std::nullptr_t) noexcept
+{
+  return l.Compare(cmValue{}) <= 0;
+}
+inline bool operator>(cmValue l, std::nullptr_t) noexcept
+{
+  return l.Compare(cmValue{}) > 0;
+}
+inline bool operator>=(cmValue l, std::nullptr_t) noexcept
+{
+  return l.Compare(cmValue{}) >= 0;
+}
+
+/**
+ * Does a string indicate a true or ON value? This is not the same as ifdef.
+ */
+inline bool cmIsOn(cm::string_view val)
+{
+  return cmValue::IsOn(val);
+}
+inline bool cmIsOn(const char* val)
+{
+  return cmValue::IsOn(val);
+}
+inline bool cmIsOn(cmValue val)
+{
+  return val.IsOn();
+}
+
+/**
+ * Does a string indicate a false or off value ? Note that this is
+ * not the same as !IsOn(...) because there are a number of
+ * ambiguous values such as "/usr/local/bin" a path will result in
+ * IsON and IsOff both returning false. Note that the special path
+ * NOTFOUND, *-NOTFOUND or IGNORE will cause IsOff to return true.
+ */
+inline bool cmIsOff(cm::string_view val)
+{
+  return cmValue::IsOff(val);
+}
+inline bool cmIsOff(const char* val)
+{
+  return cmValue::IsOff(val);
+}
+inline bool cmIsOff(cmValue val)
+{
+  return val.IsOff();
+}
+
+/** Return true if value is NOTFOUND or ends in -NOTFOUND.  */
+inline bool cmIsNOTFOUND(cm::string_view val)
+{
+  return cmValue::IsNOTFOUND(val);
+}
+inline bool cmIsNOTFOUND(cmValue val)
+{
+  return val.IsNOTFOUND();
+}
+
+/** Check for non-empty Property/Variable value.  */
+inline bool cmNonempty(cm::string_view val)
+{
+  return !cmValue::IsEmpty(val);
+}
+inline bool cmNonempty(const char* val)
+{
+  return !cmValue::IsEmpty(val);
+}
+inline bool cmNonempty(cmValue val)
+{
+  return !val.IsEmpty();
+}
+
+/**
+ * Does a string indicates that CMake/CPack/CTest internally
+ * forced this value. This is not the same as On, but this
+ * may be considered as "internally switched on".
+ */
+inline bool cmIsInternallyOn(cm::string_view val)
+{
+  return cmValue::IsInternallyOn(val);
+}
+inline bool cmIsInternallyOn(const char* val)
+{
+  return cmValue::IsInternallyOn(val);
+}
+inline bool cmIsInternallyOn(cmValue val)
+{
+  return val.IsInternallyOn();
+}
diff --git a/Source/cmVariableRequiresCommand.cxx b/Source/cmVariableRequiresCommand.cxx
index 1fe03ab..2b1efba 100644
--- a/Source/cmVariableRequiresCommand.cxx
+++ b/Source/cmVariableRequiresCommand.cxx
@@ -4,10 +4,10 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 // cmLibraryCommand
 bool cmVariableRequiresCommand(std::vector<std::string> const& args,
@@ -38,7 +38,7 @@
       }
     }
   }
-  cmProp reqVar = status.GetMakefile().GetDefinition(resultVariable);
+  cmValue reqVar = status.GetMakefile().GetDefinition(resultVariable);
   // if reqVar is unset, then set it to requirementsMet
   // if reqVar is set to true, but requirementsMet is false , then
   // set reqVar to false.
diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx
index 7c7fbca..fd5402c 100644
--- a/Source/cmVariableWatchCommand.cxx
+++ b/Source/cmVariableWatchCommand.cxx
@@ -10,9 +10,9 @@
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmVariableWatch.h"
 #include "cmake.h"
 
@@ -45,7 +45,7 @@
 
   std::string stack = *mf->GetProperty("LISTFILE_STACK");
   if (!data->Command.empty()) {
-    cmProp const currentListFile =
+    cmValue const currentListFile =
       mf->GetDefinition("CMAKE_CURRENT_LIST_FILE");
     const auto fakeLineNo =
       std::numeric_limits<decltype(cmListFileArgument::Line)>::max();
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 97cb1bf..a871e4c 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -6,6 +6,7 @@
 #include <set>
 
 #include <cm/memory>
+#include <cm/optional>
 #include <cm/string_view>
 #include <cm/vector>
 #include <cmext/algorithm>
@@ -437,7 +438,7 @@
         this->VerifyNecessaryFiles();
       }
 
-      cmProp vsProjectTypes =
+      cmValue vsProjectTypes =
         this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES");
       if (vsProjectTypes) {
         const char* tagName = "ProjectTypes";
@@ -447,11 +448,11 @@
         e1.Element(tagName, *vsProjectTypes);
       }
 
-      cmProp vsProjectName =
+      cmValue vsProjectName =
         this->GeneratorTarget->GetProperty("VS_SCC_PROJECTNAME");
-      cmProp vsLocalPath =
+      cmValue vsLocalPath =
         this->GeneratorTarget->GetProperty("VS_SCC_LOCALPATH");
-      cmProp vsProvider =
+      cmValue vsProvider =
         this->GeneratorTarget->GetProperty("VS_SCC_PROVIDER");
 
       if (vsProjectName && vsLocalPath && vsProvider) {
@@ -459,7 +460,7 @@
         e1.Element("SccLocalPath", *vsLocalPath);
         e1.Element("SccProvider", *vsProvider);
 
-        cmProp vsAuxPath =
+        cmValue vsAuxPath =
           this->GeneratorTarget->GetProperty("VS_SCC_AUXPATH");
         if (vsAuxPath) {
           e1.Element("SccAuxPath", *vsAuxPath);
@@ -470,7 +471,7 @@
         e1.Element("WinMDAssembly", "true");
       }
 
-      cmProp vsGlobalKeyword =
+      cmValue vsGlobalKeyword =
         this->GeneratorTarget->GetProperty("VS_GLOBAL_KEYWORD");
       if (!vsGlobalKeyword) {
         if (this->GlobalGenerator->TargetsAndroid()) {
@@ -482,63 +483,78 @@
         e1.Element("Keyword", *vsGlobalKeyword);
       }
 
-      cmProp vsGlobalRootNamespace =
+      cmValue vsGlobalRootNamespace =
         this->GeneratorTarget->GetProperty("VS_GLOBAL_ROOTNAMESPACE");
       if (vsGlobalRootNamespace) {
         e1.Element("RootNamespace", *vsGlobalRootNamespace);
       }
 
       e1.Element("Platform", this->Platform);
-      cmProp projLabel = this->GeneratorTarget->GetProperty("PROJECT_LABEL");
-      if (!projLabel) {
-        projLabel = &this->Name;
-      }
-      e1.Element("ProjectName", *projLabel);
+      cmValue projLabel = this->GeneratorTarget->GetProperty("PROJECT_LABEL");
+      e1.Element("ProjectName", projLabel ? projLabel : this->Name);
       {
-        cmProp targetFramework =
-          this->GeneratorTarget->GetProperty("DOTNET_TARGET_FRAMEWORK");
-        if (targetFramework) {
-          if (targetFramework->find(';') != std::string::npos) {
-            e1.Element("TargetFrameworks", *targetFramework);
-          } else {
-            e1.Element("TargetFramework", *targetFramework);
-          }
-        } else {
-          // TODO: add deprecation warning for VS_* property?
-          cmProp p = this->GeneratorTarget->GetProperty(
-            "VS_DOTNET_TARGET_FRAMEWORK_VERSION");
-          if (!p) {
-            p = this->GeneratorTarget->GetProperty(
-              "DOTNET_TARGET_FRAMEWORK_VERSION");
-          }
-          const char* targetFrameworkVersion = cmToCStr(p);
-          if (!targetFrameworkVersion && this->ProjectType == csproj &&
-              this->GlobalGenerator->TargetsWindowsCE() &&
-              this->GlobalGenerator->GetVersion() ==
-                cmGlobalVisualStudioGenerator::VS12) {
-            // VS12 .NETCF default to .NET framework 3.9
-            targetFrameworkVersion = "v3.9";
-          }
-          if (targetFrameworkVersion) {
-            e1.Element("TargetFrameworkVersion", targetFrameworkVersion);
-          }
+        cm::optional<std::string> targetFramework;
+        cm::optional<std::string> targetFrameworkVersion;
+        cm::optional<std::string> targetFrameworkIdentifier;
+        cm::optional<std::string> targetFrameworkTargetsVersion;
+        if (cmValue tf =
+              this->GeneratorTarget->GetProperty("DOTNET_TARGET_FRAMEWORK")) {
+          targetFramework = *tf;
+        } else if (cmValue vstfVer = this->GeneratorTarget->GetProperty(
+                     "VS_DOTNET_TARGET_FRAMEWORK_VERSION")) {
+          // FIXME: Someday, add a deprecation warning for VS_* property.
+          targetFrameworkVersion = *vstfVer;
+        } else if (cmValue tfVer = this->GeneratorTarget->GetProperty(
+                     "DOTNET_TARGET_FRAMEWORK_VERSION")) {
+          targetFrameworkVersion = *tfVer;
+        } else if (this->ProjectType == csproj) {
+          targetFrameworkVersion =
+            this->GlobalGenerator->GetTargetFrameworkVersion();
         }
         if (this->ProjectType == vcxproj &&
             this->GlobalGenerator->TargetsWindowsCE()) {
           e1.Element("EnableRedirectPlatform", "true");
           e1.Element("RedirectPlatformValue", this->Platform);
         }
-        if (this->ProjectType == csproj &&
-            this->GlobalGenerator->TargetsWindowsCE()) {
-          cmProp targetFrameworkId = this->GeneratorTarget->GetProperty(
-            "VS_TARGET_FRAMEWORK_IDENTIFIER");
-          e1.Element("TargetFrameworkIdentifier",
-                     targetFrameworkId ? *targetFrameworkId
-                                       : "WindowsEmbeddedCompact");
-          cmProp targetFrameworkVer = this->GeneratorTarget->GetProperty(
-            "VS_TARGET_FRAMEWORKS_TARGET_VERSION");
+        if (this->ProjectType == csproj) {
+          if (this->GlobalGenerator->TargetsWindowsCE()) {
+            // FIXME: These target VS_TARGET_FRAMEWORK* target properties
+            // are undocumented settings only ever supported for WinCE.
+            // We need a better way to control these in general.
+            if (cmValue tfId = this->GeneratorTarget->GetProperty(
+                  "VS_TARGET_FRAMEWORK_IDENTIFIER")) {
+              targetFrameworkIdentifier = *tfId;
+            }
+            if (cmValue tfTargetsVer = this->GeneratorTarget->GetProperty(
+                  "VS_TARGET_FRAMEWORKS_TARGET_VERSION")) {
+              targetFrameworkTargetsVersion = *tfTargetsVer;
+            }
+          }
+          if (!targetFrameworkIdentifier) {
+            targetFrameworkIdentifier =
+              this->GlobalGenerator->GetTargetFrameworkIdentifier();
+          }
+          if (!targetFrameworkTargetsVersion) {
+            targetFrameworkTargetsVersion =
+              this->GlobalGenerator->GetTargetFrameworkTargetsVersion();
+          }
+        }
+        if (targetFramework) {
+          if (targetFramework->find(';') != std::string::npos) {
+            e1.Element("TargetFrameworks", *targetFramework);
+          } else {
+            e1.Element("TargetFramework", *targetFramework);
+          }
+        }
+        if (targetFrameworkVersion) {
+          e1.Element("TargetFrameworkVersion", *targetFrameworkVersion);
+        }
+        if (targetFrameworkIdentifier) {
+          e1.Element("TargetFrameworkIdentifier", *targetFrameworkIdentifier);
+        }
+        if (targetFrameworkTargetsVersion) {
           e1.Element("TargetFrameworkTargetsVersion",
-                     targetFrameworkVer ? *targetFrameworkVer : "v8.0");
+                     *targetFrameworkTargetsVersion);
         }
         if (!this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString()
                .empty()) {
@@ -574,7 +590,7 @@
             globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") {
           continue;
         }
-        cmProp value = this->GeneratorTarget->GetProperty(keyIt);
+        cmValue value = this->GeneratorTarget->GetProperty(keyIt);
         if (!value)
           continue;
         e1.Element(globalKey, *value);
@@ -697,7 +713,7 @@
           props = VS10_CSharp_USER_PROPS;
           break;
       }
-      if (cmProp p = this->GeneratorTarget->GetProperty("VS_USER_PROPS")) {
+      if (cmValue p = this->GeneratorTarget->GetProperty("VS_USER_PROPS")) {
         props = *p;
       }
       if (!props.empty()) {
@@ -810,7 +826,7 @@
 void cmVisualStudio10TargetGenerator::WritePackageReferences(Elem& e0)
 {
   std::vector<std::string> packageReferences;
-  if (cmProp vsPackageReferences =
+  if (cmValue vsPackageReferences =
         this->GeneratorTarget->GetProperty("VS_PACKAGE_REFERENCES")) {
     cmExpandList(*vsPackageReferences, packageReferences);
   }
@@ -830,7 +846,7 @@
 void cmVisualStudio10TargetGenerator::WriteDotNetReferences(Elem& e0)
 {
   std::vector<std::string> references;
-  if (cmProp vsDotNetReferences =
+  if (cmValue vsDotNetReferences =
         this->GeneratorTarget->GetProperty("VS_DOTNET_REFERENCES")) {
     cmExpandList(*vsDotNetReferences, references);
   }
@@ -889,7 +905,7 @@
   e2.Element("ReferenceOutputAssembly", "true");
   if (!hint.empty()) {
     const char* privateReference = "True";
-    if (cmProp value = this->GeneratorTarget->GetProperty(
+    if (cmValue value = this->GeneratorTarget->GetProperty(
           "VS_DOTNET_REFERENCES_COPY_LOCAL")) {
       if (cmIsOff(*value)) {
         privateReference = "False";
@@ -903,7 +919,7 @@
 
 void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0)
 {
-  cmProp imports =
+  cmValue imports =
     this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT");
   if (imports) {
     std::vector<std::string> argsSplit = cmExpandedList(*imports, false);
@@ -1007,7 +1023,7 @@
           ".Designer.cs";
         if (cmsys::SystemTools::FileExists(designerResource)) {
           std::string generator = "PublicResXFileCodeGenerator";
-          if (cmProp g = oi->GetProperty("VS_RESOURCE_GENERATOR")) {
+          if (cmValue g = oi->GetProperty("VS_RESOURCE_GENERATOR")) {
             generator = *g;
           }
           if (!generator.empty()) {
@@ -1031,7 +1047,7 @@
             cm::string_view tagName =
               cm::string_view(p).substr(propNamePrefix.length());
             if (!tagName.empty()) {
-              cmProp value = props.GetPropertyValue(p);
+              cmValue value = props.GetPropertyValue(p);
               if (cmNonempty(value)) {
                 e2.Element(tagName, *value);
               }
@@ -1050,7 +1066,7 @@
     for (cmSourceFile const* oi : this->XamlObjs) {
       std::string obj = oi->GetFullPath();
       std::string xamlType;
-      cmProp xamlTypeProperty = oi->GetProperty("VS_XAML_TYPE");
+      cmValue xamlTypeProperty = oi->GetProperty("VS_XAML_TYPE");
       if (xamlTypeProperty) {
         xamlType = *xamlTypeProperty;
       } else {
@@ -1104,7 +1120,7 @@
 void cmVisualStudio10TargetGenerator::WriteWinRTReferences(Elem& e0)
 {
   std::vector<std::string> references;
-  if (cmProp vsWinRTReferences =
+  if (cmValue vsWinRTReferences =
         this->GeneratorTarget->GetProperty("VS_WINRT_REFERENCES")) {
     cmExpandList(*vsWinRTReferences, references);
   }
@@ -1147,7 +1163,7 @@
 
     if (this->ProjectType != csproj) {
       std::string configType;
-      if (cmProp vsConfigurationType =
+      if (cmValue vsConfigurationType =
             this->GeneratorTarget->GetProperty("VS_CONFIGURATION_TYPE")) {
         configType = cmGeneratorExpression::Evaluate(*vsConfigurationType,
                                                      this->LocalGenerator, c);
@@ -1209,9 +1225,9 @@
   if (!this->GlobalGenerator->TargetsWindowsCE()) {
     return;
   }
-  cmProp additionalFiles =
+  cmValue additionalFiles =
     this->GeneratorTarget->GetProperty("DEPLOYMENT_ADDITIONAL_FILES");
-  cmProp remoteDirectory =
+  cmValue remoteDirectory =
     this->GeneratorTarget->GetProperty("DEPLOYMENT_REMOTE_DIRECTORY");
   if (!(additionalFiles || remoteDirectory)) {
     return;
@@ -1233,7 +1249,7 @@
   Elem& e1, std::string const& config)
 {
   cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
-  cmProp mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG");
+  cmValue mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG");
   if (mfcFlag) {
     std::string const mfcFlagValue =
       cmGeneratorExpression::Evaluate(*mfcFlag, this->LocalGenerator, config);
@@ -1263,7 +1279,7 @@
   } else {
     e1.Element("CharacterSet", "MultiByte");
   }
-  if (cmProp projectToolsetOverride =
+  if (cmValue projectToolsetOverride =
         this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) {
     e1.Element("PlatformToolset", *projectToolsetOverride);
   } else if (const char* toolset = gg->GetPlatformToolset()) {
@@ -1309,7 +1325,7 @@
     o.RemoveFlag("Platform");
   }
 
-  if (cmProp projectToolsetOverride =
+  if (cmValue projectToolsetOverride =
         this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) {
     e1.Element("PlatformToolset", *projectToolsetOverride);
   } else if (const char* toolset = gg->GetPlatformToolset()) {
@@ -1320,7 +1336,7 @@
     cmStrCat(cmSystemTools::UpperCase(config), "_POSTFIX");
   std::string assemblyName = this->GeneratorTarget->GetOutputName(
     config, cmStateEnums::RuntimeBinaryArtifact);
-  if (cmProp postfix = this->GeneratorTarget->GetProperty(postfixName)) {
+  if (cmValue postfix = this->GeneratorTarget->GetProperty(postfixName)) {
     assemblyName += *postfix;
   }
   e1.Element("AssemblyName", assemblyName);
@@ -1341,18 +1357,18 @@
   cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
   const char* toolset = gg->GetPlatformToolset();
   e1.Element("NdkToolchainVersion", toolset ? toolset : "Default");
-  if (cmProp minApi = this->GeneratorTarget->GetProperty("ANDROID_API_MIN")) {
+  if (cmValue minApi = this->GeneratorTarget->GetProperty("ANDROID_API_MIN")) {
     e1.Element("AndroidMinAPI", "android-" + *minApi);
   }
-  if (cmProp api = this->GeneratorTarget->GetProperty("ANDROID_API")) {
+  if (cmValue api = this->GeneratorTarget->GetProperty("ANDROID_API")) {
     e1.Element("AndroidTargetAPI", "android-" + *api);
   }
 
-  if (cmProp cpuArch = this->GeneratorTarget->GetProperty("ANDROID_ARCH")) {
+  if (cmValue cpuArch = this->GeneratorTarget->GetProperty("ANDROID_ARCH")) {
     e1.Element("AndroidArch", *cpuArch);
   }
 
-  if (cmProp stlType =
+  if (cmValue stlType =
         this->GeneratorTarget->GetProperty("ANDROID_STL_TYPE")) {
     e1.Element("AndroidStlType", *stlType);
   }
@@ -1362,13 +1378,13 @@
   Elem& e1, std::string const&)
 {
   cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
-  if (cmProp projectToolsetOverride =
+  if (cmValue projectToolsetOverride =
         this->GeneratorTarget->GetProperty("VS_PLATFORM_TOOLSET")) {
     e1.Element("PlatformToolset", *projectToolsetOverride);
   } else if (const char* toolset = gg->GetPlatformToolset()) {
     e1.Element("PlatformToolset", toolset);
   }
-  if (cmProp stlType =
+  if (cmValue stlType =
         this->GeneratorTarget->GetProperty("ANDROID_STL_TYPE")) {
     if (*stlType != "none") {
       e1.Element("UseOfStl", *stlType);
@@ -1803,8 +1819,8 @@
   }
 }
 
-void cmVisualStudio10TargetGenerator::WriteHeaderSource(Elem& e1,
-                                                        cmSourceFile const* sf)
+void cmVisualStudio10TargetGenerator::WriteHeaderSource(
+  Elem& e1, cmSourceFile const* sf, ConfigToSettings const& toolSettings)
 {
   std::string const& fileName = sf->GetFullPath();
   Elem e2(e1, "ClInclude");
@@ -1815,6 +1831,7 @@
     e2.Element("DependentUpon",
                fileName.substr(0, fileName.find_last_of(".")));
   }
+  this->FinishWritingSource(e2, toolSettings);
 }
 
 void cmVisualStudio10TargetGenerator::ParseSettingsProperty(
@@ -1871,8 +1888,8 @@
   return true;
 }
 
-void cmVisualStudio10TargetGenerator::WriteExtraSource(Elem& e1,
-                                                       cmSourceFile const* sf)
+void cmVisualStudio10TargetGenerator::WriteExtraSource(
+  Elem& e1, cmSourceFile const* sf, ConfigToSettings& toolSettings)
 {
   bool toolHasSettings = false;
   const char* tool = "None";
@@ -1883,10 +1900,6 @@
   std::string copyToOutDir;
   std::string includeInVsix;
   std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
-  ConfigToSettings toolSettings;
-  for (const auto& config : this->Configurations) {
-    toolSettings[config];
-  }
 
   if (this->ProjectType == csproj && !this->InSourceBuild) {
     toolHasSettings = true;
@@ -1894,37 +1907,37 @@
   if (ext == "hlsl") {
     tool = "FXCompile";
     // Figure out the type of shader compiler to use.
-    if (cmProp st = sf->GetProperty("VS_SHADER_TYPE")) {
+    if (cmValue st = sf->GetProperty("VS_SHADER_TYPE")) {
       for (const std::string& config : this->Configurations) {
         toolSettings[config]["ShaderType"] = *st;
       }
     }
     // Figure out which entry point to use if any
-    if (cmProp se = sf->GetProperty("VS_SHADER_ENTRYPOINT")) {
+    if (cmValue se = sf->GetProperty("VS_SHADER_ENTRYPOINT")) {
       for (const std::string& config : this->Configurations) {
         toolSettings[config]["EntryPointName"] = *se;
       }
     }
     // Figure out which shader model to use if any
-    if (cmProp sm = sf->GetProperty("VS_SHADER_MODEL")) {
+    if (cmValue sm = sf->GetProperty("VS_SHADER_MODEL")) {
       for (const std::string& config : this->Configurations) {
         toolSettings[config]["ShaderModel"] = *sm;
       }
     }
     // Figure out which output header file to use if any
-    if (cmProp ohf = sf->GetProperty("VS_SHADER_OUTPUT_HEADER_FILE")) {
+    if (cmValue ohf = sf->GetProperty("VS_SHADER_OUTPUT_HEADER_FILE")) {
       for (const std::string& config : this->Configurations) {
         toolSettings[config]["HeaderFileOutput"] = *ohf;
       }
     }
     // Figure out which variable name to use if any
-    if (cmProp vn = sf->GetProperty("VS_SHADER_VARIABLE_NAME")) {
+    if (cmValue vn = sf->GetProperty("VS_SHADER_VARIABLE_NAME")) {
       for (const std::string& config : this->Configurations) {
         toolSettings[config]["VariableName"] = *vn;
       }
     }
     // Figure out if there's any additional flags to use
-    if (cmProp saf = sf->GetProperty("VS_SHADER_FLAGS")) {
+    if (cmValue saf = sf->GetProperty("VS_SHADER_FLAGS")) {
       cmGeneratorExpression ge;
       std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*saf);
 
@@ -1937,7 +1950,7 @@
       }
     }
     // Figure out if debug information should be generated
-    if (cmProp sed = sf->GetProperty("VS_SHADER_ENABLE_DEBUG")) {
+    if (cmValue sed = sf->GetProperty("VS_SHADER_ENABLE_DEBUG")) {
       cmGeneratorExpression ge;
       std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*sed);
 
@@ -1951,7 +1964,7 @@
       }
     }
     // Figure out if optimizations should be disabled
-    if (cmProp sdo = sf->GetProperty("VS_SHADER_DISABLE_OPTIMIZATIONS")) {
+    if (cmValue sdo = sf->GetProperty("VS_SHADER_DISABLE_OPTIMIZATIONS")) {
       cmGeneratorExpression ge;
       std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(*sdo);
 
@@ -1964,7 +1977,7 @@
         }
       }
     }
-    if (cmProp sofn = sf->GetProperty("VS_SHADER_OBJECT_FILE_NAME")) {
+    if (cmValue sofn = sf->GetProperty("VS_SHADER_OBJECT_FILE_NAME")) {
       for (const std::string& config : this->Configurations) {
         toolSettings[config]["ObjectFileOutput"] = *sofn;
       }
@@ -1987,7 +2000,7 @@
   } else if (ext == "vsixmanifest") {
     subType = "Designer";
   }
-  if (cmProp c = sf->GetProperty("VS_COPY_TO_OUT_DIR")) {
+  if (cmValue c = sf->GetProperty("VS_COPY_TO_OUT_DIR")) {
     tool = "Content";
     copyToOutDir = *c;
     toolHasSettings = true;
@@ -2016,7 +2029,7 @@
     }
   }
 
-  cmProp toolOverride = sf->GetProperty("VS_TOOL_OVERRIDE");
+  cmValue toolOverride = sf->GetProperty("VS_TOOL_OVERRIDE");
   if (cmNonempty(toolOverride)) {
     tool = toolOverride->c_str();
   }
@@ -2025,12 +2038,12 @@
   std::string deployLocation;
   if (this->GlobalGenerator->TargetsWindowsPhone() ||
       this->GlobalGenerator->TargetsWindowsStore()) {
-    cmProp content = sf->GetProperty("VS_DEPLOYMENT_CONTENT");
+    cmValue content = sf->GetProperty("VS_DEPLOYMENT_CONTENT");
     if (cmNonempty(content)) {
       toolHasSettings = true;
       deployContent = *content;
 
-      cmProp location = sf->GetProperty("VS_DEPLOYMENT_LOCATION");
+      cmValue location = sf->GetProperty("VS_DEPLOYMENT_LOCATION");
       if (cmNonempty(location)) {
         deployLocation = *location;
       }
@@ -2038,7 +2051,7 @@
   }
 
   if (ParsedToolTargetSettings.find(tool) == ParsedToolTargetSettings.end()) {
-    cmProp toolTargetProperty = this->GeneratorTarget->Target->GetProperty(
+    cmValue toolTargetProperty = this->GeneratorTarget->Target->GetProperty(
       "VS_SOURCE_SETTINGS_" + std::string(tool));
     ConfigToSettings toolTargetSettings;
     if (toolTargetProperty) {
@@ -2054,10 +2067,6 @@
     }
   }
 
-  if (cmProp p = sf->GetProperty("VS_SETTINGS")) {
-    ParseSettingsProperty(*p, toolSettings);
-  }
-
   if (!toolSettings.empty()) {
     toolHasSettings = true;
   }
@@ -2067,27 +2076,7 @@
   if (toolHasSettings) {
     e2.SetHasElements();
 
-    std::vector<std::string> writtenSettings;
-    for (const auto& configSettings : toolSettings) {
-      for (const auto& setting : configSettings.second) {
-
-        if (std::find(writtenSettings.begin(), writtenSettings.end(),
-                      setting.first) != writtenSettings.end()) {
-          continue;
-        }
-
-        if (PropertyIsSameInAllConfigs(toolSettings, setting.first)) {
-          e2.Element(setting.first, setting.second);
-          writtenSettings.push_back(setting.first);
-        } else {
-          e2.WritePlatformConfigTag(setting.first,
-                                    "'$(Configuration)|$(Platform)'=='" +
-                                      configSettings.first + "|" +
-                                      this->Platform + "'",
-                                    setting.second);
-        }
-      }
-    }
+    this->FinishWritingSource(e2, toolSettings);
 
     if (!deployContent.empty()) {
       cmGeneratorExpression ge;
@@ -2224,6 +2213,15 @@
       // Skip explicit reference to CMakeLists.txt source.
       continue;
     }
+
+    ConfigToSettings toolSettings;
+    for (const auto& config : this->Configurations) {
+      toolSettings[config];
+    }
+    if (cmValue p = si.Source->GetProperty("VS_SETTINGS")) {
+      ParseSettingsProperty(*p, toolSettings);
+    }
+
     const char* tool = nullptr;
     switch (si.Kind) {
       case cmGeneratorTarget::SourceKindAppManifest:
@@ -2251,10 +2249,10 @@
         }
         break;
       case cmGeneratorTarget::SourceKindExtra:
-        this->WriteExtraSource(e1, si.Source);
+        this->WriteExtraSource(e1, si.Source, toolSettings);
         break;
       case cmGeneratorTarget::SourceKindHeader:
-        this->WriteHeaderSource(e1, si.Source);
+        this->WriteHeaderSource(e1, si.Source, toolSettings);
         break;
       case cmGeneratorTarget::SourceKindIDL:
         tool = "Midl";
@@ -2364,6 +2362,8 @@
       if (!isCSharp && !exclude_configs.empty()) {
         this->WriteExcludeFromBuild(e2, exclude_configs);
       }
+
+      this->FinishWritingSource(e2, toolSettings);
     }
   }
 
@@ -2372,6 +2372,32 @@
   }
 }
 
+void cmVisualStudio10TargetGenerator::FinishWritingSource(
+  Elem& e2, ConfigToSettings const& toolSettings)
+{
+  std::vector<std::string> writtenSettings;
+  for (const auto& configSettings : toolSettings) {
+    for (const auto& setting : configSettings.second) {
+
+      if (std::find(writtenSettings.begin(), writtenSettings.end(),
+                    setting.first) != writtenSettings.end()) {
+        continue;
+      }
+
+      if (PropertyIsSameInAllConfigs(toolSettings, setting.first)) {
+        e2.Element(setting.first, setting.second);
+        writtenSettings.push_back(setting.first);
+      } else {
+        e2.WritePlatformConfigTag(setting.first,
+                                  "'$(Configuration)|$(Platform)'=='" +
+                                    configSettings.first + "|" +
+                                    this->Platform + "'",
+                                  setting.second);
+      }
+    }
+  }
+}
+
 void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags(
   Elem& e2, cmSourceFile const* source)
 {
@@ -2389,22 +2415,22 @@
   bool configDependentDefines = false;
   std::string includes;
   bool configDependentIncludes = false;
-  if (cmProp cflags = sf.GetProperty("COMPILE_FLAGS")) {
+  if (cmValue cflags = sf.GetProperty("COMPILE_FLAGS")) {
     configDependentFlags =
       cmGeneratorExpression::Find(*cflags) != std::string::npos;
     flags += *cflags;
   }
-  if (cmProp coptions = sf.GetProperty("COMPILE_OPTIONS")) {
+  if (cmValue coptions = sf.GetProperty("COMPILE_OPTIONS")) {
     configDependentOptions =
       cmGeneratorExpression::Find(*coptions) != std::string::npos;
     options += *coptions;
   }
-  if (cmProp cdefs = sf.GetProperty("COMPILE_DEFINITIONS")) {
+  if (cmValue cdefs = sf.GetProperty("COMPILE_DEFINITIONS")) {
     configDependentDefines =
       cmGeneratorExpression::Find(*cdefs) != std::string::npos;
     defines += *cdefs;
   }
-  if (cmProp cincludes = sf.GetProperty("INCLUDE_DIRECTORIES")) {
+  if (cmValue cincludes = sf.GetProperty("INCLUDE_DIRECTORIES")) {
     configDependentIncludes =
       cmGeneratorExpression::Find(*cincludes) != std::string::npos;
     includes += *cincludes;
@@ -2442,7 +2468,7 @@
     std::string configUpper = cmSystemTools::UpperCase(config);
     std::string configDefines = defines;
     std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
-    if (cmProp ccdefs = sf.GetProperty(defPropName)) {
+    if (cmValue ccdefs = sf.GetProperty(defPropName)) {
       if (!configDefines.empty()) {
         configDefines += ";";
       }
@@ -2618,7 +2644,7 @@
     const std::string cond = this->CalcCondition(config);
 
     if (ttype <= cmStateEnums::UTILITY) {
-      if (cmProp workingDir = this->GeneratorTarget->GetProperty(
+      if (cmValue workingDir = this->GeneratorTarget->GetProperty(
             "VS_DEBUGGER_WORKING_DIRECTORY")) {
         std::string genWorkingDir = cmGeneratorExpression::Evaluate(
           *workingDir, this->LocalGenerator, config);
@@ -2626,7 +2652,7 @@
                                   genWorkingDir);
       }
 
-      if (cmProp environment =
+      if (cmValue environment =
             this->GeneratorTarget->GetProperty("VS_DEBUGGER_ENVIRONMENT")) {
         std::string genEnvironment = cmGeneratorExpression::Evaluate(
           *environment, this->LocalGenerator, config);
@@ -2634,7 +2660,7 @@
                                   genEnvironment);
       }
 
-      if (cmProp debuggerCommand =
+      if (cmValue debuggerCommand =
             this->GeneratorTarget->GetProperty("VS_DEBUGGER_COMMAND")) {
         std::string genDebuggerCommand = cmGeneratorExpression::Evaluate(
           *debuggerCommand, this->LocalGenerator, config);
@@ -2642,7 +2668,7 @@
                                   genDebuggerCommand);
       }
 
-      if (cmProp commandArguments = this->GeneratorTarget->GetProperty(
+      if (cmValue commandArguments = this->GeneratorTarget->GetProperty(
             "VS_DEBUGGER_COMMAND_ARGUMENTS")) {
         std::string genCommandArguments = cmGeneratorExpression::Evaluate(
           *commandArguments, this->LocalGenerator, config);
@@ -2674,40 +2700,40 @@
 
       e1.WritePlatformConfigTag("IntDir", cond, intermediateDir);
 
-      if (cmProp sdkExecutableDirectories = this->Makefile->GetDefinition(
+      if (cmValue sdkExecutableDirectories = this->Makefile->GetDefinition(
             "CMAKE_VS_SDK_EXECUTABLE_DIRECTORIES")) {
         e1.WritePlatformConfigTag("ExecutablePath", cond,
                                   *sdkExecutableDirectories);
       }
 
-      if (cmProp sdkIncludeDirectories = this->Makefile->GetDefinition(
+      if (cmValue sdkIncludeDirectories = this->Makefile->GetDefinition(
             "CMAKE_VS_SDK_INCLUDE_DIRECTORIES")) {
         e1.WritePlatformConfigTag("IncludePath", cond, *sdkIncludeDirectories);
       }
 
-      if (cmProp sdkReferenceDirectories = this->Makefile->GetDefinition(
+      if (cmValue sdkReferenceDirectories = this->Makefile->GetDefinition(
             "CMAKE_VS_SDK_REFERENCE_DIRECTORIES")) {
         e1.WritePlatformConfigTag("ReferencePath", cond,
                                   *sdkReferenceDirectories);
       }
 
-      if (cmProp sdkLibraryDirectories = this->Makefile->GetDefinition(
+      if (cmValue sdkLibraryDirectories = this->Makefile->GetDefinition(
             "CMAKE_VS_SDK_LIBRARY_DIRECTORIES")) {
         e1.WritePlatformConfigTag("LibraryPath", cond, *sdkLibraryDirectories);
       }
 
-      if (cmProp sdkLibraryWDirectories = this->Makefile->GetDefinition(
+      if (cmValue sdkLibraryWDirectories = this->Makefile->GetDefinition(
             "CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES")) {
         e1.WritePlatformConfigTag("LibraryWPath", cond,
                                   *sdkLibraryWDirectories);
       }
 
-      if (cmProp sdkSourceDirectories =
+      if (cmValue sdkSourceDirectories =
             this->Makefile->GetDefinition("CMAKE_VS_SDK_SOURCE_DIRECTORIES")) {
         e1.WritePlatformConfigTag("SourcePath", cond, *sdkSourceDirectories);
       }
 
-      if (cmProp sdkExcludeDirectories = this->Makefile->GetDefinition(
+      if (cmValue sdkExcludeDirectories = this->Makefile->GetDefinition(
             "CMAKE_VS_SDK_EXCLUDE_DIRECTORIES")) {
         e1.WritePlatformConfigTag("ExcludePath", cond, *sdkExcludeDirectories);
       }
@@ -2905,7 +2931,7 @@
         this->Makefile->IssueMessage(MessageType::WARNING, message);
       }
     }
-    if (cmProp clr =
+    if (cmValue clr =
           this->GeneratorTarget->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
       std::string clrString = *clr;
       if (!clrString.empty()) {
@@ -3048,7 +3074,7 @@
   oh.OutputPreprocessorDefinitions(this->LangForClCompile);
 
   if (this->NsightTegra) {
-    if (cmProp processMax =
+    if (cmValue processMax =
           this->GeneratorTarget->GetProperty("ANDROID_PROCESS_MAX")) {
       e2.Element("ProcessMax", *processMax);
     }
@@ -3547,8 +3573,6 @@
   }
   Elem e2(e1, "NASM");
 
-  std::vector<std::string> includes =
-    this->GetIncludes(configName, "ASM_NASM");
   OptionsHelper nasmOptions(*(this->NasmOptions[configName]), e2);
   nasmOptions.OutputAdditionalIncludeDirectories("ASM_NASM");
   nasmOptions.OutputFlagMap();
@@ -3608,7 +3632,7 @@
   std::vector<cmSourceFile const*> manifest_srcs;
   this->GeneratorTarget->GetManifests(manifest_srcs, config);
 
-  cmProp dpiAware = this->GeneratorTarget->GetProperty("VS_DPI_AWARE");
+  cmValue dpiAware = this->GeneratorTarget->GetProperty("VS_DPI_AWARE");
 
   if (!manifest_srcs.empty() || dpiAware) {
     Elem e2(e1, "Manifest");
@@ -3669,24 +3693,24 @@
     e2.Element("EnableProGuard", "true");
   }
 
-  if (cmProp proGuardConfigLocation =
+  if (cmValue proGuardConfigLocation =
         this->GeneratorTarget->GetProperty("ANDROID_PROGUARD_CONFIG_PATH")) {
     e2.Element("ProGuardConfigLocation", *proGuardConfigLocation);
   }
 
-  if (cmProp securePropertiesLocation =
+  if (cmValue securePropertiesLocation =
         this->GeneratorTarget->GetProperty("ANDROID_SECURE_PROPS_PATH")) {
     e2.Element("SecurePropertiesLocation", *securePropertiesLocation);
   }
 
-  if (cmProp nativeLibDirectoriesExpression =
+  if (cmValue nativeLibDirectoriesExpression =
         this->GeneratorTarget->GetProperty("ANDROID_NATIVE_LIB_DIRECTORIES")) {
     std::string nativeLibDirs = cmGeneratorExpression::Evaluate(
       *nativeLibDirectoriesExpression, this->LocalGenerator, configName);
     e2.Element("NativeLibDirectories", nativeLibDirs);
   }
 
-  if (cmProp nativeLibDependenciesExpression =
+  if (cmValue nativeLibDependenciesExpression =
         this->GeneratorTarget->GetProperty(
           "ANDROID_NATIVE_LIB_DEPENDENCIES")) {
     std::string nativeLibDeps = cmGeneratorExpression::Evaluate(
@@ -3694,24 +3718,24 @@
     e2.Element("NativeLibDependencies", nativeLibDeps);
   }
 
-  if (cmProp javaSourceDir =
+  if (cmValue javaSourceDir =
         this->GeneratorTarget->GetProperty("ANDROID_JAVA_SOURCE_DIR")) {
     e2.Element("JavaSourceDir", *javaSourceDir);
   }
 
-  if (cmProp jarDirectoriesExpression =
+  if (cmValue jarDirectoriesExpression =
         this->GeneratorTarget->GetProperty("ANDROID_JAR_DIRECTORIES")) {
     std::string jarDirectories = cmGeneratorExpression::Evaluate(
       *jarDirectoriesExpression, this->LocalGenerator, configName);
     e2.Element("JarDirectories", jarDirectories);
   }
 
-  if (cmProp jarDeps =
+  if (cmValue jarDeps =
         this->GeneratorTarget->GetProperty("ANDROID_JAR_DEPENDENCIES")) {
     e2.Element("JarDependencies", *jarDeps);
   }
 
-  if (cmProp assetsDirectories =
+  if (cmValue assetsDirectories =
         this->GeneratorTarget->GetProperty("ANDROID_ASSETS_DIRECTORIES")) {
     e2.Element("AssetsDirectories", *assetsDirectories);
   }
@@ -3722,7 +3746,7 @@
     e2.Element("AndroidManifestLocation", manifest_xml);
   }
 
-  if (cmProp antAdditionalOptions =
+  if (cmValue antAdditionalOptions =
         this->GeneratorTarget->GetProperty("ANDROID_ANT_ADDITIONAL_OPTIONS")) {
     e2.Element("AdditionalOptions",
                *antAdditionalOptions + " %(AdditionalOptions)");
@@ -3777,13 +3801,13 @@
   std::string linkFlagVar = linkFlagVarBase + "_" + CONFIG;
   flags += " ";
   flags += this->Makefile->GetRequiredDefinition(linkFlagVar);
-  cmProp targetLinkFlags = this->GeneratorTarget->GetProperty("LINK_FLAGS");
+  cmValue targetLinkFlags = this->GeneratorTarget->GetProperty("LINK_FLAGS");
   if (targetLinkFlags) {
     flags += " ";
     flags += *targetLinkFlags;
   }
   std::string flagsProp = cmStrCat("LINK_FLAGS_", CONFIG);
-  if (cmProp flagsConfig = this->GeneratorTarget->GetProperty(flagsProp)) {
+  if (cmValue flagsConfig = this->GeneratorTarget->GetProperty(flagsProp)) {
     flags += " ";
     flags += *flagsConfig;
   }
@@ -3864,7 +3888,7 @@
       };
     }
 
-    if (cmProp stackVal = this->Makefile->GetDefinition(
+    if (cmValue stackVal = this->Makefile->GetDefinition(
           "CMAKE_" + linkLanguage + "_STACK_SIZE")) {
       linkOptions.AddFlag("StackReserveSize", *stackVal);
     }
@@ -4262,7 +4286,7 @@
     cmLocalGenerator* lg = dt->GetLocalGenerator();
     std::string name = dt->GetName();
     std::string path;
-    if (cmProp p = dt->GetProperty("EXTERNAL_MSPROJECT")) {
+    if (cmValue p = dt->GetProperty("EXTERNAL_MSPROJECT")) {
       path = *p;
     } else {
       path = cmStrCat(lg->GetCurrentBinaryDirectory(), '/', dt->GetName(),
@@ -4292,13 +4316,13 @@
   // This only applies to Windows 10 apps
   if (this->GlobalGenerator->TargetsWindowsStore() &&
       cmHasLiteralPrefix(this->GlobalGenerator->GetSystemVersion(), "10.0")) {
-    cmProp desktopExtensionsVersion =
+    cmValue desktopExtensionsVersion =
       this->GeneratorTarget->GetProperty("VS_DESKTOP_EXTENSIONS_VERSION");
     if (desktopExtensionsVersion) {
       this->WriteSinglePlatformExtension(e1, "WindowsDesktop",
                                          *desktopExtensionsVersion);
     }
-    cmProp mobileExtensionsVersion =
+    cmValue mobileExtensionsVersion =
       this->GeneratorTarget->GetProperty("VS_MOBILE_EXTENSIONS_VERSION");
     if (mobileExtensionsVersion) {
       this->WriteSinglePlatformExtension(e1, "WindowsMobile",
@@ -4327,7 +4351,7 @@
 {
   std::vector<std::string> sdkReferences;
   std::unique_ptr<Elem> spe1;
-  if (cmProp vsSDKReferences =
+  if (cmValue vsSDKReferences =
         this->GeneratorTarget->GetProperty("VS_SDK_REFERENCES")) {
     cmExpandList(*vsSDKReferences, sdkReferences);
     spe1 = cm::make_unique<Elem>(e0, "ItemGroup");
@@ -4339,11 +4363,11 @@
   // This only applies to Windows 10 apps
   if (this->GlobalGenerator->TargetsWindowsStore() &&
       cmHasLiteralPrefix(this->GlobalGenerator->GetSystemVersion(), "10.0")) {
-    cmProp desktopExtensionsVersion =
+    cmValue desktopExtensionsVersion =
       this->GeneratorTarget->GetProperty("VS_DESKTOP_EXTENSIONS_VERSION");
-    cmProp mobileExtensionsVersion =
+    cmValue mobileExtensionsVersion =
       this->GeneratorTarget->GetProperty("VS_MOBILE_EXTENSIONS_VERSION");
-    cmProp iotExtensionsVersion =
+    cmValue iotExtensionsVersion =
       this->GeneratorTarget->GetProperty("VS_IOT_EXTENSIONS_VERSION");
 
     if (desktopExtensionsVersion || mobileExtensionsVersion ||
@@ -4553,7 +4577,7 @@
   if (!targetPlatformVersion.empty()) {
     e1.Element("WindowsTargetPlatformVersion", targetPlatformVersion);
   }
-  cmProp targetPlatformMinVersion = this->GeneratorTarget->GetProperty(
+  cmValue targetPlatformMinVersion = this->GeneratorTarget->GetProperty(
     "VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION");
   if (targetPlatformMinVersion) {
     e1.Element("WindowsTargetPlatformMinVersion", *targetPlatformMinVersion);
@@ -5049,7 +5073,7 @@
       if (cmHasPrefix(p, propNamePrefix)) {
         std::string tagName = p.substr(propNamePrefix.length());
         if (!tagName.empty()) {
-          cmProp val = props.GetPropertyValue(p);
+          cmValue val = props.GetPropertyValue(p);
           if (cmNonempty(val)) {
             tags[tagName] = *val;
           } else {
@@ -5101,7 +5125,7 @@
   } else if (!cmHasSuffix(fullFileName, ".cs") &&
              cmHasPrefix(fullFileName, binDir)) {
     link = fullFileName.substr(binDir.length() + 1);
-  } else if (cmProp l = source->GetProperty("VS_CSHARP_Link")) {
+  } else if (cmValue l = source->GetProperty("VS_CSHARP_Link")) {
     link = *l;
   }
 
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 55c5444..a5ce5e5 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -58,6 +58,10 @@
   struct Elem;
   struct OptionsHelper;
 
+  using ConfigToSettings =
+    std::unordered_map<std::string,
+                       std::unordered_map<std::string, std::string>>;
+
   std::string ConvertPath(std::string const& path, bool forceRelative);
   std::string CalcCondition(const std::string& config) const;
   void WriteProjectConfigurations(Elem& e0);
@@ -66,12 +70,15 @@
   void WriteCEDebugProjectConfigurationValues(Elem& e0);
   void WriteMSToolConfigurationValuesManaged(Elem& e1,
                                              std::string const& config);
-  void WriteHeaderSource(Elem& e1, cmSourceFile const* sf);
-  void WriteExtraSource(Elem& e1, cmSourceFile const* sf);
+  void WriteHeaderSource(Elem& e1, cmSourceFile const* sf,
+                         ConfigToSettings const& toolSettings);
+  void WriteExtraSource(Elem& e1, cmSourceFile const* sf,
+                        ConfigToSettings& toolSettings);
   void WriteNsightTegraConfigurationValues(Elem& e1,
                                            std::string const& config);
   void WriteAndroidConfigurationValues(Elem& e1, std::string const& config);
   void WriteSource(Elem& e2, cmSourceFile const* sf);
+  void FinishWritingSource(Elem& e2, ConfigToSettings const& toolSettings);
   void WriteExcludeFromBuild(Elem& e2,
                              std::vector<size_t> const& exclude_configs);
   void WriteAllSources(Elem& e0);
@@ -252,9 +259,6 @@
   void ClassifyAllConfigSources();
   void ClassifyAllConfigSource(cmGeneratorTarget::AllConfigSource const& acs);
 
-  using ConfigToSettings =
-    std::unordered_map<std::string,
-                       std::unordered_map<std::string, std::string>>;
   std::unordered_map<std::string, ConfigToSettings> ParsedToolTargetSettings;
   bool PropertyIsSameInAllConfigs(const ConfigToSettings& toolSettings,
                                   const std::string& propName);
diff --git a/Source/cmWhileCommand.cxx b/Source/cmWhileCommand.cxx
index 327c1c7..a93a81f 100644
--- a/Source/cmWhileCommand.cxx
+++ b/Source/cmWhileCommand.cxx
@@ -16,13 +16,14 @@
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmOutputConverter.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
 class cmWhileFunctionBlocker : public cmFunctionBlocker
 {
 public:
-  cmWhileFunctionBlocker(cmMakefile* mf);
+  cmWhileFunctionBlocker(cmMakefile* mf, std::vector<cmListFileArgument> args);
   ~cmWhileFunctionBlocker() override;
 
   cm::string_view StartCommandName() const override { return "while"_s; }
@@ -34,14 +35,15 @@
   bool Replay(std::vector<cmListFileFunction> functions,
               cmExecutionStatus& inStatus) override;
 
-  std::vector<cmListFileArgument> Args;
-
 private:
   cmMakefile* Makefile;
+  std::vector<cmListFileArgument> Args;
 };
 
-cmWhileFunctionBlocker::cmWhileFunctionBlocker(cmMakefile* mf)
-  : Makefile(mf)
+cmWhileFunctionBlocker::cmWhileFunctionBlocker(
+  cmMakefile* const mf, std::vector<cmListFileArgument> args)
+  : Makefile{ mf }
+  , Args{ std::move(args) }
 {
   this->Makefile->PushLoopBlock();
 }
@@ -60,39 +62,34 @@
 bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
                                     cmExecutionStatus& inStatus)
 {
-  cmMakefile& mf = inStatus.GetMakefile();
-  std::string errorString;
-
-  std::vector<cmExpandedCommandArgument> expandedArguments;
-  mf.ExpandArguments(this->Args, expandedArguments);
-  MessageType messageType;
+  auto& mf = inStatus.GetMakefile();
 
   cmListFileBacktrace whileBT =
     mf.GetBacktrace().Push(this->GetStartingContext());
-  cmConditionEvaluator conditionEvaluator(mf, whileBT);
 
-  bool isTrue =
-    conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
+  std::vector<cmExpandedCommandArgument> expandedArguments;
+  // At least same size expected for `expandedArguments` as `Args`
+  expandedArguments.reserve(this->Args.size());
 
-  while (isTrue) {
-    if (!errorString.empty()) {
-      std::string err = "had incorrect arguments: ";
-      for (cmListFileArgument const& arg : this->Args) {
-        err += (arg.Delim ? "\"" : "");
-        err += arg.Value;
-        err += (arg.Delim ? "\"" : "");
-        err += " ";
-      }
-      err += "(";
-      err += errorString;
-      err += ").";
-      mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
-      if (messageType == MessageType::FATAL_ERROR) {
-        cmSystemTools::SetFatalErrorOccured();
-        return true;
-      }
-    }
+  auto expandArgs = [&mf](std::vector<cmListFileArgument> const& args,
+                          std::vector<cmExpandedCommandArgument>& out)
+    -> std::vector<cmExpandedCommandArgument>& {
+    out.clear();
+    mf.ExpandArguments(args, out);
+    return out;
+  };
 
+  // FIXME(#23296): For compatibility with older versions of CMake, we
+  // tolerate condition errors that evaluate to false.  We should add
+  // a policy to enforce such errors.
+  bool enforceError = true;
+  std::string errorString;
+  MessageType messageType;
+
+  for (cmConditionEvaluator conditionEvaluator(mf, whileBT);
+       (enforceError = /* enforce condition errors that evaluate to true */
+        conditionEvaluator.IsTrue(expandArgs(this->Args, expandedArguments),
+                                  errorString, messageType));) {
     // Invoke all the functions that were collected in the block.
     for (cmListFileFunction const& fn : functions) {
       cmExecutionStatus status(mf);
@@ -111,11 +108,22 @@
         return true;
       }
     }
-    expandedArguments.clear();
-    mf.ExpandArguments(this->Args, expandedArguments);
-    isTrue =
-      conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
   }
+
+  if (!errorString.empty() && enforceError) {
+    std::string err = "had incorrect arguments:\n ";
+    for (auto const& i : expandedArguments) {
+      err += " ";
+      err += cmOutputConverter::EscapeForCMake(i.GetValue());
+    }
+    err += "\n";
+    err += errorString;
+    mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
+    if (messageType == MessageType::FATAL_ERROR) {
+      cmSystemTools::SetFatalErrorOccured();
+    }
+  }
+
   return true;
 }
 
@@ -128,11 +136,9 @@
   }
 
   // create a function blocker
-  {
-    cmMakefile& makefile = status.GetMakefile();
-    auto fb = cm::make_unique<cmWhileFunctionBlocker>(&makefile);
-    fb->Args = args;
-    makefile.AddFunctionBlocker(std::move(fb));
-  }
+  auto& makefile = status.GetMakefile();
+  makefile.AddFunctionBlocker(
+    cm::make_unique<cmWhileFunctionBlocker>(&makefile, args));
+
   return true;
 }
diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h
index ff25526..4035650 100644
--- a/Source/cmWorkerPool.h
+++ b/Source/cmWorkerPool.h
@@ -87,7 +87,7 @@
      * Get the user data.
      * Only valid during the JobT::Process() call!
      */
-    void* UserData() const { return this->Pool_->UserData(); };
+    void* UserData() const { return this->Pool_->UserData(); }
 
     /**
      * Get the worker index.
@@ -138,7 +138,7 @@
     {
     }
     //! Does nothing
-    void Process() override{};
+    void Process() override {}
   };
 
   /**
diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx
index e4329af..e2c0f2d 100644
--- a/Source/cmXCodeScheme.cxx
+++ b/Source/cmXCodeScheme.cxx
@@ -204,7 +204,7 @@
 
   // Info tab begin
 
-  if (cmProp exe =
+  if (cmValue exe =
         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_EXECUTABLE")) {
 
     xout.StartElement("PathRunnable");
@@ -220,7 +220,7 @@
 
   // Arguments tab begin
 
-  if (cmProp argList =
+  if (cmValue argList =
         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ARGUMENTS")) {
     std::vector<std::string> arguments = cmExpandedList(*argList);
     if (!arguments.empty()) {
@@ -240,7 +240,7 @@
     }
   }
 
-  if (cmProp envList =
+  if (cmValue envList =
         this->Target->GetTarget()->GetProperty("XCODE_SCHEME_ENVIRONMENT")) {
     std::vector<std::string> envs = cmExpandedList(*envList);
     if (!envs.empty()) {
@@ -323,7 +323,7 @@
   cmXMLWriter& xout, const std::string& attrName, const std::string& varName,
   bool defaultValue)
 {
-  cmProp property = Target->GetTarget()->GetProperty(varName);
+  cmValue property = Target->GetTarget()->GetProperty(varName);
   bool isOn = (!property && defaultValue) || cmIsOn(property);
 
   if (isOn) {
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 73f5ad5..fda7900 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -114,7 +114,9 @@
 #  include "cmExtraSublimeTextGenerator.h"
 #endif
 
-#if defined(__linux__) || defined(_WIN32)
+// NOTE: the __linux__ macro is predefined on Android host too, but
+// main CMakeLists.txt filters out this generator by host name.
+#if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
 #  include "cmGlobalGhsMultiGenerator.h"
 #endif
 
@@ -156,17 +158,16 @@
 }
 #endif
 
-cmake::cmake(Role role, cmState::Mode mode)
+cmake::cmake(Role role, cmState::Mode mode, cmState::ProjectKind projectKind)
   : CMakeWorkingDirectory(cmSystemTools::GetCurrentWorkingDirectory())
   , FileTimeCache(cm::make_unique<cmFileTimeCache>())
 #ifndef CMAKE_BOOTSTRAP
   , VariableWatch(cm::make_unique<cmVariableWatch>())
 #endif
-  , State(cm::make_unique<cmState>())
+  , State(cm::make_unique<cmState>(mode, projectKind))
   , Messenger(cm::make_unique<cmMessenger>())
 {
   this->TraceFile.close();
-  this->State->SetMode(mode);
   this->CurrentSnapshot = this->State->CreateBaseSnapshot();
 
 #ifdef __APPLE__
@@ -598,15 +599,14 @@
   bool haveValue = false;
   std::string cachedValue;
   if (this->WarnUnusedCli) {
-    if (cmProp v = this->State->GetInitializedCacheValue(var)) {
+    if (cmValue v = this->State->GetInitializedCacheValue(var)) {
       haveValue = true;
       cachedValue = *v;
     }
   }
 
-  this->AddCacheEntry(var, value.c_str(),
-                      "No help, variable specified on the command line.",
-                      type);
+  this->AddCacheEntry(
+    var, value, "No help, variable specified on the command line.", type);
 
   if (this->WarnUnusedCli) {
     if (!haveValue ||
@@ -1468,7 +1468,7 @@
   // If there is a CMakeCache.txt file, use its settings.
   if (!cachePath.empty()) {
     if (this->LoadCache(cachePath)) {
-      cmProp existingValue =
+      cmValue existingValue =
         this->State->GetCacheEntryValue("CMAKE_HOME_DIRECTORY");
       if (existingValue) {
         this->SetHomeOutputDirectory(cachePath);
@@ -1516,16 +1516,15 @@
 int cmake::AddCMakePaths()
 {
   // Save the value in the cache
-  this->AddCacheEntry("CMAKE_COMMAND",
-                      cmSystemTools::GetCMakeCommand().c_str(),
+  this->AddCacheEntry("CMAKE_COMMAND", cmSystemTools::GetCMakeCommand(),
                       "Path to CMake executable.", cmStateEnums::INTERNAL);
 #ifndef CMAKE_BOOTSTRAP
-  this->AddCacheEntry(
-    "CMAKE_CTEST_COMMAND", cmSystemTools::GetCTestCommand().c_str(),
-    "Path to ctest program executable.", cmStateEnums::INTERNAL);
-  this->AddCacheEntry(
-    "CMAKE_CPACK_COMMAND", cmSystemTools::GetCPackCommand().c_str(),
-    "Path to cpack program executable.", cmStateEnums::INTERNAL);
+  this->AddCacheEntry("CMAKE_CTEST_COMMAND", cmSystemTools::GetCTestCommand(),
+                      "Path to ctest program executable.",
+                      cmStateEnums::INTERNAL);
+  this->AddCacheEntry("CMAKE_CPACK_COMMAND", cmSystemTools::GetCPackCommand(),
+                      "Path to cpack program executable.",
+                      cmStateEnums::INTERNAL);
 #endif
   if (!cmSystemTools::FileExists(
         (cmSystemTools::GetCMakeRoot() + "/Modules/CMake.cmake"))) {
@@ -1537,7 +1536,7 @@
       cmSystemTools::GetCMakeRoot());
     return 0;
   }
-  this->AddCacheEntry("CMAKE_ROOT", cmSystemTools::GetCMakeRoot().c_str(),
+  this->AddCacheEntry("CMAKE_ROOT", cmSystemTools::GetCMakeRoot(),
                       "Path to CMake installation.", cmStateEnums::INTERNAL);
 
   return 1;
@@ -1843,7 +1842,7 @@
   std::vector<std::string> argsSplit = cmExpandedList(var, true);
   // erase the property to avoid infinite recursion
   this->State->SetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_", "");
-  if (this->State->GetIsInTryCompile()) {
+  if (this->GetIsInTryCompile()) {
     return 0;
   }
   std::vector<SaveCacheEntry> saved;
@@ -1866,10 +1865,10 @@
       warning << "\n";
       i -= 1;
     }
-    cmProp existingValue = this->State->GetCacheEntryValue(save.key);
+    cmValue existingValue = this->State->GetCacheEntryValue(save.key);
     if (existingValue) {
       save.type = this->State->GetCacheEntryType(save.key);
-      if (cmProp help =
+      if (cmValue help =
             this->State->GetCacheEntryProperty(save.key, "HELPSTRING")) {
         save.help = *help;
       }
@@ -1885,7 +1884,7 @@
   this->LoadCache();
   // restore the changed compilers
   for (SaveCacheEntry const& i : saved) {
-    this->AddCacheEntry(i.key, i.value.c_str(), i.help.c_str(), i.type);
+    this->AddCacheEntry(i.key, i.value, i.help.c_str(), i.type);
   }
   cmSystemTools::Message(warning.str());
   // avoid reconfigure if there were errors
@@ -1918,9 +1917,9 @@
   if (this->DiagLevels.count("dev") == 1) {
     bool setDeprecatedVariables = false;
 
-    cmProp cachedWarnDeprecated =
+    cmValue cachedWarnDeprecated =
       this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
-    cmProp cachedErrorDeprecated =
+    cmValue cachedErrorDeprecated =
       this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
 
     // don't overwrite deprecated warning setting from a previous invocation
@@ -1959,7 +1958,7 @@
   // Cache variables may have already been set by a previous invocation,
   // so we cannot rely on command line options alone. Always ensure our
   // messenger is in sync with the cache.
-  cmProp value = this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
+  cmValue value = this->State->GetCacheEntryValue("CMAKE_WARN_DEPRECATED");
   this->Messenger->SetSuppressDeprecatedWarnings(value && cmIsOff(*value));
 
   value = this->State->GetCacheEntryValue("CMAKE_ERROR_DEPRECATED");
@@ -1972,7 +1971,7 @@
   this->Messenger->SetDevWarningsAsErrors(value && cmIsOff(*value));
 
   int ret = this->ActualConfigure();
-  cmProp delCacheVars =
+  cmValue delCacheVars =
     this->State->GetGlobalProperty("__CMAKE_DELETE_CACHE_CHANGE_VARS_");
   if (delCacheVars && !delCacheVars->empty()) {
     return this->HandleDeleteCacheVariables(*delCacheVars);
@@ -1992,7 +1991,7 @@
   }
   if (!res) {
     this->AddCacheEntry(
-      "CMAKE_HOME_DIRECTORY", this->GetHomeDirectory().c_str(),
+      "CMAKE_HOME_DIRECTORY", this->GetHomeDirectory(),
       "Source directory with the top level CMakeLists.txt file for this "
       "project",
       cmStateEnums::INTERNAL);
@@ -2000,8 +1999,8 @@
 
   // no generator specified on the command line
   if (!this->GlobalGenerator) {
-    cmProp genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
-    cmProp extraGenName =
+    cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
+    cmValue extraGenName =
       this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
     if (genName) {
       std::string fullName =
@@ -2024,7 +2023,7 @@
     }
   }
 
-  cmProp genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
+  cmValue genName = this->State->GetInitializedCacheValue("CMAKE_GENERATOR");
   if (genName) {
     if (!this->GlobalGenerator->MatchesGeneratorName(*genName)) {
       std::string message =
@@ -2037,26 +2036,24 @@
     }
   }
   if (!this->State->GetInitializedCacheValue("CMAKE_GENERATOR")) {
-    this->AddCacheEntry("CMAKE_GENERATOR",
-                        this->GlobalGenerator->GetName().c_str(),
+    this->AddCacheEntry("CMAKE_GENERATOR", this->GlobalGenerator->GetName(),
                         "Name of generator.", cmStateEnums::INTERNAL);
-    this->AddCacheEntry("CMAKE_EXTRA_GENERATOR",
-                        this->GlobalGenerator->GetExtraGeneratorName().c_str(),
-                        "Name of external makefile project generator.",
-                        cmStateEnums::INTERNAL);
+    this->AddCacheEntry(
+      "CMAKE_EXTRA_GENERATOR", this->GlobalGenerator->GetExtraGeneratorName(),
+      "Name of external makefile project generator.", cmStateEnums::INTERNAL);
 
     if (!this->State->GetInitializedCacheValue("CMAKE_TOOLCHAIN_FILE")) {
       std::string envToolchain;
       if (cmSystemTools::GetEnv("CMAKE_TOOLCHAIN_FILE", envToolchain) &&
           !envToolchain.empty()) {
-        this->AddCacheEntry("CMAKE_TOOLCHAIN_FILE", envToolchain.c_str(),
+        this->AddCacheEntry("CMAKE_TOOLCHAIN_FILE", envToolchain,
                             "The CMake toolchain file",
                             cmStateEnums::FILEPATH);
       }
     }
   }
 
-  if (cmProp instance =
+  if (cmValue instance =
         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_INSTANCE")) {
     if (this->GeneratorInstanceSet && this->GeneratorInstance != *instance) {
       std::string message =
@@ -2068,12 +2065,12 @@
       return -2;
     }
   } else {
-    this->AddCacheEntry(
-      "CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance.c_str(),
-      "Generator instance identifier.", cmStateEnums::INTERNAL);
+    this->AddCacheEntry("CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance,
+                        "Generator instance identifier.",
+                        cmStateEnums::INTERNAL);
   }
 
-  if (cmProp platformName =
+  if (cmValue platformName =
         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_PLATFORM")) {
     if (this->GeneratorPlatformSet &&
         this->GeneratorPlatform != *platformName) {
@@ -2086,12 +2083,11 @@
       return -2;
     }
   } else {
-    this->AddCacheEntry("CMAKE_GENERATOR_PLATFORM",
-                        this->GeneratorPlatform.c_str(),
+    this->AddCacheEntry("CMAKE_GENERATOR_PLATFORM", this->GeneratorPlatform,
                         "Name of generator platform.", cmStateEnums::INTERNAL);
   }
 
-  if (cmProp tsName =
+  if (cmValue tsName =
         this->State->GetInitializedCacheValue("CMAKE_GENERATOR_TOOLSET")) {
     if (this->GeneratorToolsetSet && this->GeneratorToolset != *tsName) {
       std::string message =
@@ -2103,15 +2099,14 @@
       return -2;
     }
   } else {
-    this->AddCacheEntry("CMAKE_GENERATOR_TOOLSET",
-                        this->GeneratorToolset.c_str(),
+    this->AddCacheEntry("CMAKE_GENERATOR_TOOLSET", this->GeneratorToolset,
                         "Name of generator toolset.", cmStateEnums::INTERNAL);
   }
 
   // reset any system configuration information, except for when we are
   // InTryCompile. With TryCompile the system info is taken from the parent's
   // info to save time
-  if (!this->State->GetIsInTryCompile()) {
+  if (!this->GetIsInTryCompile()) {
     this->GlobalGenerator->ClearEnabledLanguages();
 
     this->TruncateOutputLog("CMakeOutput.log");
@@ -2415,7 +2410,7 @@
   return 0;
 }
 
-void cmake::AddCacheEntry(const std::string& key, const char* value,
+void cmake::AddCacheEntry(const std::string& key, cmValue value,
                           const char* helpString, int type)
 {
   this->State->AddCacheEntry(key, value, helpString,
@@ -2491,7 +2486,7 @@
   return file;
 }
 
-cmProp cmake::GetCacheDefinition(const std::string& name) const
+cmValue cmake::GetCacheDefinition(const std::string& name) const
 {
   return this->State->GetInitializedCacheValue(name);
 }
@@ -2529,7 +2524,7 @@
   this->Generators.push_back(cmGlobalMinGWMakefileGenerator::NewFactory());
 #endif
 #if !defined(CMAKE_BOOTSTRAP)
-#  if defined(__linux__) || defined(_WIN32)
+#  if (defined(__linux__) && !defined(__ANDROID__)) || defined(_WIN32)
   this->Generators.push_back(cmGlobalGhsMultiGenerator::NewFactory());
 #  endif
   this->Generators.push_back(cmGlobalUnixMakefileGenerator3::NewFactory());
@@ -2622,19 +2617,14 @@
 
 void cmake::UpdateProgress(const std::string& msg, float prog)
 {
-  if (this->ProgressCallback && !this->State->GetIsInTryCompile()) {
+  if (this->ProgressCallback && !this->GetIsInTryCompile()) {
     this->ProgressCallback(msg, prog);
   }
 }
 
 bool cmake::GetIsInTryCompile() const
 {
-  return this->State->GetIsInTryCompile();
-}
-
-void cmake::SetIsInTryCompile(bool b)
-{
-  this->State->SetIsInTryCompile(b);
+  return this->State->GetProjectKind() == cmState::ProjectKind::TryCompile;
 }
 
 void cmake::AppendGlobalGeneratorsDocumentation(
@@ -2705,7 +2695,7 @@
 void cmake::UpdateConversionPathTable()
 {
   // Update the path conversion table with any specified file:
-  cmProp tablepath =
+  cmValue tablepath =
     this->State->GetInitializedCacheValue("CMAKE_PATH_TRANSLATION_FILE");
 
   if (tablepath) {
@@ -2923,6 +2913,10 @@
 {
   this->State->SetGlobalProperty(prop, value);
 }
+void cmake::SetProperty(const std::string& prop, cmValue value)
+{
+  this->State->SetGlobalProperty(prop, value);
+}
 
 void cmake::AppendProperty(const std::string& prop, const std::string& value,
                            bool asString)
@@ -2930,7 +2924,7 @@
   this->State->AppendGlobalProperty(prop, value, asString);
 }
 
-cmProp cmake::GetProperty(const std::string& prop)
+cmValue cmake::GetProperty(const std::string& prop)
 {
   return this->State->GetGlobalProperty(prop);
 }
@@ -3175,7 +3169,7 @@
 std::vector<std::string> cmake::GetDebugConfigs()
 {
   std::vector<std::string> configs;
-  if (cmProp config_list =
+  if (cmValue config_list =
         this->State->GetGlobalProperty("DEBUG_CONFIGURATIONS")) {
     // Expand the specified list and convert to upper-case.
     cmExpandList(*config_list, configs);
@@ -3324,7 +3318,7 @@
     std::cerr << "Error: could not load cache\n";
     return 1;
   }
-  cmProp cachedGenerator = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
+  cmValue cachedGenerator = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
   if (!cachedGenerator) {
     std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
     return 1;
@@ -3336,7 +3330,7 @@
     return 1;
   }
   this->SetGlobalGenerator(std::move(gen));
-  cmProp cachedGeneratorInstance =
+  cmValue cachedGeneratorInstance =
     this->State->GetCacheEntryValue("CMAKE_GENERATOR_INSTANCE");
   if (cachedGeneratorInstance) {
     cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
@@ -3345,7 +3339,7 @@
       return 1;
     }
   }
-  cmProp cachedGeneratorPlatform =
+  cmValue cachedGeneratorPlatform =
     this->State->GetCacheEntryValue("CMAKE_GENERATOR_PLATFORM");
   if (cachedGeneratorPlatform) {
     cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
@@ -3354,7 +3348,7 @@
       return 1;
     }
   }
-  cmProp cachedGeneratorToolset =
+  cmValue cachedGeneratorToolset =
     this->State->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET");
   if (cachedGeneratorToolset) {
     cmMakefile mf(this->GetGlobalGenerator(), this->GetCurrentSnapshot());
@@ -3365,7 +3359,7 @@
   }
   std::string output;
   std::string projName;
-  cmProp cachedProjectName =
+  cmValue cachedProjectName =
     this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
   if (!cachedProjectName) {
     std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
@@ -3457,12 +3451,12 @@
     std::cerr << "Error: could not load cache\n";
     return false;
   }
-  cmProp genName = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
+  cmValue genName = this->State->GetCacheEntryValue("CMAKE_GENERATOR");
   if (!genName) {
     std::cerr << "Error: could not find CMAKE_GENERATOR in Cache\n";
     return false;
   }
-  cmProp extraGenName =
+  cmValue extraGenName =
     this->State->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR");
   std::string fullName =
     cmExternalMakefileProjectGenerator::CreateFullGeneratorName(
@@ -3476,7 +3470,7 @@
     return false;
   }
 
-  cmProp cachedProjectName =
+  cmValue cachedProjectName =
     this->State->GetCacheEntryValue("CMAKE_PROJECT_NAME");
   if (!cachedProjectName) {
     std::cerr << "Error: could not find CMAKE_PROJECT_NAME in Cache\n";
@@ -3540,7 +3534,7 @@
     value = "FALSE";
   }
 
-  this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", value.c_str(),
+  this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", value,
                       "Suppress Warnings that are meant for"
                       " the author of the CMakeLists.txt files.",
                       cmStateEnums::INTERNAL);
@@ -3564,7 +3558,7 @@
     value = "TRUE";
   }
 
-  this->AddCacheEntry("CMAKE_WARN_DEPRECATED", value.c_str(),
+  this->AddCacheEntry("CMAKE_WARN_DEPRECATED", value,
                       "Whether to issue warnings for deprecated "
                       "functionality.",
                       cmStateEnums::INTERNAL);
@@ -3588,7 +3582,7 @@
     value = "TRUE";
   }
 
-  this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_ERRORS", value.c_str(),
+  this->AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_ERRORS", value,
                       "Suppress errors that are meant for"
                       " the author of the CMakeLists.txt files.",
                       cmStateEnums::INTERNAL);
@@ -3612,7 +3606,7 @@
     value = "FALSE";
   }
 
-  this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", value.c_str(),
+  this->AddCacheEntry("CMAKE_ERROR_DEPRECATED", value,
                       "Whether to issue deprecation errors for macros"
                       " and functions.",
                       cmStateEnums::INTERNAL);
diff --git a/Source/cmake.h b/Source/cmake.h
index 5a2a88f..3f2b2ed 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -21,10 +21,10 @@
 #include "cmInstalledFile.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateSnapshot.h"
 #include "cmStateTypes.h"
+#include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
 #  include <cm/optional>
@@ -168,7 +168,8 @@
   static const int DEFAULT_BUILD_PARALLEL_LEVEL = 0;
 
   /// Default constructor
-  cmake(Role role, cmState::Mode mode);
+  cmake(Role role, cmState::Mode mode,
+        cmState::ProjectKind projectKind = cmState::ProjectKind::Normal);
   /// Destructor
   ~cmake();
 
@@ -328,9 +329,21 @@
   /**
    * Given a variable name, return its value (as a string).
    */
-  cmProp GetCacheDefinition(const std::string&) const;
+  cmValue GetCacheDefinition(const std::string&) const;
   //! Add an entry into the cache
   void AddCacheEntry(const std::string& key, const char* value,
+                     const char* helpString, int type)
+  {
+    this->AddCacheEntry(key,
+                        value ? cmValue(std::string(value)) : cmValue(nullptr),
+                        helpString, type);
+  }
+  void AddCacheEntry(const std::string& key, const std::string& value,
+                     const char* helpString, int type)
+  {
+    this->AddCacheEntry(key, cmValue(value), helpString, type);
+  }
+  void AddCacheEntry(const std::string& key, cmValue value,
                      const char* helpString, int type);
 
   bool DoWriteGlobVerifyTarget() const;
@@ -356,7 +369,6 @@
 
   //! Is this cmake running as a result of a TRY_COMPILE command
   bool GetIsInTryCompile() const;
-  void SetIsInTryCompile(bool b);
 
 #ifndef CMAKE_BOOTSTRAP
   void SetWarningFromPreset(const std::string& name,
@@ -396,9 +408,14 @@
 
   //! Set/Get a property of this target file
   void SetProperty(const std::string& prop, const char* value);
+  void SetProperty(const std::string& prop, cmValue value);
+  void SetProperty(const std::string& prop, const std::string& value)
+  {
+    this->SetProperty(prop, cmValue(value));
+  }
   void AppendProperty(const std::string& prop, const std::string& value,
                       bool asString = false);
-  cmProp GetProperty(const std::string& prop);
+  cmValue GetProperty(const std::string& prop);
   bool GetPropertyAsBool(const std::string& prop);
 
   //! Get or create an cmInstalledFile instance and return a pointer to it
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 95ad320..6090ec4 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -25,11 +25,11 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageMetadata.h"
-#include "cmProperty.h"
 #include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 #include "cmake.h"
 #include "cmcmd.h"
 
@@ -375,11 +375,11 @@
       cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k);
       if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC &&
           t != cmStateEnums::UNINITIALIZED) {
-        cmProp advancedProp =
+        cmValue advancedProp =
           cm.GetState()->GetCacheEntryProperty(k, "ADVANCED");
         if (list_all_cached || !advancedProp) {
           if (list_help) {
-            cmProp help =
+            cmValue help =
               cm.GetState()->GetCacheEntryProperty(k, "HELPSTRING");
             std::cout << "// " << (help ? *help : "") << std::endl;
           }
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index db45add..bdddc4e 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -23,6 +23,7 @@
 #include "cmTransformDepfile.h"
 #include "cmUVProcessChain.h"
 #include "cmUtils.hxx"
+#include "cmValue.h"
 #include "cmVersion.h"
 #include "cmake.h"
 
@@ -385,18 +386,15 @@
   return ret;
 }
 
-int HandleLWYU(const std::string& runCmd, const std::string& /* sourceFile */,
+int HandleLWYU(const std::string& runCmd, const std::string& sourceFile,
                const std::vector<std::string>&)
 {
   // Construct the ldd -r -u (link what you use lwyu) command line
   // ldd -u -r lwuy target
-  std::vector<std::string> lwyu_cmd;
-  lwyu_cmd.emplace_back("ldd");
-  lwyu_cmd.emplace_back("-u");
-  lwyu_cmd.emplace_back("-r");
-  lwyu_cmd.push_back(runCmd);
+  std::vector<std::string> lwyu_cmd = cmExpandedList(runCmd, true);
+  lwyu_cmd.push_back(sourceFile);
 
-  // Run the ldd -u -r command line.
+  // Run the lwyu check command line,  currently ldd is expected.
   // Capture its stdout and hide its stderr.
   // Ignore its return code because the tool always returns non-zero
   // if there are any warnings, but we just want to warn.
@@ -1071,6 +1069,8 @@
         } else if (!cmSystemTools::FileExists(arg)) {
           cmSystemTools::Error(arg + ": no such file or directory (ignoring)");
           return_value = 1;
+        } else if (cmSystemTools::FileLength(arg) == 0) {
+          // Ignore empty files, this is not an error
         } else {
           // Destroy console buffers to drop cout/cerr encoding transform.
           consoleBuf.reset();
diff --git a/Source/kwsys/Status.hxx.in b/Source/kwsys/Status.hxx.in
index ed46d5c..16efaef 100644
--- a/Source/kwsys/Status.hxx.in
+++ b/Source/kwsys/Status.hxx.in
@@ -55,7 +55,10 @@
 #endif
 
   /** Return true on "Success", false otherwise.  */
-  explicit operator bool() const { return this->Kind_ == Kind::Success; }
+  bool IsSuccess() const { return this->Kind_ == Kind::Success; }
+
+  /** Return true on "Success", false otherwise.  */
+  explicit operator bool() const { return this->IsSuccess(); }
 
   /** Return the kind of status.  */
   Kind GetKind() const { return this->Kind_; }
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx
index 12f9139..f2bf85f 100644
--- a/Source/kwsys/SystemInformation.cxx
+++ b/Source/kwsys/SystemInformation.cxx
@@ -1356,14 +1356,12 @@
   std::string result = safes(symbol);
 #  if defined(KWSYS_SYSTEMINFORMATION_HAS_CPP_DEMANGLE)
   int status = 0;
-  size_t bufferLen = 1024;
-  char* buffer = (char*)malloc(1024);
   char* demangledSymbol =
-    abi::__cxa_demangle(symbol, buffer, &bufferLen, &status);
+    abi::__cxa_demangle(symbol, nullptr, nullptr, &status);
   if (!status) {
     result = demangledSymbol;
   }
-  free(buffer);
+  free(demangledSymbol);
 #  else
   (void)symbol;
 #  endif
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index f610a70..930d84c 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -2423,7 +2423,7 @@
 
   if (SystemTools::FileIsDirectory(source)) {
     status = SystemTools::MakeDirectory(destination);
-    if (!status) {
+    if (!status.IsSuccess()) {
       return status;
     }
   } else {
@@ -2448,17 +2448,17 @@
     // Create destination directory
     if (!destination_dir.empty()) {
       status = SystemTools::MakeDirectory(destination_dir);
-      if (!status) {
+      if (!status.IsSuccess()) {
         return status;
       }
     }
 
     status = SystemTools::CloneFileContent(source, real_destination);
     // if cloning did not succeed, fall back to blockwise copy
-    if (!status) {
+    if (!status.IsSuccess()) {
       status = SystemTools::CopyFileContentBlockwise(source, real_destination);
     }
-    if (!status) {
+    if (!status.IsSuccess()) {
       return status;
     }
   }
@@ -2488,11 +2488,11 @@
   Status status;
   Directory dir;
   status = dir.Load(source);
-  if (!status) {
+  if (!status.IsSuccess()) {
     return status;
   }
   status = SystemTools::MakeDirectory(destination);
-  if (!status) {
+  if (!status.IsSuccess()) {
     return status;
   }
 
@@ -2507,12 +2507,12 @@
         fullDestPath += "/";
         fullDestPath += dir.GetFile(static_cast<unsigned long>(fileNum));
         status = SystemTools::CopyADirectory(fullPath, fullDestPath, always);
-        if (!status) {
+        if (!status.IsSuccess()) {
           return status;
         }
       } else {
         status = SystemTools::CopyAFile(fullPath, destination, always);
-        if (!status) {
+        if (!status.IsSuccess()) {
           return status;
         }
       }
@@ -2664,7 +2664,7 @@
   Status status;
   Directory dir;
   status = dir.Load(source);
-  if (!status) {
+  if (!status.IsSuccess()) {
     return status;
   }
 
@@ -2678,12 +2678,12 @@
       if (SystemTools::FileIsDirectory(fullPath) &&
           !SystemTools::FileIsSymlink(fullPath)) {
         status = SystemTools::RemoveADirectory(fullPath);
-        if (!status) {
+        if (!status.IsSuccess()) {
           return status;
         }
       } else {
         status = SystemTools::RemoveFile(fullPath);
-        if (!status) {
+        if (!status.IsSuccess()) {
           return status;
         }
       }
@@ -3147,7 +3147,7 @@
     status = Status::Windows_GetLastError();
   }
   CloseHandle(hFile);
-  if (!status) {
+  if (!status.IsSuccess()) {
     return status;
   }
   PREPARSE_DATA_BUFFER data =
diff --git a/Source/kwsys/testDirectory.cxx b/Source/kwsys/testDirectory.cxx
index 06a22dc..a847462 100644
--- a/Source/kwsys/testDirectory.cxx
+++ b/Source/kwsys/testDirectory.cxx
@@ -122,7 +122,7 @@
   }
   const Status copysuccess = SystemTools::CopyADirectory(source, destination);
   const bool destinationexists = SystemTools::PathExists(destination);
-  if (copysuccess) {
+  if (copysuccess.IsSuccess()) {
     std::cerr << "CopyADirectory should have returned false" << std::endl;
     SystemTools::RemoveADirectory(destination);
     return 3;
diff --git a/Source/kwsys/testStatus.cxx b/Source/kwsys/testStatus.cxx
index f85ef42..0a767a8 100644
--- a/Source/kwsys/testStatus.cxx
+++ b/Source/kwsys/testStatus.cxx
@@ -31,6 +31,10 @@
       std::cerr << "Status Success constructor does not produce Success\n";
       res = false;
     }
+    if (!status.IsSuccess()) {
+      std::cerr << "Status Success gives false IsSuccess\n";
+      res = false;
+    }
     if (!status) {
       std::cerr << "Status Success kind is not true\n";
       res = false;
@@ -55,6 +59,10 @@
       std::cerr << "Status POSIX constructor does not produce POSIX\n";
       res = false;
     }
+    if (status.IsSuccess()) {
+      std::cerr << "Status POSIX gives true IsSuccess\n";
+      res = false;
+    }
     if (status) {
       std::cerr << "Status POSIX kind is not false\n";
       res = false;
@@ -87,6 +95,10 @@
       std::cerr << "Status Windows constructor does not produce Windows\n";
       res = false;
     }
+    if (status.IsSuccess()) {
+      std::cerr << "Status Windows gives true IsSuccess\n";
+      res = false;
+    }
     if (status) {
       std::cerr << "Status Windows kind is not false\n";
       res = false;
diff --git a/Source/kwsys/testSystemTools.cxx b/Source/kwsys/testSystemTools.cxx
index 39a19cb..6ccc7a7 100644
--- a/Source/kwsys/testSystemTools.cxx
+++ b/Source/kwsys/testSystemTools.cxx
@@ -436,7 +436,7 @@
   if (symlinkStatus.GetWindows() != ERROR_PRIVILEGE_NOT_HELD)
 #endif
   {
-    if (!symlinkStatus) {
+    if (!symlinkStatus.IsSuccess()) {
       std::cerr << "CreateSymlink for: " << testBadSymlink << " -> "
                 << testBadSymlinkTgt
                 << " failed: " << symlinkStatus.GetString() << std::endl;
diff --git a/Tests/CMakeLib/testRST.expect b/Tests/CMakeLib/testRST.expect
index 4870f65..5e3cdb1 100644
--- a/Tests/CMakeLib/testRST.expect
+++ b/Tests/CMakeLib/testRST.expect
@@ -116,6 +116,12 @@
 .. note::
  Notes are called out.
 
+.. versionadded:: 1.2
+ Version blocks are preserved.
+
+.. versionchanged:: 2.3
+ Version blocks are preserved.
+
 substituted text with multiple lines becomes one line
 
 End of first include.
diff --git a/Tests/CMakeLib/testRST.rst b/Tests/CMakeLib/testRST.rst
index 44931a7..4139801 100644
--- a/Tests/CMakeLib/testRST.rst
+++ b/Tests/CMakeLib/testRST.rst
@@ -123,6 +123,12 @@
 .. note::
  Notes are called out.
 
+.. versionadded:: 1.2
+ Version blocks are preserved.
+
+.. versionchanged:: 2.3
+ Version blocks are preserved.
+
 .. |substitution| replace::
    |nested substitution|
    with multiple lines becomes one line
diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx
index 0607f44..7d21959 100644
--- a/Tests/CMakeLib/testUVRAII.cxx
+++ b/Tests/CMakeLib/testUVRAII.cxx
@@ -169,7 +169,7 @@
   allTypes b(std::move(a));
   allTypes c = std::move(b);
   return true;
-};
+}
 
 static bool testLoopReset()
 {
@@ -192,7 +192,7 @@
   }
 
   return true;
-};
+}
 
 static bool testLoopDestructor()
 {
@@ -217,7 +217,7 @@
   }
 
   return true;
-};
+}
 
 int testUVRAII(int, char** const)
 {
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 341aba6..8b38080 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -39,6 +39,13 @@
 ")
 endif()
 
+# Suppress generator deprecation warnings in test suite.
+if(CMAKE_GENERATOR MATCHES "^Visual Studio 10 2010")
+  set(TEST_WARN_VS10_CODE "set(ENV{CMAKE_WARN_VS10} OFF)")
+else()
+  set(TEST_WARN_VS10_CODE "")
+endif()
+
 # 3.9 or later provides a definitive answer to whether we are multi-config
 # through a global property. Prior to 3.9, CMAKE_CONFIGURATION_TYPES being set
 # is assumed to mean multi-config, but developers might modify it so it is
@@ -250,6 +257,13 @@
   #---------------------------------------------------------------------------
   # Add tests below here.
 
+  if(NOT DEFINED CMake_TEST_Qt6)
+    set(CMake_TEST_Qt6 1)
+  endif()
+  if(CMake_TEST_Qt6)
+    find_package(Qt6 COMPONENTS Core Widgets QUIET NO_MODULE)
+  endif()
+
   if(NOT DEFINED CMake_TEST_Qt5)
     set(CMake_TEST_Qt5 1)
   endif()
@@ -1365,6 +1379,13 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Environment")
+  set_property(TEST Environment APPEND
+    PROPERTY ENVIRONMENT
+      "SET_FROM_AMBIENT_unset=base"
+      "SET_FROM_AMBIENT_replace=base"
+      "SET_FROM_AMBIENT_string=base"
+      "SET_FROM_AMBIENT_path=base"
+      "SET_FROM_AMBIENT_list=base")
 
   add_test(QtAutomocNoQt  ${CMAKE_CTEST_COMMAND}
     --build-and-test
@@ -1377,6 +1398,9 @@
     )
   list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/QtAutomocNoQt")
 
+  if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND)
+    add_subdirectory(Qt6Autogen)
+  endif()
   if(CMake_TEST_Qt5 AND Qt5Widgets_FOUND)
     add_subdirectory(Qt5Autogen)
   endif()
@@ -1442,12 +1466,14 @@
             GIF
             Git
             GLEW
+            GLUT
             GnuTLS
             GSL
             GTK2
             Iconv
             ICU
             Intl
+            Jasper
             JPEG
             JsonCpp
             LAPACK
@@ -1543,6 +1569,8 @@
     ADD_TEST_MACRO(FindMatlab.failure_reports   ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>)
     set(FindMatlab.r2018a_check_BUILD_OPTIONS ${FindMatlab_additional_test_options})
     ADD_TEST_MACRO(FindMatlab.r2018a_check   ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>)
+    set(FindMatlab.targets_checks_BUILD_OPTIONS ${FindMatlab_additional_test_options})
+    ADD_TEST_MACRO(FindMatlab.targets_checks      ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>)
   endif()
 
   add_test(ExternalProject ${CMAKE_CTEST_COMMAND}
@@ -1640,6 +1668,55 @@
     WORKING_DIRECTORY ${CMake_SOURCE_DIR}/Tests/ExternalProjectUpdate
     DEPENDS ExternalProjectUpdateSetup )
 
+  execute_process(
+    COMMAND ${CMAKE_COMMAND}
+    "-E" create_symlink
+    "${CMake_SOURCE_DIR}/Tests/CMakeLists.txt"        # random source file that exists
+    "${CMake_BINARY_DIR}/Tests/try_to_create_symlink" # random target file in existing directory
+    RESULT_VARIABLE _symlink_result
+    OUTPUT_VARIABLE _symlink_stdout
+    ERROR_VARIABLE _symlink_stderr
+    )
+  if(_symlink_result EQUAL 0)
+    file(REMOVE "${CMake_BINARY_DIR}/Tests/try_to_create_symlink")
+    function(add_installmode_test _mode)
+      set(ENV{CMAKE_INSTALL_MODE} _mode)
+      set(_maybe_InstallMode_CTEST_OPTIONS)
+      set(_maybe_BUILD_OPTIONS)
+      if(_isMultiConfig)
+        set(_maybe_CTEST_OPTIONS -C $<CONFIGURATION>)
+      else()
+        set(_maybe_BUILD_OPTIONS "-DCMAKE_BUILD_TYPE=$<CONFIGURATION>")
+      endif()
+      add_test(
+        NAME "InstallMode-${_mode}"
+        COMMAND
+          ${CMAKE_CTEST_COMMAND} -V ${_maybe_CTEST_OPTIONS}
+        --build-and-test
+          "${CMake_SOURCE_DIR}/Tests/InstallMode"
+          "${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}"
+        ${build_generator_args}
+        --build-project superpro
+        --build-exe-dir "${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}"
+        --force-new-ctest-process
+        --build-options
+          ${_maybe_BUILD_OPTIONS}
+          "-DCMAKE_INSTALL_PREFIX:PATH=${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}/install"
+        )
+      list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/InstallMode-${_mode}")
+      unset(ENV{CMAKE_INSTALL_MODE})
+    endfunction()
+
+    add_installmode_test(COPY)
+    add_installmode_test(REL_SYMLINK)
+    add_installmode_test(REL_SYMLINK_OR_COPY)
+    add_installmode_test(ABS_SYMLINK)
+    add_installmode_test(ABS_SYMLINK_OR_COPY)
+    add_installmode_test(SYMLINK)
+    add_installmode_test(SYMLINK_OR_COPY)
+  endif()
+
+
   # do each of the tutorial steps
   function(add_tutorial_test step_name use_mymath tutorial_arg pass_regex)
     set(tutorial_test_name Tutorial${step_name})
@@ -2157,8 +2234,9 @@
     endif()
 
     if(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio 9 "
-        AND NOT CMAKE_GENERATOR_TOOLSET)
+        AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90")
       ADD_TEST_MACRO(VSWindowsFormsResx VSWindowsFormsResx)
+      ADD_TEST_MACRO(VSManagedCustomCommand)
     endif()
 
     add_test(VSExternalInclude ${CMAKE_CTEST_COMMAND}
diff --git a/Tests/CMakeTests/CMakeHostSystemInformationTest.cmake.in b/Tests/CMakeTests/CMakeHostSystemInformationTest.cmake.in
deleted file mode 100644
index a3c2b05..0000000
--- a/Tests/CMakeTests/CMakeHostSystemInformationTest.cmake.in
+++ /dev/null
@@ -1,52 +0,0 @@
-set(BadArg1-RESULT 1)
-set(BadArg1-STDERR "missing RESULT specification")
-set(BadArg2-RESULT 1)
-set(BadArg2-STDERR "missing QUERY specification")
-set(BadArg3-RESULT 1)
-set(BadArg3-STDERR "does not recognize <key> FOOBAR")
-set(QueryList-RESULT 0)
-set(QueryList-STDERR "\\[[0-9]+;[0-9]+\\]")
-
-function(try_and_print key)
-	cmake_host_system_information(RESULT RESULT QUERY ${key})
-	message(STATUS "[${key}] [${RESULT}]")
-endfunction()
-
-message("CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
-
-try_and_print(NUMBER_OF_LOGICAL_CORES)
-try_and_print(NUMBER_OF_PHYSICAL_CORES)
-try_and_print(HOSTNAME)
-try_and_print(FQDN)
-try_and_print(TOTAL_VIRTUAL_MEMORY)
-try_and_print(AVAILABLE_VIRTUAL_MEMORY)
-try_and_print(TOTAL_PHYSICAL_MEMORY)
-try_and_print(AVAILABLE_PHYSICAL_MEMORY)
-try_and_print(IS_64BIT)
-try_and_print(HAS_FPU)
-try_and_print(HAS_MMX)
-try_and_print(HAS_MMX_PLUS)
-try_and_print(HAS_SSE)
-try_and_print(HAS_SSE2)
-try_and_print(HAS_SSE_FP)
-try_and_print(HAS_SSE_MMX)
-try_and_print(HAS_AMD_3DNOW)
-try_and_print(HAS_AMD_3DNOW_PLUS)
-try_and_print(HAS_IA64)
-try_and_print(HAS_SERIAL_NUMBER)
-try_and_print(PROCESSOR_SERIAL_NUMBER)
-try_and_print(PROCESSOR_NAME)
-try_and_print(PROCESSOR_DESCRIPTION)
-try_and_print(OS_NAME)
-try_and_print(OS_RELEASE)
-try_and_print(OS_VERSION)
-try_and_print(OS_PLATFORM)
-
-include("@CMAKE_CURRENT_SOURCE_DIR@/CheckCMakeTest.cmake")
-
-check_cmake_test(CMakeHostSystemInformation
-	BadArg1
-	BadArg2
-	BadArg3
-	QueryList
-)
diff --git a/Tests/CMakeTests/CMakeLists.txt b/Tests/CMakeTests/CMakeLists.txt
index 52959e6..9e0b891 100644
--- a/Tests/CMakeTests/CMakeLists.txt
+++ b/Tests/CMakeTests/CMakeLists.txt
@@ -31,7 +31,6 @@
 AddCMakeTest(ProcessorCount "-DKWSYS_TEST_EXE=$<TARGET_FILE:cmsysTestsCxx>")
 AddCMakeTest(PushCheckState "")
 AddCMakeTest(While "")
-AddCMakeTest(CMakeHostSystemInformation "")
 
 AddCMakeTest(FileDownload "")
 set_tests_properties(CMake.FileDownload PROPERTIES
diff --git a/Tests/CPackNSISGenerator/CMakeLists.txt b/Tests/CPackNSISGenerator/CMakeLists.txt
index 64a8ef6..5d6320b 100644
--- a/Tests/CPackNSISGenerator/CMakeLists.txt
+++ b/Tests/CPackNSISGenerator/CMakeLists.txt
@@ -19,5 +19,6 @@
 set(CPACK_NSIS_MANIFEST_DPI_AWARE ON)
 set(CPACK_NSIS_BRANDING_TEXT "CMake branding text")
 set(CPACK_NSIS_BRANDING_TEXT_TRIM_POSITION "RIGHT")
+set(CPACK_NSIS_IGNORE_LICENSE_PAGE ON)
 
 include(CPack)
diff --git a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
index 8bfcf26..31a2560 100644
--- a/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
+++ b/Tests/CPackNSISGenerator/RunCPackVerifyResult.cmake
@@ -60,3 +60,12 @@
 else()
   message(STATUS "Found BrandingText")
 endif()
+
+# license page should not be present
+file(STRINGS "${project_file}" line REGEX "!insertmacro MUI_PAGE_LICENSE")
+string(FIND "${line}" "MUI_PAGE_LICENSE" output_index)
+if("${output_index}" EQUAL "-1")
+  message(STATUS "License not found in the project")
+else()
+  message(FATAL_ERROR "License found in the project")
+endif()
diff --git a/Tests/CTestTestSerialInDepends/test.ctest b/Tests/CTestTestSerialInDepends/test.ctest
index 28ee094..cf0d314 100644
--- a/Tests/CTestTestSerialInDepends/test.ctest
+++ b/Tests/CTestTestSerialInDepends/test.ctest
@@ -2,6 +2,9 @@
 
 set(LOCK_FILE "${TEST_NAME}.lock")
 
+# Delete the old lock file in case it's lingering from a previous failed test run
+file(REMOVE "${LOCK_FILE}")
+
 if("${TEST_NAME}" STREQUAL "i_want_to_be_alone")
 	file(GLOB LOCK_FILES *.lock)
 	if(LOCK_FILES)
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt
index be5ccac..669c412 100644
--- a/Tests/Cuda/CMakeLists.txt
+++ b/Tests/Cuda/CMakeLists.txt
@@ -4,7 +4,6 @@
     PROPERTY LABELS "CUDA")
 endmacro ()
 
-add_cuda_test_macro(Cuda.ConsumeCompileFeatures CudaConsumeCompileFeatures)
 add_cuda_test_macro(Cuda.CXXStandardSetTwice CXXStandardSetTwice)
 add_cuda_test_macro(Cuda.ObjectLibrary CudaObjectLibrary)
 add_cuda_test_macro(Cuda.MixedStandardLevels1 MixedStandardLevels1)
diff --git a/Tests/Cuda/ConsumeCompileFeatures/CMakeLists.txt b/Tests/Cuda/ConsumeCompileFeatures/CMakeLists.txt
deleted file mode 100644
index b01b9d7..0000000
--- a/Tests/Cuda/ConsumeCompileFeatures/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-cmake_minimum_required(VERSION 3.18)
-project(ConsumeCompileFeatures CXX CUDA)
-#Goal for this example:
-
-#build a c++11 library that express a c++11 public compile feature
-#link a cuda library and verify it builds with c++11 enabled
-
-#build a standalone c++/cuda mixed executable where we express a c++11
-#compile feature.
-
-
-add_library(CudaConsumeLib STATIC static.cpp static.cu)
-target_compile_features(CudaConsumeLib PUBLIC cxx_nullptr)
-
-add_executable(CudaConsumeCompileFeatures main.cu)
-target_link_libraries(CudaConsumeCompileFeatures PRIVATE CudaConsumeLib)
diff --git a/Tests/Cuda/ConsumeCompileFeatures/main.cu b/Tests/Cuda/ConsumeCompileFeatures/main.cu
deleted file mode 100644
index bc32450..0000000
--- a/Tests/Cuda/ConsumeCompileFeatures/main.cu
+++ /dev/null
@@ -1,20 +0,0 @@
-
-#include <iostream>
-
-int static_cxx11_func(int);
-
-void test_functions()
-{
-  auto x = static_cxx11_func(int(42));
-  std::cout << x << std::endl;
-}
-
-int main(int argc, char** argv)
-{
-  test_functions();
-  std::cout
-    << "this executable doesn't use cuda code, just call methods defined"
-    << std::endl;
-  std::cout << "in libraries that have cuda code" << std::endl;
-  return 0;
-}
diff --git a/Tests/Cuda/ConsumeCompileFeatures/static.cpp b/Tests/Cuda/ConsumeCompileFeatures/static.cpp
deleted file mode 100644
index 565d52e..0000000
--- a/Tests/Cuda/ConsumeCompileFeatures/static.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-#include <type_traits>
-
-int static_cuda11_func(int);
-
-int static_cxx11_func(int x)
-{
-  return static_cuda11_func(x) + std::integral_constant<int, 32>::value;
-}
diff --git a/Tests/Cuda/ConsumeCompileFeatures/static.cu b/Tests/Cuda/ConsumeCompileFeatures/static.cu
deleted file mode 100644
index 73e43a8..0000000
--- a/Tests/Cuda/ConsumeCompileFeatures/static.cu
+++ /dev/null
@@ -1,9 +0,0 @@
-
-#include <type_traits>
-
-using tt = std::true_type;
-using ft = std::false_type;
-int __host__ static_cuda11_func(int x)
-{
-  return x * x + std::integral_constant<int, 17>::value;
-}
diff --git a/Tests/EnforceConfig.cmake.in b/Tests/EnforceConfig.cmake.in
index 7781ded..7c6f76a 100644
--- a/Tests/EnforceConfig.cmake.in
+++ b/Tests/EnforceConfig.cmake.in
@@ -36,3 +36,4 @@
 unset(ENV{CMAKE_EXPORT_COMPILE_COMMANDS})
 
 @TEST_HOME_ENV_CODE@
+@TEST_WARN_VS10_CODE@
diff --git a/Tests/Environment/CMakeLists.txt b/Tests/Environment/CMakeLists.txt
index 2b18d24..abcf33c 100644
--- a/Tests/Environment/CMakeLists.txt
+++ b/Tests/Environment/CMakeLists.txt
@@ -9,6 +9,7 @@
 add_test(Environment2 Environment)
 add_test(EchoEnvironment1 ${CMAKE_COMMAND} -E environment)
 add_test(EchoEnvironment2 ${CMAKE_COMMAND} -E environment)
+add_test(EchoEnvironment3 ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_SOURCE_DIR}/check_mod.cmake")
 
 # Make sure "CMAKE_ENV.*Happy Thanksgiving" is in the output of
 # the "1" tests:
@@ -24,3 +25,79 @@
 set_tests_properties(Environment2 EchoEnvironment2 PROPERTIES
   FAIL_REGULAR_EXPRESSION "CMAKE_ENV.*Happy Thanksgiving"
 )
+
+set_property(TEST EchoEnvironment3
+  PROPERTY ENVIRONMENT
+    "SET_FROM_ENVIRONMENT_PROPERTY_unset=base"
+    "SET_FROM_ENVIRONMENT_PROPERTY_replace=base"
+    "SET_FROM_ENVIRONMENT_PROPERTY_string=base"
+    "SET_FROM_ENVIRONMENT_PROPERTY_path=base"
+    "SET_FROM_ENVIRONMENT_PROPERTY_list=base"
+)
+
+set_property(TEST EchoEnvironment3
+  PROPERTY ENVIRONMENT_MODIFICATION
+    # Modifying variables set in the ambient environment (see properties for
+    # this test in `Tests/CMakeLists.txt`).
+    "SET_FROM_AMBIENT_unset=unset:"
+    "SET_FROM_AMBIENT_replace=set:new"
+    "SET_FROM_AMBIENT_string=string_append:new"
+    "SET_FROM_AMBIENT_path=path_list_append:new"
+    "SET_FROM_AMBIENT_list=cmake_list_append:new"
+
+    # Modifying variables set in the `ENVIRONMENT` property.
+    "SET_FROM_ENVIRONMENT_PROPERTY_unset=unset:"
+    "SET_FROM_ENVIRONMENT_PROPERTY_replace=set:new"
+    "SET_FROM_ENVIRONMENT_PROPERTY_string=string_append:new"
+    "SET_FROM_ENVIRONMENT_PROPERTY_path=path_list_append:new"
+    "SET_FROM_ENVIRONMENT_PROPERTY_list=cmake_list_append:new"
+
+    # Variables expected to be unset.
+    "UNSET_EXPLICIT=set:value"
+    "UNSET_EXPLICIT=unset:"
+    "UNSET_VIA_RESET=set:value"
+    "UNSET_VIA_RESET=reset:"
+
+    # Direct settings.
+    "DIRECT=set:old"
+    "DIRECT=set:new"
+
+    # String manipulation.
+    "STRING_MANIP=set:-core-"
+    "STRING_MANIP=string_append:post-"
+    "STRING_MANIP=string_prepend:-pre"
+    "STRING_MANIP=string_append:suffix"
+    "STRING_MANIP=string_prepend:prefix"
+
+    # String manipulation on non-existent.
+    "STRING_DNE=string_append:post-"
+    "STRING_DNE=string_prepend:-pre"
+    "STRING_DNE=string_append:suffix"
+    "STRING_DNE=string_prepend:prefix"
+
+    # Path manipulation.
+    "PATH_MANIP=set:core"
+    "PATH_MANIP=path_list_append:post"
+    "PATH_MANIP=path_list_prepend:pre"
+    "PATH_MANIP=path_list_append:suffix"
+    "PATH_MANIP=path_list_prepend:prefix"
+
+    # Path manipulation on non-existent.
+    "PATH_DNE=path_list_append:post"
+    "PATH_DNE=path_list_prepend:pre"
+    "PATH_DNE=path_list_append:suffix"
+    "PATH_DNE=path_list_prepend:prefix"
+
+    # CMake list manipulation.
+    "CMAKE_LIST_MANIP=set:core"
+    "CMAKE_LIST_MANIP=cmake_list_append:post"
+    "CMAKE_LIST_MANIP=cmake_list_prepend:pre"
+    "CMAKE_LIST_MANIP=cmake_list_append:suffix"
+    "CMAKE_LIST_MANIP=cmake_list_prepend:prefix"
+
+    # CMake list manipulation on non-existent.
+    "CMAKE_LIST_DNE=cmake_list_append:post"
+    "CMAKE_LIST_DNE=cmake_list_prepend:pre"
+    "CMAKE_LIST_DNE=cmake_list_append:suffix"
+    "CMAKE_LIST_DNE=cmake_list_prepend:prefix"
+)
diff --git a/Tests/Environment/check_mod.cmake b/Tests/Environment/check_mod.cmake
new file mode 100644
index 0000000..0f885f0
--- /dev/null
+++ b/Tests/Environment/check_mod.cmake
@@ -0,0 +1,79 @@
+execute_process(
+  COMMAND ${CMAKE_COMMAND} -E environment
+  OUTPUT_VARIABLE out
+  ERROR_VARIABLE err
+  RESULT_VARIABLE res)
+
+if (res)
+  message(FATAL_ERROR "Failed with exit code ${res}: ${err}")
+endif ()
+
+if (CMAKE_HOST_WIN32)
+  set(path_sep ";")
+else ()
+  set(path_sep ":")
+endif ()
+
+set(unexpect_SET_FROM_AMBIENT_unset "")
+set(unexpect_SET_FROM_ENVIRONMENT_PROPERTY_unset "")
+set(unexpect_UNSET_EXPLICIT "")
+set(unexpect_UNSET_VIA_RESET "")
+set(expect_DIRECT "new")
+set(expect_STRING_MANIP "prefix-pre-core-post-suffix")
+set(expect_PATH_MANIP "prefix${path_sep}pre${path_sep}core${path_sep}post${path_sep}suffix")
+set(expect_CMAKE_LIST_MANIP "prefix;pre;core;post;suffix")
+set(expect_STRING_DNE "prefix-prepost-suffix")
+set(expect_PATH_DNE "prefix${path_sep}pre${path_sep}post${path_sep}suffix")
+set(expect_CMAKE_LIST_DNE "prefix;pre;post;suffix")
+set(expect_SET_FROM_AMBIENT_replace "new")
+set(expect_SET_FROM_AMBIENT_string "basenew")
+set(expect_SET_FROM_AMBIENT_path "base${path_sep}new")
+set(expect_SET_FROM_AMBIENT_list "base;new")
+set(expect_SET_FROM_ENVIRONMENT_PROPERTY_replace "new")
+set(expect_SET_FROM_ENVIRONMENT_PROPERTY_string "basenew")
+set(expect_SET_FROM_ENVIRONMENT_PROPERTY_path "base${path_sep}new")
+set(expect_SET_FROM_ENVIRONMENT_PROPERTY_list "base;new")
+
+set(expected_vars
+  SET_FROM_AMBIENT_replace
+  SET_FROM_AMBIENT_string
+  SET_FROM_AMBIENT_path
+  SET_FROM_AMBIENT_list
+  SET_FROM_ENVIRONMENT_PROPERTY_replace
+  SET_FROM_ENVIRONMENT_PROPERTY_string
+  SET_FROM_ENVIRONMENT_PROPERTY_path
+  SET_FROM_ENVIRONMENT_PROPERTY_list
+  DIRECT
+  STRING_MANIP
+  PATH_MANIP
+  CMAKE_LIST_MANIP
+  STRING_DNE
+  PATH_DNE
+  CMAKE_LIST_DNE)
+
+while (out)
+  string(FIND "${out}" "\n" nl_pos)
+  string(SUBSTRING "${out}" 0 "${nl_pos}" line)
+  math(EXPR line_next "${nl_pos} + 1")
+  string(SUBSTRING "${out}" "${line_next}" -1 out)
+
+  string(FIND "${line}" "=" eq_pos)
+  string(SUBSTRING "${line}" 0 "${eq_pos}" name)
+  math(EXPR value_start "${eq_pos} + 1")
+  string(SUBSTRING "${line}" "${value_start}" -1 value)
+
+  if (DEFINED "unexpect_${name}")
+    message(SEND_ERROR "Found `${name}=${value}` when it should have been unset")
+  elseif (DEFINED "expect_${name}")
+    list(REMOVE_ITEM expected_vars "${name}")
+    if (expect_${name} STREQUAL value)
+      message(STATUS "Found `${name}=${value}` as expected")
+    else ()
+      message(SEND_ERROR "Found `${name}=${value}` when it should have been ${expect_${name}}")
+    endif ()
+  endif ()
+endwhile ()
+
+if (expected_vars)
+  message(SEND_ERROR "Did not test expected variables: ${expected_vars}")
+endif ()
diff --git a/Tests/FindBLAS/CMakeLists.txt b/Tests/FindBLAS/CMakeLists.txt
index 482cee8..00b254d 100644
--- a/Tests/FindBLAS/CMakeLists.txt
+++ b/Tests/FindBLAS/CMakeLists.txt
@@ -1,49 +1,2 @@
-foreach(variant IN LISTS CMake_TEST_FindBLAS)
-  if(variant MATCHES "^([^.]+)\\.(.*)$")
-    set(vendor "${CMAKE_MATCH_1}")
-    set(alt_compiler "-DCMAKE_C_COMPILER=${CMAKE_MATCH_2}")
-  else()
-    set(vendor "${variant}")
-    set(alt_compiler "")
-  endif()
-  if(vendor STREQUAL "All" AND CMake_TEST_FindBLAS_All)
-    set(EXPECT_All "-DEXPECT_All=${CMake_TEST_FindBLAS_All}")
-  else()
-    set(EXPECT_All "")
-  endif()
-  add_test(NAME FindBLAS.Test_${variant} COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindBLAS/Test"
-    "${CMake_BINARY_DIR}/Tests/FindBLAS/Test_${variant}"
-    ${build_generator_args}
-    --build-project TestFindBLAS
-    --build-options ${build_options} ${alt_compiler} ${EXPECT_All} -DBLA_VENDOR=${vendor}
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-endforeach()
-
-foreach(variant IN LISTS CMake_TEST_FindBLAS_STATIC)
-  if(variant MATCHES "^([^.]+)\\.(.*)$")
-    set(vendor "${CMAKE_MATCH_1}")
-    set(alt_compiler "-DCMAKE_C_COMPILER=${CMAKE_MATCH_2}")
-  else()
-    set(vendor "${variant}")
-    set(alt_compiler "")
-  endif()
-  if(vendor STREQUAL "All" AND CMake_TEST_FindBLAS_STATIC_All)
-    set(EXPECT_All "-DEXPECT_All=${CMake_TEST_FindBLAS_STATIC_All}")
-  else()
-    set(EXPECT_All "")
-  endif()
-  add_test(NAME FindBLAS.Test_${variant}_Static COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindBLAS/Test"
-    "${CMake_BINARY_DIR}/Tests/FindBLAS/Test_${variant}_Static"
-    ${build_generator_args}
-    --build-project TestFindBLAS
-    --build-options ${build_options} ${alt_compiler} ${EXPECT_All} -DBLA_VENDOR=${vendor} -DBLA_STATIC=ON
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-endforeach()
+include("${CMake_SOURCE_DIR}/Tests/FindBLAS/add_BLAS_LAPACK_tests.cmake")
+add_BLAS_LAPACK_tests(CMake_TEST_FindBLAS)
diff --git a/Tests/FindBLAS/Test/CMakeLists.txt b/Tests/FindBLAS/Test/CMakeLists.txt
index 7379749..14bf19f 100644
--- a/Tests/FindBLAS/Test/CMakeLists.txt
+++ b/Tests/FindBLAS/Test/CMakeLists.txt
@@ -4,12 +4,18 @@
 
 find_package(BLAS REQUIRED)
 
+if(NOT BLA_SIZEOF_INTEGER)
+  set(BLA_SIZEOF_INTEGER 4)
+endif()
+
 add_executable(test_tgt main.c)
 target_link_libraries(test_tgt BLAS::BLAS)
+target_compile_definitions(test_tgt PUBLIC BLA_SIZEOF_INTEGER=${BLA_SIZEOF_INTEGER})
 add_test(NAME test_tgt COMMAND test_tgt)
 
 add_executable(test_var main.c)
 target_link_libraries(test_var PRIVATE ${BLAS_LIBRARIES})
+target_compile_definitions(test_var PUBLIC BLA_SIZEOF_INTEGER=${BLA_SIZEOF_INTEGER})
 add_test(NAME test_var COMMAND test_var)
 
 if((BLA_VENDOR STREQUAL "Intel10_64lp") OR
@@ -17,4 +23,9 @@
   if(NOT BLAS_LIBRARIES MATCHES "^(-Wl,--start-group;)?[^;]*mkl_intel_lp64")
     message(FATAL_ERROR "BLAS_LIBRARIES does not start in mkl_intel_lp64:\n ${BLAS_LIBRARIES}")
   endif()
+elseif((BLA_VENDOR STREQUAL "Intel10_64ilp") OR
+   (BLA_VENDOR STREQUAL "All" AND EXPECT_All STREQUAL "Intel10_64ilp"))
+  if(NOT BLAS_LIBRARIES MATCHES "^(-Wl,--start-group;)?[^;]*mkl_intel_ilp64")
+    message(FATAL_ERROR "BLAS_LIBRARIES does not start in mkl_intel_ilp64:\n ${BLAS_LIBRARIES}")
+  endif()
 endif()
diff --git a/Tests/FindBLAS/Test/main.c b/Tests/FindBLAS/Test/main.c
index e61b02c..4fc9fe4 100644
--- a/Tests/FindBLAS/Test/main.c
+++ b/Tests/FindBLAS/Test/main.c
@@ -1,16 +1,25 @@
 #include <assert.h>
+#include <stdint.h>
 #include <string.h>
 
+#if BLA_SIZEOF_INTEGER == 4
+typedef int32_t blas_int;
+#elif BLA_SIZEOF_INTEGER == 8
+typedef int64_t blas_int;
+#else
+#  error BLA_SIZEOF_INTEGER is not declared!
+#endif
+
 // declare what parts of the blas C-API we need
-void dswap_(int* N, double* X, int* incX, double* Y, int* incY);
+void dswap_(blas_int* N, double* X, blas_int* incX, double* Y, blas_int* incY);
 
 int main()
 {
   double x[4] = { 1, 2, 3, 4 };
   double y[4] = { 8, 7, 7, 6 };
-  int N = 4;
-  int incX = 1;
-  int incY = 1;
+  blas_int N = 4;
+  blas_int incX = 1;
+  blas_int incY = 1;
   dswap_(&N, x, &incX, y, &incY);
   return 0;
 }
diff --git a/Tests/FindBLAS/add_BLAS_LAPACK_tests.cmake b/Tests/FindBLAS/add_BLAS_LAPACK_tests.cmake
new file mode 100644
index 0000000..42fe386
--- /dev/null
+++ b/Tests/FindBLAS/add_BLAS_LAPACK_tests.cmake
@@ -0,0 +1,53 @@
+function(add_BLAS_LAPACK_tests var)
+  if(var MATCHES "^CMake_TEST_Find(BLAS|LAPACK)$")
+    set(package "${CMAKE_MATCH_1}")
+  else()
+    message(FATAL_ERROR "Test list variable '${var}' not supported.")
+  endif()
+
+  set(all "")
+  set(compiler "")
+  set(model "")
+  set(static "")
+
+  set(sizeof_int_lp64 4)
+  set(sizeof_int_ilp64 8)
+
+  foreach(variant IN LISTS ${var})
+    if(variant MATCHES "^(all|compiler|model|static)=(.*)$")
+      set("${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
+      continue()
+    elseif(variant MATCHES "^([^=]+)=(.*)$")
+      message(FATAL_ERROR "Unknown variable '${variant}'")
+    endif()
+    set(variant_name "${variant}")
+    set(variant_options "-DBLA_VENDOR=${variant}")
+    if(variant STREQUAL "All" AND all)
+      list(APPEND variant_options "-DEXPECT_All=${all}")
+    endif()
+    if(model)
+      if(NOT variant_name MATCHES "Intel10_64")
+        string(APPEND variant_name "_${model}")
+      endif()
+      list(APPEND variant_options "-DBLA_SIZEOF_INTEGER=${sizeof_int_${model}}")
+    endif()
+    if(compiler)
+      string(APPEND variant_name "_${compiler}")
+      list(APPEND variant_options "-DCMAKE_C_COMPILER=${compiler}")
+    endif()
+    if(static)
+      string(APPEND variant_name "_Static")
+      list(APPEND variant_options "-DBLA_STATIC=ON")
+    endif()
+    add_test(NAME Find${package}.Test_${variant_name} COMMAND
+      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/Find${package}/Test"
+      "${CMake_BINARY_DIR}/Tests/Find${package}/Test_${variant_name}"
+      ${build_generator_args}
+      --build-project TestFind${package}
+      --build-options ${build_options} ${variant_options}
+      --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+      )
+  endforeach()
+endfunction()
diff --git a/Tests/FindGLUT/CMakeLists.txt b/Tests/FindGLUT/CMakeLists.txt
new file mode 100644
index 0000000..e75ec40
--- /dev/null
+++ b/Tests/FindGLUT/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_test(NAME FindGLUT.Test COMMAND ${CMAKE_CTEST_COMMAND}
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindGLUT/Test"
+  "${CMake_BINARY_DIR}/Tests/FindGLUT/Test"
+  ${build_generator_args}
+  --build-project TestFindGLUT
+  --build-options ${build_options}
+  --test-command ${CMAKE_CTEST_COMMAND} -V
+  )
diff --git a/Tests/FindGLUT/Test/CMakeLists.txt b/Tests/FindGLUT/Test/CMakeLists.txt
new file mode 100644
index 0000000..f6440b2
--- /dev/null
+++ b/Tests/FindGLUT/Test/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.21)
+project(TestFindGLUT C)
+include(CTest)
+
+find_package(GLUT REQUIRED)
+
+add_executable(testglut_tgt main.c)
+target_link_libraries(testglut_tgt GLUT::GLUT)
+add_test(NAME testglut_tgt COMMAND testglut_tgt)
+
+add_executable(testglut_var main.c)
+target_include_directories(testglut_var PRIVATE ${GLUT_INCLUDE_DIR})
+target_link_libraries(testglut_var PRIVATE ${GLUT_LIBRARIES})
+add_test(NAME testglut_var COMMAND testglut_var)
+
+set_tests_properties(testglut_tgt testglut_var
+    PROPERTIES WILL_FAIL true)
diff --git a/Tests/FindGLUT/Test/main.c b/Tests/FindGLUT/Test/main.c
new file mode 100644
index 0000000..1c8569c
--- /dev/null
+++ b/Tests/FindGLUT/Test/main.c
@@ -0,0 +1,11 @@
+#include <GL/glut.h>
+#include <stdio.h>
+
+int main()
+{
+  /* The following should call exit(1) and print
+      freeglut  ERROR:  Function <glutCreateWindow> called
+      without first calling 'glutInit'.
+    to stderr */
+  glutCreateWindow("gluttest");
+}
diff --git a/Tests/FindJasper/CMakeLists.txt b/Tests/FindJasper/CMakeLists.txt
new file mode 100644
index 0000000..1bfae78
--- /dev/null
+++ b/Tests/FindJasper/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_test(NAME FindJasper.Test COMMAND
+  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+  --build-and-test
+  "${CMake_SOURCE_DIR}/Tests/FindJasper/Test"
+  "${CMake_BINARY_DIR}/Tests/FindJasper/Test"
+  ${build_generator_args}
+  --build-project TestFindJasper
+  --build-options ${build_options}
+  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+  )
diff --git a/Tests/FindJasper/Test/CMakeLists.txt b/Tests/FindJasper/Test/CMakeLists.txt
new file mode 100644
index 0000000..1e9467d
--- /dev/null
+++ b/Tests/FindJasper/Test/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.21)
+project(TestFindJasper C)
+include(CTest)
+
+find_package(Jasper)
+
+add_definitions(-DCMAKE_EXPECTED_JASPER_VERSION=${JASPER_VERSION_STRING})
+
+add_executable(test_jasper_tgt main.c)
+target_link_libraries(test_jasper_tgt Jasper::Jasper)
+add_test(NAME test_jasper_tgt COMMAND test_jasper_tgt)
+
+add_executable(test_jasper_var main.c)
+target_include_directories(test_jasper_var PRIVATE ${JASPER_INCLUDE_DIRS})
+target_link_libraries(test_jasper_var PRIVATE ${JASPER_LIBRARIES})
+add_test(NAME test_jasper_var COMMAND test_jasper_var)
diff --git a/Tests/FindJasper/Test/main.c b/Tests/FindJasper/Test/main.c
new file mode 100644
index 0000000..771344d
--- /dev/null
+++ b/Tests/FindJasper/Test/main.c
@@ -0,0 +1,17 @@
+#include <assert.h>
+// clang-format off
+#include <stdio.h>
+#include <jasper/jasper.h>
+// clang-format on
+
+int main()
+{
+  /* Without any JPEG file to open, test that the call fails as
+     expected.  This tests that linking worked. */
+  jas_init();
+  jas_image_t* img = jas_image_create0();
+  jas_image_destroy(img);
+  jas_cleanup();
+
+  return (JAS_VERSION != CMAKE_EXPECTED_JASPER_VERSION);
+}
diff --git a/Tests/FindLAPACK/CMakeLists.txt b/Tests/FindLAPACK/CMakeLists.txt
index 535c7b6..a2470ef 100644
--- a/Tests/FindLAPACK/CMakeLists.txt
+++ b/Tests/FindLAPACK/CMakeLists.txt
@@ -1,49 +1,2 @@
-foreach(variant IN LISTS CMake_TEST_FindLAPACK)
-  if(variant MATCHES "^([^.]+)\\.(.*)$")
-    set(vendor "${CMAKE_MATCH_1}")
-    set(alt_compiler "-DCMAKE_C_COMPILER=${CMAKE_MATCH_2}")
-  else()
-    set(vendor "${variant}")
-    set(alt_compiler "")
-  endif()
-  if(vendor STREQUAL "All" AND CMake_TEST_FindLAPACK_All)
-    set(EXPECT_All "-DEXPECT_All=${CMake_TEST_FindLAPACK_All}")
-  else()
-    set(EXPECT_All "")
-  endif()
-  add_test(NAME FindLAPACK.Test_${variant} COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindLAPACK/Test"
-    "${CMake_BINARY_DIR}/Tests/FindLAPACK/Test_${variant}"
-    ${build_generator_args}
-    --build-project TestFindLAPACK
-    --build-options ${build_options} ${alt_compiler} ${EXPECT_All} -DBLA_VENDOR=${vendor}
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-endforeach()
-
-foreach(variant IN LISTS CMake_TEST_FindLAPACK_STATIC)
-  if(variant MATCHES "^([^.]+)\\.(.*)$")
-    set(vendor "${CMAKE_MATCH_1}")
-    set(alt_compiler "-DCMAKE_C_COMPILER=${CMAKE_MATCH_2}")
-  else()
-    set(vendor "${variant}")
-    set(alt_compiler "")
-  endif()
-  if(vendor STREQUAL "All" AND CMake_TEST_FindLAPACK_STATIC_All)
-    set(EXPECT_All "-DEXPECT_All=${CMake_TEST_FindLAPACK_STATIC_All}")
-  else()
-    set(EXPECT_All "")
-  endif()
-  add_test(NAME FindLAPACK.Test_${variant}_Static COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindLAPACK/Test"
-    "${CMake_BINARY_DIR}/Tests/FindLAPACK/Test_${variant}_Static"
-    ${build_generator_args}
-    --build-project TestFindLAPACK
-    --build-options ${build_options} ${alt_compiler} ${EXPECT_All} -DBLA_VENDOR=${vendor} -DBLA_STATIC=ON
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-endforeach()
+include("${CMake_SOURCE_DIR}/Tests/FindBLAS/add_BLAS_LAPACK_tests.cmake")
+add_BLAS_LAPACK_tests(CMake_TEST_FindLAPACK)
diff --git a/Tests/FindLAPACK/Test/CMakeLists.txt b/Tests/FindLAPACK/Test/CMakeLists.txt
index 23a43ac..f5d5a73 100644
--- a/Tests/FindLAPACK/Test/CMakeLists.txt
+++ b/Tests/FindLAPACK/Test/CMakeLists.txt
@@ -4,12 +4,18 @@
 
 find_package(LAPACK REQUIRED)
 
+if(NOT BLA_SIZEOF_INTEGER)
+  set(BLA_SIZEOF_INTEGER 4)
+endif()
+
 add_executable(test_tgt main.c)
 target_link_libraries(test_tgt LAPACK::LAPACK)
+target_compile_definitions(test_tgt PUBLIC BLA_SIZEOF_INTEGER=${BLA_SIZEOF_INTEGER})
 add_test(NAME test_tgt COMMAND test_tgt)
 
 add_executable(test_var main.c)
 target_link_libraries(test_var PRIVATE ${LAPACK_LIBRARIES})
+target_compile_definitions(test_var PUBLIC BLA_SIZEOF_INTEGER=${BLA_SIZEOF_INTEGER})
 add_test(NAME test_var COMMAND test_var)
 
 if((BLA_VENDOR STREQUAL "Intel10_64lp") OR
@@ -17,4 +23,9 @@
   if(NOT LAPACK_LIBRARIES MATCHES "^(-Wl,--start-group;)?[^;]*mkl_intel_lp64")
     message(FATAL_ERROR "LAPACK_LIBRARIES does not start in mkl_intel_lp64:\n ${LAPACK_LIBRARIES}")
   endif()
+elseif((BLA_VENDOR STREQUAL "Intel10_64ilp") OR
+   (BLA_VENDOR STREQUAL "All" AND EXPECT_All STREQUAL "Intel10_64ilp"))
+  if(NOT LAPACK_LIBRARIES MATCHES "^(-Wl,--start-group;)?[^;]*mkl_intel_ilp64")
+    message(FATAL_ERROR "LAPACK_LIBRARIES does not start in mkl_intel_ilp64:\n ${LAPACK_LIBRARIES}")
+  endif()
 endif()
diff --git a/Tests/FindLAPACK/Test/main.c b/Tests/FindLAPACK/Test/main.c
index 5873e7b..dd33fb3 100644
--- a/Tests/FindLAPACK/Test/main.c
+++ b/Tests/FindLAPACK/Test/main.c
@@ -1,8 +1,18 @@
 #include <assert.h>
+#include <stdint.h>
 #include <string.h>
 
+#if BLA_SIZEOF_INTEGER == 4
+typedef int32_t blas_int;
+#elif BLA_SIZEOF_INTEGER == 8
+typedef int64_t blas_int;
+#else
+#  error BLA_SIZEOF_INTEGER is not declared!
+#endif
+
 // declare what parts of the lapack C-API we need
-void dgesv_(int*, int*, double*, int*, int*, double*, int*, int*);
+void dgesv_(blas_int*, blas_int*, double*, blas_int*, blas_int*, double*,
+            blas_int*, blas_int*);
 
 int main()
 {
@@ -10,11 +20,11 @@
     0, 1, 2, 3, 4, 5, 6, 7,
   };
   double B[2] = { 0, 5 };
-  int ipiv[2] = { 0, 0 };
-  int info = 0;
+  blas_int ipiv[2] = { 0, 0 };
+  blas_int info = 0;
 
-  int dim = 2;
-  int numCols = 1;
+  blas_int dim = 2;
+  blas_int numCols = 1;
   dgesv_(&dim, &numCols, A, &dim, ipiv, B, &dim, &info);
   return 0;
 }
diff --git a/Tests/FindMatlab/targets_checks/CMakeLists.txt b/Tests/FindMatlab/targets_checks/CMakeLists.txt
new file mode 100644
index 0000000..4af7cc3
--- /dev/null
+++ b/Tests/FindMatlab/targets_checks/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required (VERSION 2.8.12)
+enable_testing()
+project(targets_checks)
+
+set(MATLAB_FIND_DEBUG TRUE)
+
+if(NOT "${MCR_ROOT}" STREQUAL "")
+    set(Matlab_ROOT_DIR "${MCR_ROOT}")
+    if(NOT EXISTS "${MCR_ROOT}")
+        message(FATAL_ERROR "MCR does not exist ${MCR_ROOT}")
+    endif()
+endif()
+
+# the success of the following command is dependent on the current configuration:
+# - on 32bits builds (cmake is building with 32 bits), it looks for 32 bits Matlab
+# - on 64bits builds (cmake is building with 64 bits), it looks for 64 bits Matlab
+find_package(Matlab REQUIRED COMPONENTS ENG_LIBRARY MAT_LIBRARY
+                    OPTIONAL_COMPONENTS MAIN_PROGRAM)
+
+if(NOT TARGET Matlab::mx)
+    message(FATAL_ERROR "Matlab::mx target does not exist")
+endif()
+
+if(NOT TARGET Matlab::mex)
+    message(FATAL_ERROR "Matlab::mex target does not exist")
+endif()
+
+if(NOT TARGET Matlab::eng)
+    message(FATAL_ERROR "Matlab::eng target does not exist")
+endif()
+
+if(NOT TARGET Matlab::mat)
+    message(FATAL_ERROR "Matlab::mat target does not exist")
+endif()
+
+matlab_add_mex(
+    # target name
+    NAME cmake_matlab_test_wrapper1
+    # output name
+    OUTPUT_NAME cmake_matlab_mex1
+    SRC ${CMAKE_CURRENT_SOURCE_DIR}/../matlab_wrapper1.cpp
+    DOCUMENTATION ${CMAKE_CURRENT_SOURCE_DIR}/../help_text1.m.txt
+    LINK_TO Matlab::mex
+    )
diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt
index d4c19c7..f00565e 100644
--- a/Tests/IncludeDirectories/CMakeLists.txt
+++ b/Tests/IncludeDirectories/CMakeLists.txt
@@ -2,17 +2,26 @@
 project(IncludeDirectories)
 
 if (((CMAKE_C_COMPILER_ID STREQUAL GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.4)
-    OR (CMAKE_C_COMPILER_ID STREQUAL Clang AND NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") OR CMAKE_C_COMPILER_ID STREQUAL AppleClang)
+    OR (CMAKE_C_COMPILER_ID STREQUAL Clang AND NOT "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC")
+    OR CMAKE_C_COMPILER_ID STREQUAL NVHPC
+    OR CMAKE_C_COMPILER_ID STREQUAL AppleClang
+    OR ("x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC" AND
+       CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "19.29.30036.3" AND
+       NOT CMAKE_GENERATOR MATCHES "Visual Studio")) # No support for VS generators yet.
     AND (CMAKE_GENERATOR STREQUAL "Unix Makefiles"
       OR CMAKE_GENERATOR STREQUAL "Ninja"
       OR (CMAKE_GENERATOR STREQUAL "Xcode" AND NOT XCODE_VERSION VERSION_LESS 6.0)))
-  include(CheckCXXCompilerFlag)
-  check_cxx_compiler_flag(-Wunused-variable run_sys_includes_test)
-  if(run_sys_includes_test)
-    # The Bullseye wrapper appears to break the -isystem effect.
-    execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE out ERROR_VARIABLE out)
-    if("x${out}" MATCHES "Bullseye")
-      set(run_sys_includes_test 0)
+  if ("x${CMAKE_C_COMPILER_ID}" STREQUAL "xMSVC")
+    set(run_sys_includes_test 1)
+  else ()
+    include(CheckCXXCompilerFlag)
+    check_cxx_compiler_flag(-Wunused-variable run_sys_includes_test)
+    if(run_sys_includes_test)
+      # The Bullseye wrapper appears to break the -isystem effect.
+      execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE out ERROR_VARIABLE out)
+      if("x${out}" MATCHES "Bullseye")
+        set(run_sys_includes_test 0)
+      endif()
     endif()
   endif()
   if (run_sys_includes_test)
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt
index dee39c8..a746a68 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt
+++ b/Tests/IncludeDirectories/SystemIncludeDirectories/CMakeLists.txt
@@ -6,9 +6,17 @@
 add_library(systemlib systemlib.cpp)
 target_include_directories(systemlib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/systemlib")
 
+function (apply_error_flags target)
+  if (MSVC)
+    target_compile_options(${target} PRIVATE /we4101)
+  else ()
+    target_compile_options(${target} PRIVATE -Werror=unused-variable)
+  endif ()
+endfunction ()
+
 add_library(upstream upstream.cpp)
 target_link_libraries(upstream LINK_PUBLIC systemlib)
-target_compile_options(upstream PRIVATE -Werror=unused-variable)
+apply_error_flags(upstream)
 
 target_include_directories(upstream SYSTEM PUBLIC
   $<TARGET_PROPERTY:systemlib,INTERFACE_INCLUDE_DIRECTORIES>
@@ -29,7 +37,7 @@
 
 add_library(consumer consumer.cpp)
 target_link_libraries(consumer upstream config_specific)
-target_compile_options(consumer PRIVATE -Werror=unused-variable)
+apply_error_flags(consumer)
 
 add_library(iface IMPORTED INTERFACE)
 set_property(TARGET iface PROPERTY INTERFACE_INCLUDE_DIRECTORIES
@@ -38,21 +46,21 @@
 
 add_library(imported_consumer imported_consumer.cpp)
 target_link_libraries(imported_consumer iface)
-target_compile_options(imported_consumer PRIVATE -Werror=unused-variable)
+apply_error_flags(imported_consumer)
 
 add_library(imported_consumer2 imported_consumer.cpp)
 target_link_libraries(imported_consumer2 imported_consumer)
-target_compile_options(imported_consumer2 PRIVATE -Werror=unused-variable)
+apply_error_flags(imported_consumer2)
 
 # add a target which has a relative system include
 add_library(somelib imported_consumer.cpp)
 target_include_directories(somelib SYSTEM PUBLIC "systemlib_header_only")
-target_compile_options(somelib PRIVATE -Werror=unused-variable)
+apply_error_flags(somelib)
 
 # add a target which consumes a relative system include
 add_library(otherlib upstream.cpp)
 target_link_libraries(otherlib PUBLIC somelib)
-target_compile_options(somelib PRIVATE -Werror=unused-variable)
+apply_error_flags(otherlib)
 
 macro(do_try_compile error_option)
   set(TC_ARGS
@@ -61,7 +69,11 @@
     LINK_LIBRARIES iface
   )
   if (${error_option} STREQUAL WITH_ERROR)
-    list(APPEND TC_ARGS COMPILE_DEFINITIONS -Werror=unused-variable)
+    if (MSVC)
+      list(APPEND TC_ARGS COMPILE_DEFINITIONS /we4101)
+    else ()
+      list(APPEND TC_ARGS COMPILE_DEFINITIONS -Werror=unused-variable)
+    endif ()
   endif()
   try_compile(${TC_ARGS})
 endmacro()
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp b/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp
index a13f08f..3da308d 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp
+++ b/Tests/IncludeDirectories/SystemIncludeDirectories/consumer.cpp
@@ -1,5 +1,6 @@
 
-#include "config_iface.h"
+#include <config_iface.h>
+
 #include "upstream.h"
 
 int consumer()
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/imported_consumer.cpp b/Tests/IncludeDirectories/SystemIncludeDirectories/imported_consumer.cpp
index 1dbe819..53759b1 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectories/imported_consumer.cpp
+++ b/Tests/IncludeDirectories/SystemIncludeDirectories/imported_consumer.cpp
@@ -1,5 +1,5 @@
 
-#include "systemlib.h"
+#include <systemlib.h>
 
 int main()
 {
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectories/upstream.h b/Tests/IncludeDirectories/SystemIncludeDirectories/upstream.h
index a670c2a..3daf69e 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectories/upstream.h
+++ b/Tests/IncludeDirectories/SystemIncludeDirectories/upstream.h
@@ -2,7 +2,7 @@
 #ifndef UPSTREAM_H
 #define UPSTREAM_H
 
-#include "systemlib.h"
+#include <systemlib.h>
 
 #ifdef _WIN32
 __declspec(dllexport)
diff --git a/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/CMakeLists.txt b/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/CMakeLists.txt
index 70dfa01..5d58633 100644
--- a/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/CMakeLists.txt
+++ b/Tests/IncludeDirectories/SystemIncludeDirectoriesPerLang/CMakeLists.txt
@@ -7,14 +7,14 @@
   INTERFACE_INCLUDE_DIRECTORIES "$<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_SOURCE_DIR}>"
   INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "$<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_SOURCE_DIR}>"
 )
-target_compile_options(c_interface INTERFACE "$<$<COMPILE_LANG_AND_ID:C,GNU,Clang>:-Werror=unused-variable>")
+target_compile_options(c_interface INTERFACE "$<$<COMPILE_LANG_AND_ID:C,GNU,Clang>:-Werror=unused-variable>;$<$<COMPILE_LANG_AND_ID:C,MSVC>:/we4101>")
 
 add_library(cxx_interface INTERFACE)
 set_target_properties(cxx_interface PROPERTIES
   INTERFACE_INCLUDE_DIRECTORIES "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_system_include>"
   INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_system_include>"
 )
-target_compile_options(cxx_interface INTERFACE "$<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>:-Werror=unused-variable>")
+target_compile_options(cxx_interface INTERFACE "$<$<COMPILE_LANG_AND_ID:CXX,GNU,Clang>:-Werror=unused-variable>;$<$<COMPILE_LANG_AND_ID:C,MSVC>:/we4101>")
 
 # The C header must come before the C++ header for this test to smoke out the
 # failure. The order of sources is how CMake determines the include cache
diff --git a/Tests/InstallMode/CMakeLists.txt b/Tests/InstallMode/CMakeLists.txt
new file mode 100644
index 0000000..96c83a0
--- /dev/null
+++ b/Tests/InstallMode/CMakeLists.txt
@@ -0,0 +1,124 @@
+cmake_minimum_required(VERSION 3.20.0)
+
+project(superpro LANGUAGES NONE)
+
+add_subdirectory(superpro)
+
+include(Subproject.cmake)
+add_subproject(static_lib   DIR subpro_a_static_lib)
+add_subproject(shared_lib   DIR subpro_b_shared_lib)
+add_subproject(nested_lib   DIR subpro_c_nested_lib NO_INSTALL)
+add_subproject(executable   DIR subpro_d_executable
+  DEPENDS
+    static_lib
+    shared_lib
+    nested_lib
+)
+
+include(CTest)
+if(BUILD_TESTING)
+  enable_language(CXX)  # required by GNUInstallDirs
+  include(GNUInstallDirs)
+
+  macro(testme _name _path _symlink)
+    add_test(
+      NAME "${_name}"
+      WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+      COMMAND
+        "${CMAKE_COMMAND}"
+        "-DFILE_PATH=${CMAKE_INSTALL_PREFIX}/${_path}"
+        "-DEXPECT_SYMLINK:BOOL=${_symlink}"
+        "-DEXPECT_ABSOLUTE:BOOL=${ARGN}"
+        "-P" "${CMAKE_SOURCE_DIR}/Test.cmake"
+    )
+  endmacro()
+
+  set(_mode $ENV{CMAKE_INSTALL_MODE})
+  if(NOT "${_mode}" OR "${_mode}" STREQUAL "COPY")
+    set(expect_symlink NO)
+  elseif("${_mode}" MATCHES "(REL_)?SYMLINK(_OR_COPY)?")
+    set(expect_symlink YES)
+    set(expect_absolute NO)
+  elseif("${_mode}" MATCHES "ABS_SYMLINK(_OR_COPY)?")
+    set(expect_symlink YES)
+    set(expect_absolute YES)
+  endif()
+
+  # toplevel project should respect CMAKE_INSTALL_MODE
+
+  testme(superproj_file_copy
+    "file_copy.txt" NO)
+  testme(superproj_file_copy_file
+    "file_copy_file.txt" NO)
+  testme(superproj_file_install
+    "file_install.txt"
+    ${expect_symlink}
+    ${expect_absolute})
+  testme(superproj_file_create_link_symbolic
+    "file_create_link_symbolic.txt" YES YES)
+
+  # subprojects should receive and respect CMAKE_INSTALL_MODE too
+
+  testme(subpro_a_static_lib_header
+    "${CMAKE_INSTALL_INCLUDEDIR}/static_lib.h"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+  testme(subpro_a_static_lib_libfile
+    "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}the_static_lib${CMAKE_STATIC_LIBRARY_SUFFIX}"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+
+  testme(subpro_b_shared_lib_header
+    "${CMAKE_INSTALL_INCLUDEDIR}/shared_lib.h"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+
+  if(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG AND
+      "${CMAKE_CXX_CREATE_SHARED_MODULE}" MATCHES "SONAME_FLAG")
+    # due to semver, this is always a link
+    testme(subpro_b_shared_lib_libfile
+      "${CMAKE_INSTALL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}the_shared_lib${CMAKE_SHARED_LIBRARY_SUFFIX}"
+      YES
+      ${expect_absolute}
+    )
+    # this is the actual shared lib, so should follow CMAKE_INSTALL_MODE rules
+    testme(subpro_b_shared_lib_libfile_versuffix
+      "${CMAKE_INSTALL_LIBDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}the_shared_lib${CMAKE_SHARED_LIBRARY_SUFFIX}.2.3.4"
+      ${expect_symlink}
+      ${expect_absolute}
+    )
+  endif()
+
+  testme(subpro_d_executable_exefile
+    "${CMAKE_INSTALL_BINDIR}/the_executable${CMAKE_EXECUTABLE_SUFFIX}"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+
+  # nested subprojects should receive and respect CMAKE_INSTALL_MODE too
+
+  testme(subsubpro_c1_header
+    "${CMAKE_INSTALL_INCLUDEDIR}/c1_lib.h"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+  testme(subsubpro_c1_libfile
+    "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}the_c1_lib${CMAKE_STATIC_LIBRARY_SUFFIX}"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+
+  testme(subsubpro_c2_header
+    "${CMAKE_INSTALL_INCLUDEDIR}/c2_lib.h"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+  testme(subsubpro_c2_libfile
+    "${CMAKE_INSTALL_LIBDIR}/${CMAKE_STATIC_LIBRARY_PREFIX}the_c2_lib${CMAKE_STATIC_LIBRARY_SUFFIX}"
+    ${expect_symlink}
+    ${expect_absolute}
+  )
+endif()
diff --git a/Tests/InstallMode/README.txt b/Tests/InstallMode/README.txt
new file mode 100644
index 0000000..a4316eb
--- /dev/null
+++ b/Tests/InstallMode/README.txt
@@ -0,0 +1,43 @@
+This is an example superbuild project to demonstrate the use of the
+CMAKE_INSTALL_MODE environment variable on.
+
+The project hierarchy is like (B = Builds / D = Link Dependency):
+
++---------------------------------------------------------------------+
+|                           Superbuild (Top)                          |
++---------------------------------------------------------------------+
+        |                 |                 |                 |
+        |                 |                 |                 |
+       (B)               (B)               (B)               (B)
+        |                 |                 |                 |
+        v                 v                 v                 v
++---------------+ +---------------+ +---------------+ +---------------+
+| A: Static Lib | | B: Shared Lib | | C: Nested     | | D: Executable |
+|    Project    | |    Project    | |    Superbuild | |    Project    |
++---------------+ +---------------+ +---------------+ +---------------+
+        ^               ^               |       |        |   |   |
+        |               |              (B)     (B)       |   |   |
+        |               |               |       |        |   |   |
+        |               |               v       |        |   |   |
+        |               |  +----------------+   |        |   |   |
+        |               |  | C1: Static Lib |   |        |   |   |
+        |               |  |     Project    |   |       (D) (D) (D)
+        |               |  +----------------+   |        |   |   |
+        |               |       ^               |        |   |   |
+        |               |       |               v        |   |   |
+        |               |      (D) +----------------+    |   |   |
+        |               |       |  | C2: Static Lib |<---+   |   |
+        |               |       +--|     Project    |        |   |
+        |               |          +----------------+        |   |
+        |               |                                    |   |
+        |               +------------------------------------+   |
+        |                                                        |
+        +--------------------------------------------------------+
+
+The superbuild system is built on top of ExternalProject_Add().
+
+NOTE that the subprojects will configure, build and install
+during the build phase ('make') of the top-level project.
+There is no install target in the top-level project!
+The CMAKE_INSTALL_PREFIX is therefore populated during the build
+phase already.
diff --git a/Tests/InstallMode/Subproject.cmake b/Tests/InstallMode/Subproject.cmake
new file mode 100644
index 0000000..826e61e
--- /dev/null
+++ b/Tests/InstallMode/Subproject.cmake
@@ -0,0 +1,73 @@
+include(ExternalProject)
+
+# add_subproject(<name> [NO_INSTALL] [DIR <dirname>] [DEPENDS [subpro_dep ...]])
+function(add_subproject _name)
+  cmake_parse_arguments(_arg "NO_INSTALL" "DIR" "DEPENDS" ${ARGN})
+
+  if(_arg_UNPARSED_ARGUMENTS)
+    message(FATAL_ERROR "There are unparsed arguments")
+  endif()
+
+  set(_maybe_NO_INSTALL)
+  if(_arg_NO_INSTALL)
+    set(_maybe_NO_INSTALL INSTALL_COMMAND "")
+  endif()
+
+  if(CMAKE_GENERATOR MATCHES "Ninja Multi-Config")
+    # Replace list separator before passing on to ExternalProject_Add
+    string(REPLACE ";" "|" _CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}")
+    string(REPLACE ";" "|" _CROSS_CONFIGS "${CMAKE_CROSS_CONFIGS}")
+    string(REPLACE ";" "|" _DEFAULT_CONFIGS "${CMAKE_DEFAULT_CONFIGS}")
+
+    set(_maybe_NINJA_MULTICONFIG_ARGS
+      "-DCMAKE_CONFIGURATION_TYPES:STRINGS=${_CONFIGURATION_TYPES}"
+      "-DCMAKE_CROSS_CONFIGS:STRINGS=${_CROSS_CONFIGS}"
+      "-DCMAKE_DEFAULT_BUILD_TYPE:STRING=${CMAKE_DEFAULT_BUILD_TYPE}"
+      "-DCMAKE_DEFAULT_CONFIGS:STRINGS=${_DEFAULT_CONFIGS}"
+    )
+  endif()
+
+  ExternalProject_Add("${_name}"
+    DOWNLOAD_COMMAND      ""
+    UPDATE_COMMAND        ""
+    UPDATE_DISCONNECTED   ON
+
+    "${_maybe_NO_INSTALL}"
+
+    BUILD_ALWAYS          ON
+
+    LOG_DOWNLOAD          OFF
+    LOG_UPDATE            OFF
+    LOG_PATCH             OFF
+    LOG_CONFIGURE         OFF
+    LOG_BUILD             OFF
+    LOG_INSTALL           OFF
+
+    SOURCE_DIR            "${PROJECT_SOURCE_DIR}/${_arg_DIR}"
+
+    # Private build directory per subproject
+    BINARY_DIR            "${PROJECT_BINARY_DIR}/subproject/${_arg_DIR}"
+
+    # Common install directory, populated immediately
+    # during build (during build - not install - of superproject)
+    INSTALL_DIR           "${CMAKE_INSTALL_PREFIX}"
+
+    DEPENDS
+      ${_arg_DEPENDS}
+
+    LIST_SEPARATOR "|"
+    CMAKE_ARGS
+      "-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>"
+
+      # We can rely on ExternalProject to pick the right
+      # generator (and architecture/toolset where applicable),
+      # however, we need to explicitly inherit other parent
+      # project's build settings.
+      "-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
+      "${_maybe_NINJA_MULTICONFIG_ARGS}"
+
+      # Subproject progress reports clutter up the output, disable
+      "-DCMAKE_TARGET_MESSAGES:BOOL=OFF"
+      "-DCMAKE_RULE_MESSAGES:BOOL=OFF"
+  )
+endfunction()
diff --git a/Tests/InstallMode/Test.cmake b/Tests/InstallMode/Test.cmake
new file mode 100644
index 0000000..46c8fa1
--- /dev/null
+++ b/Tests/InstallMode/Test.cmake
@@ -0,0 +1,38 @@
+message("Testing...")
+message("FILE_PATH =       ${FILE_PATH}")
+message("EXPECT_SYMLINK =  ${EXPECT_SYMLINK}")
+message("EXPECT_ABSOLUTE = ${EXPECT_ABSOLUTE}")
+
+if(NOT DEFINED FILE_PATH)
+  message(FATAL_ERROR "FILE_PATH variable must be defined")
+endif()
+
+if(NOT EXISTS "${FILE_PATH}")
+  message(FATAL_ERROR "File ${FILE_PATH} does not exist")
+endif()
+
+if(NOT DEFINED EXPECT_SYMLINK)
+  message(FATAL_ERROR "EXPECT_SYMLINK must be defined")
+endif()
+
+if(EXPECT_SYMLINK)
+  if(NOT DEFINED EXPECT_ABSOLUTE)
+    message(FATAL_ERROR "EXPECT_ABSOLUTE variable must be defined")
+  endif()
+
+  if(NOT IS_SYMLINK "${FILE_PATH}")
+    message(FATAL_ERROR "${FILE_PATH} must be a symlink")
+  endif()
+
+  file(READ_SYMLINK "${FILE_PATH}" TARGET_PATH)
+
+  if(EXPECT_ABSOLUTE AND NOT IS_ABSOLUTE "${TARGET_PATH}")
+    message(FATAL_ERROR "${FILE_PATH} must be an absolute symlink")
+  elseif(NOT EXPECT_ABSOLUTE AND IS_ABSOLUTE "${TARGET_PATH}")
+    message(FATAL_ERROR "${FILE_PATH} must be a relative symlink")
+  endif()
+else()
+  if(IS_SYMLINK "${FILE_PATH}")
+    message(FATAL_ERROR "${FILE_PATH} must NOT be a symlink")
+  endif()
+endif()
diff --git a/Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt b/Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt
new file mode 100644
index 0000000..7cd32cc
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/CMakeLists.txt
@@ -0,0 +1,60 @@
+# This CMakeLists.txt is part of the subproject A (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(static_lib_project VERSION 1.2.3 LANGUAGES CXX)
+
+include(GNUInstallDirs)
+
+add_library(the_static_lib STATIC
+  "include/static_lib.h"
+  "src/static_lib.cpp"
+)
+
+target_include_directories(the_static_lib PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+  DIRECTORY   "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+  DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+  TARGETS
+    the_static_lib
+  EXPORT main
+  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+  "cmake/PackageConfig.cmake.in"
+  "${PROJECT_NAME}Config.cmake"
+  INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+  PATH_VARS
+    CMAKE_INSTALL_INCLUDEDIR
+    CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+  VERSION       "${PROJECT_VERSION}"
+  COMPATIBILITY SameMajorVersion
+)
+
+install(
+  EXPORT      main
+  FILE        "${PROJECT_NAME}Targets.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+  FILES
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..0fe72c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,8 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR  "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR      "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
diff --git a/Tests/InstallMode/subpro_a_static_lib/include/static_lib.h b/Tests/InstallMode/subpro_a_static_lib/include/static_lib.h
new file mode 100644
index 0000000..bd82d2e
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/include/static_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void static_hello();
diff --git a/Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp b/Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp
new file mode 100644
index 0000000..fe1cd85
--- /dev/null
+++ b/Tests/InstallMode/subpro_a_static_lib/src/static_lib.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+#include <static_lib.h>
+
+using namespace std;
+
+void static_hello()
+{
+  cout << "Hello from static_lib" << endl;
+}
diff --git a/Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt b/Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt
new file mode 100644
index 0000000..b3d9cb2
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/CMakeLists.txt
@@ -0,0 +1,75 @@
+# This CMakeLists.txt is part of the subproject B (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(shared_lib_project VERSION 2.3.4 LANGUAGES CXX)
+
+include(GNUInstallDirs)
+include(GenerateExportHeader)
+
+add_library(the_shared_lib SHARED
+  "include/shared_lib.h"
+  "src/shared_lib.cpp"
+)
+
+generate_export_header(the_shared_lib
+  BASE_NAME shared_lib
+  EXPORT_FILE_NAME include/shared_lib_export.h
+)
+
+set_target_properties(the_shared_lib
+  PROPERTIES
+    VERSION   "${PROJECT_VERSION}"
+    SOVERSION "${PROJECT_VERSION}"
+)
+
+target_include_directories(the_shared_lib PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+  DIRECTORY
+    "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+    "${CMAKE_CURRENT_BINARY_DIR}/include/"
+  DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+  TARGETS
+    the_shared_lib
+  EXPORT main
+  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+  "cmake/PackageConfig.cmake.in"
+  "${PROJECT_NAME}Config.cmake"
+  INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+  PATH_VARS
+    CMAKE_INSTALL_INCLUDEDIR
+    CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+  VERSION       "${PROJECT_VERSION}"
+  COMPATIBILITY SameMajorVersion
+)
+
+install(
+  EXPORT      main
+  FILE        "${PROJECT_NAME}Targets.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+  FILES
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..0fe72c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,8 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR  "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR      "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
diff --git a/Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h b/Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h
new file mode 100644
index 0000000..550b2b4
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/include/shared_lib.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <shared_lib_export.h>
+
+void SHARED_LIB_EXPORT shared_hello();
diff --git a/Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp b/Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp
new file mode 100644
index 0000000..2820d5d
--- /dev/null
+++ b/Tests/InstallMode/subpro_b_shared_lib/src/shared_lib.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+#include <shared_lib.h>
+
+using namespace std;
+
+void shared_hello()
+{
+  cout << "Hello from shared_lib" << endl;
+}
diff --git a/Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt b/Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt
new file mode 100644
index 0000000..e397c02
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.20.0)
+
+project(subpro_c_nested_lib LANGUAGES NONE)
+
+include(../Subproject.cmake)
+add_subproject(c1_lib   DIR subsubpro_c1_lib)
+add_subproject(c2_lib   DIR subsubpro_c2_lib
+  DEPENDS
+    c1_lib
+)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt
new file mode 100644
index 0000000..89f3755
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/CMakeLists.txt
@@ -0,0 +1,61 @@
+# This CMakeLists.txt is a nested subproject of the
+# subproject C (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(c1_lib_project VERSION 1.2.3 LANGUAGES CXX)
+
+include(GNUInstallDirs)
+
+add_library(the_c1_lib STATIC
+  "include/c1_lib.h"
+  "src/c1_lib.cpp"
+)
+
+target_include_directories(the_c1_lib PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+  DIRECTORY   "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+  DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+  TARGETS
+    the_c1_lib
+  EXPORT main
+  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+  "cmake/PackageConfig.cmake.in"
+  "${PROJECT_NAME}Config.cmake"
+  INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+  PATH_VARS
+    CMAKE_INSTALL_INCLUDEDIR
+    CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+  VERSION       "${PROJECT_VERSION}"
+  COMPATIBILITY SameMajorVersion
+)
+
+install(
+  EXPORT      main
+  FILE        "${PROJECT_NAME}Targets.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+  FILES
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..0fe72c9
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,8 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR  "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR      "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h
new file mode 100644
index 0000000..245f9d4
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/include/c1_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void c1_hello();
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp
new file mode 100644
index 0000000..c405056
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c1_lib/src/c1_lib.cpp
@@ -0,0 +1,10 @@
+#include <iostream>
+
+#include <c1_lib.h>
+
+using namespace std;
+
+void c1_hello()
+{
+  cout << "Hello from c1_lib" << endl;
+}
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt
new file mode 100644
index 0000000..7580c77
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/CMakeLists.txt
@@ -0,0 +1,71 @@
+# This CMakeLists.txt is a nested subproject of the
+# subproject C (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(c2_lib_project VERSION 1.2.3 LANGUAGES CXX)
+
+find_package(c1_lib_project REQUIRED)
+
+include(GNUInstallDirs)
+
+add_library(the_c2_lib STATIC
+  "include/c2_lib.h"
+  "src/c2_lib.cpp"
+)
+
+target_link_libraries(the_c2_lib
+  PUBLIC
+    the_c1_lib
+)
+
+# This is to fix an issue on AIX/GCC (see commit 4fc47424)
+set_property(TARGET the_c2_lib PROPERTY NO_SYSTEM_FROM_IMPORTED 1)
+
+target_include_directories(the_c2_lib PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+)
+
+install(
+  DIRECTORY   "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+  DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
+)
+
+install(
+  TARGETS
+    the_c2_lib
+  EXPORT main
+  ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+)
+
+set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
+
+include(CMakePackageConfigHelpers)
+
+configure_package_config_file(
+  "cmake/PackageConfig.cmake.in"
+  "${PROJECT_NAME}Config.cmake"
+  INSTALL_DESTINATION "${INSTALL_CMAKE_DIR}"
+  PATH_VARS
+    CMAKE_INSTALL_INCLUDEDIR
+    CMAKE_INSTALL_LIBDIR
+)
+
+write_basic_package_version_file("${PROJECT_NAME}Version.cmake"
+  VERSION       "${PROJECT_VERSION}"
+  COMPATIBILITY SameMajorVersion
+)
+
+install(
+  EXPORT      main
+  FILE        "${PROJECT_NAME}Targets.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
+
+install(
+  FILES
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
+    "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Version.cmake"
+  DESTINATION "${INSTALL_CMAKE_DIR}"
+)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in
new file mode 100644
index 0000000..45a177a
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/cmake/PackageConfig.cmake.in
@@ -0,0 +1,11 @@
+set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@)
+
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
+
+set_and_check(@PROJECT_NAME@_INCLUDE_DIR  "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
+set_and_check(@PROJECT_NAME@_LIB_DIR      "@PACKAGE_CMAKE_INSTALL_LIBDIR@")
+
+include(CMakeFindDependencyMacro)
+find_dependency(c1_lib_project REQUIRED)
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h
new file mode 100644
index 0000000..5056814
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/include/c2_lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void c2_hello();
diff --git a/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp
new file mode 100644
index 0000000..cd2c932
--- /dev/null
+++ b/Tests/InstallMode/subpro_c_nested_lib/subsubpro_c2_lib/src/c2_lib.cpp
@@ -0,0 +1,12 @@
+#include <iostream>
+
+#include <c1_lib.h>
+#include <c2_lib.h>
+
+using namespace std;
+
+void c2_hello()
+{
+  cout << "Hello from c2_lib and also..." << endl;
+  c1_hello();
+}
diff --git a/Tests/InstallMode/subpro_d_executable/CMakeLists.txt b/Tests/InstallMode/subpro_d_executable/CMakeLists.txt
new file mode 100644
index 0000000..60189e2
--- /dev/null
+++ b/Tests/InstallMode/subpro_d_executable/CMakeLists.txt
@@ -0,0 +1,27 @@
+# This CMakeLists.txt is part of the subproject B (ExternalProject_Add).
+
+cmake_minimum_required(VERSION 3.20)
+project(subpro_d_executable LANGUAGES CXX)
+
+find_package(static_lib_project REQUIRED)
+find_package(shared_lib_project REQUIRED)
+find_package(c2_lib_project REQUIRED)
+
+add_executable(the_executable
+  "src/main.cpp"
+)
+
+target_link_libraries(the_executable PRIVATE
+  the_static_lib
+  the_shared_lib
+  the_c2_lib
+)
+
+# This is to fix an issue on AIX/GCC (see commit 4fc47424)
+set_property(TARGET the_executable PROPERTY NO_SYSTEM_FROM_IMPORTED 1)
+
+install(
+  TARGETS
+    the_executable
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
+)
diff --git a/Tests/InstallMode/subpro_d_executable/src/main.cpp b/Tests/InstallMode/subpro_d_executable/src/main.cpp
new file mode 100644
index 0000000..ec12004
--- /dev/null
+++ b/Tests/InstallMode/subpro_d_executable/src/main.cpp
@@ -0,0 +1,13 @@
+#include <cstdlib>
+
+#include <c2_lib.h>
+#include <shared_lib.h>
+#include <static_lib.h>
+
+int main()
+{
+  static_hello();
+  shared_hello();
+  c2_hello();
+  return EXIT_SUCCESS;
+}
diff --git a/Tests/InstallMode/superpro/CMakeLists.txt b/Tests/InstallMode/superpro/CMakeLists.txt
new file mode 100644
index 0000000..ae4d25c
--- /dev/null
+++ b/Tests/InstallMode/superpro/CMakeLists.txt
@@ -0,0 +1,29 @@
+# This CMakeLists.txt is part of the superproject (add_subdirectory).
+
+# Below file transfers are executed at configuration time!
+
+file(
+  COPY
+    "file_copy.txt"
+  DESTINATION
+    "${CMAKE_INSTALL_PREFIX}"
+)
+
+file(COPY_FILE
+  "${CMAKE_CURRENT_SOURCE_DIR}/file_copy_file.txt"
+  "${CMAKE_INSTALL_PREFIX}/file_copy_file.txt"
+)
+
+file(
+  INSTALL
+    "file_install.txt"
+  DESTINATION
+    "${CMAKE_INSTALL_PREFIX}"
+)
+
+file(
+  CREATE_LINK
+    "${CMAKE_CURRENT_SOURCE_DIR}/file_create_link_symbolic.txt"
+    "${CMAKE_INSTALL_PREFIX}/file_create_link_symbolic.txt"
+  SYMBOLIC
+)
diff --git a/Tests/InstallMode/superpro/file_copy.txt b/Tests/InstallMode/superpro/file_copy.txt
new file mode 100644
index 0000000..aacbb96
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_copy.txt
@@ -0,0 +1 @@
+This file should always be copied into CMAKE_INSTALL_PREFIX.
diff --git a/Tests/InstallMode/superpro/file_copy_file.txt b/Tests/InstallMode/superpro/file_copy_file.txt
new file mode 100644
index 0000000..aacbb96
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_copy_file.txt
@@ -0,0 +1 @@
+This file should always be copied into CMAKE_INSTALL_PREFIX.
diff --git a/Tests/InstallMode/superpro/file_create_link_symbolic.txt b/Tests/InstallMode/superpro/file_create_link_symbolic.txt
new file mode 100644
index 0000000..16a481b
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_create_link_symbolic.txt
@@ -0,0 +1,2 @@
+This file should always be installed into CMAKE_INSTALL_PREFIX
+as a symbolic link to the original file.
diff --git a/Tests/InstallMode/superpro/file_install.txt b/Tests/InstallMode/superpro/file_install.txt
new file mode 100644
index 0000000..eac9782
--- /dev/null
+++ b/Tests/InstallMode/superpro/file_install.txt
@@ -0,0 +1,6 @@
+This file should be placed in CMAKE_INSTALL_PREFIX
+as a copy if the CMAKE_INSTALL_MODE environment variable
+is unset or equals "COPY".
+If the variable's value is "SYMLINK" or "SYMLINK_OR_COPY",
+the CMAKE_INSTALL_PREFIX should rather receive a symbolic
+link to this file.
diff --git a/Tests/MSManifest/Subdir/CMakeLists.txt b/Tests/MSManifest/Subdir/CMakeLists.txt
index 8664572..3b4fccc 100644
--- a/Tests/MSManifest/Subdir/CMakeLists.txt
+++ b/Tests/MSManifest/Subdir/CMakeLists.txt
@@ -7,4 +7,8 @@
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
   add_executable(MSManifestNone main.c)
   set_property(TARGET MSManifestNone PROPERTY LINK_FLAGS "/MANIFEST:NO")
+elseif(WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang")
+  add_test(NAME MSManifest.Single COMMAND
+    ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSManifest>
+    -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
 endif()
diff --git a/Tests/MSManifest/Subdir2/CMakeLists.txt b/Tests/MSManifest/Subdir2/CMakeLists.txt
index 19d8de7..0d960ad 100644
--- a/Tests/MSManifest/Subdir2/CMakeLists.txt
+++ b/Tests/MSManifest/Subdir2/CMakeLists.txt
@@ -6,7 +6,7 @@
  ${CMAKE_CURRENT_BINARY_DIR}/test_manifest2.manifest
  ${CMAKE_CURRENT_BINARY_DIR}/test_manifest3.manifest)
 
-if(MSVC AND NOT MSVC_VERSION LESS 1400)
+if((MSVC AND NOT MSVC_VERSION LESS 1400) OR (WIN32 AND CMAKE_C_COMPILER_ID MATCHES "Clang") )
   add_test(NAME MSManifest.Multiple COMMAND
     ${CMAKE_COMMAND} -Dexe=$<TARGET_FILE:MSMultipleManifest>
     -P ${CMAKE_CURRENT_SOURCE_DIR}/check.cmake)
diff --git a/Tests/Qt6Autogen/CMakeLists.txt b/Tests/Qt6Autogen/CMakeLists.txt
new file mode 100644
index 0000000..a603b50
--- /dev/null
+++ b/Tests/Qt6Autogen/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Set Qt test version and include the Autogen test macros
+set(QT_TEST_VERSION 6)
+include("../QtAutogen/TestMacros.cmake")
+
+# Common tests
+include("../QtAutogen/Tests.cmake")
+
+set(TEST_BUILD_DIRS "${TEST_BUILD_DIRS}" PARENT_SCOPE)
diff --git a/Tests/QtAutogen/AutogenCoreTest.cmake b/Tests/QtAutogen/AutogenCoreTest.cmake
index 5803859..e62a6aa 100644
--- a/Tests/QtAutogen/AutogenCoreTest.cmake
+++ b/Tests/QtAutogen/AutogenCoreTest.cmake
@@ -21,27 +21,27 @@
     qt4_generate_moc(${ARGN})
   endmacro()
 
-elseif(QT_TEST_VERSION EQUAL 5)
+elseif(QT_TEST_VERSION GREATER_EQUAL 5)
 
-  find_package(Qt5Core REQUIRED)
+  find_package(Qt${QT_TEST_VERSION}Core REQUIRED)
 
-  set(QT_QTCORE_TARGET Qt5::Core)
-  set(QT_LIBRARIES Qt5::Core)
+  set(QT_QTCORE_TARGET Qt${QT_TEST_VERSION}::Core)
+  set(QT_LIBRARIES Qt${QT_TEST_VERSION}::Core)
 
   # Include directories
-  include_directories(${Qt5Core_INCLUDE_DIRS})
+  include_directories(${Qt${QT_TEST_VERSION}Core_INCLUDE_DIRS})
 
   # Definitions
-  if(Qt5_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC)
+  if(Qt${QT_TEST_VERSION}_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC)
     add_definitions(${CMAKE_CXX_COMPILE_OPTIONS_PIC})
   endif()
 
   # Qt macros
   macro(qtx_wrap_cpp)
-    qt5_wrap_cpp(${ARGN})
+    cmake_language(CALL qt${QT_TEST_VERSION}_wrap_cpp ${ARGN})
   endmacro()
   macro(qtx_generate_moc)
-    qt5_generate_moc(${ARGN})
+    cmake_language(CALL qt${QT_TEST_VERSION}_generate_moc ${ARGN})
   endmacro()
 
 else()
diff --git a/Tests/QtAutogen/AutogenGuiTest.cmake b/Tests/QtAutogen/AutogenGuiTest.cmake
index b76d341..e5eb751 100644
--- a/Tests/QtAutogen/AutogenGuiTest.cmake
+++ b/Tests/QtAutogen/AutogenGuiTest.cmake
@@ -21,27 +21,27 @@
     qt4_generate_moc(${ARGN})
   endmacro()
 
-elseif(QT_TEST_VERSION EQUAL 5)
+elseif(QT_TEST_VERSION GREATER_EQUAL 5)
 
-  find_package(Qt5Widgets REQUIRED)
+  find_package(Qt${QT_TEST_VERSION}Widgets REQUIRED)
 
-  set(QT_QTCORE_TARGET Qt5::Core)
-  set(QT_LIBRARIES Qt5::Widgets)
+  set(QT_QTCORE_TARGET Qt${QT_TEST_VERSION}::Core)
+  set(QT_LIBRARIES Qt${QT_TEST_VERSION}::Widgets)
 
   # Include directories
-  include_directories(${Qt5Widgets_INCLUDE_DIRS})
+  include_directories(${Qt${QT_TEST_VERSION}Widgets_INCLUDE_DIRS})
 
   # Definitions
-  if(Qt5_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC)
+  if(Qt${QT_TEST_VERSION}_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC)
     add_definitions(${CMAKE_CXX_COMPILE_OPTIONS_PIC})
   endif()
 
   # Qt macros
   macro(qtx_wrap_cpp)
-    qt5_wrap_cpp(${ARGN})
+    cmake_language(CALL qt${QT_TEST_VERSION}_wrap_cpp ${ARGN})
   endmacro()
   macro(qtx_generate_moc)
-    qt5_generate_moc(${ARGN})
+    cmake_language(CALL qt${QT_TEST_VERSION}_generate_moc ${ARGN})
   endmacro()
 
 else()
diff --git a/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt b/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt
index 17855ff..cac680f 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt
+++ b/Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.11)
+cmake_minimum_required(VERSION 3.16)
 project(AutogenOriginDependsOff)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/AutogenOriginDependsOn/CMakeLists.txt b/Tests/QtAutogen/AutogenOriginDependsOn/CMakeLists.txt
index 5aabe0e..37ce90b 100644
--- a/Tests/QtAutogen/AutogenOriginDependsOn/CMakeLists.txt
+++ b/Tests/QtAutogen/AutogenOriginDependsOn/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(AutogenOriginDependsOn)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt b/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt
index 492b5db..cf3f155 100644
--- a/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt
+++ b/Tests/QtAutogen/AutogenTargetDepends/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(AutogenTargetDepends)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/Complex/CMakeLists.txt b/Tests/QtAutogen/Complex/CMakeLists.txt
index d9fdf5c..8a9a5d2 100644
--- a/Tests/QtAutogen/Complex/CMakeLists.txt
+++ b/Tests/QtAutogen/Complex/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(Complex)
 include("../AutogenGuiTest.cmake")
 
@@ -16,6 +16,8 @@
 # Pass Qt compiler features to targets that don't link against Qt
 target_compile_features(codeeditorLib PRIVATE ${QT_COMPILE_FEATURES})
 target_compile_features(privateSlot PRIVATE ${QT_COMPILE_FEATURES})
+target_link_libraries(codeeditorLib PRIVATE ${QT_LIBRARIES})
+target_link_libraries(privateSlot PRIVATE ${QT_LIBRARIES})
 
 configure_file(generated_resource.qrc.in generated_resource.qrc @ONLY)
 add_custom_command(
diff --git a/Tests/QtAutogen/Complex/codeeditor.cpp b/Tests/QtAutogen/Complex/codeeditor.cpp
index 80d6a55..9c9dbc3 100644
--- a/Tests/QtAutogen/Complex/codeeditor.cpp
+++ b/Tests/QtAutogen/Complex/codeeditor.cpp
@@ -70,7 +70,11 @@
     ++digits;
   }
 
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
+  int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
+#else
   int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;
+#endif
 
   return space;
 }
diff --git a/Tests/QtAutogen/GlobalAutogenTarget/CMakeLists.txt b/Tests/QtAutogen/GlobalAutogenTarget/CMakeLists.txt
index e95c626..5c70e43 100644
--- a/Tests/QtAutogen/GlobalAutogenTarget/CMakeLists.txt
+++ b/Tests/QtAutogen/GlobalAutogenTarget/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.16)
 project(GlobalAutogenTarget)
 include("../AutogenCoreTest.cmake")
 
@@ -74,6 +74,7 @@
         -T "${CMAKE_GENERATOR_TOOLSET}"
         "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
         "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+        "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
         "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
     WORKING_DIRECTORY "${GAT_BDIR}"
     OUTPUT_VARIABLE output
diff --git a/Tests/QtAutogen/GlobalAutogenTarget/GAT/CMakeLists.txt b/Tests/QtAutogen/GlobalAutogenTarget/GAT/CMakeLists.txt
index 3925197..0e3df6f 100644
--- a/Tests/QtAutogen/GlobalAutogenTarget/GAT/CMakeLists.txt
+++ b/Tests/QtAutogen/GlobalAutogenTarget/GAT/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.16)
 project(GAT)
 include("../../AutogenCoreTest.cmake")
 
@@ -9,7 +9,7 @@
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTOUIC ON)
 set(CMAKE_AUTORCC ON)
-# Disable ORIGN_DEPENDS and enable AUTOGEN global targets
+# Disable ORIGIN_DEPENDS and enable AUTOGEN global targets
 set(CMAKE_AUTOGEN_ORIGIN_DEPENDS OFF)
 set(CMAKE_GLOBAL_AUTOGEN_TARGET ON)
 set(CMAKE_GLOBAL_AUTORCC_TARGET ON)
diff --git a/Tests/QtAutogen/MacOsFW/CMakeLists.txt b/Tests/QtAutogen/MacOsFW/CMakeLists.txt
index c08efc4..27c5f43 100644
--- a/Tests/QtAutogen/MacOsFW/CMakeLists.txt
+++ b/Tests/QtAutogen/MacOsFW/CMakeLists.txt
@@ -1,21 +1,23 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MacOsFW)
 include("../AutogenGuiTest.cmake")
 
-find_package(Qt5Test REQUIRED)
+find_package(Qt${QT_TEST_VERSION}Test REQUIRED)
 
-set(CMAKE_CXX_STANDARD 11)
+if(QT_TEST_VERSION EQUAL 5)
+  set(CMAKE_CXX_STANDARD 11)
+elseif(QT_TEST_VERSION EQUAL 6)
+  set(CMAKE_CXX_STANDARD 17)
+else()
+  message(FATAL_ERROR "Unsupported Qt version: ${QT_TEST_VERSION}")
+endif()
+
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/bin)
 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/lib)
 set(CMAKE_INSTALL_NAME_DIR ${CMAKE_BINARY_DIR}/output/lib)
 
-if(POLICY CMP0042) # in CMake 3.0.0+
-  set (CMAKE_MACOSX_RPATH OFF) # otherwise ON by default
-endif(POLICY CMP0042)
-
-if(POLICY CMP0068) # in CMake 3.9+
-  cmake_policy(SET CMP0068 NEW)
-endif(POLICY CMP0068)
+set (CMAKE_MACOSX_RPATH OFF) # otherwise ON by default
+cmake_policy(SET CMP0068 NEW)
 
 add_subdirectory(src)
 add_subdirectory(test)
diff --git a/Tests/QtAutogen/MacOsFW/src/CMakeLists.txt b/Tests/QtAutogen/MacOsFW/src/CMakeLists.txt
index a02be00..67e2c8a 100644
--- a/Tests/QtAutogen/MacOsFW/src/CMakeLists.txt
+++ b/Tests/QtAutogen/MacOsFW/src/CMakeLists.txt
@@ -9,7 +9,7 @@
 include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}
   ${CMAKE_CURRENT_BINARY_DIR}
-  ${Qt5Core_INCLUDE_DIRS}
+  ${Qt${QT_TEST_VERSION}Core_INCLUDE_DIRS}
 )
 
 add_library(macos_fw_lib SHARED
@@ -28,6 +28,4 @@
   POSITION_INDEPENDENT_CODE ON
   PUBLIC_HEADER "${MACOS_FW_LIB_HDRS}"
 )
-target_link_libraries(macos_fw_lib
-  Qt5::Core
-)
+target_link_libraries(macos_fw_lib PRIVATE ${QT_QTCORE_TARGET})
diff --git a/Tests/QtAutogen/MacOsFW/test/CMakeLists.txt b/Tests/QtAutogen/MacOsFW/test/CMakeLists.txt
index 521c184..8cb1bc5 100644
--- a/Tests/QtAutogen/MacOsFW/test/CMakeLists.txt
+++ b/Tests/QtAutogen/MacOsFW/test/CMakeLists.txt
@@ -4,16 +4,16 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/../src
 )
 include_directories(SYSTEM
-  ${Qt5Core_INCLUDE_DIRS}
-  ${Qt5Widgets_INCLUDE_DIRS}
+  ${Qt${QT_TEST_VERSION}Core_INCLUDE_DIRS}
+  ${Qt${QT_TEST_VERSION}Widgets_INCLUDE_DIRS}
 )
 
 set(testname AutomocMacosFWLib)
 add_executable(${testname} testMacosFWLib.cpp)
 set_target_properties(${testname} PROPERTIES AUTOMOC TRUE)
-target_link_libraries(${testname}
-  Qt5::Core
-  Qt5::Widgets
-  Qt5::Test
+target_link_libraries(${testname} PRIVATE
+  Qt${QT_TEST_VERSION}::Core
+  Qt${QT_TEST_VERSION}::Widgets
+  Qt${QT_TEST_VERSION}::Test
   macos_fw_lib
 )
diff --git a/Tests/QtAutogen/ManySources/CMakeLists.txt b/Tests/QtAutogen/ManySources/CMakeLists.txt
index df8a2a6..f9ac7db 100644
--- a/Tests/QtAutogen/ManySources/CMakeLists.txt
+++ b/Tests/QtAutogen/ManySources/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(ManySources)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/MocCMP0071/CMakeLists.txt b/Tests/QtAutogen/MocCMP0071/CMakeLists.txt
index 5c58a82..7ab3a8e 100644
--- a/Tests/QtAutogen/MocCMP0071/CMakeLists.txt
+++ b/Tests/QtAutogen/MocCMP0071/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocCMP0071)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/MocCMP0071/NEW/CMakeLists.txt b/Tests/QtAutogen/MocCMP0071/NEW/CMakeLists.txt
index 954fe3d..4375b17 100644
--- a/Tests/QtAutogen/MocCMP0071/NEW/CMakeLists.txt
+++ b/Tests/QtAutogen/MocCMP0071/NEW/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.10)
 cmake_policy(SET CMP0071 NEW)
 
 # *Generate* files
diff --git a/Tests/QtAutogen/MocCMP0071/OLD/CMakeLists.txt b/Tests/QtAutogen/MocCMP0071/OLD/CMakeLists.txt
index 68fa067..3771487 100644
--- a/Tests/QtAutogen/MocCMP0071/OLD/CMakeLists.txt
+++ b/Tests/QtAutogen/MocCMP0071/OLD/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.10)
 cmake_policy(SET CMP0071 OLD)
 
 # *Generate* files
diff --git a/Tests/QtAutogen/MocCMP0100/CMakeLists.txt b/Tests/QtAutogen/MocCMP0100/CMakeLists.txt
index 559cffe..b19dc6a 100644
--- a/Tests/QtAutogen/MocCMP0100/CMakeLists.txt
+++ b/Tests/QtAutogen/MocCMP0100/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocCMP0100)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/MocCMP0100/NEW/CMakeLists.txt b/Tests/QtAutogen/MocCMP0100/NEW/CMakeLists.txt
index 654b31e..aaaf7fb 100644
--- a/Tests/QtAutogen/MocCMP0100/NEW/CMakeLists.txt
+++ b/Tests/QtAutogen/MocCMP0100/NEW/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.16)
 cmake_policy(SET CMP0100 NEW)
 
 add_executable(mocCMP0100New
diff --git a/Tests/QtAutogen/MocCMP0100/OLD/CMakeLists.txt b/Tests/QtAutogen/MocCMP0100/OLD/CMakeLists.txt
index 2be0535..006cda1 100644
--- a/Tests/QtAutogen/MocCMP0100/OLD/CMakeLists.txt
+++ b/Tests/QtAutogen/MocCMP0100/OLD/CMakeLists.txt
@@ -1,4 +1,3 @@
-cmake_minimum_required(VERSION 3.16)
 cmake_policy(SET CMP0100 OLD)
 
 # Generate moc files externally.
diff --git a/Tests/QtAutogen/MocInclude/CMakeLists.txt b/Tests/QtAutogen/MocInclude/CMakeLists.txt
index 04c8baf..4224d2f 100644
--- a/Tests/QtAutogen/MocInclude/CMakeLists.txt
+++ b/Tests/QtAutogen/MocInclude/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.16)
 project(MocInclude)
 get_filename_component(CS_REAL ${CMAKE_CURRENT_SOURCE_DIR} REALPATH)
 include("${CS_REAL}/../AutogenCoreTest.cmake")
diff --git a/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt b/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt
index 8b11b46..c28616b 100644
--- a/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt
+++ b/Tests/QtAutogen/MocIncludeSymlink/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.15)
+cmake_minimum_required(VERSION 3.16)
 project(MocIncludeSymlink)
 include("../AutogenCoreTest.cmake")
 
@@ -63,6 +63,7 @@
     CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
                 "-DCMAKE_AUTOMOC_PATH_PREFIX=ON"
                 "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+                "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
                 "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
     OUTPUT_VARIABLE output
   )
diff --git a/Tests/QtAutogen/MocMacroName/CMakeLists.txt b/Tests/QtAutogen/MocMacroName/CMakeLists.txt
index bf13d18..d8dc4a3 100644
--- a/Tests/QtAutogen/MocMacroName/CMakeLists.txt
+++ b/Tests/QtAutogen/MocMacroName/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocMacroName)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/MocOnly/CMakeLists.txt b/Tests/QtAutogen/MocOnly/CMakeLists.txt
index f4fde58..b5d405a 100644
--- a/Tests/QtAutogen/MocOnly/CMakeLists.txt
+++ b/Tests/QtAutogen/MocOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocOnly)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/MocOptions/CMakeLists.txt b/Tests/QtAutogen/MocOptions/CMakeLists.txt
index 19ee658..169e6cb 100644
--- a/Tests/QtAutogen/MocOptions/CMakeLists.txt
+++ b/Tests/QtAutogen/MocOptions/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocOptions)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/MocOsMacros/CMakeLists.txt b/Tests/QtAutogen/MocOsMacros/CMakeLists.txt
index b0125f6..db44da3 100644
--- a/Tests/QtAutogen/MocOsMacros/CMakeLists.txt
+++ b/Tests/QtAutogen/MocOsMacros/CMakeLists.txt
@@ -1,10 +1,10 @@
-cmake_minimum_required(VERSION 3.11)
+cmake_minimum_required(VERSION 3.16)
 project(MocOsMacros)
 include("../AutogenCoreTest.cmake")
 
 # Tests if moc processes Q_OS_XXX macros
 
-message( "Qt5Core_VERSION: ${Qt5Core_VERSION}" )
+message( "Qt${QT_TEST_VERSION}Core_VERSION: ${Qt${QT_TEST_VERSION}Core_VERSION}" )
 message(
   "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND: "
   ${CMAKE_CXX_COMPILER_PREDEFINES_COMMAND} )
@@ -12,7 +12,7 @@
 # On some platforms (e.g. MAC) Q_OS_XXX requires moc to include moc_predefs.h
 # which is supported since Qt 5.8 and requires
 # CMAKE_CXX_COMPILER_PREDEFINES_COMMAND to be defined.
-if( ( ${Qt5Core_VERSION} VERSION_GREATER_EQUAL "5.8" ) AND
+if( ( ${Qt${QT_TEST_VERSION}Core_VERSION} VERSION_GREATER_EQUAL "5.8" ) AND
   DEFINED CMAKE_CXX_COMPILER_PREDEFINES_COMMAND
 )
   message( "Test enabled!" )
diff --git a/Tests/QtAutogen/MocSkipSource/CMakeLists.txt b/Tests/QtAutogen/MocSkipSource/CMakeLists.txt
index c886736..b9e20c8 100644
--- a/Tests/QtAutogen/MocSkipSource/CMakeLists.txt
+++ b/Tests/QtAutogen/MocSkipSource/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocSkipSource)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/ObjectLibrary/CMakeLists.txt b/Tests/QtAutogen/ObjectLibrary/CMakeLists.txt
index e8af6c9..88d0998 100644
--- a/Tests/QtAutogen/ObjectLibrary/CMakeLists.txt
+++ b/Tests/QtAutogen/ObjectLibrary/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(ObjectLibrary)
 include("../AutogenCoreTest.cmake")
 
@@ -11,7 +11,7 @@
 # Object library b defined locally
 include_directories(b)
 add_library(b OBJECT b/classb.cpp)
-target_compile_features(b PRIVATE ${QT_COMPILE_FEATURES})
+target_link_libraries(b PRIVATE ${QT_LIBRARIES})
 
 # Executable with OBJECT library generator expressions
 add_executable(someProgram main.cpp $<TARGET_OBJECTS:a> $<TARGET_OBJECTS:b>)
diff --git a/Tests/QtAutogen/ObjectLibrary/a/CMakeLists.txt b/Tests/QtAutogen/ObjectLibrary/a/CMakeLists.txt
index fe76ac3..5ace3ae 100644
--- a/Tests/QtAutogen/ObjectLibrary/a/CMakeLists.txt
+++ b/Tests/QtAutogen/ObjectLibrary/a/CMakeLists.txt
@@ -1,2 +1,2 @@
 add_library(a OBJECT classa.cpp)
-target_compile_features(a PRIVATE ${QT_COMPILE_FEATURES})
+target_link_libraries(a PRIVATE ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/Parallel/CMakeLists.txt b/Tests/QtAutogen/Parallel/CMakeLists.txt
index 299bcbf..29628eb 100644
--- a/Tests/QtAutogen/Parallel/CMakeLists.txt
+++ b/Tests/QtAutogen/Parallel/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(Parallel)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/Parallel1/CMakeLists.txt b/Tests/QtAutogen/Parallel1/CMakeLists.txt
index 5c7d547..d94aba5 100644
--- a/Tests/QtAutogen/Parallel1/CMakeLists.txt
+++ b/Tests/QtAutogen/Parallel1/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(Parallel1)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/Parallel2/CMakeLists.txt b/Tests/QtAutogen/Parallel2/CMakeLists.txt
index 668aea4..58667a8 100644
--- a/Tests/QtAutogen/Parallel2/CMakeLists.txt
+++ b/Tests/QtAutogen/Parallel2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(Parallel2)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/Parallel3/CMakeLists.txt b/Tests/QtAutogen/Parallel3/CMakeLists.txt
index 5c50f5e..cc3aae0 100644
--- a/Tests/QtAutogen/Parallel3/CMakeLists.txt
+++ b/Tests/QtAutogen/Parallel3/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(Parallel3)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/Parallel4/CMakeLists.txt b/Tests/QtAutogen/Parallel4/CMakeLists.txt
index 2c40c6a..2b81e98 100644
--- a/Tests/QtAutogen/Parallel4/CMakeLists.txt
+++ b/Tests/QtAutogen/Parallel4/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(Parallel4)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt
index cddece3..5543b9a 100644
--- a/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt
+++ b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(ParallelAUTO)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/RccEmpty/CMakeLists.txt b/Tests/QtAutogen/RccEmpty/CMakeLists.txt
index a8e2af1..c8f9d17 100644
--- a/Tests/QtAutogen/RccEmpty/CMakeLists.txt
+++ b/Tests/QtAutogen/RccEmpty/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RccEmpty)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RccOffMocLibrary/CMakeLists.txt b/Tests/QtAutogen/RccOffMocLibrary/CMakeLists.txt
index 61b9601..b44973d 100644
--- a/Tests/QtAutogen/RccOffMocLibrary/CMakeLists.txt
+++ b/Tests/QtAutogen/RccOffMocLibrary/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RccOffMocLibrary)
 include("../AutogenCoreTest.cmake")
 
@@ -9,9 +9,7 @@
 add_library(empty STATIC empty.cpp not_generated_file.qrc)
 set_target_properties(empty PROPERTIES AUTORCC OFF)
 set_target_properties(empty PROPERTIES AUTOMOC TRUE)
-target_link_libraries(empty no_link_language)
+target_link_libraries(empty PRIVATE no_link_language ${QT_LIBRARIES})
 add_library(no_link_language STATIC empty.h)
 set_target_properties(no_link_language PROPERTIES AUTOMOC TRUE)
-# Pass Qt compiler features to targets that don't link against Qt
-target_compile_features(no_link_language PRIVATE ${QT_COMPILE_FEATURES})
-target_compile_features(empty PRIVATE ${QT_COMPILE_FEATURES})
+target_link_libraries(no_link_language PRIVATE ${QT_LIBRARIES})
diff --git a/Tests/QtAutogen/RccOnly/CMakeLists.txt b/Tests/QtAutogen/RccOnly/CMakeLists.txt
index f3776f5..f3271e0 100644
--- a/Tests/QtAutogen/RccOnly/CMakeLists.txt
+++ b/Tests/QtAutogen/RccOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RccOnly)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RccSkipSource/CMakeLists.txt b/Tests/QtAutogen/RccSkipSource/CMakeLists.txt
index 4223274..a79177f 100644
--- a/Tests/QtAutogen/RccSkipSource/CMakeLists.txt
+++ b/Tests/QtAutogen/RccSkipSource/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RccSkipSource)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt b/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt
index c53e857..76dbdf5 100644
--- a/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocBasic/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RerunMocBasic)
 include("../AutogenCoreTest.cmake")
 
@@ -63,6 +63,7 @@
           ${_D_CMAKE_GENERATOR_INSTANCE}
           "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
           "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+          "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
           "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   RESULT_VARIABLE exit_code
   OUTPUT_VARIABLE output
diff --git a/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt b/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt
index 42f2f57..e27a824 100644
--- a/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocBasic/MocBasic/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocBasic)
 include("../../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RerunMocOnAddFile/CMakeLists.txt b/Tests/QtAutogen/RerunMocOnAddFile/CMakeLists.txt
index 2677659..3a9244d 100644
--- a/Tests/QtAutogen/RerunMocOnAddFile/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocOnAddFile/CMakeLists.txt
@@ -1,6 +1,6 @@
 # This test checks whether adding a source file to the project triggers an AUTOMOC re-run.
 
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RerunMocOnAddFile)
 include("../AutogenCoreTest.cmake")
 
@@ -64,6 +64,7 @@
   MocOnAddFile
   CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
               "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+              "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
               "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   OUTPUT_VARIABLE output
 )
diff --git a/Tests/QtAutogen/RerunMocOnAddFile/MocOnAddFile/CMakeLists.txt.in b/Tests/QtAutogen/RerunMocOnAddFile/MocOnAddFile/CMakeLists.txt.in
index 9e5e21c..e600137 100644
--- a/Tests/QtAutogen/RerunMocOnAddFile/MocOnAddFile/CMakeLists.txt.in
+++ b/Tests/QtAutogen/RerunMocOnAddFile/MocOnAddFile/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocOnAddFile)
 include("@CMAKE_CURRENT_LIST_DIR@/../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RerunMocOnMissingDependency/CMakeLists.txt b/Tests/QtAutogen/RerunMocOnMissingDependency/CMakeLists.txt
index c5811eb..b17e4c5 100644
--- a/Tests/QtAutogen/RerunMocOnMissingDependency/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocOnMissingDependency/CMakeLists.txt
@@ -1,6 +1,6 @@
 # This test checks whether a missing dependency of the moc output triggers an AUTOMOC re-run.
 
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RerunMocOnMissingDependency)
 include("../AutogenCoreTest.cmake")
 
@@ -13,10 +13,10 @@
 set(testProjectTemplateDir "${CMAKE_CURRENT_SOURCE_DIR}/MocOnMissingDependency")
 set(testProjectSrc "${CMAKE_CURRENT_BINARY_DIR}/MocOnMissingDependency")
 set(testProjectBinDir "${CMAKE_CURRENT_BINARY_DIR}/MocOnMissingDependency-build")
-if(DEFINED Qt5Core_VERSION AND Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
-    set(moc_depfiles_supported TRUE)
+if(DEFINED Qt${QT_TEST_VERSION}Core_VERSION AND Qt${QT_TEST_VERSION}Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
+  set(moc_depfiles_supported TRUE)
 else()
-    set(moc_depfiles_supported FALSE)
+  set(moc_depfiles_supported FALSE)
 endif()
 
 # Utility macros
@@ -48,6 +48,7 @@
   MocOnMissingDependency
   CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
               "-DCMAKE_AUTOGEN_VERBOSE=ON"
+              "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
               "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   OUTPUT_VARIABLE output
 )
diff --git a/Tests/QtAutogen/RerunMocPlugin/CMakeLists.txt b/Tests/QtAutogen/RerunMocPlugin/CMakeLists.txt
index a7fb2d7..2d2edb4 100644
--- a/Tests/QtAutogen/RerunMocPlugin/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocPlugin/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RerunMocPlugin)
 include("../AutogenCoreTest.cmake")
 
@@ -63,6 +63,7 @@
   MocPlugin
   CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
               "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+              "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
               "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   OUTPUT_VARIABLE output
 )
diff --git a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/CMakeLists.txt b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/CMakeLists.txt
index 5068289..8097fd0 100644
--- a/Tests/QtAutogen/RerunMocPlugin/MocPlugin/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunMocPlugin/MocPlugin/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(MocPlugin)
 include("../../AutogenGuiTest.cmake")
 
@@ -22,7 +22,7 @@
 set(CMAKE_AUTOMOC TRUE)
 
 include_directories("${CMAKE_CURRENT_BINARY_DIR}/jsonFiles")
-link_libraries(Qt5::Widgets)
+link_libraries(Qt${QT_TEST_VERSION}::Widgets)
 
 add_library(PlugA STATIC StyleA.cpp)
 add_library(PlugB STATIC StyleB.cpp)
diff --git a/Tests/QtAutogen/RerunRccConfigChange/CMakeLists.txt b/Tests/QtAutogen/RerunRccConfigChange/CMakeLists.txt
index 33c01ac..1598bfd 100644
--- a/Tests/QtAutogen/RerunRccConfigChange/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunRccConfigChange/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.11.2)
+cmake_minimum_required(VERSION 3.16)
 project(RerunRccConfigChange)
 include("../AutogenCoreTest.cmake")
 
@@ -33,6 +33,7 @@
   RccConfigChange
   CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
               "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+              "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
               "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   OUTPUT_VARIABLE output
 )
diff --git a/Tests/QtAutogen/RerunRccConfigChange/RccConfigChange/CMakeLists.txt b/Tests/QtAutogen/RerunRccConfigChange/RccConfigChange/CMakeLists.txt
index e2dd0ac..83f3a45 100644
--- a/Tests/QtAutogen/RerunRccConfigChange/RccConfigChange/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunRccConfigChange/RccConfigChange/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.11.2)
+cmake_minimum_required(VERSION 3.16)
 project(RccConfigChange)
 include("../../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RerunRccDepends/CMakeLists.txt b/Tests/QtAutogen/RerunRccDepends/CMakeLists.txt
index 1301550..15e38db 100644
--- a/Tests/QtAutogen/RerunRccDepends/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunRccDepends/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RerunRccDepends)
 include("../AutogenCoreTest.cmake")
 
@@ -66,6 +66,7 @@
   RccDepends
   CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
               "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+              "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
               "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   OUTPUT_VARIABLE output
 )
diff --git a/Tests/QtAutogen/RerunRccDepends/RccDepends/CMakeLists.txt b/Tests/QtAutogen/RerunRccDepends/RccDepends/CMakeLists.txt
index 150f849..6bba163 100644
--- a/Tests/QtAutogen/RerunRccDepends/RccDepends/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunRccDepends/RccDepends/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(RccDepends)
 include("../../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
index 9b114e9..979e878 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
+++ b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt
@@ -66,6 +66,7 @@
           "${build_type_extra}"
           "-DQT_TEST_VERSION=${QT_TEST_VERSION}"
           "-DCMAKE_AUTOGEN_VERBOSE=${CMAKE_AUTOGEN_VERBOSE}"
+          "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
           "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
   RESULT_VARIABLE exit_code
   OUTPUT_VARIABLE output
diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
index c787db1..1775725 100644
--- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
+++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 
 project(UicOnFileChange)
 include("@CMAKE_CURRENT_LIST_DIR@/../AutogenGuiTest.cmake")
diff --git a/Tests/QtAutogen/StaticLibraryCycle/CMakeLists.txt b/Tests/QtAutogen/StaticLibraryCycle/CMakeLists.txt
index f3536ba..25ea822 100644
--- a/Tests/QtAutogen/StaticLibraryCycle/CMakeLists.txt
+++ b/Tests/QtAutogen/StaticLibraryCycle/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(StaticLibraryCycle)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/TestMacros.cmake b/Tests/QtAutogen/TestMacros.cmake
index 9dcf31f..529592e 100644
--- a/Tests/QtAutogen/TestMacros.cmake
+++ b/Tests/QtAutogen/TestMacros.cmake
@@ -7,8 +7,17 @@
 endif()
 list(APPEND Autogen_BUILD_OPTIONS
     "-DCMAKE_AUTOGEN_VERBOSE=1"
-    "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
 )
+if(Qt${QT_TEST_VERSION}Core_DIR)
+  get_filename_component(prefix "${Qt${QT_TEST_VERSION}Core_DIR}" DIRECTORY)
+  get_filename_component(prefix "${prefix}" DIRECTORY)
+  get_filename_component(prefix "${prefix}" DIRECTORY)
+  list(APPEND Autogen_BUILD_OPTIONS "-DCMAKE_PREFIX_PATH:STRING=${prefix}")
+else()
+  list(APPEND Autogen_BUILD_OPTIONS
+    "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
+  )
+endif()
 # XXX(xcode-per-cfg-src): Drop the NO_PER_CONFIG_SOURCES exclusion
 # when the Xcode generator supports per-config sources.
 if(CMAKE_GENERATOR STREQUAL "Xcode")
diff --git a/Tests/QtAutogen/UicInclude/CMakeLists.txt b/Tests/QtAutogen/UicInclude/CMakeLists.txt
index 929868b..042d18f 100644
--- a/Tests/QtAutogen/UicInclude/CMakeLists.txt
+++ b/Tests/QtAutogen/UicInclude/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(UicInclude)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/UicInterface/CMakeLists.txt b/Tests/QtAutogen/UicInterface/CMakeLists.txt
index e022764..167ae49 100644
--- a/Tests/QtAutogen/UicInterface/CMakeLists.txt
+++ b/Tests/QtAutogen/UicInterface/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(UicInterface)
 include("../AutogenGuiTest.cmake")
 
@@ -11,12 +11,12 @@
 set(CMAKE_VERBOSE_MAKEFILE ON)
 
 add_library(KI18n klocalizedstring.cpp)
-target_link_libraries(KI18n ${QT_QTCORE_TARGET})
+target_link_libraries(KI18n PRIVATE ${QT_QTCORE_TARGET})
 
 set(autouic_options
   -tr tr2$<$<NOT:$<BOOL:$<TARGET_PROPERTY:NO_KUIT_SEMANTIC>>>:x>i18n
 )
-if (NOT Qt5Widgets_VERSION VERSION_LESS 5.3.0)
+if (NOT Qt${QT_TEST_VERSION}Widgets_VERSION VERSION_LESS 5.3.0)
   list(APPEND autouic_options -include klocalizedstring.h)
 endif()
 
diff --git a/Tests/QtAutogen/UicNoGui/CMakeLists.txt b/Tests/QtAutogen/UicNoGui/CMakeLists.txt
index 076299d..b938a17 100644
--- a/Tests/QtAutogen/UicNoGui/CMakeLists.txt
+++ b/Tests/QtAutogen/UicNoGui/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.13)
+cmake_minimum_required(VERSION 3.16)
 project(UicNoGui)
 include("../AutogenCoreTest.cmake")
 
diff --git a/Tests/QtAutogen/UicOnly/CMakeLists.txt b/Tests/QtAutogen/UicOnly/CMakeLists.txt
index b163254..e25b44d 100644
--- a/Tests/QtAutogen/UicOnly/CMakeLists.txt
+++ b/Tests/QtAutogen/UicOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(UicOnly)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/QtAutogen/UicSkipSource/CMakeLists.txt b/Tests/QtAutogen/UicSkipSource/CMakeLists.txt
index dc3b7d4e..15dfe65 100644
--- a/Tests/QtAutogen/UicSkipSource/CMakeLists.txt
+++ b/Tests/QtAutogen/UicSkipSource/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.10)
+cmake_minimum_required(VERSION 3.16)
 project(UicSkipSource)
 include("../AutogenGuiTest.cmake")
 
diff --git a/Tests/RunCMake/Autogen/CMP0111-imported-target-prelude.cmake b/Tests/RunCMake/Autogen/CMP0111-imported-target-prelude.cmake
index 8fa6041..6d26f99 100644
--- a/Tests/RunCMake/Autogen/CMP0111-imported-target-prelude.cmake
+++ b/Tests/RunCMake/Autogen/CMP0111-imported-target-prelude.cmake
@@ -1,6 +1,6 @@
 enable_language(CXX)
 
-find_package(Qt5 REQUIRED COMPONENTS Core)
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core)
 
 # Detect `-NOTFOUND` libraries at generate time.
 cmake_policy(SET CMP0111 NEW)
diff --git a/Tests/RunCMake/Autogen/CMakeLists.txt b/Tests/RunCMake/Autogen/CMakeLists.txt
index 9a66cde..2632ffa 100644
--- a/Tests/RunCMake/Autogen/CMakeLists.txt
+++ b/Tests/RunCMake/Autogen/CMakeLists.txt
@@ -1,3 +1,3 @@
-cmake_minimum_required(VERSION 3.13)
+cmake_minimum_required(VERSION 3.16)
 project(${RunCMake_TEST} NONE)
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/Autogen/QtInFunction.cmake b/Tests/RunCMake/Autogen/QtInFunction.cmake
index a44bc5a..6c42cc2 100644
--- a/Tests/RunCMake/Autogen/QtInFunction.cmake
+++ b/Tests/RunCMake/Autogen/QtInFunction.cmake
@@ -1,9 +1,9 @@
 enable_language(CXX)
 
 function (use_autogen target)
-  find_package(Qt5 REQUIRED COMPONENTS Core Widgets)
-  set(Qt5Core_VERSION_MAJOR "${Qt5Core_VERSION_MAJOR}" PARENT_SCOPE)
-  set(Qt5Core_VERSION_MINOR "${Qt5Core_VERSION_MINOR}" PARENT_SCOPE)
+  find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets)
+  set(Qt${with_qt_version}Core_VERSION_MAJOR "${Qt${with_qt_version}Core_VERSION_MAJOR}" PARENT_SCOPE)
+  set(Qt${with_qt_version}Core_VERSION_MINOR "${Qt${with_qt_version}Core_VERSION_MINOR}" PARENT_SCOPE)
   set_property(TARGET "${target}" PROPERTY AUTOMOC 1)
   set_property(TARGET "${target}" PROPERTY AUTORCC 1)
   set_property(TARGET "${target}" PROPERTY AUTOUIC 1)
diff --git a/Tests/RunCMake/Autogen/QtInFunctionNested.cmake b/Tests/RunCMake/Autogen/QtInFunctionNested.cmake
index 5421ba0..2e7b8ad 100644
--- a/Tests/RunCMake/Autogen/QtInFunctionNested.cmake
+++ b/Tests/RunCMake/Autogen/QtInFunctionNested.cmake
@@ -1,9 +1,9 @@
 enable_language(CXX)
 
 function (use_autogen target)
-  find_package(Qt5 REQUIRED COMPONENTS Core Widgets)
-  set(Qt5Core_VERSION_MAJOR "${Qt5Core_VERSION_MAJOR}" PARENT_SCOPE)
-  set(Qt5Core_VERSION_MINOR "${Qt5Core_VERSION_MINOR}" PARENT_SCOPE)
+  find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets)
+  set(Qt${with_qt_version}Core_VERSION_MAJOR "${Qt${with_qt_version}Core_VERSION_MAJOR}" PARENT_SCOPE)
+  set(Qt${with_qt_version}Core_VERSION_MINOR "${Qt${with_qt_version}Core_VERSION_MINOR}" PARENT_SCOPE)
   set_property(TARGET "${target}" PROPERTY AUTOMOC 1)
   set_property(TARGET "${target}" PROPERTY AUTORCC 1)
   set_property(TARGET "${target}" PROPERTY AUTOUIC 1)
diff --git a/Tests/RunCMake/Autogen/QtInFunctionProperty.cmake b/Tests/RunCMake/Autogen/QtInFunctionProperty.cmake
index 35f1cd1..d9296c4 100644
--- a/Tests/RunCMake/Autogen/QtInFunctionProperty.cmake
+++ b/Tests/RunCMake/Autogen/QtInFunctionProperty.cmake
@@ -1,13 +1,13 @@
 enable_language(CXX)
 
 function (use_autogen target)
-  find_package(Qt5 REQUIRED COMPONENTS Core Widgets)
+  find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets)
   set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
     PROPERTY
-      Qt5Core_VERSION_MAJOR "${Qt5Core_VERSION_MAJOR}")
+      Qt${with_qt_version}Core_VERSION_MAJOR "${Qt${with_qt_version}Core_VERSION_MAJOR}")
   set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
     PROPERTY
-      Qt5Core_VERSION_MINOR "${Qt5Core_VERSION_MINOR}")
+      Qt${with_qt_version}Core_VERSION_MINOR "${Qt${with_qt_version}Core_VERSION_MINOR}")
   set_property(TARGET "${target}" PROPERTY AUTOMOC 1)
   set_property(TARGET "${target}" PROPERTY AUTORCC 1)
   set_property(TARGET "${target}" PROPERTY AUTOUIC 1)
diff --git a/Tests/RunCMake/Autogen/RunCMakeTest.cmake b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
index bbcbd5e..e4c5811 100644
--- a/Tests/RunCMake/Autogen/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
@@ -1,7 +1,13 @@
 include(RunCMake)
 
 run_cmake(NoQt)
-if (with_qt5)
+if (DEFINED with_qt_version)
+  set(RunCMake_TEST_OPTIONS
+    -Dwith_qt_version=${with_qt_version}
+    "-DQt${with_qt_version}_DIR:PATH=${Qt${with_qt_version}_DIR}"
+    "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
+  )
+
   run_cmake(QtInFunction)
   run_cmake(QtInFunctionNested)
   run_cmake(QtInFunctionProperty)
diff --git a/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CMakeLists.txt b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CMakeLists.txt
new file mode 100644
index 0000000..bbc08e6
--- /dev/null
+++ b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.21)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CheckEnvironmentVar-build-stdout.txt b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CheckEnvironmentVar-build-stdout.txt
new file mode 100644
index 0000000..f0f2efc
--- /dev/null
+++ b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CheckEnvironmentVar-build-stdout.txt
@@ -0,0 +1 @@
+This message is printed by echo_message.bat
diff --git a/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CheckEnvironmentVar.cmake b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CheckEnvironmentVar.cmake
new file mode 100644
index 0000000..63d6068
--- /dev/null
+++ b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/CheckEnvironmentVar.cmake
@@ -0,0 +1,2 @@
+set(CMAKE_MSVCIDE_RUN_PATH "${CMAKE_SOURCE_DIR}")
+add_custom_target(main COMMAND echo_message)
diff --git a/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/RunCMakeTest.cmake b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/RunCMakeTest.cmake
new file mode 100644
index 0000000..a424ff2
--- /dev/null
+++ b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/RunCMakeTest.cmake
@@ -0,0 +1,8 @@
+include(RunCMake)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CheckEnvironmentVar-build)
+run_cmake(CheckEnvironmentVar)
+set(RunCMake_TEST_NO_CLEAN 1)
+run_cmake_command(CheckEnvironmentVar-build ${CMAKE_COMMAND} --build . --config Debug --target main)
+unset(RunCMake_TEST_BINARY_DIR)
+unset(RunCMake_TEST_NO_CLEAN)
diff --git a/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/echo_message.bat b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/echo_message.bat
new file mode 100755
index 0000000..9037d02
--- /dev/null
+++ b/Tests/RunCMake/CMAKE_MSVCIDE_RUN_PATH/echo_message.bat
@@ -0,0 +1 @@
+echo This message is printed by echo_message.bat
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW-stdout.txt
similarity index 100%
copy from Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt
copy to Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW-stdout.txt
diff --git a/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW.cmake b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW.cmake
new file mode 100644
index 0000000..5a2b018
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-NEW.cmake
@@ -0,0 +1,9 @@
+include(CMakeDependentOption)
+
+cmake_policy(SET CMP0127 NEW)
+
+set(A 1)
+set(B 1)
+set(C 0)
+cmake_dependent_option(USE_FOO "Use Foo" ON "A AND (B OR C)" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")
diff --git a/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt
new file mode 100644
index 0000000..b16e84b
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stderr.txt
@@ -0,0 +1,9 @@
+^CMake Warning \(dev\) at [^
+]*/Modules/CMakeDependentOption.cmake:[0-9]+ \(message\):
+  Policy CMP0127 is not set: cmake_dependent_option\(\) supports full Condition
+  Syntax.  Run "cmake --help-policy CMP0127" for policy details.  Use the
+  cmake_policy command to set the policy and suppress this warning.
+Call Stack \(most recent call first\):
+  [^
+]*/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN.cmake:[0-9]+ \(cmake_dependent_option\)
+This warning is for project developers.  Use -Wno-dev to suppress it.$
diff --git a/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stdout.txt b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stdout.txt
new file mode 100644
index 0000000..d89dbd3
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN-stdout.txt
@@ -0,0 +1,2 @@
+-- USE_FOO='OFF'
+-- USE_BAR='ON'
diff --git a/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN.cmake b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN.cmake
new file mode 100644
index 0000000..00d440d
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Parentheses-CMP0127-WARN.cmake
@@ -0,0 +1,9 @@
+include(CMakeDependentOption)
+
+set(A 1)
+set(B 1)
+set(C 0)
+cmake_dependent_option(USE_FOO "Use Foo" ON "A AND (B OR C)" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")
+cmake_dependent_option(USE_BAR "Use Bar" ON "A;B" OFF)
+message(STATUS "USE_BAR='${USE_BAR}'")
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW-stdout.txt
similarity index 100%
rename from Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt
rename to Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW-stdout.txt
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW.cmake b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW.cmake
new file mode 100644
index 0000000..e92c1e6
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-NEW.cmake
@@ -0,0 +1,7 @@
+include(CMakeDependentOption)
+
+cmake_policy(SET CMP0127 NEW)
+
+set(FOO "lower")
+cmake_dependent_option(USE_FOO "Use Foo" ON "FOO MATCHES \"(UPPER|lower)\"" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stdout.txt
similarity index 100%
copy from Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt
copy to Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD-stdout.txt
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex.cmake b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD.cmake
similarity index 83%
rename from Tests/RunCMake/CMakeDependentOption/Regex.cmake
rename to Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD.cmake
index 8342a01..81df003 100644
--- a/Tests/RunCMake/CMakeDependentOption/Regex.cmake
+++ b/Tests/RunCMake/CMakeDependentOption/Regex-CMP0127-OLD.cmake
@@ -1,5 +1,7 @@
 include(CMakeDependentOption)
 
+cmake_policy(SET CMP0127 OLD)
+
 set(FOO "lower")
 cmake_dependent_option(USE_FOO "Use Foo" ON "FOO MATCHES (UPPER|lower)" OFF)
 message(STATUS "USE_FOO='${USE_FOO}'")
diff --git a/Tests/RunCMake/CMakeDependentOption/RunCMakeTest.cmake b/Tests/RunCMake/CMakeDependentOption/RunCMakeTest.cmake
index e1045f2..61e046f 100644
--- a/Tests/RunCMake/CMakeDependentOption/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakeDependentOption/RunCMakeTest.cmake
@@ -1,3 +1,6 @@
 include(RunCMake)
 
-run_cmake_script(Regex)
+run_cmake_script(Regex-CMP0127-NEW)
+run_cmake_script(Regex-CMP0127-OLD)
+run_cmake_script(Parentheses-CMP0127-NEW)
+run_cmake_script(Parentheses-CMP0127-WARN)
diff --git a/Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt b/Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD-stdout.txt
similarity index 100%
copy from Tests/RunCMake/CMakeDependentOption/Regex-stdout.txt
copy to Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD-stdout.txt
diff --git a/Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD.cmake b/Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD.cmake
new file mode 100644
index 0000000..6db2128
--- /dev/null
+++ b/Tests/RunCMake/CMakeDependentOption/Simple-CMP0127-OLD.cmake
@@ -0,0 +1,6 @@
+include(CMakeDependentOption)
+
+set(A1 1)
+set(bb 1)
+cmake_dependent_option(USE_FOO "Use Foo" ON "A1;bb" OFF)
+message(STATUS "USE_FOO='${USE_FOO}'")
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 86cb849..e8fb13f 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -151,8 +151,23 @@
 if(CMAKE_GENERATOR MATCHES "Make")
   add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU})
 endif()
-if(CMake_TEST_Qt5)
+unset(ninja_test_with_qt_version)
+unset(ninja_qt_args)
+if(CMake_TEST_Qt6)
+  find_package(Qt6Widgets QUIET NO_MODULE)
+  if(Qt6Widgets_FOUND)
+    set(ninja_test_with_qt_version 6)
+    # Work around Qt6 not finding sibling dependencies without CMAKE_PREFIX_PATH
+    cmake_path(GET Qt6_DIR  PARENT_PATH base_dir)  # <base>/lib/cmake
+    cmake_path(GET base_dir PARENT_PATH base_dir)  # <base>/lib
+    cmake_path(GET base_dir PARENT_PATH base_dir)  # <base>
+    set(ninja_qt_args -DCMAKE_PREFIX_PATH=${base_dir})
+  endif()
+elseif(CMake_TEST_Qt5)
   find_package(Qt5Widgets QUIET NO_MODULE)
+  if(Qt5Widgets_FOUND)
+    set(ninja_test_with_qt_version 5)
+  endif()
 endif()
 if(CMAKE_GENERATOR MATCHES "Ninja")
   set(Ninja_ARGS
@@ -162,18 +177,27 @@
   if(CMAKE_Fortran_COMPILER)
     list(APPEND Ninja_ARGS -DTEST_Fortran=1)
   endif()
-  if(CMake_TEST_Qt5 AND Qt5Core_FOUND)
-    list(APPEND Ninja_ARGS -DCMake_TEST_Qt5=1 -DQt5Core_DIR=${Qt5Core_DIR} -DCMAKE_TEST_Qt5Core_Version=${Qt5Core_VERSION})
-    if(Qt5Widgets_FOUND)
-      list(APPEND Ninja_ARGS -DQt5Widgets_DIR=${Qt5Widgets_DIR})
-    endif()
+  if(ninja_test_with_qt_version)
+    list(APPEND Ninja_ARGS
+      -DCMake_TEST_Qt_version=${ninja_test_with_qt_version}
+      -DQt${ninja_test_with_qt_version}_DIR=${Qt${ninja_test_with_qt_version}_DIR}
+      -DQt${ninja_test_with_qt_version}Core_DIR=${Qt${ninja_test_with_qt_version}Core_DIR}
+      -DCMake_TEST_Qt${ninja_test_with_qt_version}Core_Version=${Qt${ninja_test_with_qt_version}Core_VERSION}
+      -DQt${ninja_test_with_qt_version}Widgets_DIR=${Qt${ninja_test_with_qt_version}Widgets_DIR}
+      ${ninja_qt_args}
+    )
   endif()
   add_RunCMake_test(Ninja)
   set(NinjaMultiConfig_ARGS
     -DCYGWIN=${CYGWIN} -DMSYS=${MSYS}
     )
-  if(CMake_TEST_Qt5 AND Qt5Core_FOUND)
-    list(APPEND NinjaMultiConfig_ARGS -DCMake_TEST_Qt5=1 -DQt5Core_DIR=${Qt5Core_DIR} -DCMAKE_TEST_Qt5Core_Version=${Qt5Core_VERSION})
+  if(ninja_test_with_qt_version)
+    list(APPEND NinjaMultiConfig_ARGS
+      -DCMake_TEST_Qt_version=${ninja_test_with_qt_version}
+      -DQt${ninja_test_with_qt_version}Core_DIR=${Qt${ninja_test_with_qt_version}Core_DIR}
+      -DCMake_TEST_Qt${ninja_test_with_qt_version}Core_Version=${Qt${ninja_test_with_qt_version}Core_VERSION}
+      ${ninja_qt_args}
+    )
   endif()
   if(DEFINED CMake_TEST_CUDA)
     list(APPEND NinjaMultiConfig_ARGS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
@@ -205,11 +229,29 @@
 if(CMake_TEST_APPLE_SILICON)
   add_RunCMake_test(AppleSilicon)
 endif()
-set(autogen_with_qt5 FALSE)
-if(CMake_TEST_Qt5 AND Qt5Widgets_FOUND)
-  set(autogen_with_qt5 TRUE)
+set(want_NoQt_test TRUE)
+if(CMake_TEST_Qt6 AND Qt6Widgets_FOUND)
+  # Work around Qt6 not finding sibling dependencies without CMAKE_PREFIX_PATH
+  cmake_path(GET Qt6_DIR  PARENT_PATH base_dir)  # <base>/lib/cmake
+  cmake_path(GET base_dir PARENT_PATH base_dir)  # <base>/lib
+  cmake_path(GET base_dir PARENT_PATH base_dir)  # <base>
+  add_RunCMake_test(AutogenQt6 TEST_DIR Autogen
+    -Dwith_qt_version=6
+    "-DQt6_DIR:PATH=${Qt6_DIR}"
+    "-DCMAKE_PREFIX_PATH:STRING=${base_dir}"
+  )
+  set(want_NoQt_test FALSE)
 endif ()
-add_RunCMake_test(Autogen -Dwith_qt5=${autogen_with_qt5})
+if(CMake_TEST_Qt5 AND Qt5Widgets_FOUND)
+  add_RunCMake_test(AutogenQt5 TEST_DIR Autogen
+    -Dwith_qt_version=5
+    "-DQt5_DIR:PATH=${Qt5_DIR}"
+  )
+  set(want_NoQt_test FALSE)
+endif ()
+if(want_NoQt_test)
+  add_RunCMake_test(AutogenNoQt TEST_DIR Autogen)
+endif()
 
 add_RunCMake_test(ArtifactOutputDirs)
 
@@ -310,7 +352,7 @@
 add_RunCMake_test(ToolchainFile)
 add_RunCMake_test(find_dependency)
 add_RunCMake_test(CompileDefinitions)
-add_RunCMake_test(CompileFeatures)
+add_RunCMake_test(CompileFeatures -DCMake_NO_C_STANDARD=${CMake_NO_C_STANDARD} -DCMake_NO_CXX_STANDARD=${CMake_NO_CXX_STANDARD})
 add_RunCMake_test(Policy)
 add_RunCMake_test(PolicyScope)
 add_RunCMake_test(WriteBasicConfigVersionFile)
@@ -349,6 +391,7 @@
 endif()
 add_RunCMake_test(execute_process)
 add_RunCMake_test(export)
+add_RunCMake_test(cmake_host_system_information)
 add_RunCMake_test(cmake_language)
 add_RunCMake_test(cmake_minimum_required)
 add_RunCMake_test(cmake_parse_arguments)
@@ -374,12 +417,11 @@
 add_RunCMake_test(ctest_skipped_test)
 add_RunCMake_test(ctest_update)
 add_RunCMake_test(ctest_upload)
+add_RunCMake_test(ctest_environment)
 add_RunCMake_test(ctest_fixtures)
-add_RunCMake_test(file -DMSYS=${MSYS})
+add_RunCMake_test(file -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
 add_RunCMake_test(file-CHMOD -DMSYS=${MSYS})
-if(HAVE_ELF_H OR CMAKE_SYSTEM_NAME STREQUAL "AIX")
-  add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DHAVE_ELF_H=${HAVE_ELF_H})
-endif()
+add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(find_file)
 add_RunCMake_test(find_library -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
 add_RunCMake_test(find_package -DMSYS=${MSYS})
@@ -538,12 +580,16 @@
   add_RunCMake_test(SymlinkTrees)
 endif ()
 
-find_package(Qt4 QUIET)
-find_package(Qt5Core QUIET)
-if (QT4_FOUND AND Qt5Core_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.1.0)
+if(CMake_TEST_Qt4)
+  find_package(Qt4 QUIET)
+endif()
+if(CMake_TEST_Qt5)
+  find_package(Qt5Core QUIET)
+endif()
+if (CMake_TEST_Qt4 AND CMake_TEST_Qt5 AND QT4_FOUND AND Qt5Core_FOUND AND NOT Qt5Core_VERSION VERSION_LESS 5.1.0)
   add_RunCMake_test(IncompatibleQt)
 endif()
-if (QT4_FOUND)
+if (CMake_TEST_Qt4 AND QT4_FOUND)
   add_RunCMake_test(ObsoleteQtMacros -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE})
 endif()
 
@@ -557,6 +603,7 @@
 endif()
 
 if("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
+  add_RunCMake_test(CMAKE_MSVCIDE_RUN_PATH)
   add_RunCMake_test(include_external_msproject -DVS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME})
   if("${CMAKE_GENERATOR}" MATCHES "Visual Studio (9|10)" AND NOT CMAKE_VS_DEVENV_COMMAND)
     set(NO_USE_FOLDERS 1)
diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake
index 746ff8b..7997c78 100644
--- a/Tests/RunCMake/CPack/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake
@@ -7,6 +7,7 @@
 run_cpack_test(CUSTOM_BINARY_SPEC_FILE "RPM.CUSTOM_BINARY_SPEC_FILE" false "MONOLITHIC;COMPONENT")
 run_cpack_test(CUSTOM_NAMES "RPM.CUSTOM_NAMES;DEB.CUSTOM_NAMES;TGZ;DragNDrop" true "COMPONENT")
 run_cpack_test(DEBUGINFO "RPM.DEBUGINFO;DEB.DEBUGINFO" true "COMPONENT")
+run_cpack_test(DEBUGINFO "DEB.DEBUGINFO" true "MONOLITHIC")
 run_cpack_test_subtests(DEFAULT_PERMISSIONS "CMAKE_var_set;CPACK_var_set;both_set;invalid_CMAKE_var;invalid_CPACK_var" "RPM.DEFAULT_PERMISSIONS;DEB.DEFAULT_PERMISSIONS" false "MONOLITHIC;COMPONENT")
 run_cpack_test(DEPENDENCIES "RPM.DEPENDENCIES;DEB.DEPENDENCIES" true "COMPONENT")
 run_cpack_test(DIST "RPM.DIST" false "MONOLITHIC")
diff --git a/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake
index cf4aa51..b3e6485 100644
--- a/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake
+++ b/Tests/RunCMake/CPack/tests/DEBUGINFO/ExpectedFiles.cmake
@@ -1,8 +1,5 @@
 set(whitespaces_ "[\t\n\r ]*")
 
-set(EXPECTED_FILES_COUNT "6")
-set(EXPECTED_FILES_NAME_GENERATOR_SPECIFIC_FORMAT TRUE)
-
 if(GENERATOR_TYPE STREQUAL "RPM")
   set(NAME "Debuginfo")
   set(DEBUG_SUFFIX "debuginfo")
@@ -15,30 +12,49 @@
   set(DEBUG_PKG "ddeb")
 endif()
 
-set(EXPECTED_FILE_1_NAME "${NAME}")
-set(EXPECTED_FILE_1_COMPONENT "applications")
-set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/test_prog")
+set(EXPECTED_FILES_NAME_GENERATOR_SPECIFIC_FORMAT TRUE)
 
-set(EXPECTED_FILE_2 "TestDinfo-pkg*-headers.${PKG}")
-set(EXPECTED_FILE_CONTENT_2_LIST "/bar;/bar/CMakeLists.txt")
+if(PACKAGING_TYPE STREQUAL "COMPONENT")
+  set(EXPECTED_FILES_COUNT "6")
 
-set(EXPECTED_FILE_3 "TestDinfo-pkg*-libs.${PKG}")
-set(EXPECTED_FILE_CONTENT_3_LIST "/bas;/bas/libtest_lib.so")
+  set(EXPECTED_FILE_1_NAME "${NAME}")
+  set(EXPECTED_FILE_1_COMPONENT "applications")
+  set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/test_prog")
 
-set(EXPECTED_FILE_4 "${NAME}-applications-${DEBUG_SUFFIX}*.${DEBUG_PKG}")
-if(GENERATOR_TYPE STREQUAL "RPM")
-  set(EXPECTED_FILE_CONTENT_4 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp.*\.debug.*")
-elseif(GENERATOR_TYPE STREQUAL "DEB")
-  set(EXPECTED_FILE_CONTENT_4 ".*/usr/lib/debug/.build-id/.*\.debug.*")
+  set(EXPECTED_FILE_2 "TestDinfo-pkg*-headers.${PKG}")
+  set(EXPECTED_FILE_CONTENT_2_LIST "/bar;/bar/CMakeLists.txt")
+
+  set(EXPECTED_FILE_3 "TestDinfo-pkg*-libs.${PKG}")
+  set(EXPECTED_FILE_CONTENT_3_LIST "/bas;/bas/libtest_lib.so")
+
+  set(EXPECTED_FILE_4 "${NAME}-applications-${DEBUG_SUFFIX}*.${DEBUG_PKG}")
+  if(GENERATOR_TYPE STREQUAL "RPM")
+    set(EXPECTED_FILE_CONTENT_4 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/main.cpp.*\.debug.*")
+  elseif(GENERATOR_TYPE STREQUAL "DEB")
+    set(EXPECTED_FILE_CONTENT_4 ".*/usr/lib/debug/.build-id/.*\.debug.*")
+  endif()
+
+  if(GENERATOR_TYPE STREQUAL "RPM")
+    set(EXPECTED_FILE_5 "libs-DebugInfoPackage.rpm")
+    set(EXPECTED_FILE_CONTENT_5 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/test_lib.cpp.*\.debug.*")
+  elseif(GENERATOR_TYPE STREQUAL "DEB")
+    set(EXPECTED_FILE_5 "TestDinfo-pkg-libs-dbgsym.ddeb")
+    set(EXPECTED_FILE_CONTENT_5 ".*/usr/lib/debug/.build-id/.*\.debug.*")
+  endif()
+
+  set(EXPECTED_FILE_6 "TestDinfo-pkg*-appheaders.${PKG}")
+  set(EXPECTED_FILE_CONTENT_6_LIST "/include;/include/test_lib.hpp")
+
+elseif(PACKAGING_TYPE STREQUAL "MONOLITHIC" AND GENERATOR_TYPE STREQUAL "DEB")
+  set(EXPECTED_FILES_COUNT "2")
+
+  set(EXPECTED_FILE_1 "TestDinfo-pkg.deb")
+  set(
+    EXPECTED_FILE_CONTENT_1_LIST
+    "/bar;/bar/CMakeLists.txt;/bas;/bas/libtest_lib.so;/foo;/foo/test_prog;/include;/include/test_lib.hpp"
+  )
+
+  set(EXPECTED_FILE_2 "TestDinfo-pkg-dbgsym.ddeb")
+  set(EXPECTED_FILE_CONTENT_2 ".*/usr/lib/debug/.build-id/.*\.debug.*")
+
 endif()
-
-if(GENERATOR_TYPE STREQUAL "RPM")
-  set(EXPECTED_FILE_5 "libs-DebugInfoPackage.rpm")
-  set(EXPECTED_FILE_CONTENT_5 ".*/src${whitespaces_}/src/src_1${whitespaces_}/src/src_1/test_lib.cpp.*\.debug.*")
-elseif(GENERATOR_TYPE STREQUAL "DEB")
-  set(EXPECTED_FILE_5 "TestDinfo-pkg-libs-dbgsym.ddeb")
-  set(EXPECTED_FILE_CONTENT_5 ".*/usr/lib/debug/.build-id/.*\.debug.*")
-endif()
-
-set(EXPECTED_FILE_6 "TestDinfo-pkg*-appheaders.${PKG}")
-set(EXPECTED_FILE_CONTENT_6_LIST "/include;/include/test_lib.hpp")
diff --git a/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake b/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake
index 9ff1f8a..e9cebbf 100644
--- a/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake
+++ b/Tests/RunCMake/CPack/tests/DEBUGINFO/test.cmake
@@ -28,6 +28,8 @@
 install(FILES CMakeLists.txt DESTINATION bar COMPONENT headers)
 install(TARGETS test_lib DESTINATION bas COMPONENT libs)
 
+set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON)
+
 set(CPACK_RPM_APPLICATIONS_FILE_NAME "RPM-DEFAULT")
 set(CPACK_RPM_APPLICATIONS_DEBUGINFO_PACKAGE ON)
 set(CPACK_DEBIAN_APPLICATIONS_FILE_NAME "DEB-DEFAULT")
diff --git a/Tests/RunCMake/CPack/tests/EXTRA/DEB-stderr.txt b/Tests/RunCMake/CPack/tests/EXTRA/DEB-stderr.txt
index 37360e8..8b6ca94 100644
--- a/Tests/RunCMake/CPack/tests/EXTRA/DEB-stderr.txt
+++ b/Tests/RunCMake/CPack/tests/EXTRA/DEB-stderr.txt
@@ -1,6 +1,6 @@
 CPack Warning: Adding file to tar:
-#top level directory: .*/Tests/RunCMake/DEB.EXTRA/CPack/EXTRA-build/_CPack_Packages/Linux/DEB/extra-0.1.1-Linux/bas
+#top level directory: .*/Tests/RunCMake/DEB.EXTRA/CPack/EXTRA-build/_CPack_Packages/[^/]+/DEB/extra-0.1.1-[^/]+/bas
 #missing file: .*/Tests/RunCMake/DEB.EXTRA/CPack/EXTRA-build/conffiles
 CPack Warning: Adding file to tar:
-#top level directory: .*/Tests/RunCMake/DEB.EXTRA/CPack/EXTRA-build/_CPack_Packages/Linux/DEB/extra-0.1.1-Linux/foo
+#top level directory: .*/Tests/RunCMake/DEB.EXTRA/CPack/EXTRA-build/_CPack_Packages/[^/]+/DEB/extra-0.1.1-[^/]+/foo
 #missing file: .*/Tests/RunCMake/DEB.EXTRA/CPack/EXTRA-build/conffiles
diff --git a/Tests/RunCMake/CPack/tests/MD5SUMS/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/MD5SUMS/ExpectedFiles.cmake
index d1a3a5f..f859215 100644
--- a/Tests/RunCMake/CPack/tests/MD5SUMS/ExpectedFiles.cmake
+++ b/Tests/RunCMake/CPack/tests/MD5SUMS/ExpectedFiles.cmake
@@ -1,2 +1,2 @@
 set(EXPECTED_FILES_COUNT "1")
-set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/CMakeLists.txt")
+set(EXPECTED_FILE_CONTENT_1_LIST "/bar;/bar/CMakeLists.txt;/baz;/baz/CMakeLists.txt;/foo;/foo/CMakeLists.txt")
diff --git a/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake
index fbdda9c..13af097 100644
--- a/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake
+++ b/Tests/RunCMake/CPack/tests/MD5SUMS/VerifyResult.cmake
@@ -1,3 +1,4 @@
 set(whitespaces_ "[\t\n\r ]*")
-set(md5sums_md5sums "^.* usr/foo/CMakeLists\.txt${whitespaces_}$")
+set(hashsyms_ "[a-f0-9]+")
+set(md5sums_md5sums "^${hashsyms_}  usr/bar/CMakeLists\.txt${whitespaces_}${hashsyms_}  usr/baz/CMakeLists\.txt${whitespaces_}${hashsyms_}  usr/foo/CMakeLists\.txt${whitespaces_}$")
 verifyDebControl("${FOUND_FILE_1}" "md5sums" "md5sums")
diff --git a/Tests/RunCMake/CPack/tests/MD5SUMS/test.cmake b/Tests/RunCMake/CPack/tests/MD5SUMS/test.cmake
index 15c5892..3c922d2 100644
--- a/Tests/RunCMake/CPack/tests/MD5SUMS/test.cmake
+++ b/Tests/RunCMake/CPack/tests/MD5SUMS/test.cmake
@@ -1,4 +1,6 @@
 install(FILES CMakeLists.txt DESTINATION foo COMPONENT test)
+install(FILES CMakeLists.txt DESTINATION bar COMPONENT test)
+install(FILES CMakeLists.txt DESTINATION baz COMPONENT test)
 
 if(PACKAGING_TYPE STREQUAL "COMPONENT")
   set(CPACK_COMPONENTS_ALL test)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-OFF.cmake
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt
new file mode 100644
index 0000000..202ef80
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Warning:
+  The "Visual Studio 10 2010" generator is deprecated and will be removed in
+  a future version of CMake.
+
+  Add CMAKE_WARN_VS10=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS10-WARN-ON.cmake
diff --git a/Tests/RunCMake/CommandLine/EnvBuildType-stdout.txt b/Tests/RunCMake/CommandLine/EnvBuildType-stdout.txt
new file mode 100644
index 0000000..03b92a7
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvBuildType-stdout.txt
@@ -0,0 +1,2 @@
+-- ENV{CMAKE_BUILD_TYPE}='BuildTypeEnv'
+-- CMAKE_BUILD_TYPE='BuildTypeEnv'
diff --git a/Tests/RunCMake/CommandLine/EnvBuildType.cmake b/Tests/RunCMake/CommandLine/EnvBuildType.cmake
new file mode 100644
index 0000000..e5e6d04
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvBuildType.cmake
@@ -0,0 +1,2 @@
+message(STATUS "ENV{CMAKE_BUILD_TYPE}='$ENV{CMAKE_BUILD_TYPE}'")
+message(STATUS "CMAKE_BUILD_TYPE='${CMAKE_BUILD_TYPE}'")
diff --git a/Tests/RunCMake/CommandLine/EnvBuildTypeIgnore-stdout.txt b/Tests/RunCMake/CommandLine/EnvBuildTypeIgnore-stdout.txt
new file mode 100644
index 0000000..4a26732
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvBuildTypeIgnore-stdout.txt
@@ -0,0 +1,2 @@
+-- ENV{CMAKE_BUILD_TYPE}='BuildTypeEnv'
+-- CMAKE_BUILD_TYPE='BuildTypeOpt'
diff --git a/Tests/RunCMake/CommandLine/EnvBuildTypeIgnore.cmake b/Tests/RunCMake/CommandLine/EnvBuildTypeIgnore.cmake
new file mode 100644
index 0000000..f21666f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvBuildTypeIgnore.cmake
@@ -0,0 +1 @@
+include(EnvBuildType.cmake)
diff --git a/Tests/RunCMake/CommandLine/EnvConfigTypes-stdout.txt b/Tests/RunCMake/CommandLine/EnvConfigTypes-stdout.txt
new file mode 100644
index 0000000..bfec18f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvConfigTypes-stdout.txt
@@ -0,0 +1,2 @@
+-- ENV{CMAKE_CONFIGURATION_TYPES}='ConfigTypesEnv'
+-- CMAKE_CONFIGURATION_TYPES='ConfigTypesEnv'
diff --git a/Tests/RunCMake/CommandLine/EnvConfigTypes.cmake b/Tests/RunCMake/CommandLine/EnvConfigTypes.cmake
new file mode 100644
index 0000000..8c9b63a
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvConfigTypes.cmake
@@ -0,0 +1,2 @@
+message(STATUS "ENV{CMAKE_CONFIGURATION_TYPES}='$ENV{CMAKE_CONFIGURATION_TYPES}'")
+message(STATUS "CMAKE_CONFIGURATION_TYPES='${CMAKE_CONFIGURATION_TYPES}'")
diff --git a/Tests/RunCMake/CommandLine/EnvConfigTypesIgnore-stdout.txt b/Tests/RunCMake/CommandLine/EnvConfigTypesIgnore-stdout.txt
new file mode 100644
index 0000000..7800a4f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvConfigTypesIgnore-stdout.txt
@@ -0,0 +1,2 @@
+-- ENV{CMAKE_CONFIGURATION_TYPES}='ConfigTypesEnv'
+-- CMAKE_CONFIGURATION_TYPES='ConfigTypesOpt'
diff --git a/Tests/RunCMake/CommandLine/EnvConfigTypesIgnore.cmake b/Tests/RunCMake/CommandLine/EnvConfigTypesIgnore.cmake
new file mode 100644
index 0000000..fcbbaea
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/EnvConfigTypesIgnore.cmake
@@ -0,0 +1 @@
+include(EnvConfigTypes.cmake)
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index c58b1d0..dc066f1 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -344,6 +344,26 @@
   run_EnvironmentExportCompileCommands()
 endif()
 
+function(run_EnvironmentBuildType)
+  set(ENV{CMAKE_BUILD_TYPE} "BuildTypeEnv")
+  run_cmake(EnvBuildType)
+  run_cmake_with_options(EnvBuildTypeIgnore -DCMAKE_BUILD_TYPE=BuildTypeOpt)
+  unset(ENV{CMAKE_BUILD_TYPE})
+endfunction()
+
+function(run_EnvironmentConfigTypes)
+  set(ENV{CMAKE_CONFIGURATION_TYPES} "ConfigTypesEnv")
+  run_cmake(EnvConfigTypes)
+  run_cmake_with_options(EnvConfigTypesIgnore -DCMAKE_CONFIGURATION_TYPES=ConfigTypesOpt)
+  unset(ENV{CMAKE_CONFIGURATION_TYPES})
+endfunction()
+
+if(RunCMake_GENERATOR MATCHES "Make|^Ninja$")
+  run_EnvironmentBuildType()
+elseif(RunCMake_GENERATOR MATCHES "Ninja Multi-Config|Visual Studio|Xcode")
+  run_EnvironmentConfigTypes()
+endif()
+
 function(run_EnvironmentToolchain)
   set(ENV{CMAKE_TOOLCHAIN_FILE} "${RunCMake_SOURCE_DIR}/EnvToolchain-toolchain.cmake")
   run_cmake(EnvToolchainAbsolute)
@@ -627,9 +647,10 @@
 
 file(WRITE "${out}/first_file.txt" "first file to append\n")
 file(WRITE "${out}/second_file.txt" "second file to append\n")
+file(WRITE "${out}/empty_file.txt" "")
 file(WRITE "${out}/unicode_file.txt" "àéùç - 한국어") # Korean in Korean
 run_cmake_command(E_cat_good_cat
-  ${CMAKE_COMMAND} -E cat "${out}/first_file.txt" "${out}/second_file.txt" "${out}/unicode_file.txt")
+  ${CMAKE_COMMAND} -E cat "${out}/first_file.txt" "${out}/second_file.txt" "${out}/empty_file.txt" "${out}/unicode_file.txt")
 unset(out)
 
 run_cmake_command(E_cat_good_binary_cat
@@ -898,3 +919,10 @@
 set(RunCMake_TEST_OPTIONS --profiling-format=google-trace --profiling-output=${ProfilingTestOutput})
 run_cmake(ProfilingTest)
 unset(RunCMake_TEST_OPTIONS)
+
+if(RunCMake_GENERATOR MATCHES "^Visual Studio 10 2010")
+  run_cmake_with_options(DeprecateVS10-WARN-ON -DCMAKE_WARN_VS10=ON)
+  unset(ENV{CMAKE_WARN_VS10})
+  run_cmake(DeprecateVS10-WARN-ON)
+  run_cmake_with_options(DeprecateVS10-WARN-OFF -DCMAKE_WARN_VS10=OFF)
+endif()
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128Common.cmake b/Tests/RunCMake/CompileFeatures/CMP0128Common.cmake
new file mode 100644
index 0000000..b309d74
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128Common.cmake
@@ -0,0 +1,7 @@
+enable_language(@lang@)
+
+# Make sure the compile command is not hidden.
+string(REPLACE "${CMAKE_START_TEMP_FILE}" "" CMAKE_@lang@_COMPILE_OBJECT "${CMAKE_@lang@_COMPILE_OBJECT}")
+string(REPLACE "${CMAKE_END_TEMP_FILE}" "" CMAKE_@lang@_COMPILE_OBJECT "${CMAKE_@lang@_COMPILE_OBJECT}")
+
+add_library(foo "@RunCMake_SOURCE_DIR@/empty.@ext@")
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardDefault-build-check.cmake b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardDefault-build-check.cmake
new file mode 100644
index 0000000..4e85397
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardDefault-build-check.cmake
@@ -0,0 +1,12 @@
+foreach(flag @flags@)
+  string(FIND "${actual_stdout}" "${flag}" position)
+
+  if(NOT position EQUAL -1)
+    set(found TRUE)
+    break()
+  endif()
+endforeach()
+
+if(NOT found)
+  set(RunCMake_TEST_FAILED "No compile flags from \"@flags@\" found for LANG_STANDARD=default and @lang@_EXTENSIONS=@extensions_opposite@.")
+endif()
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardDefault.cmake b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardDefault.cmake
new file mode 100644
index 0000000..5b7358a
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardDefault.cmake
@@ -0,0 +1,2 @@
+set(CMAKE_@lang@_EXTENSIONS @extensions_opposite@)
+set(CMAKE_@lang@_STANDARD @standard_default@)
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardUnset-build-check.cmake b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardUnset-build-check.cmake
new file mode 100644
index 0000000..abe293c
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardUnset-build-check.cmake
@@ -0,0 +1,12 @@
+foreach(flag @flags@)
+  string(FIND "${actual_stdout}" "${flag}" position)
+
+  if(NOT position EQUAL -1)
+    set(found TRUE)
+    break()
+  endif()
+endforeach()
+
+if(NOT found)
+  set(RunCMake_TEST_FAILED "No compile flags from \"@flags@\" found for CMAKE_@lang@_EXTENSIONS=@extensions_opposite@.")
+endif()
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardUnset.cmake b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardUnset.cmake
new file mode 100644
index 0000000..6923c11
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128NewExtensionsStandardUnset.cmake
@@ -0,0 +1 @@
+set(CMAKE_@lang@_EXTENSIONS @extensions_opposite@)
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128NewNoUnnecessaryFlag-build-check.cmake b/Tests/RunCMake/CompileFeatures/CMP0128NewNoUnnecessaryFlag-build-check.cmake
new file mode 100644
index 0000000..4f767fa
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128NewNoUnnecessaryFlag-build-check.cmake
@@ -0,0 +1,8 @@
+foreach(flag @flags@)
+  string(FIND "${actual_stdout}" "${flag}" position)
+
+  if(NOT position EQUAL -1)
+    set(RunCMake_TEST_FAILED "\"${flag}\" compile flag found.")
+    break()
+  endif()
+endforeach()
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128NewNoUnnecessaryFlag.cmake b/Tests/RunCMake/CompileFeatures/CMP0128NewNoUnnecessaryFlag.cmake
new file mode 100644
index 0000000..73c0641
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128NewNoUnnecessaryFlag.cmake
@@ -0,0 +1,2 @@
+set(CMAKE_@lang@_EXTENSIONS @extensions_default@)
+set(CMAKE_@lang@_STANDARD @standard_default@)
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128OldSameStandard-build-check.cmake b/Tests/RunCMake/CompileFeatures/CMP0128OldSameStandard-build-check.cmake
new file mode 100644
index 0000000..8074b9d
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128OldSameStandard-build-check.cmake
@@ -0,0 +1,12 @@
+foreach(flag @flags@)
+  string(FIND "${actual_stdout}" "${flag}" position)
+
+  if(NOT position EQUAL -1)
+    set(found TRUE)
+    break()
+  endif()
+endforeach()
+
+if(NOT found)
+  set(RunCMake_TEST_FAILED "No compile flags from \"@flags@\" found for LANG_STANDARD=default.")
+endif()
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128OldSameStandard.cmake b/Tests/RunCMake/CompileFeatures/CMP0128OldSameStandard.cmake
new file mode 100644
index 0000000..3be0f10
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128OldSameStandard.cmake
@@ -0,0 +1 @@
+set(CMAKE_@lang@_STANDARD @standard_default@)
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
new file mode 100644
index 0000000..320c2ba
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) in CMakeLists\.txt:
+  Policy CMP0128 is not set: Selection of language standard and extension
+  flags improved\.  Run "cmake --help-policy CMP0128" for policy details\.  Use
+  the cmake_policy command to set the policy and suppress this warning\.
+
+  For compatibility with older versions of CMake, unnecessary flags for
+  language standard or compiler extensions may be added.
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch.cmake b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch.cmake
new file mode 100644
index 0000000..7974093
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnMatch.cmake
@@ -0,0 +1,5 @@
+cmake_policy(SET CMP0128 OLD)
+set(CMAKE_POLICY_WARNING_CMP0128 ON)
+
+set(CMAKE_@lang@_EXTENSIONS @extensions_default@)
+set(CMAKE_@lang@_STANDARD @standard_default@)
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
new file mode 100644
index 0000000..068cba9
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) in CMakeLists\.txt:
+  Policy CMP0128 is not set: Selection of language standard and extension
+  flags improved\.  Run "cmake --help-policy CMP0128" for policy details\.  Use
+  the cmake_policy command to set the policy and suppress this warning\.
+
+  For compatibility with older versions of CMake, compiler extensions won't
+  be @opposite@\.
+This warning is for project developers\.  Use -Wno-dev to suppress it\.
diff --git a/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset.cmake b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset.cmake
new file mode 100644
index 0000000..33425a1
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/CMP0128WarnUnset.cmake
@@ -0,0 +1,4 @@
+cmake_policy(SET CMP0128 OLD)
+set(CMAKE_POLICY_WARNING_CMP0128 ON)
+
+set(CMAKE_@lang@_EXTENSIONS @extensions_opposite@)
diff --git a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
index 5a70da2..ad9619e 100644
--- a/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CompileFeatures/RunCMakeTest.cmake
@@ -9,17 +9,8 @@
 run_cmake(NotAFeature_OriginDebugTransitive)
 run_cmake(NotAFeature_OriginDebugCommand)
 
-run_cmake(generate_feature_list)
-file(READ
-  "${RunCMake_BINARY_DIR}/generate_feature_list-build/c_features.txt"
-  C_FEATURES
-)
-file(READ
-  "${RunCMake_BINARY_DIR}/generate_feature_list-build/cxx_features.txt"
-  CXX_FEATURES
-)
-include("${RunCMake_BINARY_DIR}/generate_feature_list-build/c_standard_default.cmake")
-include("${RunCMake_BINARY_DIR}/generate_feature_list-build/cxx_standard_default.cmake")
+run_cmake(compiler_introspection)
+include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake")
 
 if (NOT C_FEATURES)
   run_cmake(NoSupportedCFeatures)
@@ -43,21 +34,159 @@
   unset(RunCMake_TEST_OPTIONS)
 endif()
 
+configure_file("${RunCMake_SOURCE_DIR}/CMakeLists.txt" "${RunCMake_BINARY_DIR}/CMakeLists.txt" COPYONLY)
+file(READ "${RunCMake_SOURCE_DIR}/CMP0128Common.cmake" cmp0128_common)
+
+function(test_build)
+  set(test ${name}-${lang})
+
+  file(READ "${RunCMake_SOURCE_DIR}/${name}.cmake" cmake)
+  file(CONFIGURE OUTPUT "${RunCMake_BINARY_DIR}/${test}.cmake" CONTENT "${cmake}${cmp0128_common}" @ONLY)
+  if(EXISTS "${RunCMake_SOURCE_DIR}/${name}-build-check.cmake")
+    configure_file("${RunCMake_SOURCE_DIR}/${name}-build-check.cmake" "${RunCMake_BINARY_DIR}/${test}-build-check.cmake" @ONLY)
+  endif()
+  if(EXISTS "${RunCMake_SOURCE_DIR}/${name}-stderr.txt")
+    configure_file("${RunCMake_SOURCE_DIR}/${name}-stderr.txt" "${RunCMake_BINARY_DIR}/${test}-stderr.txt" @ONLY)
+  endif()
+
+  set(RunCMake_SOURCE_DIR "${RunCMake_BINARY_DIR}")
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test}-build")
+  run_cmake(${test})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . ${ARGN})
+endfunction()
+
+# Mangle flags such as they're in verbose build output.
+macro(mangle_flags variable)
+  set(result "${${variable}}")
+
+  if(RunCMake_GENERATOR MATCHES "Visual Studio" AND MSVC_TOOLSET_VERSION GREATER_EQUAL 141)
+    string(REPLACE "-" "/" result "${result}")
+  elseif(RunCMake_GENERATOR STREQUAL "Xcode" AND CMAKE_XCODE_BUILD_SYSTEM GREATER_EQUAL 12)
+    string(REPLACE "=" [[\\=]] result "${result}")
+  endif()
+
+  string(REPLACE ";" " " result "${result}")
+  list(APPEND flags "${result}")
+endmacro()
+
+function(test_cmp0128_old_same_standard)
+  if(extensions_default)
+    set(flag_ext "_EXT")
+  endif()
+
+  set(flag "${${lang}${${lang}_STANDARD_DEFAULT}${flag_ext}_FLAG}")
+
+  if(NOT flag)
+    return()
+  endif()
+
+  mangle_flags(flag)
+
+  set(name CMP0128OldSameStandard)
+  test_build(--verbose)
+endfunction()
+
+function(test_cmp0128_new_extensions_opposite)
+  if(extensions_opposite)
+    set(flag_ext "_EXT")
+  endif()
+
+  set(flag "${${lang}${${lang}_STANDARD_DEFAULT}${flag_ext}_FLAG}")
+
+  if(NOT flag)
+    return()
+  endif()
+
+  mangle_flags(flag)
+
+  # Make sure we enable/disable extensions when:
+  # 1. LANG_STANDARD is unset.
+  set(name CMP0128NewExtensionsStandardUnset)
+  set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0128=NEW)
+  test_build(--verbose)
+
+  # 2. LANG_STANDARD matches CMAKE_LANG_STANDARD_DEFAULT.
+  set(name CMP0128NewExtensionsStandardDefault)
+  test_build(--verbose)
+endfunction()
+
+function(test_cmp0128_new_no_unnecessary_flag)
+  set(standard_flag "${${lang}${${lang}_STANDARD_DEFAULT}_FLAG}")
+  set(extension_flag "${${lang}${${lang}_STANDARD_DEFAULT}_EXT_FLAG}")
+
+  if(NOT standard_flag AND NOT extension_flag)
+    return()
+  endif()
+
+  mangle_flags(standard_flag)
+  mangle_flags(extension_flag)
+
+  set(name CMP0128NewNoUnnecessaryFlag)
+  set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0128=NEW)
+  test_build(--verbose)
+endfunction()
+
+function(test_cmp0128_warn_match)
+  set(name CMP0128WarnMatch)
+  test_build()
+endfunction()
+
+function(test_cmp0128_warn_unset)
+  # For compilers that had CMAKE_<LANG>_EXTENSION_COMPILE_OPTION (only IAR)
+  # there is no behavioural change and thus no warning.
+  if(NOT "${${lang}_EXT_FLAG}" STREQUAL "")
+    return()
+  endif()
+
+  if(extensions_opposite)
+    set(opposite "enabled")
+  else()
+    set(opposite "disabled")
+  endif()
+
+  set(name CMP0128WarnUnset)
+  test_build()
+endfunction()
+
+function(test_lang lang ext)
+  if(CMake_NO_${lang}_STANDARD)
+    return()
+  endif()
+
+  set(extensions_default "${${lang}_EXTENSIONS_DEFAULT}")
+  set(standard_default "${${lang}_STANDARD_DEFAULT}")
+
+  if(extensions_default)
+    set(extensions_opposite OFF)
+  else()
+    set(extensions_opposite ON)
+  endif()
+
+  test_cmp0128_new_extensions_opposite()
+  test_cmp0128_new_no_unnecessary_flag()
+  test_cmp0128_old_same_standard()
+  test_cmp0128_warn_match()
+  test_cmp0128_warn_unset()
+endfunction()
+
+if(C_STANDARD_DEFAULT)
+  test_lang(C c)
+endif()
+
 if(CXX_STANDARD_DEFAULT)
   run_cmake(NotAStandard)
 
   foreach(standard 98 11)
-    file(READ
-      "${RunCMake_BINARY_DIR}/generate_feature_list-build/cxx${standard}_flag.txt"
-      CXX${standard}_FLAG
-    )
     if (CXX${standard}_FLAG STREQUAL NOTFOUND)
       run_cmake(RequireCXX${standard})
       run_cmake(RequireCXX${standard}Variable)
     endif()
-    if (CXX${standard}EXT_FLAG STREQUAL NOTFOUND)
+    if (CXX${standard}_EXT_FLAG STREQUAL NOTFOUND)
       run_cmake(RequireCXX${standard}Ext)
       run_cmake(RequireCXX${standard}ExtVariable)
     endif()
   endforeach()
+
+  test_lang(CXX cpp)
 endif()
diff --git a/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake b/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake
new file mode 100644
index 0000000..5691344
--- /dev/null
+++ b/Tests/RunCMake/CompileFeatures/compiler_introspection.cmake
@@ -0,0 +1,38 @@
+enable_language(C CXX)
+
+set(info "")
+
+if(MSVC_TOOLSET_VERSION)
+  string(APPEND info "
+set(MSVC_TOOLSET_VERSION ${MSVC_TOOLSET_VERSION})
+
+")
+endif()
+
+if(CMAKE_XCODE_BUILD_SYSTEM)
+  string(APPEND info "
+set(CMAKE_XCODE_BUILD_SYSTEM ${CMAKE_XCODE_BUILD_SYSTEM})
+
+")
+endif()
+
+macro(info lang)
+  string(APPEND info "\
+set(${lang}_STANDARD_DEFAULT ${CMAKE_${lang}_STANDARD_DEFAULT})
+set(${lang}_EXTENSIONS_DEFAULT ${CMAKE_${lang}_EXTENSIONS_DEFAULT})
+set(${lang}_FEATURES ${CMAKE_${lang}_COMPILE_FEATURES})
+
+set(${lang}_EXT_FLAG ${CMAKE_${lang}_EXTENSION_COMPILE_OPTION})
+")
+
+  foreach(standard ${ARGN})
+    string(APPEND info "\
+set(${lang}${standard}_FLAG ${CMAKE_${lang}${standard}_STANDARD_COMPILE_OPTION})
+set(${lang}${standard}_EXT_FLAG ${CMAKE_${lang}${standard}_EXTENSION_COMPILE_OPTION})
+")
+  endforeach()
+endmacro()
+
+info(C 90 99 11 17 23)
+info(CXX 98 11 14 17 20 23)
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/CompileFeatures/empty.c b/Tests/RunCMake/CompileFeatures/empty.c
index 11ec041..8d91e77 100644
--- a/Tests/RunCMake/CompileFeatures/empty.c
+++ b/Tests/RunCMake/CompileFeatures/empty.c
@@ -1,7 +1,7 @@
 #ifdef _WIN32
 __declspec(dllexport)
 #endif
-  int empty()
+  int empty(void)
 {
   return 0;
 }
diff --git a/Tests/RunCMake/CompileFeatures/generate_feature_list.cmake b/Tests/RunCMake/CompileFeatures/generate_feature_list.cmake
deleted file mode 100644
index 5c58052..0000000
--- a/Tests/RunCMake/CompileFeatures/generate_feature_list.cmake
+++ /dev/null
@@ -1,43 +0,0 @@
-
-enable_language(C)
-
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/c_features.txt"
-  "${CMAKE_C_COMPILE_FEATURES}"
-)
-
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx_features.txt"
-  "${CMAKE_CXX_COMPILE_FEATURES}"
-)
-
-if(DEFINED CMAKE_C_STANDARD_DEFAULT)
-  set(c_standard_default_code "set(C_STANDARD_DEFAULT \"${CMAKE_C_STANDARD_DEFAULT}\")\n")
-else()
-  set(c_standard_default_code "unset(C_STANDARD_DEFAULT)\n")
-endif()
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/c_standard_default.cmake" "${c_standard_default_code}")
-
-if(DEFINED CMAKE_CXX_STANDARD_DEFAULT)
-  set(cxx_standard_default_code "set(CXX_STANDARD_DEFAULT \"${CMAKE_CXX_STANDARD_DEFAULT}\")\n")
-else()
-  set(cxx_standard_default_code "unset(CXX_STANDARD_DEFAULT)\n")
-endif()
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx_standard_default.cmake" "${cxx_standard_default_code}")
-
-foreach(standard 98 11)
-  set(CXX${standard}_FLAG NOTFOUND)
-  if (DEFINED CMAKE_CXX${standard}_STANDARD_COMPILE_OPTION)
-    set(CXX${standard}_FLAG ${CMAKE_CXX${standard}_STANDARD_COMPILE_OPTION})
-  endif()
-
-  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx${standard}_flag.txt"
-    "${CXX${standard}_FLAG}"
-  )
-  set(CXX${standard}EXT_FLAG NOTFOUND)
-  if (DEFINED CMAKE_CXX${standard}_EXTENSION_COMPILE_OPTION)
-    set(CXX${standard}EXT_FLAG ${CMAKE_CXX${standard}_EXTENSION_COMPILE_OPTION})
-  endif()
-
-  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx${standard}ext_flag.txt"
-    "${CXX${standard}EXT_FLAG}"
-  )
-endforeach()
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
index 9baeab7..e83c45e 100644
--- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -9,6 +9,7 @@
 run_cmake(DownloadFile)
 run_cmake(SameGenerator)
 run_cmake(VarDefinitions)
+run_cmake(VarPassthroughs)
 run_cmake(GetProperties)
 run_cmake(UsesTerminalOverride)
 run_cmake(MakeAvailable)
diff --git a/Tests/RunCMake/FetchContent/VarPassthroughs.cmake b/Tests/RunCMake/FetchContent/VarPassthroughs.cmake
new file mode 100644
index 0000000..ad743d8
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/VarPassthroughs.cmake
@@ -0,0 +1,38 @@
+include(FetchContent)
+
+set(CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY AAAA)
+set(CMAKE_TLS_VERIFY BBBB)
+set(CMAKE_TLS_CAINFO CCCC)
+set(CMAKE_NETRC DDDD)
+set(CMAKE_NETRC_FILE EEEE)
+
+FetchContent_Declare(PassThrough
+  DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E echo "Download command executed"
+)
+FetchContent_Populate(PassThrough)
+
+set(gen_file ${FETCHCONTENT_BASE_DIR}/passthrough-subbuild/CMakeLists.txt)
+if(NOT EXISTS ${gen_file})
+  message(FATAL_ERROR "File does not exist: ${gen_file}")
+endif()
+file(READ ${gen_file} contents)
+
+if(NOT contents MATCHES "CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY \\[==\\[AAAA\\]==\\]")
+  message(FATAL_ERROR "Missing CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY")
+endif()
+
+if(NOT contents MATCHES "CMAKE_TLS_VERIFY \\[==\\[BBBB\\]==\\]")
+  message(FATAL_ERROR "Missing CMAKE_TLS_VERIFY")
+endif()
+
+if(NOT contents MATCHES "CMAKE_TLS_CAINFO \\[==\\[CCCC\\]==\\]")
+  message(FATAL_ERROR "Missing CMAKE_TLS_CAINFO")
+endif()
+
+if(NOT contents MATCHES "CMAKE_NETRC \\[==\\[DDDD\\]==\\]")
+  message(FATAL_ERROR "Missing CMAKE_NETRC")
+endif()
+
+if(NOT contents MATCHES "CMAKE_NETRC_FILE \\[==\\[EEEE\\]==\\]")
+  message(FATAL_ERROR "Missing CMAKE_NETRC_FILE")
+endif()
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake
new file mode 100644
index 0000000..e49ff22
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_MATCHING_ARGN.cmake
@@ -0,0 +1,17 @@
+if(WIN32)
+    set(ENV{PKG_CONFIG} "\"${CMAKE_CURRENT_SOURCE_DIR}\\dummy-pkg-config.bat\" --static --print-errors")
+else()
+    set(ENV{PKG_CONFIG} "\"${CMAKE_CURRENT_SOURCE_DIR}/dummy-pkg-config.sh\" --static --print-errors")
+endif()
+
+find_package(PkgConfig REQUIRED)
+
+if(NOT PKG_CONFIG_ARGN STREQUAL "--static;--print-errors")
+  message(SEND_ERROR "PKG_CONFIG_ARGN has wrong value '${PKG_CONFIG_ARGN}'")
+endif()
+
+_pkgconfig_invoke("none" "prefix" "output" "")
+
+if(NOT prefix_output STREQUAL "Received;--static;Received;--print-errors")
+  message(SEND_ERROR "prefix_output has wrong value '${prefix_output}'")
+endif()
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt
new file mode 100644
index 0000000..6615d80
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt
@@ -0,0 +1,3 @@
+-- ZOT_LIBRARIES='zot'
+-- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/zot/lib/prefix-zot-suffix'
+-- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/zot/lib;-lzot'
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake
new file mode 100644
index 0000000..9f654b5
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake
@@ -0,0 +1,29 @@
+find_package(PkgConfig REQUIRED)
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/zot/lib/pkgconfig/zot.pc" "
+prefix=${CMAKE_CURRENT_BINARY_DIR}/zot
+libdir=\${prefix}/lib
+
+Name: Zot
+Description: Dummy packaget to test LIBRARY_DIR support
+Version: 1.0
+Libs: -L\${libdir} -lzot
+")
+
+# Create a "library" file to find in libdir.
+set(CMAKE_FIND_LIBRARY_PREFIXES "prefix-")
+set(CMAKE_FIND_LIBRARY_SUFFIXES "-suffix")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/zot/lib/prefix-zot-suffix")
+
+# 'pkg-config --libs' drops -L flags in PKG_CONFIG_SYSTEM_LIBRARY_PATH by default.
+set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "${CMAKE_CURRENT_BINARY_DIR}/zot/lib")
+
+# 'pkgconf --libs' also drops -L flags in LIBRARY_PATH by default.
+set(ENV{LIBRARY_PATH}                   "${CMAKE_CURRENT_BINARY_DIR}/zot/lib")
+
+set(ENV{PKG_CONFIG_PATH}                "${CMAKE_CURRENT_BINARY_DIR}/zot/lib/pkgconfig")
+pkg_check_modules(ZOT REQUIRED zot)
+
+message(STATUS "ZOT_LIBRARIES='${ZOT_LIBRARIES}'")
+message(STATUS "ZOT_LINK_LIBRARIES='${ZOT_LINK_LIBRARIES}'")
+message(STATUS "ZOT_LDFLAGS='${ZOT_LDFLAGS}'")
diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
index 78ccd96..f36d1eb 100644
--- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
@@ -13,6 +13,7 @@
 run_cmake(FindPkgConfig_PKGCONFIG_PATH_NO_CMAKE_PATH)
 run_cmake(FindPkgConfig_PKGCONFIG_PATH_NO_CMAKE_ENVIRONMENT_PATH)
 run_cmake(FindPkgConfig_extract_frameworks)
+run_cmake(FindPkgConfig_GET_MATCHING_ARGN)
 
 if(APPLE)
   run_cmake(FindPkgConfig_extract_frameworks_target)
@@ -31,4 +32,8 @@
   run_cmake(FindPkgConfig_VERSION_OPERATORS)
   run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME)
   run_cmake(FindPkgConfig_empty_target)
+
+  if(NOT RunCMake_BINARY_DIR MATCHES " ")
+    run_cmake(FindPkgConfig_LIBRARY_PATH)
+  endif()
 endif ()
diff --git a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat
index b038370..c91713b 100755
--- a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat
+++ b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.bat
@@ -1,5 +1,10 @@
 @ECHO OFF
 
+rem variables to get around `--static --version` printing the received
+rem message and then version
+set static=false
+set print_errors=false
+
 :LOOP
 
 IF "%1"=="" (
@@ -21,7 +26,19 @@
     EXIT /B 0
   )
 )
+IF "%1"=="--static" (
+  set static=true
+)
+IF "%1"=="--print-errors" (
+  set print_errors=true
+)
 SHIFT
 IF NOT "%~1"=="" GOTO LOOP
 
+IF "%static%"=="true" ECHO Received --static
+IF "%print_errors%"=="true" ECHO Received --print-errors
+
+IF "%static%"=="true" GOTO :EOF
+IF "%print_errors%"=="true" GOTO :EOF
+
 EXIT /B 255
diff --git a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh
index 56bba30..4021bf7 100755
--- a/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh
+++ b/Tests/RunCMake/FindPkgConfig/dummy-pkg-config.sh
@@ -4,6 +4,11 @@
 # to the --exists argument with the PKG_CONFIG_PATH environment variable
 # and returns 1 if they are different.
 
+# variables to get around `--static --version` printing the received
+# message and then version
+static=false
+print_errors=false
+
 while [ $# -gt 0 ]; do
   case $1 in
     --version)
@@ -17,7 +22,21 @@
       echo "Found:    ${PKG_CONFIG_PATH}"
       [ "${last}" = "${PKG_CONFIG_PATH}" ] && exit 0 || exit 1
       ;;
+    --static)
+      static=true
+      ;;
+    --print-errors)
+      print_errors=true
+      ;;
   esac
   shift
 done
+
+$static && echo "Received --static"
+$print_errors && echo "Received --print-errors"
+
+if $static || $print_errors; then
+  exit 0
+fi
+
 exit 255
diff --git a/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt b/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt
new file mode 100644
index 0000000..cae3679
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/CMP0085-OLD-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_minimum_required\):
+  The OLD behavior for policy CMP0085 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.$
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-basic-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-basic-stdout.txt
new file mode 100644
index 0000000..385159d
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-basic-stdout.txt
@@ -0,0 +1,7 @@
+Test project .*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change
+  Test #1: basic\.case_foo
+  Test #2: basic\.case_bar
+  Test #3: basic\.disabled_case \(Disabled\)
+  Test #4: basic\.DISABLEDnot_really_case
+
+Total Tests: 4
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-typed-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-typed-stdout.txt
new file mode 100644
index 0000000..095fdcd
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change-typed-stdout.txt
@@ -0,0 +1,5 @@
+Test project .*/Tests/RunCMake/GoogleTest/GoogleTest-discovery-arg-change
+  Test #1: typed/short\.case
+  Test #2: typed/float\.case
+
+Total Tests: 2
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt
new file mode 100644
index 0000000..d06cd0a
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-skip-test-stdout.txt
@@ -0,0 +1,10 @@
+Test project .*
+    Start 36: skip_test.test1
+1/1 Test #36: skip_test.test1 \.+\*\*\*Skipped +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of 1
+
+Total Test time \(real\) = +[0-9.]+ sec
+
+The following tests did not run:
+.*36 - skip_test\.test1 \(Skipped\)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-skip-timeout-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-skip-timeout-stdout.txt
deleted file mode 100644
index eeecb6a..0000000
--- a/Tests/RunCMake/GoogleTest/GoogleTest-skip-timeout-stdout.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Test project .*
-    Start 20: skip_test.test1
-1/1 Test #20: skip_test.test1 \.+\*\*\*Skipped +[0-9.]+ sec
-
-100% tests passed, 0 tests failed out of 1
-
-Total Test time \(real\) = +[0-9.]+ sec
-
-The following tests did not run:
-.*20 - skip_test\.test1 \(Skipped\)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt
new file mode 100644
index 0000000..cf08267
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-test3-stdout.txt
@@ -0,0 +1,16 @@
+Test project .*
+    Start 27: TEST:basic\.case_foo!3
+1/4 Test #27: TEST:basic\.case_foo!3 \.+ +Passed +[0-9.]+ sec
+    Start 28: TEST:basic\.case_bar!3
+2/4 Test #28: TEST:basic\.case_bar!3 \.+ +Passed +[0-9.]+ sec
+    Start 29: TEST:basic\.disabled_case!3
+3/4 Test #29: TEST:basic\.disabled_case!3 \.+\*+Not Run \(Disabled\) +[0-9.]+ sec
+    Start 30: TEST:basic\.DISABLEDnot_really_case!3
+4/4 Test #30: TEST:basic\.DISABLEDnot_really_case!3 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of 3
+
+Total Test time \(real\) = +[0-9.]+ sec
+
+The following tests did not run:
+.*29 - TEST:basic.disabled_case!3 \(Disabled\)
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt b/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt
new file mode 100644
index 0000000..4a9d75b
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTest-test4-stdout.txt
@@ -0,0 +1,9 @@
+Test project .*
+    Start 31: TEST:typed/short\.case!4
+1/2 Test #31: TEST:typed/short\.case!4 \.+ +Passed +[0-9.]+ sec
+    Start 32: TEST:typed/float\.case!4
+2/2 Test #32: TEST:typed/float\.case!4 \.+ +Passed +[0-9.]+ sec
+
+100% tests passed, 0 tests failed out of 2
+
+Total Test time \(real\) = +[0-9.]+ sec
diff --git a/Tests/RunCMake/GoogleTest/GoogleTest.cmake b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
index 8efd117..221d6ad 100644
--- a/Tests/RunCMake/GoogleTest/GoogleTest.cmake
+++ b/Tests/RunCMake/GoogleTest/GoogleTest.cmake
@@ -24,6 +24,24 @@
   PROPERTIES LABELS TEST2
 )
 
+gtest_discover_tests(
+  fake_gtest
+  TEST_PREFIX TEST:
+  TEST_SUFFIX !3
+  TEST_FILTER basic*
+  EXTRA_ARGS how now "\"brown\" cow"
+  PROPERTIES LABELS TEST3
+)
+
+gtest_discover_tests(
+  fake_gtest
+  TEST_PREFIX TEST:
+  TEST_SUFFIX !4
+  TEST_FILTER typed*
+  EXTRA_ARGS how now "\"brown\" cow"
+  PROPERTIES LABELS TEST4
+)
+
 add_executable(no_tests_defined no_tests_defined.cpp)
 xcode_sign_adhoc(no_tests_defined)
 
diff --git a/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryArgChange.cmake b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryArgChange.cmake
new file mode 100644
index 0000000..e4e13c5
--- /dev/null
+++ b/Tests/RunCMake/GoogleTest/GoogleTestDiscoveryArgChange.cmake
@@ -0,0 +1,14 @@
+enable_language(CXX)
+include(GoogleTest)
+
+enable_testing()
+
+include(xcode_sign_adhoc.cmake)
+
+add_executable(fake_gtest fake_gtest.cpp)
+xcode_sign_adhoc(fake_gtest)
+
+gtest_discover_tests(
+  fake_gtest
+  TEST_FILTER "${TEST_FILTER}*"
+)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
index 530c8ab..33a4b43 100644
--- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -1,5 +1,12 @@
 include(RunCMake)
 
+if(RunCMake_GENERATOR STREQUAL "Borland Makefiles" OR
+   RunCMake_GENERATOR STREQUAL "Watcom WMake")
+  set(fs_delay 3)
+else()
+  set(fs_delay 1.125)
+endif()
+
 function(run_GoogleTest DISCOVERY_MODE)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-build)
@@ -40,6 +47,20 @@
     --no-label-summary
   )
 
+  run_cmake_command(GoogleTest-test3
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L TEST3
+    --no-label-summary
+  )
+
+  run_cmake_command(GoogleTest-test4
+    ${CMAKE_CTEST_COMMAND}
+    -C Debug
+    -L TEST4
+    --no-label-summary
+  )
+
   run_cmake_command(GoogleTest-test-missing
     ${CMAKE_CTEST_COMMAND}
     -C Debug
@@ -139,6 +160,46 @@
   )
 endfunction()
 
+function(run_GoogleTest_discovery_arg_change DISCOVERY_MODE)
+  # Use a single build tree for a few tests without cleaning.
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-arg-change)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake_with_options(GoogleTestDiscoveryArgChange
+    -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE}
+    -DTEST_FILTER=basic
+  )
+  run_cmake_command(GoogleTest-discovery-arg-change-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Release
+    --target fake_gtest
+  )
+  run_cmake_command(GoogleTest-discovery-arg-change-basic
+    ${CMAKE_CTEST_COMMAND}
+    -C Release
+    -N
+  )
+  execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${fs_delay}) # handle 1s resolution
+  run_cmake_with_options(GoogleTestDiscoveryArgChange
+    -DCMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE=${DISCOVERY_MODE}
+    -DTEST_FILTER=typed
+  )
+  run_cmake_command(GoogleTest-discovery-arg-change-build
+    ${CMAKE_COMMAND}
+    --build .
+    --config Release
+    --target fake_gtest
+  )
+  run_cmake_command(GoogleTest-discovery-arg-change-typed
+    ${CMAKE_CTEST_COMMAND}
+    -C Release
+    -N
+  )
+endfunction()
+
 function(run_GoogleTest_discovery_multi_config)
   # Use a single build tree for a few tests without cleaning.
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/GoogleTest-discovery-multi-config)
@@ -181,6 +242,10 @@
   run_GoogleTestXML(${DISCOVERY_MODE})
   message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...")
   run_GoogleTest_discovery_timeout(${DISCOVERY_MODE})
+  if(# VS 9 does not rebuild if POST_BUILD command changes.
+      NOT "${DISCOVERY_MODE};${RunCMake_GENERATOR}" MATCHES "^POST_BUILD;Visual Studio 9")
+    run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE})
+  endif()
 endforeach()
 
 if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
diff --git a/Tests/RunCMake/GoogleTest/fake_gtest.cpp b/Tests/RunCMake/GoogleTest/fake_gtest.cpp
index 1956c37..b2a5cb4 100644
--- a/Tests/RunCMake/GoogleTest/fake_gtest.cpp
+++ b/Tests/RunCMake/GoogleTest/fake_gtest.cpp
@@ -7,27 +7,42 @@
   // it only requires that we produces output in the expected format when
   // invoked with --gtest_list_tests. Thus, we fake that here. This allows us
   // to test the module without actually needing Google Test.
+  bool is_filtered =
+    argc > 2 && std::string(argv[2]).find("--gtest_filter=") == 0;
+  bool is_basic_only =
+    is_filtered && std::string(argv[2]).find("basic*") != std::string::npos;
+  bool is_typed_only =
+    is_filtered && std::string(argv[2]).find("typed*") != std::string::npos;
+
   if (argc > 1 && std::string(argv[1]) == "--gtest_list_tests") {
-    std::cout << "basic." << std::endl;
-    std::cout << "  case_foo" << std::endl;
-    std::cout << "  case_bar" << std::endl;
-    std::cout << "  DISABLED_disabled_case" << std::endl;
-    std::cout << "  DISABLEDnot_really_case" << std::endl;
-    std::cout << "DISABLED_disabled." << std::endl;
-    std::cout << "  case" << std::endl;
-    std::cout << "DISABLEDnotreally." << std::endl;
-    std::cout << "  case" << std::endl;
-    std::cout << "typed/0.  # TypeParam = short" << std::endl;
-    std::cout << "  case" << std::endl;
-    std::cout << "typed/1.  # TypeParam = float" << std::endl;
-    std::cout << "  case" << std::endl;
-    std::cout << "value/test." << std::endl;
-    std::cout << "  case/0  # GetParam() = 1" << std::endl;
-    std::cout << "  case/1  # GetParam() = \"foo\"" << std::endl;
-    std::cout << "param/special." << std::endl;
-    std::cout << "  case/0  # GetParam() = \"semicolon;\"" << std::endl;
-    std::cout << "  case/1  # GetParam() = \"backslash\\\"" << std::endl;
-    std::cout << "  case/2  # GetParam() = \"${var}\"" << std::endl;
+    if (!is_typed_only) {
+      std::cout << "basic." << std::endl;
+      std::cout << "  case_foo" << std::endl;
+      std::cout << "  case_bar" << std::endl;
+      std::cout << "  DISABLED_disabled_case" << std::endl;
+      std::cout << "  DISABLEDnot_really_case" << std::endl;
+    }
+    if (!is_basic_only && !is_typed_only) {
+      std::cout << "DISABLED_disabled." << std::endl;
+      std::cout << "  case" << std::endl;
+      std::cout << "DISABLEDnotreally." << std::endl;
+      std::cout << "  case" << std::endl;
+    }
+    if (!is_basic_only) {
+      std::cout << "typed/0.  # TypeParam = short" << std::endl;
+      std::cout << "  case" << std::endl;
+      std::cout << "typed/1.  # TypeParam = float" << std::endl;
+      std::cout << "  case" << std::endl;
+    }
+    if (!is_basic_only && !is_typed_only) {
+      std::cout << "value/test." << std::endl;
+      std::cout << "  case/0  # GetParam() = 1" << std::endl;
+      std::cout << "  case/1  # GetParam() = \"foo\"" << std::endl;
+      std::cout << "param/special." << std::endl;
+      std::cout << "  case/0  # GetParam() = \"semicolon;\"" << std::endl;
+      std::cout << "  case/1  # GetParam() = \"backslash\\\"" << std::endl;
+      std::cout << "  case/2  # GetParam() = \"${var}\"" << std::endl;
+    }
     return 0;
   }
 
diff --git a/Tests/RunCMake/Ninja/Qt5AutoMocDeps.cmake b/Tests/RunCMake/Ninja/Qt5AutoMocDeps.cmake
deleted file mode 100644
index 46b840f..0000000
--- a/Tests/RunCMake/Ninja/Qt5AutoMocDeps.cmake
+++ /dev/null
@@ -1,21 +0,0 @@
-enable_language(CXX)
-
-find_package(Qt5Core REQUIRED)
-
-set(CMAKE_AUTOMOC ON)
-
-add_library(simple_lib SHARED simple_lib.cpp)
-add_executable(app_with_qt app.cpp app_qt.cpp)
-target_link_libraries(app_with_qt PRIVATE simple_lib Qt5::Core)
-
-if(Qt5Widgets_DIR)
-  find_package(Qt5Widgets REQUIRED)
-  qt5_wrap_ui(_headers MyWindow.ui)
-  add_executable(app_with_widget app.cpp MyWindow.cpp ${_headers})
-  target_link_libraries(app_with_widget PRIVATE Qt5::Widgets)
-  target_include_directories(app_with_widget PRIVATE "${CMAKE_BINARY_DIR}")
-endif()
-
-add_subdirectory(QtSubDir1)
-add_subdirectory(QtSubDir2)
-add_subdirectory(QtSubDir3)
diff --git a/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake b/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake
new file mode 100644
index 0000000..c441169
--- /dev/null
+++ b/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake
@@ -0,0 +1,27 @@
+enable_language(CXX)
+
+set(QtX Qt${with_qt_version})
+
+find_package(${QtX} REQUIRED COMPONENTS Core)
+
+set(CMAKE_AUTOMOC ON)
+
+add_library(simple_lib SHARED simple_lib.cpp)
+add_executable(app_with_qt app.cpp app_qt.cpp)
+target_link_libraries(app_with_qt PRIVATE simple_lib ${QtX}::Core)
+
+if(${QtX}Widgets_DIR)
+  find_package(${QtX} REQUIRED COMPONENTS Widgets)
+  if(with_qt_version STREQUAL 5)
+    qt5_wrap_ui(_headers MyWindow.ui)
+  else()
+    qt_wrap_ui(_headers MyWindow.ui)
+  endif()
+  add_executable(app_with_widget app.cpp MyWindow.cpp ${_headers})
+  target_link_libraries(app_with_widget PRIVATE ${QtX}::Widgets)
+  target_include_directories(app_with_widget PRIVATE "${CMAKE_BINARY_DIR}")
+endif()
+
+add_subdirectory(QtSubDir1)
+add_subdirectory(QtSubDir2)
+add_subdirectory(QtSubDir3)
diff --git a/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt
index 64016b6..3a12dcd 100644
--- a/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt
+++ b/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt
@@ -1,4 +1,4 @@
 cmake_policy(SET CMP0116 OLD)
 
 add_executable(sub_exe_1 ../app.cpp)
-target_link_libraries(sub_exe_1 PRIVATE Qt5::Core)
+target_link_libraries(sub_exe_1 PRIVATE ${QtX}::Core)
diff --git a/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt
index 3176426..a2f77e4 100644
--- a/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt
+++ b/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt
@@ -1,4 +1,4 @@
 cmake_policy(SET CMP0116 NEW)
 
 add_executable(sub_exe_2 ../app.cpp)
-target_link_libraries(sub_exe_2 PRIVATE Qt5::Core)
+target_link_libraries(sub_exe_2 PRIVATE ${QtX}::Core)
diff --git a/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt
index d38cfe0..70644fa 100644
--- a/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt
+++ b/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt
@@ -1,2 +1,2 @@
 add_executable(sub_exe_3 ../app.cpp)
-target_link_libraries(sub_exe_3 PRIVATE Qt5::Core)
+target_link_libraries(sub_exe_3 PRIVATE ${QtX}::Core)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 3f239b9..2a5b556 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -319,12 +319,17 @@
 endfunction()
 run_ChangeBuildType()
 
-function(run_Qt5AutoMocDeps)
-  if(CMake_TEST_Qt5 AND CMAKE_TEST_Qt5Core_Version VERSION_GREATER_EQUAL 5.15.0)
-    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Qt5AutoMocDeps-build)
-    set(RunCMake_TEST_OPTIONS "-DQt5Core_DIR=${Qt5Core_DIR}" "-DQt5Widgets_DIR=${Qt5Widgets_DIR}")
-    run_cmake(Qt5AutoMocDeps)
-    unset(RunCMake_TEST_OPTIONS)
+function(run_QtAutoMocDeps)
+  set(QtX Qt${CMake_TEST_Qt_version})
+  if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
+    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps-build)
+    run_cmake_with_options(QtAutoMocDeps
+      "-Dwith_qt_version=${CMake_TEST_Qt_version}"
+      "-D${QtX}_DIR=${${QtX}_DIR}"
+      "-D${QtX}Core_DIR=${${QtX}Core_DIR}"
+      "-D${QtX}Widgets_DIR=${${QtX}Widgets_DIR}"
+      "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
+    )
     # Build the project.
     run_ninja("${RunCMake_TEST_BINARY_DIR}")
     # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
@@ -352,4 +357,6 @@
     run_ninja("${RunCMake_TEST_BINARY_DIR}")
   endif()
 endfunction()
-run_Qt5AutoMocDeps()
+if(CMake_TEST_Qt_version)
+  run_QtAutoMocDeps()
+endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt
new file mode 100644
index 0000000..80e9c2f
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-in-release-graph-ninja-stdout.txt
@@ -0,0 +1,2 @@
+^\[1/1\] Generating echo_depend_target\.txt
+'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt
new file mode 100644
index 0000000..1a79877
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-debug-ninja-stdout.txt
@@ -0,0 +1,2 @@
+^\[1/1\] Generating echo_depend_target\.txt
+'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Debug[\/]echo(\.exe)?' 'Debug'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt
new file mode 100644
index 0000000..80e9c2f
--- /dev/null
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex-echo_depend_target-release-ninja-stdout.txt
@@ -0,0 +1,2 @@
+^\[1/1\] Generating echo_depend_target\.txt
+'[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build'\$ '[^']*[\/]Tests[\/]RunCMake[\/]NinjaMultiConfig[\/]CustomCommandOutputGenex-build[\/]Release[\/]echo(\.exe)?' 'Release'$
diff --git a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex.cmake b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex.cmake
index bb68a50..2de5a3a 100644
--- a/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/CustomCommandOutputGenex.cmake
@@ -143,6 +143,16 @@
     PROPERTY SYMBOLIC 1)
 add_custom_target(echo_dbgx DEPENDS "$<$<CONFIG:Debug>:echo_dbgx_Debug.txt>")
 
+# A non-cross-config custom command expresses target dependencies in command config.
+add_custom_command(
+  OUTPUT echo_depend_target.txt
+  COMMAND ${CMAKE_COMMAND} -E env $<TARGET_FILE:echo> $<CONFIG>
+  # A real project should do:
+  #   DEPENDS $<TARGET_FILE:echo>
+  # but here we are testing the target-level dependency implied by TARGET_FILE.
+  )
+add_custom_target(echo_depend_target DEPENDS echo_depend_target.txt)
+
 add_custom_target(echo_target_raw
   BYPRODUCTS echo_target_raw_$<CONFIG>.txt
   COMMENT echo_target_raw
diff --git a/Tests/RunCMake/NinjaMultiConfig/Qt5-automoc-check-ninja-stdout.txt b/Tests/RunCMake/NinjaMultiConfig/QtX-automoc-check-ninja-stdout.txt
similarity index 100%
rename from Tests/RunCMake/NinjaMultiConfig/Qt5-automoc-check-ninja-stdout.txt
rename to Tests/RunCMake/NinjaMultiConfig/QtX-automoc-check-ninja-stdout.txt
diff --git a/Tests/RunCMake/NinjaMultiConfig/Qt5-debug-in-release-graph-build-check.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake
similarity index 100%
rename from Tests/RunCMake/NinjaMultiConfig/Qt5-debug-in-release-graph-build-check.cmake
rename to Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake
diff --git a/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
similarity index 78%
rename from Tests/RunCMake/NinjaMultiConfig/Qt5.cmake
rename to Tests/RunCMake/NinjaMultiConfig/QtX.cmake
index 578256a..130f883 100644
--- a/Tests/RunCMake/NinjaMultiConfig/Qt5.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
@@ -1,18 +1,21 @@
 enable_language(CXX)
 
-find_package(Qt5Core REQUIRED)
+set(QtX Qt${with_qt_version})
+
+find_package(${QtX} REQUIRED COMPONENTS Core)
 
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTOMOC_COMPILER_PREDEFINES OFF)
 
+# Source files are always named qt5.* for simplicity but apply to Qt5 and later
 add_executable(exe qt5.cxx)
-target_link_libraries(exe PRIVATE Qt5::Core)
+target_link_libraries(exe PRIVATE ${QtX}::Core)
 
 include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake)
 generate_output_files(exe)
 
 set(moc_writes_depfiles 0)
-if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
+if(${QtX}Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
   set(moc_writes_depfiles 1)
 endif()
 
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index 7c100eb..4a0c130 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -344,6 +344,16 @@
 run_ninja(CustomCommandOutputGenex clean-release-graph build-Release.ninja -t clean)
 run_ninja(CustomCommandOutputGenex echo_dbgx-debug-in-release-graph build-Release.ninja echo_dbgx:Debug)
 run_ninja(CustomCommandOutputGenex clean-release-graph build-Release.ninja -t clean)
+# echo_depend_target
+run_ninja(CustomCommandOutputGenex echo_depend_target-debug-prep build-Debug.ninja echo:Debug)
+run_ninja(CustomCommandOutputGenex echo_depend_target-debug build-Debug.ninja echo_depend_target)
+run_ninja(CustomCommandOutputGenex clean-debug-graph build-Debug.ninja -t clean)
+run_ninja(CustomCommandOutputGenex echo_depend_target-release-prep build-Release.ninja echo:Release)
+run_ninja(CustomCommandOutputGenex echo_depend_target-release build-Release.ninja echo_depend_target)
+run_ninja(CustomCommandOutputGenex clean-release-graph build-Release.ninja -t clean)
+run_ninja(CustomCommandOutputGenex echo_depend_target-debug-in-release-graph-prep build-Release.ninja echo:Release)
+run_ninja(CustomCommandOutputGenex echo_depend_target-debug-in-release-graph build-Release.ninja echo_depend_target:Debug)
+run_ninja(CustomCommandOutputGenex clean-release-graph build-Release.ninja -t clean)
 # echo_target_raw
 run_ninja(CustomCommandOutputGenex echo_target_raw-debug build-Debug.ninja echo_target_raw:Debug)
 run_ninja(CustomCommandOutputGenex clean-debug-graph build-Debug.ninja -t clean)
@@ -446,14 +456,20 @@
   run_ninja(CudaSimple all-clean build-Debug.ninja clean:Debug)
 endif()
 
-if(CMake_TEST_Qt5)
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Qt5-build)
-  set(RunCMake_TEST_OPTIONS "-DCMAKE_CROSS_CONFIGS=all" "-DQt5Core_DIR=${Qt5Core_DIR}")
-  run_cmake_configure(Qt5)
+if(CMake_TEST_Qt_version)
+  set(QtX Qt${CMake_TEST_Qt_version})
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtX-build)
+  set(RunCMake_TEST_OPTIONS
+    "-DCMAKE_CROSS_CONFIGS=all"
+    "-Dwith_qt_version:STRING=${CMake_TEST_Qt_version}"
+    "-D${QtX}Core_DIR=${${QtX}Core_DIR}"
+    "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
+  )
+  run_cmake_configure(QtX)
   unset(RunCMake_TEST_OPTIONS)
   include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake)
-  run_cmake_build(Qt5 debug-in-release-graph Release exe:Debug)
-  if(CMAKE_TEST_Qt5Core_Version VERSION_GREATER_EQUAL 5.15.0)
-    run_ninja(Qt5 automoc-check build-Debug.ninja -t query exe_autogen/timestamp)
+  run_cmake_build(QtX debug-in-release-graph Release exe:Debug)
+  if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
+    run_ninja(QtX automoc-check build-Debug.ninja -t query exe_autogen/timestamp)
   endif()
 endif()
diff --git a/Tests/RunCMake/ParseImplicitData/linux-custom_clang-C-Clang-13.0.0.input b/Tests/RunCMake/ParseImplicitData/linux-custom_clang-C-Clang-13.0.0.input
new file mode 100644
index 0000000..a80592c
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-custom_clang-C-Clang-13.0.0.input
@@ -0,0 +1,56 @@
+CMAKE_LANG=C
+CMAKE_LINKER=/opt/llvm-13/bin/ld.lld
+CMAKE_C_COMPILER_ABI=ELF
+CMAKE_C_COMPILER_AR=/opt/llvm-13/bin/llvm-ar
+CMAKE_C_COMPILER_ARCHITECTURE_ID=
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=Clang
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=/opt/llvm-13/bin/llvm-ranlib
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=13.0.0
+CMAKE_C_COMPILER_VERSION_INTERAL=
+Change Dir: /tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp
+
+Run Build Command(s):/usr/bin/make -f Makefile cmTC_91833/fast && /usr/bin/make  -f CMakeFiles/cmTC_91833.dir/build.make CMakeFiles/cmTC_91833.dir/build
+make[1]: Entering directory '/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp'
+Building C object CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o
+/opt/llvm-13/bin/clang   -v -MD -MT CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o -MF CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o.d -o CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o -c /opt/cmake-3.21.3/share/cmake-3.21/Modules/CMakeCCompilerABI.c
+clang version 13.0.0
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /opt/llvm-13/bin
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Candidate multilib: x32@mx32
+Selected multilib: .@m64
+ (in-process)
+ "/opt/llvm-13/bin/clang-13" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all --mrelax-relocations -disable-free -main-file-name CMakeCCompilerABI.c -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp -resource-dir /opt/llvm-13/lib/clang/13.0.0 -dependency-file CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o.d -MT CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o -sys-header-deps -internal-isystem /opt/llvm-13/lib/clang/13.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp -ferror-limit 19 -fgnuc-version=4.2.1 -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o -x c /opt/cmake-3.21.3/share/cmake-3.21/Modules/CMakeCCompilerABI.c
+clang -cc1 version 13.0.0 based upon LLVM 13.0.0 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include"
+ignoring nonexistent directory "/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/llvm-13/lib/clang/13.0.0/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+Linking C executable cmTC_91833
+/opt/cmake-3.21.3/bin/cmake -E cmake_link_script CMakeFiles/cmTC_91833.dir/link.txt --verbose=1
+/opt/llvm-13/bin/clang  -v -rdynamic CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o -o cmTC_91833
+clang version 13.0.0
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /opt/llvm-13/bin
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Candidate multilib: x32@mx32
+Selected multilib: .@m64
+ "/opt/llvm-13/bin/ld.lld" -export-dynamic -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_91833 /lib/x86_64-linux-gnu/crt1.o /lib/x86_64-linux-gnu/crti.o /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/clang_rt.crtbegin.o -L/opt/llvm-13/bin/../lib/x86_64-pc-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib64 -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64 -L/opt/llvm-13/bin/../lib -L/lib -L/usr/lib CMakeFiles/cmTC_91833.dir/CMakeCCompilerABI.c.o /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/libclang_rt.builtins.a --as-needed -l:libunwind.so --no-as-needed -lc /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/libclang_rt.builtins.a --as-needed -l:libunwind.so --no-as-needed /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/clang_rt.crtend.o /lib/x86_64-linux-gnu/crtn.o
+make[1]: Leaving directory '/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitData/linux-custom_clang-CXX-Clang-13.0.0.input b/Tests/RunCMake/ParseImplicitData/linux-custom_clang-CXX-Clang-13.0.0.input
new file mode 100644
index 0000000..e656390
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/linux-custom_clang-CXX-Clang-13.0.0.input
@@ -0,0 +1,58 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=/opt/llvm-13/bin/ld.lld
+CMAKE_CXX_COMPILER_ABI=ELF
+CMAKE_CXX_COMPILER_AR=/opt/llvm-13/bin/llvm-ar
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=Clang
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=/opt/llvm-13/bin/llvm-ranlib
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=13.0.0
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+Change Dir: /tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp
+
+Run Build Command(s):/usr/bin/make -f Makefile cmTC_5372b/fast && /usr/bin/make  -f CMakeFiles/cmTC_5372b.dir/build.make CMakeFiles/cmTC_5372b.dir/build
+make[1]: Entering directory '/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp'
+Building CXX object CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o
+/opt/llvm-13/bin/clang++   -v -MD -MT CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o -MF CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o.d -o CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o -c /opt/cmake-3.21.3/share/cmake-3.21/Modules/CMakeCXXCompilerABI.cpp
+clang version 13.0.0
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /opt/llvm-13/bin
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Candidate multilib: x32@mx32
+Selected multilib: .@m64
+ (in-process)
+ "/opt/llvm-13/bin/clang-13" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -mrelax-all --mrelax-relocations -disable-free -main-file-name CMakeCXXCompilerABI.cpp -mrelocation-model static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -v -fcoverage-compilation-dir=/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp -resource-dir /opt/llvm-13/lib/clang/13.0.0 -dependency-file CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o.d -MT CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o -sys-header-deps -internal-isystem /opt/llvm-13/bin/../include/x86_64-pc-linux-gnu/c++/v1 -internal-isystem /opt/llvm-13/bin/../include/c++/v1 -internal-isystem /opt/llvm-13/lib/clang/13.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdeprecated-macro -fdebug-compilation-dir=/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp -ferror-limit 19 -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o -x c++ /opt/cmake-3.21.3/share/cmake-3.21/Modules/CMakeCXXCompilerABI.cpp
+clang -cc1 version 13.0.0 based upon LLVM 13.0.0 default target x86_64-pc-linux-gnu
+ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/9/../../../../x86_64-linux-gnu/include"
+ignoring nonexistent directory "/include"
+#include "..." search starts here:
+#include <...> search starts here:
+ /opt/llvm-13/bin/../include/x86_64-pc-linux-gnu/c++/v1
+ /opt/llvm-13/bin/../include/c++/v1
+ /opt/llvm-13/lib/clang/13.0.0/include
+ /usr/local/include
+ /usr/include/x86_64-linux-gnu
+ /usr/include
+End of search list.
+Linking CXX executable cmTC_5372b
+/opt/cmake-3.21.3/bin/cmake -E cmake_link_script CMakeFiles/cmTC_5372b.dir/link.txt --verbose=1
+/opt/llvm-13/bin/clang++  -v -rdynamic CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o -o cmTC_5372b
+clang version 13.0.0
+Target: x86_64-pc-linux-gnu
+Thread model: posix
+InstalledDir: /opt/llvm-13/bin
+Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
+Candidate multilib: .@m64
+Candidate multilib: 32@m32
+Candidate multilib: x32@mx32
+Selected multilib: .@m64
+ "/opt/llvm-13/bin/ld.lld" -export-dynamic -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_x86_64 -export-dynamic -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o cmTC_5372b /lib/x86_64-linux-gnu/crt1.o /lib/x86_64-linux-gnu/crti.o /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/clang_rt.crtbegin.o -L/opt/llvm-13/bin/../lib/x86_64-pc-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/9 -L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib64 -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64 -L/opt/llvm-13/bin/../lib -L/lib -L/usr/lib CMakeFiles/cmTC_5372b.dir/CMakeCXXCompilerABI.cpp.o -lc++ -lm /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/libclang_rt.builtins.a -l:libunwind.so -lc /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/libclang_rt.builtins.a -l:libunwind.so /opt/llvm-13/lib/clang/13.0.0/lib/x86_64-pc-linux-gnu/clang_rt.crtend.o /lib/x86_64-linux-gnu/crtn.o
+make[1]: Leaving directory '/tmp/cmake/Tests/RunCMake/ParseImplicitData/build/CMakeFiles/CMakeTmp'
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
index e501f12..75c26a7 100644
--- a/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/ParseImplicitIncludeInfo.cmake
@@ -38,6 +38,7 @@
   linux-C-XL-16.1.0.0 linux-CXX-XL-16.1.0.0
   linux-CUDA-NVIDIA-10.1.168-CLANG linux-CUDA-NVIDIA-10.1.168-XLClang-v-empty
     linux-CUDA-NVIDIA-9.2.148-GCC
+  linux-custom_clang-C-Clang-13.0.0 linux-custom_clang-CXX-Clang-13.0.0
   mingw.org-C-GNU-4.9.3 mingw.org-CXX-GNU-4.9.3
   netbsd-C-GNU-4.8.5 netbsd-CXX-GNU-4.8.5
     netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-custom_clang-C-Clang-13.0.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-custom_clang-C-Clang-13.0.0.output
new file mode 100644
index 0000000..2435c62
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-custom_clang-C-Clang-13.0.0.output
@@ -0,0 +1 @@
+/opt/llvm-13/lib/clang/13.0.0/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
new file mode 100644
index 0000000..de5a368
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitIncludeInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
@@ -0,0 +1 @@
+/opt/llvm-13/include/x86_64-pc-linux-gnu/c\+\+/v1;/opt/llvm-13/include/c\+\+/v1;/opt/llvm-13/lib/clang/13.0.0/include;/usr/local/include;/usr/include/x86_64-linux-gnu;/usr/include
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
index dcdc7f1..51c8832 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
@@ -38,6 +38,7 @@
   linux-C-XL-16.1.0.0 linux-CXX-XL-16.1.0.0
   linux-CUDA-NVIDIA-10.1.168-CLANG linux-CUDA-NVIDIA-10.1.168-XLClang-v
     linux-CUDA-NVIDIA-9.2.148-GCC
+  linux-custom_clang-C-Clang-13.0.0 linux-custom_clang-CXX-Clang-13.0.0
   mingw.org-C-GNU-4.9.3 mingw.org-CXX-GNU-4.9.3
   netbsd-C-GNU-4.8.5 netbsd-CXX-GNU-4.8.5
     netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output
new file mode 100644
index 0000000..108712d
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-C-Clang-13.0.0.output
@@ -0,0 +1,3 @@
+libs=-l:libunwind.so;c;-l:libunwind.so
+dirs=/opt/llvm-13/lib/x86_64-pc-linux-gnu;/usr/lib/gcc/x86_64-linux-gnu/9;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/opt/llvm-13/lib;/lib;/usr/lib
+library_arch=x86_64-linux-gnu
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
new file mode 100644
index 0000000..e4a8a70
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/linux-custom_clang-CXX-Clang-13.0.0.output
@@ -0,0 +1,3 @@
+libs=c\+\+;m;-l:libunwind.so;c;-l:libunwind.so
+dirs=/opt/llvm-13/lib/x86_64-pc-linux-gnu;/usr/lib/gcc/x86_64-linux-gnu/9;/usr/lib64;/lib/x86_64-linux-gnu;/lib64;/usr/lib/x86_64-linux-gnu;/opt/llvm-13/lib;/lib;/usr/lib
+library_arch=x86_64-linux-gnu
diff --git a/Tests/RunCMake/PrecompileHeaders/PchIncludedAllLanguages.cmake b/Tests/RunCMake/PrecompileHeaders/PchIncludedAllLanguages.cmake
new file mode 100644
index 0000000..a455410
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchIncludedAllLanguages.cmake
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.15)
+project(PchIncludedAllLanguages C CXX)
+
+if(CMAKE_CXX_COMPILE_OPTIONS_USE_PCH)
+  add_definitions(-DHAVE_PCH_SUPPORT)
+endif()
+
+add_executable(main
+  main.cpp
+  pch-included.c
+  pch-included.cpp
+)
+target_precompile_headers(main PRIVATE pch.h)
+
+enable_testing()
+add_test(NAME main COMMAND main)
diff --git a/Tests/RunCMake/PrecompileHeaders/PchLibObjLibExe.cmake b/Tests/RunCMake/PrecompileHeaders/PchLibObjLibExe.cmake
new file mode 100644
index 0000000..b4fdb71
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/PchLibObjLibExe.cmake
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.16)
+project(PchLibObjLibExe CXX)
+
+foreach(i 1 2 3)
+    file(WRITE ${CMAKE_BINARY_DIR}/empty${i}.cpp "void nothing${i}() {}\n")
+endforeach()
+
+add_library(base_lib_static STATIC ${CMAKE_BINARY_DIR}/empty1.cpp)
+target_precompile_headers(base_lib_static PRIVATE <vector>)
+
+add_library(object_lib OBJECT ${CMAKE_BINARY_DIR}/empty2.cpp)
+target_precompile_headers(object_lib REUSE_FROM base_lib_static)
+
+add_library(mid_lib_static STATIC ${CMAKE_BINARY_DIR}/empty3.cpp)
+target_link_libraries(mid_lib_static PRIVATE object_lib)
+
+add_executable(exec main.cpp)
+target_link_libraries(exec PRIVATE mid_lib_static)
+set_target_properties(exec PROPERTIES MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>)
+
+target_precompile_headers(exec PRIVATE <string>)
+
+enable_testing()
+add_test(NAME exec COMMAND exec)
diff --git a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
index 8cc59d2..ca5b52e 100644
--- a/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
+++ b/Tests/RunCMake/PrecompileHeaders/RunCMakeTest.cmake
@@ -28,3 +28,5 @@
   endif()
 endif()
 run_test(PchReuseFromObjLib)
+run_test(PchIncludedAllLanguages)
+run_test(PchLibObjLibExe)
diff --git a/Tests/RunCMake/PrecompileHeaders/pch-included.c b/Tests/RunCMake/PrecompileHeaders/pch-included.c
new file mode 100644
index 0000000..96fce69
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/pch-included.c
@@ -0,0 +1,8 @@
+#ifndef HAVE_PCH_SUPPORT
+#  include "pch.h"
+#endif
+
+int testC(void)
+{
+  return PCH_INCLUDED;
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/pch-included.cpp b/Tests/RunCMake/PrecompileHeaders/pch-included.cpp
new file mode 100644
index 0000000..bf4d95d
--- /dev/null
+++ b/Tests/RunCMake/PrecompileHeaders/pch-included.cpp
@@ -0,0 +1,8 @@
+#ifndef HAVE_PCH_SUPPORT
+#  include "pch.h"
+#endif
+
+int testCpp()
+{
+  return PCH_INCLUDED;
+}
diff --git a/Tests/RunCMake/PrecompileHeaders/pch.h b/Tests/RunCMake/PrecompileHeaders/pch.h
index 81b6d9e..20672d1 100644
--- a/Tests/RunCMake/PrecompileHeaders/pch.h
+++ b/Tests/RunCMake/PrecompileHeaders/pch.h
@@ -1,3 +1,5 @@
 #pragma once
 
 #define PCH_INCLUDED 1
+
+int testC(void);
diff --git a/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt b/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt
new file mode 100644
index 0000000..fca7a73
--- /dev/null
+++ b/Tests/RunCMake/UseSWIG/CMP0086-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0086-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0086 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt
index 95fc5ca..39415af 100644
--- a/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt
+++ b/Tests/RunCMake/VS10Project/VsDpiAwareBadParam-stderr.txt
@@ -1,3 +1,3 @@
-CMake Error: Bad parameter for VS_DPI_AWARE: Bar
 CMake Error: Bad parameter for VS_DPI_AWARE: Foo
+CMake Error: Bad parameter for VS_DPI_AWARE: Bar
 CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/VS10Project/VsSettings-check.cmake b/Tests/RunCMake/VS10Project/VsSettings-check.cmake
index 0f8b26c..13cc8e2 100644
--- a/Tests/RunCMake/VS10Project/VsSettings-check.cmake
+++ b/Tests/RunCMake/VS10Project/VsSettings-check.cmake
@@ -4,20 +4,29 @@
     return()
   endif()
 
-  set(SettingFound FALSE)
+  set(Setting1Found FALSE)
+  set(Setting2Found FALSE)
 
   file(STRINGS "${projectFile}" lines)
   foreach(line IN LISTS lines)
     if(line MATCHES "<SourceProperty1.*Debug.*>SourceProperty1Value</SourceProperty1>")
       message("SourceProperty1 setting found")
-      set(SettingFound TRUE)
+      set(Setting1Found TRUE)
+    endif()
+    if(line MATCHES "<SourceProperty2.*Debug.*>SourceProperty2Value</SourceProperty2>")
+      message("SourceProperty2 setting found")
+      set(Setting2Found TRUE)
     endif()
   endforeach()
 
-  if (NOT SettingFound)
+  if (NOT Setting1Found)
     set(RunCMake_TEST_FAILED "SourceProperty1 setting was not found")
     return()
   endif()
+  if (NOT Setting2Found)
+    set(RunCMake_TEST_FAILED "SourceProperty2 setting was not found")
+    return()
+  endif()
 endmacro()
 
 ensure_props_set("${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
diff --git a/Tests/RunCMake/VS10Project/VsSettings.cmake b/Tests/RunCMake/VS10Project/VsSettings.cmake
index a4b321b..3a046f1 100644
--- a/Tests/RunCMake/VS10Project/VsSettings.cmake
+++ b/Tests/RunCMake/VS10Project/VsSettings.cmake
@@ -3,3 +3,5 @@
 add_library(foo foo.cpp shader.hlsl)
 set_property(SOURCE shader.hlsl PROPERTY VS_SETTINGS
   "$<$<CONFIG:DEBUG>:SourceProperty1=SourceProperty1Value>")
+set_property(SOURCE foo.cpp PROPERTY VS_SETTINGS
+  "$<$<CONFIG:DEBUG>:SourceProperty2=SourceProperty2Value>")
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake
index f4fe07f..54375d5 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOff.cmake
@@ -1,7 +1,7 @@
 add_executable(app MACOSX_BUNDLE main.m)
 
 set_target_properties(app PROPERTIES
-  XCODE_EMBED_FRAMEWORKS "${EXTERNAL_FWK}"
+  XCODE_EMBED_FRAMEWORKS "${EXTERNAL_DEPENDENCY}"
   XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY OFF
   XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY OFF
 )
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake
index e4ea55d..23fd49d 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir-build-check.cmake
@@ -1,3 +1,3 @@
-if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/Debug/app.app/Contents/Frameworks/sharedFrameworkExt.framework)
-  set(RunCMake_TEST_FAILED "Framework was not embedded at the expected location")
+if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/Debug/app.app/Contents/Frameworks/${EXTERNAL_DEPENDENCY_NAME})
+  set(RunCMake_TEST_FAILED "${EXTERNAL_DEPENDENCY_NAME} was not embedded at the expected location")
 endif()
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake
index 79d8d77..c76226f 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnNoSubdir.cmake
@@ -1,7 +1,7 @@
 add_executable(app MACOSX_BUNDLE main.m)
 
 set_target_properties(app PROPERTIES
-  XCODE_EMBED_FRAMEWORKS "${EXTERNAL_FWK}"
+  XCODE_EMBED_FRAMEWORKS "${EXTERNAL_DEPENDENCY}"
   XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY ON
   XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
 )
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake
index 57c79ea..732f04c 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir-build-check.cmake
@@ -1,3 +1,3 @@
-if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/Debug/app.app/Contents/Frameworks/subdir/sharedFrameworkExt.framework)
-  set(RunCMake_TEST_FAILED "Framework was not embedded at the expected location")
+if(NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/Debug/app.app/Contents/Frameworks/subdir/${EXTERNAL_DEPENDENCY_NAME})
+  set(RunCMake_TEST_FAILED "${EXTERNAL_DEPENDENCY_NAME} was not embedded at the expected location")
 endif()
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake
index 4c78199..f83b1bf 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedFrameworksFlagsOnWithSubdir.cmake
@@ -1,7 +1,7 @@
 add_executable(app MACOSX_BUNDLE main.m)
 
 set_target_properties(app PROPERTIES
-  XCODE_EMBED_FRAMEWORKS "${EXTERNAL_FWK}"
+  XCODE_EMBED_FRAMEWORKS "${EXTERNAL_DEPENDENCY}"
   XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY ON
   XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
   XCODE_EMBED_FRAMEWORKS_PATH "subdir"
diff --git a/Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake b/Tests/RunCMake/XcodeProject-Embed/ExternalDependencies.cmake
similarity index 72%
rename from Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake
rename to Tests/RunCMake/XcodeProject-Embed/ExternalDependencies.cmake
index 64e2f95..8c7b06a 100644
--- a/Tests/RunCMake/XcodeProject-Embed/ExternalFramework.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/ExternalDependencies.cmake
@@ -1,2 +1,4 @@
 add_library(sharedFrameworkExt SHARED func.m)
 set_target_properties(sharedFrameworkExt PROPERTIES FRAMEWORK TRUE)
+
+add_library(sharedDylibExt SHARED func.m)
diff --git a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
index e94d084..f3a6918 100644
--- a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
@@ -1,47 +1,49 @@
 include(RunCMake)
 
-# Build a framework that the other tests will use and treat as external.
+# Build dependencies that the other tests will use and treat as external.
 # Always build in the Debug configuration so that the path to the framework
 # is predictable.
-function(ExternalFramework)
+function(ExternalDependencies)
   set(RunCMake_TEST_NO_CLEAN 1)
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExternalFramework-build)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExternalDependencies-build)
   set(externalFramework ${RunCMake_TEST_BINARY_DIR}/Debug/sharedFrameworkExt.framework PARENT_SCOPE)
+  set(externalDylib ${RunCMake_TEST_BINARY_DIR}/Debug/libsharedDylibExt.dylib PARENT_SCOPE)
 
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(ExternalFramework)
-  run_cmake_command(ExternalFramework-build
+  run_cmake(ExternalDependencies)
+  run_cmake_command(ExternalDependencies-build
     ${CMAKE_COMMAND} --build ${RunCMake_TEST_BINARY_DIR}
                      --config Debug
-                     --target sharedFrameworkExt
+                     --target sharedFrameworkExt sharedDylibExt
   )
 endfunction()
-ExternalFramework()
+ExternalDependencies()
 
-set(RunCMake_TEST_OPTIONS -DEXTERNAL_FWK=${externalFramework})
-
-run_cmake(EmbedFrameworksFlagsOff)
-
-function(TestFlagsOn testName)
+function(TestFlagsOn testName dependencyName)
   set(RunCMake_TEST_NO_CLEAN 1)
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${testName}-build)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${testName}-${dependencyName}-build)
 
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
   run_cmake(${testName})
-  run_cmake_command(${testName}-build
+  run_cmake_command(${testName}-${dependencyName}-build
     ${CMAKE_COMMAND} --build ${RunCMake_TEST_BINARY_DIR}
                      --config Debug
                      --target app
   )
 endfunction()
 
-TestFlagsOn(EmbedFrameworksFlagsOnNoSubdir)
-TestFlagsOn(EmbedFrameworksFlagsOnWithSubdir)
-
+foreach(dependency ${externalFramework} ${externalDylib})
+  cmake_path(GET dependency FILENAME dependencyName)
+  set(RunCMake_TEST_OPTIONS -DEXTERNAL_DEPENDENCY=${dependency} -DEXTERNAL_DEPENDENCY_NAME=${dependencyName})
+  run_cmake(EmbedFrameworksFlagsOff)
+  TestFlagsOn(EmbedFrameworksFlagsOnNoSubdir ${dependencyName})
+  TestFlagsOn(EmbedFrameworksFlagsOnWithSubdir ${dependencyName})
+endforeach()
+unset(RunCMake_TEST_OPTIONS)
 
 function(TestAppExtension platform)
   set(testName EmbedAppExtensions-${platform})
diff --git a/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt b/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt
new file mode 100644
index 0000000..50838c3
--- /dev/null
+++ b/Tests/RunCMake/add_subdirectory/CMP0082-OLD-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at CMakeLists.txt:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0082 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.$
diff --git a/Tests/RunCMake/cmake_host_system_information/000-FirstFallbackScript.cmake b/Tests/RunCMake/cmake_host_system_information/000-FirstFallbackScript.cmake
new file mode 100644
index 0000000..ad873eb
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/000-FirstFallbackScript.cmake
@@ -0,0 +1 @@
+message(WARNING "The warning text to match just to make sure the script get executed")
diff --git a/Tests/RunCMake/cmake_host_system_information/999-LastFallbackScript.cmake b/Tests/RunCMake/cmake_host_system_information/999-LastFallbackScript.cmake
new file mode 100644
index 0000000..08d8da8
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/999-LastFallbackScript.cmake
@@ -0,0 +1,21 @@
+if(DEFINED CMAKE_GET_OS_RELEASE_FALLBACK_RESULT)
+  message(FATAL_ERROR "The `CMAKE_GET_OS_RELEASE_FALLBACK_RESULT` expected to be unset at this moment")
+endif()
+
+set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME UnitTest)
+set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME "Just a Unit Test")
+set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID unittest)
+set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE nothing)
+set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION 0.0.1)
+set(CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID 0.0.1)
+
+list(
+    APPEND CMAKE_GET_OS_RELEASE_FALLBACK_RESULT
+    CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
+    CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
+    IGNORED_VARIABLE_NAME_WHICH_IS_NOT_STARTED_WITH_EXPECTED_PREFIX
+    CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
+    CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID_LIKE
+    CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
+    CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
+  )
diff --git a/Tests/RunCMake/cmake_host_system_information/BadArg1-result.txt b/Tests/RunCMake/cmake_host_system_information/BadArg1-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/BadArg1-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_host_system_information/BadArg1-stderr.txt b/Tests/RunCMake/cmake_host_system_information/BadArg1-stderr.txt
new file mode 100644
index 0000000..ed995e6
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/BadArg1-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at BadArg1\.cmake:1 \(cmake_host_system_information\):
+  cmake_host_system_information missing RESULT specification.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:7 \(include\)
diff --git a/Tests/CMakeTests/CMakeHostSystemInformation-BadArg1.cmake b/Tests/RunCMake/cmake_host_system_information/BadArg1.cmake
similarity index 100%
rename from Tests/CMakeTests/CMakeHostSystemInformation-BadArg1.cmake
rename to Tests/RunCMake/cmake_host_system_information/BadArg1.cmake
diff --git a/Tests/RunCMake/cmake_host_system_information/BadArg2-result.txt b/Tests/RunCMake/cmake_host_system_information/BadArg2-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/BadArg2-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_host_system_information/BadArg2-stderr.txt b/Tests/RunCMake/cmake_host_system_information/BadArg2-stderr.txt
new file mode 100644
index 0000000..b78c927
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/BadArg2-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at BadArg2\.cmake:1 \(cmake_host_system_information\):
+  cmake_host_system_information missing QUERY specification
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:7 \(include\)
diff --git a/Tests/CMakeTests/CMakeHostSystemInformation-BadArg2.cmake b/Tests/RunCMake/cmake_host_system_information/BadArg2.cmake
similarity index 100%
rename from Tests/CMakeTests/CMakeHostSystemInformation-BadArg2.cmake
rename to Tests/RunCMake/cmake_host_system_information/BadArg2.cmake
diff --git a/Tests/RunCMake/cmake_host_system_information/BadArg3-result.txt b/Tests/RunCMake/cmake_host_system_information/BadArg3-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/BadArg3-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_host_system_information/BadArg3-stderr.txt b/Tests/RunCMake/cmake_host_system_information/BadArg3-stderr.txt
new file mode 100644
index 0000000..c3f1314
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/BadArg3-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at BadArg3\.cmake:1 \(cmake_host_system_information\):
+  cmake_host_system_information does not recognize <key> FOOBAR
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:7 \(include\)
diff --git a/Tests/CMakeTests/CMakeHostSystemInformation-BadArg3.cmake b/Tests/RunCMake/cmake_host_system_information/BadArg3.cmake
similarity index 100%
rename from Tests/CMakeTests/CMakeHostSystemInformation-BadArg3.cmake
rename to Tests/RunCMake/cmake_host_system_information/BadArg3.cmake
diff --git a/Tests/RunCMake/cmake_host_system_information/CMakeLists.txt b/Tests/RunCMake/cmake_host_system_information/CMakeLists.txt
new file mode 100644
index 0000000..0198f9b
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.21)
+
+project(${RunCMake_TEST} NONE)
+
+set(CMAKE_SYSROOT ${PROJECT_SOURCE_DIR}/${RunCMake_TEST})
+
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/cmake_host_system_information/CentOS6-stdout.txt b/Tests/RunCMake/cmake_host_system_information/CentOS6-stdout.txt
new file mode 100644
index 0000000..50dbb08
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/CentOS6-stdout.txt
@@ -0,0 +1,7 @@
+-- CENTOS6_ID=`centos`
+-- CENTOS6_ID_LIKE=`rhel`
+-- CENTOS6_NAME=`CentOS`
+-- CENTOS6_PRETTY_NAME=`CentOS release 6\.10 \(Final\)`
+-- CENTOS6_USED_FALLBACK_SCRIPT=`.*/Modules/Internal/OSRelease/010-TryOldCentOS.cmake`
+-- CENTOS6_VERSION=`6\.10`
+-- CENTOS6_VERSION_ID=`6\.10`
diff --git a/Tests/RunCMake/cmake_host_system_information/CentOS6.cmake b/Tests/RunCMake/cmake_host_system_information/CentOS6.cmake
new file mode 100644
index 0000000..3bc632b
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/CentOS6.cmake
@@ -0,0 +1,5 @@
+cmake_host_system_information(RESULT CENTOS6 QUERY DISTRIB_INFO)
+
+foreach(VAR IN LISTS CENTOS6)
+  message(STATUS "${VAR}=`${${VAR}}`")
+endforeach()
diff --git a/Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release b/Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release
new file mode 100644
index 0000000..294ccc9
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/CentOS6/etc/centos-release
@@ -0,0 +1 @@
+CentOS release 6.10 (Final)
diff --git a/Tests/RunCMake/cmake_host_system_information/Debian6-stdout.txt b/Tests/RunCMake/cmake_host_system_information/Debian6-stdout.txt
new file mode 100644
index 0000000..4193d6f
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Debian6-stdout.txt
@@ -0,0 +1,5 @@
+-- DEBIAN6_ID=`debian`
+-- DEBIAN6_NAME=`Debian`
+-- DEBIAN6_USED_FALLBACK_SCRIPT=`.*/Modules/Internal/OSRelease/020-TryDebianVersion.cmake`
+-- DEBIAN6_VERSION=`6\.0\.10`
+-- DEBIAN6_VERSION_ID=`6\.0\.10`
diff --git a/Tests/RunCMake/cmake_host_system_information/Debian6.cmake b/Tests/RunCMake/cmake_host_system_information/Debian6.cmake
new file mode 100644
index 0000000..cbf83a9
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Debian6.cmake
@@ -0,0 +1,5 @@
+cmake_host_system_information(RESULT DEBIAN6 QUERY DISTRIB_INFO)
+
+foreach(VAR IN LISTS DEBIAN6)
+  message(STATUS "${VAR}=`${${VAR}}`")
+endforeach()
diff --git a/Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version b/Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version
new file mode 100644
index 0000000..c7d48f0
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Debian6/etc/debian_version
@@ -0,0 +1 @@
+6.0.10
diff --git a/Tests/RunCMake/cmake_host_system_information/Exherbo-stdout.txt b/Tests/RunCMake/cmake_host_system_information/Exherbo-stdout.txt
new file mode 100644
index 0000000..11ae71f
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Exherbo-stdout.txt
@@ -0,0 +1,9 @@
+-- TEST1_ANSI_COLOR=`0;32`
+-- TEST1_BUG_REPORT_URL=`https://bugs.exherbo.org/`
+-- TEST1_HOME_URL=`https://www.exherbo.org/`
+-- TEST1_ID=`exherbo`
+-- TEST1_NAME=`Exherbo`
+-- TEST1_PRETTY_NAME=`Exherbo Linux`
+-- TEST1_SUPPORT_URL=`irc://irc.freenode.net/#exherbo`
+-- TEST2_ID=`exherbo`
+-- TEST2_VERSION=``
diff --git a/Tests/RunCMake/cmake_host_system_information/Exherbo.cmake b/Tests/RunCMake/cmake_host_system_information/Exherbo.cmake
new file mode 100644
index 0000000..7fc26d8
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Exherbo.cmake
@@ -0,0 +1,11 @@
+cmake_host_system_information(RESULT TEST1 QUERY DISTRIB_INFO)
+
+foreach(VAR IN LISTS TEST1)
+  message(STATUS "${VAR}=`${${VAR}}`")
+endforeach()
+
+# Query individual variables
+cmake_host_system_information(RESULT TEST2 QUERY DISTRIB_ID DISTRIB_VERSION)
+list(POP_FRONT TEST2 TEST2_ID TEST2_VERSION)
+message(STATUS "TEST2_ID=`${TEST2_ID}`")
+message(STATUS "TEST2_VERSION=`${TEST2_VERSION}`")
diff --git a/Tests/RunCMake/cmake_host_system_information/Exherbo/etc/os-release b/Tests/RunCMake/cmake_host_system_information/Exherbo/etc/os-release
new file mode 100644
index 0000000..944c9b4
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Exherbo/etc/os-release
@@ -0,0 +1,7 @@
+NAME="Exherbo"
+PRETTY_NAME="Exherbo Linux"
+ID="exherbo"
+ANSI_COLOR="0;32"
+HOME_URL="https://www.exherbo.org/"
+SUPPORT_URL="irc://irc.freenode.net/#exherbo"
+BUG_REPORT_URL="https://bugs.exherbo.org/"
diff --git a/Tests/RunCMake/cmake_host_system_information/QueryKeys-stdout.txt b/Tests/RunCMake/cmake_host_system_information/QueryKeys-stdout.txt
new file mode 100644
index 0000000..f583deb
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/QueryKeys-stdout.txt
@@ -0,0 +1,27 @@
+-- NUMBER_OF_LOGICAL_CORES=`[0-9]+`
+-- NUMBER_OF_PHYSICAL_CORES=`[0-9]+`
+-- HOSTNAME=`.*`
+-- FQDN=`.*`
+-- TOTAL_VIRTUAL_MEMORY=`[0-9]+`
+-- AVAILABLE_VIRTUAL_MEMORY=`[0-9]+`
+-- TOTAL_PHYSICAL_MEMORY=`[0-9]+`
+-- AVAILABLE_PHYSICAL_MEMORY=`[0-9]+`
+-- IS_64BIT=`[01]`
+-- HAS_FPU=`[01]`
+-- HAS_MMX=`[01]`
+-- HAS_MMX_PLUS=`[01]`
+-- HAS_SSE=`[01]`
+-- HAS_SSE2=`[01]`
+-- HAS_SSE_FP=`[01]`
+-- HAS_SSE_MMX=`[01]`
+-- HAS_AMD_3DNOW=`[01]`
+-- HAS_AMD_3DNOW_PLUS=`[01]`
+-- HAS_IA64=`[01]`
+-- HAS_SERIAL_NUMBER=`[01]`
+-- PROCESSOR_SERIAL_NUMBER=`.*`
+-- PROCESSOR_NAME=`.*`
+-- PROCESSOR_DESCRIPTION=`.*`
+-- OS_NAME=`.*`
+-- OS_RELEASE=`.*`
+-- OS_VERSION=`.*`
+-- OS_PLATFORM=`.*`
diff --git a/Tests/RunCMake/cmake_host_system_information/QueryKeys.cmake b/Tests/RunCMake/cmake_host_system_information/QueryKeys.cmake
new file mode 100644
index 0000000..f22a0ae
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/QueryKeys.cmake
@@ -0,0 +1,32 @@
+function(try_and_print key)
+  cmake_host_system_information(RESULT RESULT QUERY ${key})
+  message(STATUS "${key}=`${RESULT}`")
+endfunction()
+
+try_and_print(NUMBER_OF_LOGICAL_CORES)
+try_and_print(NUMBER_OF_PHYSICAL_CORES)
+try_and_print(HOSTNAME)
+try_and_print(FQDN)
+try_and_print(TOTAL_VIRTUAL_MEMORY)
+try_and_print(AVAILABLE_VIRTUAL_MEMORY)
+try_and_print(TOTAL_PHYSICAL_MEMORY)
+try_and_print(AVAILABLE_PHYSICAL_MEMORY)
+try_and_print(IS_64BIT)
+try_and_print(HAS_FPU)
+try_and_print(HAS_MMX)
+try_and_print(HAS_MMX_PLUS)
+try_and_print(HAS_SSE)
+try_and_print(HAS_SSE2)
+try_and_print(HAS_SSE_FP)
+try_and_print(HAS_SSE_MMX)
+try_and_print(HAS_AMD_3DNOW)
+try_and_print(HAS_AMD_3DNOW_PLUS)
+try_and_print(HAS_IA64)
+try_and_print(HAS_SERIAL_NUMBER)
+try_and_print(PROCESSOR_SERIAL_NUMBER)
+try_and_print(PROCESSOR_NAME)
+try_and_print(PROCESSOR_DESCRIPTION)
+try_and_print(OS_NAME)
+try_and_print(OS_RELEASE)
+try_and_print(OS_VERSION)
+try_and_print(OS_PLATFORM)
diff --git a/Tests/RunCMake/cmake_host_system_information/QueryList-stdout.txt b/Tests/RunCMake/cmake_host_system_information/QueryList-stdout.txt
new file mode 100644
index 0000000..eebe0d4
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/QueryList-stdout.txt
@@ -0,0 +1 @@
+-- \[[0-9]+;[0-9]+\]
diff --git a/Tests/CMakeTests/CMakeHostSystemInformation-QueryList.cmake b/Tests/RunCMake/cmake_host_system_information/QueryList.cmake
similarity index 77%
rename from Tests/CMakeTests/CMakeHostSystemInformation-QueryList.cmake
rename to Tests/RunCMake/cmake_host_system_information/QueryList.cmake
index 1c3156d..f24aa2b 100644
--- a/Tests/CMakeTests/CMakeHostSystemInformation-QueryList.cmake
+++ b/Tests/RunCMake/cmake_host_system_information/QueryList.cmake
@@ -2,4 +2,4 @@
   QUERY NUMBER_OF_LOGICAL_CORES NUMBER_OF_PHYSICAL_CORES
 )
 
-message("[${RESULT}]")
+message(STATUS "[${RESULT}]")
diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
new file mode 100644
index 0000000..87b6944
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
@@ -0,0 +1,23 @@
+include(RunCMake)
+
+run_cmake(BadArg1)
+run_cmake(BadArg2)
+run_cmake(BadArg3)
+
+run_cmake(QueryList)
+run_cmake(QueryKeys)
+
+run_cmake(UnitTest)
+run_cmake(Exherbo)
+run_cmake(Ubuntu)
+
+run_cmake(CentOS6)
+run_cmake(Debian6)
+
+run_cmake(UserFallbackScript)
+
+if(RunCMake_GENERATOR MATCHES "^Visual Studio " AND NOT RunCMake_GENERATOR STREQUAL "Visual Studio 9 2008")
+  run_cmake(VsMSBuild)
+else()
+  run_cmake(VsMSBuildMissing)
+endif()
diff --git a/Tests/RunCMake/cmake_host_system_information/Ubuntu-stdout.txt b/Tests/RunCMake/cmake_host_system_information/Ubuntu-stdout.txt
new file mode 100644
index 0000000..d1a18da
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Ubuntu-stdout.txt
@@ -0,0 +1,14 @@
+-- TEST1_BUG_REPORT_URL=`https://bugs\.launchpad\.net/ubuntu/`
+-- TEST1_HOME_URL=`https://www\.ubuntu\.com/`
+-- TEST1_ID=`ubuntu`
+-- TEST1_ID_LIKE=`debian`
+-- TEST1_NAME=`Ubuntu`
+-- TEST1_PRETTY_NAME=`Ubuntu 20\.04\.2 LTS`
+-- TEST1_PRIVACY_POLICY_URL=`https://www\.ubuntu\.com/legal/terms-and-policies/privacy-policy`
+-- TEST1_SUPPORT_URL=`https://help\.ubuntu\.com/`
+-- TEST1_UBUNTU_CODENAME=`focal`
+-- TEST1_VERSION=`20\.04\.2 LTS \(Focal Fossa\)`
+-- TEST1_VERSION_CODENAME=`focal`
+-- TEST1_VERSION_ID=`20\.04`
+-- TEST2_ID=`ubuntu`
+-- TEST2_VERSION=`20\.04\.2 LTS \(Focal Fossa\)`
diff --git a/Tests/RunCMake/cmake_host_system_information/Ubuntu.cmake b/Tests/RunCMake/cmake_host_system_information/Ubuntu.cmake
new file mode 100644
index 0000000..7fc26d8
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Ubuntu.cmake
@@ -0,0 +1,11 @@
+cmake_host_system_information(RESULT TEST1 QUERY DISTRIB_INFO)
+
+foreach(VAR IN LISTS TEST1)
+  message(STATUS "${VAR}=`${${VAR}}`")
+endforeach()
+
+# Query individual variables
+cmake_host_system_information(RESULT TEST2 QUERY DISTRIB_ID DISTRIB_VERSION)
+list(POP_FRONT TEST2 TEST2_ID TEST2_VERSION)
+message(STATUS "TEST2_ID=`${TEST2_ID}`")
+message(STATUS "TEST2_VERSION=`${TEST2_VERSION}`")
diff --git a/Tests/RunCMake/cmake_host_system_information/Ubuntu/etc/os-release b/Tests/RunCMake/cmake_host_system_information/Ubuntu/etc/os-release
new file mode 100644
index 0000000..f228f22
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/Ubuntu/etc/os-release
@@ -0,0 +1,12 @@
+NAME="Ubuntu"
+VERSION="20.04.2 LTS (Focal Fossa)"
+ID=ubuntu
+ID_LIKE=debian
+PRETTY_NAME="Ubuntu 20.04.2 LTS"
+VERSION_ID="20.04"
+HOME_URL="https://www.ubuntu.com/"
+SUPPORT_URL="https://help.ubuntu.com/"
+BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
+PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
+VERSION_CODENAME=focal
+UBUNTU_CODENAME=focal
diff --git a/Tests/RunCMake/cmake_host_system_information/UnitTest-stdout.txt b/Tests/RunCMake/cmake_host_system_information/UnitTest-stdout.txt
new file mode 100644
index 0000000..db6f487
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/UnitTest-stdout.txt
@@ -0,0 +1,7 @@
+-- UNIT_TEST_A_LIST_LIKE_VARIABLE=`satu;dua;tiga`
+-- UNIT_TEST_DBL_QUOTED_VALUE=`"The" value in double "quotes"`
+-- UNIT_TEST_DBL_QUOTED_VALUE_STIPPED_COMMENT=`Blah blah blah`
+-- UNIT_TEST_NON_SPACE_VALUE=`Blah-blah-blah`
+-- UNIT_TEST_QUOTED_VALUE=`'The' value in single 'quotes'`
+-- UNIT_TEST_QUOTED_VALUE_STIPPED_COMMENT=`The value in single quotes`
+-- UNIT_TEST_THE_URL_WITH_ANCHOR_TEST=`https://blah.blah/resource#anchor`
diff --git a/Tests/RunCMake/cmake_host_system_information/UnitTest.cmake b/Tests/RunCMake/cmake_host_system_information/UnitTest.cmake
new file mode 100644
index 0000000..d9a0aca
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/UnitTest.cmake
@@ -0,0 +1,5 @@
+cmake_host_system_information(RESULT UNIT_TEST QUERY DISTRIB_INFO)
+
+foreach(VAR IN LISTS UNIT_TEST)
+  message(STATUS "${VAR}=`${${VAR}}`")
+endforeach()
diff --git a/Tests/RunCMake/cmake_host_system_information/UnitTest/etc/os-release b/Tests/RunCMake/cmake_host_system_information/UnitTest/etc/os-release
new file mode 100644
index 0000000..66c33b5
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/UnitTest/etc/os-release
@@ -0,0 +1,9 @@
+# Comment string gonna be ignored
+NON_SPACE_VALUE=Blah-blah-blah
+QUOTED_VALUE='\'The\' value in single \'quotes\''
+QUOTED_VALUE_STIPPED_COMMENT='The value in single quotes'# The comment right after `'`
+DBL_QUOTED_VALUE="\"The\" value in double \"quotes\""
+DBL_QUOTED_VALUE_STIPPED_COMMENT="Blah blah blah"# The comment right after `'`
+THE_URL_WITH_ANCHOR_TEST="https://blah.blah/resource#anchor" # And a comment after
+A_LIST_LIKE_VARIABLE='satu;dua;tiga'
+INCORRECT_ESCAPE_IGNORED=\'This line gonna be ignored'
diff --git a/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stderr.txt b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stderr.txt
new file mode 100644
index 0000000..78acea2
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stderr.txt
@@ -0,0 +1,5 @@
+CMake Warning at 000-FirstFallbackScript\.cmake:[0-9]+ \(message\):
+  The warning text to match just to make sure the script get executed
+Call Stack \(most recent call first\):
+  UserFallbackScript\.cmake:[0-9]+ \(cmake_host_system_information\)
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stdout.txt b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stdout.txt
new file mode 100644
index 0000000..acaf47e
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript-stdout.txt
@@ -0,0 +1,7 @@
+-- UFS_ID=`unittest`
+-- UFS_ID_LIKE=`nothing`
+-- UFS_NAME=`UnitTest`
+-- UFS_PRETTY_NAME=`Just a Unit Test`
+-- UFS_USED_FALLBACK_SCRIPT=`.*/999-LastFallbackScript\.cmake`
+-- UFS_VERSION=`0\.0\.1`
+-- UFS_VERSION_ID=`0\.0\.1`
diff --git a/Tests/RunCMake/cmake_host_system_information/UserFallbackScript.cmake b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript.cmake
new file mode 100644
index 0000000..660aa1c
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/UserFallbackScript.cmake
@@ -0,0 +1,12 @@
+list(
+    APPEND CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
+    ${CMAKE_CURRENT_SOURCE_DIR}/000-FirstFallbackScript.cmake
+    ${CMAKE_CURRENT_SOURCE_DIR}/Ignored-Script.cmake
+    ${CMAKE_CURRENT_SOURCE_DIR}/999-LastFallbackScript.cmake
+  )
+
+cmake_host_system_information(RESULT UFS QUERY DISTRIB_INFO)
+
+foreach(VAR IN LISTS UFS)
+  message(STATUS "${VAR}=`${${VAR}}`")
+endforeach()
diff --git a/Tests/RunCMake/cmake_host_system_information/VsMSBuild.cmake b/Tests/RunCMake/cmake_host_system_information/VsMSBuild.cmake
new file mode 100644
index 0000000..30e616e
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/VsMSBuild.cmake
@@ -0,0 +1,4 @@
+cmake_host_system_information(RESULT msbuild QUERY VS_MSBUILD_COMMAND)
+if(NOT EXISTS "${msbuild}")
+  message(FATAL_ERROR "VS_MSBUILD_COMMAND returned path that does not exist:\n ${msbuild}")
+endif()
diff --git a/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing-result.txt b/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing-stderr.txt b/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing-stderr.txt
new file mode 100644
index 0000000..3a7dfef
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Error at VsMSBuildMissing.cmake:[0-9]+ \(cmake_host_system_information\):
+  cmake_host_system_information does not recognize <key> VS_MSBUILD_COMMAND
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing.cmake b/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing.cmake
new file mode 100644
index 0000000..23c0364
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/VsMSBuildMissing.cmake
@@ -0,0 +1 @@
+cmake_host_system_information(RESULT msbuild QUERY VS_MSBUILD_COMMAND)
diff --git a/Tests/RunCMake/ctest_environment/CMakeLists.txt.in b/Tests/RunCMake/ctest_environment/CMakeLists.txt.in
new file mode 100644
index 0000000..c9c4a64
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/CMakeLists.txt.in
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.21.0)
+project("@CASE_NAME@" NONE)
+include("@CASE_SOURCE_DIR@/@CASE_NAME@.cmake")
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op-result.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op-stderr.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op-stderr.txt
new file mode 100644
index 0000000..5b56d6f
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op-stderr.txt
@@ -0,0 +1 @@
+Error: Unrecognized environment manipulation argument: unknown
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op.cmake b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op.cmake
new file mode 100644
index 0000000..d6ca4b2
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-invalid-op.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+add_test(NAME cmake_version COMMAND "${CMAKE_COMMAND}" --version)
+
+set_property(TEST cmake_version
+  PROPERTY ENVIRONMENT_MODIFICATION
+    INVALID_OP=unknown:)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon-result.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon-stderr.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon-stderr.txt
new file mode 100644
index 0000000..3ba6ba7
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon-stderr.txt
@@ -0,0 +1 @@
+Error: Missing `:` after the operation in: MISSING_COLON=unset
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon.cmake b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon.cmake
new file mode 100644
index 0000000..601dd8b
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-colon.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+
+add_test(NAME cmake_version COMMAND "${CMAKE_COMMAND}" --version)
+set_property(TEST cmake_version
+  PROPERTY ENVIRONMENT_MODIFICATION
+    MISSING_COLON=unset)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals-result.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals-stderr.txt b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals-stderr.txt
new file mode 100644
index 0000000..20bc9a5
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals-stderr.txt
@@ -0,0 +1 @@
+Error: Missing `=` after the variable name in: MISSING_EQUAL
diff --git a/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals.cmake b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals.cmake
new file mode 100644
index 0000000..18448cf
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/ENVIRONMENT_MODIFICATION-no-equals.cmake
@@ -0,0 +1,6 @@
+include(CTest)
+
+add_test(NAME cmake_version COMMAND "${CMAKE_COMMAND}" --version)
+set_property(TEST cmake_version
+  PROPERTY ENVIRONMENT_MODIFICATION
+    MISSING_EQUAL)
diff --git a/Tests/RunCMake/ctest_environment/RunCMakeTest.cmake b/Tests/RunCMake/ctest_environment/RunCMakeTest.cmake
new file mode 100644
index 0000000..3447779
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/RunCMakeTest.cmake
@@ -0,0 +1,12 @@
+include(RunCTest)
+
+# Isolate our ctest runs from external environment.
+unset(ENV{CTEST_PARALLEL_LEVEL})
+unset(ENV{CTEST_OUTPUT_ON_FAILURE})
+
+set(CASE_SOURCE_DIR "${RunCMake_SOURCE_DIR}")
+set(RunCTest_VERBOSE_FLAG "-VV")
+
+run_ctest(ENVIRONMENT_MODIFICATION-invalid-op)
+run_ctest(ENVIRONMENT_MODIFICATION-no-colon)
+run_ctest(ENVIRONMENT_MODIFICATION-no-equals)
diff --git a/Tests/RunCMake/ctest_environment/test.cmake.in b/Tests/RunCMake/ctest_environment/test.cmake.in
new file mode 100644
index 0000000..ca23c83
--- /dev/null
+++ b/Tests/RunCMake/ctest_environment/test.cmake.in
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.7)
+
+set(CTEST_SITE                          "test-site")
+set(CTEST_BUILD_NAME                    "test-build-name")
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR               "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+
+set(ctest_test_args "@CASE_CTEST_TEST_ARGS@")
+ctest_start(Experimental)
+ctest_configure()
+ctest_build()
+ctest_test(${ctest_test_args})
diff --git a/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-check.cmake b/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-check.cmake
new file mode 100644
index 0000000..9d2a059
--- /dev/null
+++ b/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-check.cmake
@@ -0,0 +1,10 @@
+function (find_xml_file name)
+  file(GLOB test_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/${name}.xml")
+  if (NOT test_xml_file)
+    message(FATAL_ERROR
+      "${name}.xml not created.")
+  endif ()
+endfunction ()
+
+find_xml_file(DynamicAnalysis)
+find_xml_file(DynamicAnalysis-Test)
diff --git a/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-stderr.txt b/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-stderr.txt
new file mode 100644
index 0000000..e1fc77a
--- /dev/null
+++ b/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-stderr.txt
@@ -0,0 +1 @@
+Cannot find memory tester output file: .*/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-build/Testing/Temporary/MemoryChecker.1.log\.\*
diff --git a/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-stdout.txt b/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-stdout.txt
new file mode 100644
index 0000000..b3473bf
--- /dev/null
+++ b/Tests/RunCMake/ctest_memcheck/ExpectedOutputs-stdout.txt
@@ -0,0 +1,2 @@
+Memory checking results:
+left shift of negative value -256 - 1
diff --git a/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake b/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake
index 6e0a91c..cb8f696 100644
--- a/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_memcheck/RunCMakeTest.cmake
@@ -95,6 +95,19 @@
 unset(CTEST_EXTRA_CODE)
 
 #-----------------------------------------------------------------------------
+# add output test
+set(CTEST_EXTRA_CODE
+"set(CTEST_MEMORYCHECK_SANITIZER_OPTIONS \"simulate_sanitizer=1\")
+")
+set(CMAKELISTS_EXTRA_CODE
+"add_test(NAME TestSan COMMAND \"\${CMAKE_COMMAND}\"
+-P \"${RunCMake_SOURCE_DIR}/testUndefinedBehaviorSanitizer.cmake\")
+")
+run_mc_test(ExpectedOutputs "" -DMEMCHECK_TYPE=UndefinedBehaviorSanitizer)
+unset(CMAKELISTS_EXTRA_CODE)
+unset(CTEST_EXTRA_CODE)
+
+#-----------------------------------------------------------------------------
 set(CTEST_EXTRA_CODE "string(REPLACE \" \" \"\\\\ \" PRE_POST_COMMAND \"\${CTEST_MEMORYCHECK_COMMAND}\")
 
 set(CTEST_CUSTOM_PRE_MEMCHECK \"\${PRE_POST_COMMAND} pre command\")
diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
index 5f9b32d..de81049 100644
--- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
@@ -158,13 +158,25 @@
   COMMAND ${CMAKE_COMMAND} -E
   echo <DartMeasurement type="numeric/double" name="my_custom_value">1.4847</DartMeasurement>)
 add_test(
+  NAME double_measurement2
+  COMMAND ${CMAKE_COMMAND} -E
+  echo <CTestMeasurement type="numeric/double" name="another_custom_value">1.8474</CTestMeasurement>)
+add_test(
   NAME img_measurement
   COMMAND ${CMAKE_COMMAND} -E
   echo <DartMeasurementFile name="TestImage" type="image/png">]] ${IMAGE_DIR}/cmake-logo-16.png [[</DartMeasurementFile>)
 add_test(
+  NAME img_measurement2
+  COMMAND ${CMAKE_COMMAND} -E
+  echo <CTestMeasurementFile name="TestImage2" type="image/png">]] ${IMAGE_DIR}/cmake-logo-16.png [[</CTestMeasurementFile>)
+add_test(
   NAME file_measurement
   COMMAND ${CMAKE_COMMAND} -E
   echo <DartMeasurementFile name="my_test_input_data" type="file">]] ${IMAGE_DIR}/cmake-logo-16.png [[</DartMeasurementFile>)
+add_test(
+  NAME file_measurement2
+  COMMAND ${CMAKE_COMMAND} -E
+  echo <CTestMeasurementFile name="another_test_input_data" type="file">]] ${IMAGE_DIR}/cmake-logo-16.png [[</CTestMeasurementFile>)
   ]])
   run_ctest(TestMeasurements)
 endfunction()
@@ -194,3 +206,16 @@
   run_ctest(TestChangingLabels)
 endfunction()
 run_changing_labels()
+
+# Verify that test output can add additional labels
+function(run_extra_labels)
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_test(
+  NAME custom_labels
+  COMMAND ${CMAKE_COMMAND} -E
+  echo before\n<CTestLabel>label2</CTestLabel>\n<CTestLabel>label1</CTestLabel>\n<CTestLabel>label3</CTestLabel>\n<CTestLabel>label2</CTestLabel>\nafter)
+set_tests_properties(custom_labels PROPERTIES LABELS "label1")
+  ]])
+  run_ctest(TestExtraLabels)
+endfunction()
+run_extra_labels()
diff --git a/Tests/RunCMake/ctest_test/TestExtraLabels-check.cmake b/Tests/RunCMake/ctest_test/TestExtraLabels-check.cmake
new file mode 100644
index 0000000..eaa50d1
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/TestExtraLabels-check.cmake
@@ -0,0 +1,25 @@
+file(READ "${RunCMake_TEST_BINARY_DIR}/Testing/TAG" _tag)
+string(REGEX REPLACE "^([^\n]*)\n.*$" "\\1" _date "${_tag}")
+file(READ "${RunCMake_TEST_BINARY_DIR}/Testing/${_date}/Test.xml" _test_contents)
+
+# Check labels.
+STRING(REGEX MATCHALL [[<Label>label1</Label>]] matches "${_test_contents}")
+list(LENGTH matches n_matches)
+if(NOT n_matches EQUAL 1)
+  string(APPEND RunCMake_TEST_FAILED  "expected 1 match for label1, found ${n_matches}")
+endif()
+STRING(REGEX MATCHALL [[<Label>label2</Label>]] matches "${_test_contents}")
+list(LENGTH matches n_matches)
+if(NOT n_matches EQUAL 1)
+  string(APPEND RunCMake_TEST_FAILED  "expected 1 match for label2, found ${n_matches}")
+endif()
+STRING(REGEX MATCHALL [[<Label>label3</Label>]] matches "${_test_contents}")
+list(LENGTH matches n_matches)
+if(NOT n_matches EQUAL 1)
+  string(APPEND RunCMake_TEST_FAILED  "expected 1 match for label3, found ${n_matches}")
+endif()
+
+# Check test output.
+if(NOT _test_contents MATCHES "<Value>before\nafter\n</Value>")
+  string(APPEND RunCMake_TEST_FAILED "Could not find expected output in Test.xml")
+endif()
diff --git a/Tests/RunCMake/ctest_test/TestMeasurements-check.cmake b/Tests/RunCMake/ctest_test/TestMeasurements-check.cmake
index 0095db0..7e0928d 100644
--- a/Tests/RunCMake/ctest_test/TestMeasurements-check.cmake
+++ b/Tests/RunCMake/ctest_test/TestMeasurements-check.cmake
@@ -10,13 +10,31 @@
 if(NOT _test_contents MATCHES "<Value>1.4847</Value>")
   string(APPEND RunCMake_TEST_FAILED "Could not find expected measurement value in Test.xml")
 endif()
+# Check the other double measurement.
+if(NOT _test_contents MATCHES [[NamedMeasurement type="numeric/double" name="another_custom_value"]])
+  string(APPEND RunCMake_TEST_FAILED
+    "Could not find expected <NamedMeasurement> tag(2) for type='numeric/double' in Test.xml")
+endif()
+if(NOT _test_contents MATCHES "<Value>1.8474</Value>")
+  string(APPEND RunCMake_TEST_FAILED "Could not find expected measurement value(2) in Test.xml")
+endif()
 # Check img measurement.
 if(NOT _test_contents MATCHES [[NamedMeasurement name="TestImage" type="image/png" encoding="base64"]])
   string(APPEND RunCMake_TEST_FAILED
     "Could not find expected <NamedMeasurement> tag for type='image/png' in Test.xml")
 endif()
+# Check img measurement 2.
+if(NOT _test_contents MATCHES [[NamedMeasurement name="TestImage2" type="image/png" encoding="base64"]])
+  string(APPEND RunCMake_TEST_FAILED
+    "Could not find expected <NamedMeasurement> tag(2) for type='image/png' in Test.xml")
+endif()
 # Check file measurement.
 if(NOT _test_contents MATCHES [[NamedMeasurement name="my_test_input_data" encoding="base64" compression="tar/gzip" filename="cmake-logo-16.png" type="file"]])
   string(APPEND RunCMake_TEST_FAILED
     "Could not find expected <NamedMeasurement> tag for type='file' in Test.xml")
 endif()
+# Check file measurement 2.
+if(NOT _test_contents MATCHES [[NamedMeasurement name="another_test_input_data" encoding="base64" compression="tar/gzip" filename="cmake-logo-16.png" type="file"]])
+  string(APPEND RunCMake_TEST_FAILED
+    "Could not find expected <NamedMeasurement> tag(2) for type='file' in Test.xml")
+endif()
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-notfile-all-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-notfile-all-stderr.txt
index 5b86322..9ed1728 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-notfile-all-stderr.txt
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-notfile-all-stderr.txt
@@ -1,2 +1,4 @@
 ^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
-  file Could not resolve file libtest\.so$
+  file Could not resolve runtime dependencies:
+
+    libtest\.so$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-unresolved-all-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-unresolved-all-stderr.txt
index eaca512..51010a1 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-unresolved-all-stderr.txt
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/linux-unresolved-all-stderr.txt
@@ -1,2 +1,4 @@
 ^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
-  file Could not resolve file libunresolved\.so$
+  file Could not resolve runtime dependencies:
+
+    libunresolved\.so$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-unresolved-all-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-unresolved-all-stderr.txt
index 01762b4..5743d27 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-unresolved-all-stderr.txt
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-unresolved-all-stderr.txt
@@ -1,2 +1,4 @@
 ^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
-  file Could not resolve file @rpath/libunresolved\.dylib$
+  file Could not resolve runtime dependencies:
+
+    @rpath/libunresolved\.dylib$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr.txt
index a20654c..0efcb57 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr.txt
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr.txt
@@ -1,2 +1,4 @@
 ^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
-  file Could not resolve file (lib)?unresolved\.dll$
+  file Could not resolve runtime dependencies:
+
+    (lib)?unresolved\.dll$
diff --git a/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake b/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake
index eb7b497..525df09 100644
--- a/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file-RPATH/RunCMakeTest.cmake
@@ -1,9 +1,18 @@
 include(RunCMake)
 
-if(HAVE_ELF_H)
-  run_cmake_command(ELF ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/ELF.cmake)
-endif()
+run_cmake_command(ELF ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/ELF.cmake)
 
 if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
   run_cmake_command(XCOFF ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/XCOFF.cmake)
 endif()
+
+run_cmake_command(TextCheck ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextCheck.cmake)
+run_cmake_command(TextCheckEmpty ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextCheckEmpty.cmake)
+
+run_cmake_command(TextChange ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextChange.cmake)
+run_cmake_command(TextChangeEmpty ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextChangeEmpty.cmake)
+
+run_cmake_command(TextSet ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextSet.cmake)
+run_cmake_command(TextSetEmpty ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextSetEmpty.cmake)
+
+run_cmake_command(TextRemove ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/TextRemove.cmake)
diff --git a/Tests/RunCMake/file-RPATH/TextChange-result.txt b/Tests/RunCMake/file-RPATH/TextChange-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextChange-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-RPATH/TextChange-stderr.txt b/Tests/RunCMake/file-RPATH/TextChange-stderr.txt
new file mode 100644
index 0000000..48a1bf5
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextChange-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Error at [^
+]*/Tests/RunCMake/file-RPATH/TextChange.cmake:[0-9]+ \(file\):
+  file RPATH_CHANGE could not write new RPATH:
+
+    /new/rpath
+
+  to the file:
+
+    [^
+]*/Tests/RunCMake/file-RPATH/TextChange-build/not_a_binary.txt
+
+  The file format is not recognized\.$
diff --git a/Tests/RunCMake/file-RPATH/TextChange.cmake b/Tests/RunCMake/file-RPATH/TextChange.cmake
new file mode 100644
index 0000000..5c599ec
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextChange.cmake
@@ -0,0 +1,3 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_CHANGE FILE "${f}" OLD_RPATH "/old/rpath" NEW_RPATH "/new/rpath")
diff --git a/Tests/RunCMake/file-RPATH/TextChangeEmpty.cmake b/Tests/RunCMake/file-RPATH/TextChangeEmpty.cmake
new file mode 100644
index 0000000..8f855d9
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextChangeEmpty.cmake
@@ -0,0 +1,3 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_CHANGE FILE "${f}" OLD_RPATH "/old/rpath" NEW_RPATH "")
diff --git a/Tests/RunCMake/file-RPATH/TextCheck.cmake b/Tests/RunCMake/file-RPATH/TextCheck.cmake
new file mode 100644
index 0000000..1f21ba7
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextCheck.cmake
@@ -0,0 +1,6 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_CHECK FILE "${f}" RPATH "/some/rpath")
+if(EXISTS "${f}")
+  message(FATAL_ERROR "RPATH_CHECK did not remove\n ${f}\nfor non-empty RPATH")
+endif()
diff --git a/Tests/RunCMake/file-RPATH/TextCheckEmpty.cmake b/Tests/RunCMake/file-RPATH/TextCheckEmpty.cmake
new file mode 100644
index 0000000..806c73d
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextCheckEmpty.cmake
@@ -0,0 +1,6 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_CHECK FILE "${f}" RPATH "")
+if(NOT EXISTS "${f}")
+  message(FATAL_ERROR "RPATH_CHECK removed\n ${f}\nfor empty RPATH")
+endif()
diff --git a/Tests/RunCMake/file-RPATH/TextRemove.cmake b/Tests/RunCMake/file-RPATH/TextRemove.cmake
new file mode 100644
index 0000000..aecaf6f
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextRemove.cmake
@@ -0,0 +1,3 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_REMOVE FILE "${f}")
diff --git a/Tests/RunCMake/file-RPATH/TextSet-result.txt b/Tests/RunCMake/file-RPATH/TextSet-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextSet-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/file-RPATH/TextSet-stderr.txt b/Tests/RunCMake/file-RPATH/TextSet-stderr.txt
new file mode 100644
index 0000000..811c9a5
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextSet-stderr.txt
@@ -0,0 +1,12 @@
+^CMake Error at [^
+]*/Tests/RunCMake/file-RPATH/TextSet.cmake:[0-9]+ \(file\):
+  file RPATH_SET could not write new RPATH:
+
+    /new/rpath
+
+  to the file:
+
+    [^
+]*/Tests/RunCMake/file-RPATH/TextSet-build/not_a_binary.txt
+
+  The file format is not recognized\.$
diff --git a/Tests/RunCMake/file-RPATH/TextSet.cmake b/Tests/RunCMake/file-RPATH/TextSet.cmake
new file mode 100644
index 0000000..f4b09d2
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextSet.cmake
@@ -0,0 +1,3 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_SET FILE "${f}" NEW_RPATH "/new/rpath")
diff --git a/Tests/RunCMake/file-RPATH/TextSetEmpty.cmake b/Tests/RunCMake/file-RPATH/TextSetEmpty.cmake
new file mode 100644
index 0000000..d9517e0
--- /dev/null
+++ b/Tests/RunCMake/file-RPATH/TextSetEmpty.cmake
@@ -0,0 +1,3 @@
+set(f "${CMAKE_CURRENT_BINARY_DIR}/not_a_binary.txt")
+file(WRITE "${f}" "Not a binary.\n")
+file(RPATH_SET FILE "${f}" NEW_RPATH "")
diff --git a/Tests/RunCMake/file/READ_ELF-stderr.txt b/Tests/RunCMake/file/READ_ELF-stderr.txt
index 7b32804..fd02ca1 100644
--- a/Tests/RunCMake/file/READ_ELF-stderr.txt
+++ b/Tests/RunCMake/file/READ_ELF-stderr.txt
@@ -1,2 +1,19 @@
-.*file READ_ELF must be called with at least three additional arguments\.
-.*file READ_ELF given FILE "XXX" that does not exist\.
+^CMake Error at READ_ELF.cmake:1 \(file\):
+  file READ_ELF must be called with at least three additional arguments\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at READ_ELF.cmake:2 \(file\):
+  file READ_ELF given FILE "XXX" that does not exist\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
++
+CMake Error at READ_ELF.cmake:4 \(file\):
+  file READ_ELF given FILE:
+
+    [^
+]*/Tests/RunCMake/file/READ_ELF.cmake
+
+  that is not a valid ELF file\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/file/READ_ELF.cmake b/Tests/RunCMake/file/READ_ELF.cmake
index cd02c9b..8778fc2 100644
--- a/Tests/RunCMake/file/READ_ELF.cmake
+++ b/Tests/RunCMake/file/READ_ELF.cmake
@@ -1,2 +1,4 @@
 file(READ_ELF XXX)
 file(READ_ELF XXX RPATH YYY)
+file(READ_ELF ${CMAKE_CURRENT_LIST_FILE} RPATH YYY CAPTURE_ERROR err)
+file(READ_ELF ${CMAKE_CURRENT_LIST_FILE} RPATH YYY)
diff --git a/Tests/RunCMake/file/RunCMakeTest.cmake b/Tests/RunCMake/file/RunCMakeTest.cmake
index 9efd3f4..aff4735 100644
--- a/Tests/RunCMake/file/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file/RunCMakeTest.cmake
@@ -97,7 +97,9 @@
   run_cmake(READ_SYMLINK)
   run_cmake(READ_SYMLINK-noexist)
   run_cmake(READ_SYMLINK-notsymlink)
-  run_cmake(INSTALL-FOLLOW_SYMLINK_CHAIN)
+  if(NOT CYGWIN)
+    run_cmake(INSTALL-FOLLOW_SYMLINK_CHAIN)
+  endif()
 endif()
 
 run_cmake(REAL_PATH-unexpected-arg)
diff --git a/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt b/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt
new file mode 100644
index 0000000..185720b
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt
@@ -0,0 +1,4 @@
+.*find_library considered the following locations.*
+.*\(lib\)library_no_exist\(\\.tbd\|\\.dylib\|\\.so\|\\.a\).*
+.*The item was found at.*
+.*lib/libcreated.a.*
diff --git a/Tests/RunCMake/find_library/FromScriptMode-stderr-windows.txt b/Tests/RunCMake/find_library/FromScriptMode-stderr-windows.txt
new file mode 100644
index 0000000..501ec0f
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromScriptMode-stderr-windows.txt
@@ -0,0 +1,4 @@
+.*find_library considered the following locations.*
+.*\(\|lib\)library_no_exist\(\\.lib\|\\.dll\\.a\|\\.a\).*
+.*The item was found at.*
+.*lib/libcreated.a.*
diff --git a/Tests/RunCMake/find_library/FromScriptMode-stderr.txt b/Tests/RunCMake/find_library/FromScriptMode-stderr.txt
new file mode 100644
index 0000000..046f680
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromScriptMode-stderr.txt
@@ -0,0 +1,4 @@
+.*find_library considered the following locations.*
+.*\(lib\)library_no_exist\(\\.so\|\\.a\).*
+.*The item was found at.*
+.*lib/libcreated.a.*
diff --git a/Tests/RunCMake/find_library/FromScriptMode.cmake b/Tests/RunCMake/find_library/FromScriptMode.cmake
new file mode 100644
index 0000000..4d3c699
--- /dev/null
+++ b/Tests/RunCMake/find_library/FromScriptMode.cmake
@@ -0,0 +1,15 @@
+
+if(TEMP_DIR)
+  file(REMOVE_RECURSE "${TEMP_DIR}")
+  file(MAKE_DIRECTORY "${TEMP_DIR}")
+  file(MAKE_DIRECTORY "${TEMP_DIR}/lib")
+  file(WRITE "${TEMP_DIR}/lib/libcreated.a" "created")
+endif()
+
+set(CMAKE_FIND_DEBUG_MODE 1)
+find_library(CREATED_LIBRARY NAMES library_no_exist)
+
+set(CMAKE_PREFIX_PATH "${TEMP_DIR}")
+find_library(CREATED_LIBRARY NAMES created)
+message(STATUS "CREATED_LIBRARY='${CREATED_LIBRARY}'")
+set(CMAKE_FIND_DEBUG_MODE 0)
diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake
index e297173..ad02c82 100644
--- a/Tests/RunCMake/find_library/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake
@@ -10,3 +10,5 @@
 run_cmake(PrefixInPATH)
 run_cmake(Required)
 run_cmake(NO_CACHE)
+
+run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp")
diff --git a/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt b/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt
new file mode 100644
index 0000000..0db83aa
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0084-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0084-OLD.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0084 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/find_package/MissingNormalForceRequired-result.txt b/Tests/RunCMake/find_package/MissingNormalForceRequired-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingNormalForceRequired-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt b/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt
new file mode 100644
index 0000000..f6c0b44
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingNormalForceRequired-stderr.txt
@@ -0,0 +1,20 @@
+CMake Error at MissingNormalForceRequired.cmake:2 \(find_package\):
+  No "FindNotHere.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Warning \(dev\) at MissingNormalForceRequired.cmake:2 \(find_package\):
+  FindNotHere.cmake must either be part of this project itself, in this case
+  adjust CMAKE_MODULE_PATH so that it points to the correct location inside
+  its source tree\.
+
+  Or it must be installed by a package which has already been found via
+  find_package\(\)\.  In this case make sure that package has indeed been found
+  and adjust CMAKE_MODULE_PATH to contain the location where that package has
+  installed FindNotHere\.cmake\.  This must be a location provided by that
+  package.  This error in general means that the buildsystem of this project
+  is relying on a Find-module without ensuring that it is actually available\.
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:3 \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it\.
diff --git a/Tests/RunCMake/find_package/MissingNormalForceRequired.cmake b/Tests/RunCMake/find_package/MissingNormalForceRequired.cmake
new file mode 100644
index 0000000..5935316
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingNormalForceRequired.cmake
@@ -0,0 +1,3 @@
+set(CMAKE_REQUIRE_FIND_PACKAGE_NotHere ON)
+find_package(NotHere MODULE)
+message(FATAL_ERROR "This error must not be reachable.")
diff --git a/Tests/RunCMake/find_package/RequiredOptionValuesClash-result.txt b/Tests/RunCMake/find_package/RequiredOptionValuesClash-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package/RequiredOptionValuesClash-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/RequiredOptionValuesClash-stderr.txt b/Tests/RunCMake/find_package/RequiredOptionValuesClash-stderr.txt
new file mode 100644
index 0000000..b4fdd98
--- /dev/null
+++ b/Tests/RunCMake/find_package/RequiredOptionValuesClash-stderr.txt
@@ -0,0 +1,11 @@
+CMake Error at RequiredOptionValuesClash.cmake:4 \(find_package\):
+  find_package for module Foo was made REQUIRED with
+  CMAKE_REQUIRE_FIND_PACKAGE_Foo but CMAKE_DISABLE_FIND_PACKAGE_Foo is
+  enabled.  A REQUIRED package cannot be disabled.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
++
+CMake Error at RequiredOptionValuesClash.cmake:5 \(message\):
+  This error must not be reachable\.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/find_package/RequiredOptionValuesClash.cmake b/Tests/RunCMake/find_package/RequiredOptionValuesClash.cmake
new file mode 100644
index 0000000..04fece7
--- /dev/null
+++ b/Tests/RunCMake/find_package/RequiredOptionValuesClash.cmake
@@ -0,0 +1,5 @@
+set(CMAKE_DISABLE_FIND_PACKAGE_Foo ON)
+set(CMAKE_REQUIRE_FIND_PACKAGE_Foo ON)
+
+find_package(Foo)
+message(FATAL_ERROR "This error must not be reachable.")
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index 3ba04c8..b20a889 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -6,6 +6,7 @@
 run_cmake(FromPATHEnv)
 run_cmake(FromPrefixPath)
 run_cmake(MissingNormal)
+run_cmake(MissingNormalForceRequired)
 run_cmake(MissingNormalRequired)
 run_cmake(MissingNormalVersion)
 run_cmake(MissingNormalWarnNoModuleOld)
@@ -23,6 +24,7 @@
 run_cmake(PackageRootNestedModule)
 run_cmake(PolicyPush)
 run_cmake(PolicyPop)
+run_cmake(RequiredOptionValuesClash)
 run_cmake(SetFoundFALSE)
 run_cmake(WrongVersion)
 run_cmake(WrongVersionConfig)
diff --git a/Tests/RunCMake/if/IncompleteMatches-stdout.txt b/Tests/RunCMake/if/IncompleteMatches-stdout.txt
new file mode 100644
index 0000000..634d988
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatches-stdout.txt
@@ -0,0 +1,6 @@
+-- Test #1 passed
+-- Test #2 passed
+-- Test #3 passed
+-- Test #4 passed
+-- Test #5 passed
+-- Test #6 passed
diff --git a/Tests/RunCMake/if/IncompleteMatches.cmake b/Tests/RunCMake/if/IncompleteMatches.cmake
new file mode 100644
index 0000000..7142cfc
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatches.cmake
@@ -0,0 +1,36 @@
+if(MATCHES)
+  message(SEND_ERROR "Test #1 failed")
+else()
+  message(STATUS "Test #1 passed")
+endif()
+
+if("" MATCHES "")
+  message(STATUS "Test #2 passed")
+else()
+  message(SEND_ERROR "Test #2 failed")
+endif()
+
+if(MATCHES RHS)
+  message(SEND_ERROR "Test #3 failed")
+else()
+  message(STATUS "Test #3 passed")
+endif()
+
+set(RHS "")
+if(MATCHES RHS)
+  message(SEND_ERROR "Test #4 failed")
+else()
+  message(STATUS "Test #4 passed")
+endif()
+
+if(MATCHES "^$")
+  message(SEND_ERROR "Test #5 failed")
+else()
+  message(STATUS "Test #5 passed")
+endif()
+
+if("" MATCHES "^$")
+  message(STATUS "Test #6 passed")
+else()
+  message(SEND_ERROR "Test #6 failed")
+endif()
diff --git a/Tests/RunCMake/if/IncompleteMatchesFail-result.txt b/Tests/RunCMake/if/IncompleteMatchesFail-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatchesFail-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt b/Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt
new file mode 100644
index 0000000..6e8ac80
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatchesFail-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at IncompleteMatchesFail\.cmake:1 \(if\):
+  if given arguments:
+
+    "LHS" "MATCHES"
+
+  Unknown arguments specified
diff --git a/Tests/RunCMake/if/IncompleteMatchesFail.cmake b/Tests/RunCMake/if/IncompleteMatchesFail.cmake
new file mode 100644
index 0000000..a3ef2b8
--- /dev/null
+++ b/Tests/RunCMake/if/IncompleteMatchesFail.cmake
@@ -0,0 +1,2 @@
+if(LHS MATCHES)
+endif()
diff --git a/Tests/RunCMake/if/RunCMakeTest.cmake b/Tests/RunCMake/if/RunCMakeTest.cmake
index f54edf7..6baa840 100644
--- a/Tests/RunCMake/if/RunCMakeTest.cmake
+++ b/Tests/RunCMake/if/RunCMakeTest.cmake
@@ -9,7 +9,11 @@
 run_cmake(elseif-message)
 run_cmake(misplaced-elseif)
 
+run_cmake(unbalanced-parenthesis)
+
 run_cmake(MatchesSelf)
+run_cmake(IncompleteMatches)
+run_cmake(IncompleteMatchesFail)
 
 run_cmake(TestNameThatExists)
 run_cmake(TestNameThatDoesNotExist)
diff --git a/Tests/RunCMake/if/unbalanced-parenthesis-result.txt b/Tests/RunCMake/if/unbalanced-parenthesis-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/if/unbalanced-parenthesis-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt b/Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt
new file mode 100644
index 0000000..d2260a0
--- /dev/null
+++ b/Tests/RunCMake/if/unbalanced-parenthesis-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at unbalanced-parenthesis\.cmake:[0-9]+ \(if\):
+  if given arguments:
+
+    "\("
+
+  mismatched parenthesis in condition
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/if/unbalanced-parenthesis.cmake b/Tests/RunCMake/if/unbalanced-parenthesis.cmake
new file mode 100644
index 0000000..45932f6
--- /dev/null
+++ b/Tests/RunCMake/if/unbalanced-parenthesis.cmake
@@ -0,0 +1,5 @@
+set(paren "(")
+if(${paren})
+  message(STATUS "Condition incorrectly true")
+endif()
+message(STATUS "Code incorrectly accepted")
diff --git a/Tests/RunCMake/install/CMP0087-OLD-stderr.txt b/Tests/RunCMake/install/CMP0087-OLD-stderr.txt
new file mode 100644
index 0000000..5233ebc
--- /dev/null
+++ b/Tests/RunCMake/install/CMP0087-OLD-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at CMP0087-OLD/CMakeLists.txt:5 \(cmake_policy\):
+  The OLD behavior for policy CMP0087 will be removed from a future version
+  of CMake.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD.$
diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-check.cmake b/Tests/RunCMake/install/EXPORT-TargetTwice-check.cmake
new file mode 100644
index 0000000..97677ca
--- /dev/null
+++ b/Tests/RunCMake/install/EXPORT-TargetTwice-check.cmake
@@ -0,0 +1,19 @@
+set(pkg1_cmake "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/pkg1/pkg1.cmake")
+file(STRINGS "${pkg1_cmake}" pkg1_includes REGEX INTERFACE_INCLUDE_DIRECTORIES)
+set(pkg1_expect [[INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg1/inc"]])
+if(NOT pkg1_includes MATCHES "${pkg1_expect}")
+  set(RunCMake_TEST_FAILED "pkg1 has unexpected INTERFACE_INCLUDE_DIRECTORIES line:
+  ${pkg1_includes}
+It does not match:
+  ${pkg1_expect}")
+endif()
+
+set(pkg2_cmake "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/pkg2/pkg2.cmake")
+file(STRINGS "${pkg2_cmake}" pkg2_includes REGEX INTERFACE_INCLUDE_DIRECTORIES)
+set(pkg2_expect [[INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg2/inc"]])
+if(NOT pkg2_includes MATCHES "${pkg2_expect}")
+  set(RunCMake_TEST_FAILED "pkg2 has unexpected INTERFACE_INCLUDE_DIRECTORIES line:
+  ${pkg2_includes}
+It does not match:
+  ${pkg2_expect}")
+endif()
diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt
new file mode 100644
index 0000000..592f79c
--- /dev/null
+++ b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg1.txt
@@ -0,0 +1,5 @@
+.+
+set_target_properties\(pkg1::foo PROPERTIES
+.+INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg1/inc"
+\)
+.+
diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt
new file mode 100644
index 0000000..ebfc43e
--- /dev/null
+++ b/Tests/RunCMake/install/EXPORT-TargetTwice-pkg2.txt
@@ -0,0 +1,5 @@
+.+
+set_target_properties\(pkg2::foo PROPERTIES
+.+INTERFACE_INCLUDE_DIRECTORIES "\${_IMPORT_PREFIX}/pkg2/inc"
+\)
+.+
diff --git a/Tests/RunCMake/install/EXPORT-TargetTwice.cmake b/Tests/RunCMake/install/EXPORT-TargetTwice.cmake
new file mode 100644
index 0000000..cac4ff2
--- /dev/null
+++ b/Tests/RunCMake/install/EXPORT-TargetTwice.cmake
@@ -0,0 +1,17 @@
+enable_language(C)
+
+add_library(foo STATIC empty.c)
+
+install(TARGETS foo
+  EXPORT pkg1
+  ARCHIVE  DESTINATION pkg1/lib
+  INCLUDES DESTINATION pkg1/inc
+  )
+install(EXPORT pkg1 DESTINATION pkg1)
+
+install(TARGETS foo
+  EXPORT pkg2
+  ARCHIVE  DESTINATION pkg2/lib
+  INCLUDES DESTINATION pkg2/inc
+  )
+install(EXPORT pkg2 DESTINATION pkg2)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index f79a3ea..7c12d4a 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -82,6 +82,7 @@
 run_cmake(EXPORT-UnknownExport)
 run_cmake(EXPORT-NamelinkOnly)
 run_cmake(EXPORT-SeparateNamelink)
+run_cmake(EXPORT-TargetTwice)
 run_cmake(CMP0062-OLD)
 run_cmake(CMP0062-NEW)
 run_cmake(CMP0062-WARN)
diff --git a/Tests/RunCMake/string/Timestamp-stderr.txt b/Tests/RunCMake/string/Timestamp-stderr.txt
index cd4dcb3..d54777b 100644
--- a/Tests/RunCMake/string/Timestamp-stderr.txt
+++ b/Tests/RunCMake/string/Timestamp-stderr.txt
@@ -1 +1 @@
-RESULT=2005-08-07 23:19:49 Sunday=Sun August=Aug 05 day=219 wd=0 week=32 %I=11 epoch=1123456789
+RESULT=2005-08-07 23:19:49 Sunday=Sun August=Aug 05 day=219 wd=0 week=32 w_iso=31 %I=11 epoch=1123456789
diff --git a/Tests/RunCMake/string/Timestamp.cmake b/Tests/RunCMake/string/Timestamp.cmake
index cba258d..7fd6d72 100644
--- a/Tests/RunCMake/string/Timestamp.cmake
+++ b/Tests/RunCMake/string/Timestamp.cmake
@@ -1,3 +1,3 @@
 set(ENV{SOURCE_DATE_EPOCH} "1123456789")
-string(TIMESTAMP RESULT "%Y-%m-%d %H:%M:%S %A=%a %B=%b %y day=%j wd=%w week=%U %%I=%I epoch=%s" UTC)
+string(TIMESTAMP RESULT "%Y-%m-%d %H:%M:%S %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s" UTC)
 message("RESULT=${RESULT}")
diff --git a/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt b/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt
index 5b4761c..cf4c540 100644
--- a/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt
+++ b/Tests/RunCMake/target_compile_features/cxx_not_enabled-stderr.txt
@@ -1,4 +1,2 @@
-^CMake Error at cxx_not_enabled.cmake:[0-9]+ \(target_compile_features\):
-  target_compile_features cannot use features from non-enabled language CXX
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)$
+^CMake Error:.*CMake can not determine linker language for target: main.*
+CMake Generate step failed.  Build files cannot be regenerated correctly.$
diff --git a/Tests/RunCMake/try_compile/EnvConfig.c b/Tests/RunCMake/try_compile/EnvConfig.c
new file mode 100644
index 0000000..5b1d400
--- /dev/null
+++ b/Tests/RunCMake/try_compile/EnvConfig.c
@@ -0,0 +1,7 @@
+#ifdef TC_CONFIG_BAD
+#  error "Built in 'Bad' config"
+#endif
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/try_compile/EnvConfig.cmake b/Tests/RunCMake/try_compile/EnvConfig.cmake
new file mode 100644
index 0000000..4040c59
--- /dev/null
+++ b/Tests/RunCMake/try_compile/EnvConfig.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+set(ENV{CMAKE_BUILD_TYPE} "Bad")
+set(ENV{CMAKE_CONFIGURATION_TYPES} "Bad;Debug")
+
+add_library(tc_defs INTERFACE IMPORTED)
+target_compile_definitions(tc_defs INTERFACE "TC_CONFIG_$<UPPER_CASE:$<CONFIG>>")
+
+try_compile(ENV_CONFIG_RESULT "${CMAKE_BINARY_DIR}"
+  SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/EnvConfig.c"
+  COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/EnvConfig.bin"
+  OUTPUT_VARIABLE tc_output
+  LINK_LIBRARIES tc_defs
+  )
+if(NOT ENV_CONFIG_RESULT)
+  string(REPLACE "\n" "\n  " tc_output "  ${tc_output}")
+  message(FATAL_ERROR "try_compile failed:\n${tc_output}")
+endif()
diff --git a/Tests/RunCMake/try_compile/RunCMakeTest.cmake b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
index fffb038..b19fd0e 100644
--- a/Tests/RunCMake/try_compile/RunCMakeTest.cmake
+++ b/Tests/RunCMake/try_compile/RunCMakeTest.cmake
@@ -16,6 +16,8 @@
 run_cmake(NonSourceCopyFile)
 run_cmake(NonSourceCompileDefinitions)
 
+run_cmake(EnvConfig)
+
 set(RunCMake_TEST_OPTIONS --debug-trycompile)
 run_cmake(PlatformVariables)
 run_cmake(WarnDeprecated)
diff --git a/Tests/RunCMake/while/RunCMakeTest.cmake b/Tests/RunCMake/while/RunCMakeTest.cmake
index 7da80ac..bb9b991 100644
--- a/Tests/RunCMake/while/RunCMakeTest.cmake
+++ b/Tests/RunCMake/while/RunCMakeTest.cmake
@@ -5,3 +5,5 @@
 run_cmake(EndMismatch)
 run_cmake(EndAlone)
 run_cmake(EndAloneArgs)
+
+run_cmake(unbalanced-parenthesis)
diff --git a/Tests/RunCMake/while/unbalanced-parenthesis-stdout.txt b/Tests/RunCMake/while/unbalanced-parenthesis-stdout.txt
new file mode 100644
index 0000000..d45e194
--- /dev/null
+++ b/Tests/RunCMake/while/unbalanced-parenthesis-stdout.txt
@@ -0,0 +1 @@
+-- Code incorrectly accepted
diff --git a/Tests/RunCMake/while/unbalanced-parenthesis.cmake b/Tests/RunCMake/while/unbalanced-parenthesis.cmake
new file mode 100644
index 0000000..39d736b
--- /dev/null
+++ b/Tests/RunCMake/while/unbalanced-parenthesis.cmake
@@ -0,0 +1,7 @@
+set(paren "(")
+while(${paren})
+  message(STATUS "Condition incorrectly true")
+  break()
+endwhile()
+# FIXME(#23296): The above condition error is tolerated for compatibility.
+message(STATUS "Code incorrectly accepted")
diff --git a/Tests/UseSWIG/CMakeLists.txt b/Tests/UseSWIG/CMakeLists.txt
index 6c0d5e4..c76e8a0 100644
--- a/Tests/UseSWIG/CMakeLists.txt
+++ b/Tests/UseSWIG/CMakeLists.txt
@@ -67,7 +67,7 @@
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
 if(SWIG_FOUND AND NOT SWIG_VERSION VERSION_LESS "4.0.2"
-    AND CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode")
+    AND CMAKE_GENERATOR MATCHES "Make|Ninja|Xcode|Visual Studio (1[1-9]|[2-9][0-9])")
   add_test(NAME UseSWIG.Depfile.BasicPython COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
diff --git a/Tests/VSManagedCustomCommand/CMakeLists.txt b/Tests/VSManagedCustomCommand/CMakeLists.txt
new file mode 100644
index 0000000..2228875
--- /dev/null
+++ b/Tests/VSManagedCustomCommand/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.20)
+project(VSManagedCustomCommand CXX)
+
+add_custom_command(OUTPUT middle.txt
+  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/example.txt ${CMAKE_CURRENT_BINARY_DIR}/middle.txt
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/example.txt
+  )
+
+add_custom_command(OUTPUT example.cxx
+  COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/middle.txt ${CMAKE_CURRENT_BINARY_DIR}/example.cxx
+  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/middle.txt
+  )
+
+add_library(example SHARED example.cxx)
+set_property(TARGET example PROPERTY COMMON_LANGUAGE_RUNTIME "")
diff --git a/Tests/VSManagedCustomCommand/example.txt b/Tests/VSManagedCustomCommand/example.txt
new file mode 100644
index 0000000..05f95f1
--- /dev/null
+++ b/Tests/VSManagedCustomCommand/example.txt
@@ -0,0 +1,4 @@
+__declspec(dllexport) int example()
+{
+  return 0;
+}
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index 0ccb721..69b4e2f 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeDeveloperReference_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.1...3.19 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.1...3.20 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index f1bdcc4..8c55c85 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -101,6 +101,7 @@
 
   # Wrappers for 3rd-party libraries
   { include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },
+  { include: [ "@<.*json/config.h>", private, "<cm3p/json/value.h>", public ] },
   { include: [ "@<.*json/forwards.h>", private, "<cm3p/json/value.h>", public ] },
   { include: [ "@<.*uv/.+\\.h>", private, "<cm3p/uv.h>", public ] },
   { include: [ "@<.*expat_external.h>", private, "<cm3p/expat.h>", public ] },
diff --git a/Utilities/KWIML/include/kwiml/int.h b/Utilities/KWIML/include/kwiml/int.h
index b2e14d5..25a063a 100644
--- a/Utilities/KWIML/include/kwiml/int.h
+++ b/Utilities/KWIML/include/kwiml/int.h
@@ -162,7 +162,11 @@
 #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
 # define KWIML_INT_HAVE_INTTYPES_H 1
 #elif defined(_MSC_VER) /* MSVC */
-# define KWIML_INT_NO_INTTYPES_H 1
+# if _MSC_VER >= 1800
+#  define KWIML_INT_HAVE_INTTYPES_H 1
+# else
+#  define KWIML_INT_NO_INTTYPES_H 1
+# endif
 #elif defined(__BORLANDC__) /* Borland */
 # define KWIML_INT_NO_INTTYPES_H 1
 #elif defined(__WATCOMC__) /* Watcom */
@@ -272,6 +276,15 @@
 # define KWIML_INT_BROKEN_PRIXPTR 1
 #endif
 
+#if defined(_MSC_VER) && _MSC_VER < 1900
+  /* MSVC scanf seems broken on 8-bit sizes until 19.00 */
+# define KWIML_INT_BROKEN_SCNd8 1
+# define KWIML_INT_BROKEN_SCNi8 1
+# define KWIML_INT_BROKEN_SCNo8 1
+# define KWIML_INT_BROKEN_SCNu8 1
+# define KWIML_INT_BROKEN_SCNx8 1
+#endif
+
 #if (defined(__SUNPRO_C)||defined(__SUNPRO_CC)) && defined(_CHAR_IS_UNSIGNED)
 # define KWIML_INT_BROKEN_INT8_T 1 /* system type defined incorrectly */
 #elif defined(__BORLANDC__) && defined(_CHAR_UNSIGNED)
@@ -303,7 +316,7 @@
 #elif defined(__BORLANDC__)
 # define KWIML_INT_private_NO_SCN8
 # define KWIML_INT_private_NO_SCN64
-#elif defined(_MSC_VER)
+#elif defined(_MSC_VER) && _MSC_VER < 1900
 # define KWIML_INT_private_NO_SCN8
 #elif defined(__WATCOMC__)
 # define KWIML_INT_private_NO_SCN8
diff --git a/Utilities/Release/macos/sign-notarize.bash b/Utilities/Release/macos/sign-notarize.bash
new file mode 100755
index 0000000..377eced
--- /dev/null
+++ b/Utilities/Release/macos/sign-notarize.bash
@@ -0,0 +1,118 @@
+#!/usr/bin/env bash
+set -e
+readonly usage='usage: sign-notarize.bash -i <id> -d <dev-acct> -k <key-item> [-p <provider>] [--] <package>.dmg
+
+Sign and notarize the "CMake.app" bundle inside the given "<package>.dmg" disk image.
+Also produce a "<package>.tar.gz" tarball containing the same "CMake.app".
+
+Options:
+
+    -i <id>                Signing Identity
+    -d <dev-acct>          Developer account name
+    -k <key-item>          Keychain item containing account credentials
+    -p <provider>          Provider short name
+'
+
+cleanup() {
+    if test -d "$tmpdir"; then
+        rm -rf "$tmpdir"
+    fi
+    if test -d "$vol_path"; then
+        hdiutil detach "$vol_path"
+    fi
+}
+
+trap "cleanup" EXIT
+
+die() {
+    echo "$@" 1>&2; exit 1
+}
+
+id=''
+dev_acct=''
+key_item=''
+provider=''
+while test "$#" != 0; do
+    case "$1" in
+    -i) shift; id="$1" ;;
+    -d) shift; dev_acct="$1" ;;
+    -k) shift; key_item="$1" ;;
+    -p) shift; provider="$1" ;;
+    --) shift ; break ;;
+    -*) die "$usage" ;;
+    *) break ;;
+    esac
+    shift
+done
+case "$1" in
+*.dmg) readonly dmg="$1"; shift ;;
+*) die "$usage" ;;
+esac
+test "$#" = 0 || die "$usage"
+
+# Verify arguments.
+if test -z "$id" -o -z "$dev_acct" -o -z "$key_item"; then
+    die "$usage"
+fi
+if test -n "$provider"; then
+    provider="--provider $provider"
+fi
+
+# Verify environment.
+if ! xcnotary="$(type -p xcnotary)"; then
+    die "'xcnotary' not found in PATH"
+fi
+readonly xcnotary
+
+readonly tmpdir="$(mktemp -d)"
+
+# Prepare entitlements.
+readonly entitlements_xml="$tmpdir/entitlements.xml"
+echo '<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>com.apple.security.cs.allow-dyld-environment-variables</key>
+  <true/>
+</dict>
+</plist>' > "$entitlements_xml"
+
+# Extract SLA
+readonly sla_xml="$tmpdir/sla.xml"
+hdiutil udifderez -xml "$dmg" > "$sla_xml"
+plutil -remove 'blkx' "$sla_xml"
+plutil -remove 'plst' "$sla_xml"
+
+# Convert from read-only original image to read-write.
+readonly udrw_dmg="$tmpdir/udrw.dmg"
+hdiutil convert "$dmg" -format UDRW -o "${udrw_dmg}"
+
+# Mount the temporary udrw image.
+readonly vol_name="$(basename "${dmg%.dmg}")"
+readonly vol_path="/Volumes/$vol_name"
+hdiutil attach "${udrw_dmg}"
+
+codesign --verify --timestamp --options=runtime --verbose --deep \
+  -s "$id" \
+  --entitlements "$entitlements_xml" \
+  "$vol_path/CMake.app/Contents/bin/cmake" \
+  "$vol_path/CMake.app/Contents/bin/ccmake" \
+  "$vol_path/CMake.app/Contents/bin/ctest" \
+  "$vol_path/CMake.app/Contents/bin/cpack" \
+  "$vol_path/CMake.app/Contents/share/cmake"*"/Modules/Internal/CPack/CPack.OSXScriptLauncher.in" \
+  "$vol_path/CMake.app"
+
+xcnotary notarize "$vol_path/CMake.app" -d "$dev_acct" -k "$key_item" $provider
+
+# Create a tarball of the volume next to the original disk image.
+readonly tar_gz="${dmg/%.dmg/.tar.gz}"
+tar cvzf "$tar_gz" -C /Volumes "$vol_name/CMake.app"
+
+# Unmount the modified udrw image.
+hdiutil detach "$vol_path"
+
+# Convert back to read-only, compressed image.
+hdiutil convert "${udrw_dmg}" -format UDZO -imagekey zlib-level=9 -ov -o "$dmg"
+
+# Re-insert SLA.
+hdiutil udifrez -xml "${sla_xml}" 'FIXME_WHY_IS_THIS_ARGUMENT_NEEDED' "$dmg"
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index 34ff4de..dd8e7a8 100755
--- a/Utilities/Scripts/update-curl.bash
+++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@
 readonly ownership="Curl Upstream <curl-library@cool.haxx.se>"
 readonly subtree="Utilities/cmcurl"
 readonly repo="https://github.com/curl/curl.git"
-readonly tag="curl-7_77_0"
+readonly tag="curl-7_79_1"
 readonly shortlog=false
 readonly paths="
   CMake/*
diff --git a/Utilities/Scripts/update-elf.bash b/Utilities/Scripts/update-elf.bash
new file mode 100755
index 0000000..1a065ba
--- /dev/null
+++ b/Utilities/Scripts/update-elf.bash
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+set -e
+set -x
+shopt -s dotglob
+
+readonly name="elf"
+readonly ownership="FreeBSD Upstream <kwrobot@kitware.com>"
+readonly subtree="Utilities/cmelf"
+readonly repo="https://github.com/freebsd/freebsd-src.git"
+readonly tag="main"
+readonly shortlog=false
+readonly paths="
+  sys/sys/elf32.h
+  sys/sys/elf64.h
+  sys/sys/elf_common.h
+"
+
+extract_source () {
+    git_archive
+    pushd "${extractdir}/${name}-reduced"
+    echo "* -whitespace" > .gitattributes
+    mv sys/sys/* .
+    sed -i -e 's/<sys\/elf_common.h>/"elf_common.h"/g' -e 's/u_int32_t/uint32_t/g' *.h
+    popd
+}
+
+. "${BASH_SOURCE%/*}/update-third-party.bash"
diff --git a/Utilities/Scripts/update-jsoncpp.bash b/Utilities/Scripts/update-jsoncpp.bash
index f8fe544..5ed00e7 100755
--- a/Utilities/Scripts/update-jsoncpp.bash
+++ b/Utilities/Scripts/update-jsoncpp.bash
@@ -8,7 +8,7 @@
 readonly ownership="JsonCpp Upstream <kwrobot@kitware.com>"
 readonly subtree="Utilities/cmjsoncpp"
 readonly repo="https://github.com/open-source-parsers/jsoncpp.git"
-readonly tag="1.8.2"
+readonly tag="1.9.4"
 readonly shortlog=false
 readonly paths="
   LICENSE
diff --git a/Utilities/Scripts/update-libarchive.bash b/Utilities/Scripts/update-libarchive.bash
index 3db89ff..3a24e2b 100755
--- a/Utilities/Scripts/update-libarchive.bash
+++ b/Utilities/Scripts/update-libarchive.bash
@@ -8,7 +8,7 @@
 readonly ownership="LibArchive Upstream <libarchive-discuss@googlegroups.com>"
 readonly subtree="Utilities/cmlibarchive"
 readonly repo="https://github.com/libarchive/libarchive.git"
-readonly tag="v3.4.2"
+readonly tag="v3.5.1"
 readonly shortlog=false
 readonly paths="
   CMakeLists.txt
diff --git a/Utilities/Scripts/update-third-party.bash b/Utilities/Scripts/update-third-party.bash
index 0d0a667..bfe6828 100644
--- a/Utilities/Scripts/update-third-party.bash
+++ b/Utilities/Scripts/update-third-party.bash
@@ -42,6 +42,22 @@
 # For convenience, the function may use the "git_archive" function which
 # does a standard "git archive" extraction using the (optional) "paths"
 # variable to only extract a subset of the source tree.
+#
+# Dependencies
+#
+# To update third party packages from git repositories with submodule,
+# you will need to install the "git-archive-all" Python package with
+#
+#   pip install git-archive-all
+#
+# or install it from https://github.com/Kentzo/git-archive-all.
+#
+# This package installs a script named "git-archive-all" where pip
+# installs executables. If you run pip under your user privileges (i.e.,
+# not using "sudo"), this location may be $HOME/.local/bin. Make sure
+# that directory is in your path so that git can find the
+# "git-archive-all" script.
+#
 ########################################################################
 
 ########################################################################
@@ -52,11 +68,24 @@
         tar -C "$extractdir" -x
 }
 
+confirm_archive_all_exists () {
+    which git-archive-all || die "git requires an archive-all command. Please run 'pip install git-archive-all'"
+}
+
+git_archive_all () {
+    confirm_archive_all_exists
+    local tmptarball="temp.tar"
+    git archive-all --prefix="" "$tmptarball"
+    mkdir -p "$extractdir/$name-reduced"
+    tar -C "$extractdir/$name-reduced" -xf "$tmptarball" $paths
+    rm -f "$tmptarball"
+}
+
 disable_custom_gitattributes() {
     pushd "${extractdir}/${name}-reduced"
     # Git does not allow custom attributes in a subdirectory where we
     # are about to merge the `.gitattributes` file, so disable them.
-    sed -i '/^\[attr\]/ {s/^/#/}' .gitattributes
+    sed -i '/^\[attr\]/ {s/^/#/;}' .gitattributes
     popd
 }
 
@@ -71,6 +100,9 @@
 
 readonly regex_date='20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
 readonly basehash_regex="$name $regex_date ([0-9a-f]*)"
+readonly toplevel_dir="$( git rev-parse --show-toplevel )"
+
+cd "$toplevel_dir"
 
 ########################################################################
 # Sanity checking
@@ -95,7 +127,7 @@
 else
     readonly basehash="$( git rev-list --author="$ownership" --grep="$basehash_regex" -n 1 HEAD )"
 fi
-readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$basehash_regex"'/ {s/.*(//;s/)//;p}' | egrep '^[0-9a-f]+$' )"
+readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$basehash_regex"'/ {s/.*(//;s/)//;p;}' | egrep '^[0-9a-f]+$' )"
 
 [ -n "$basehash" ] || \
     warn "'basehash' is empty; performing initial import"
@@ -111,7 +143,7 @@
 trap "rm -rf '$workdir'" EXIT
 
 # Get upstream
-git clone "$repo" "$upstreamdir"
+git clone --recursive "$repo" "$upstreamdir"
 
 if [ -n "$basehash" ]; then
     # Remove old worktrees
@@ -120,7 +152,7 @@
     git worktree add "$extractdir" "$basehash"
     # Clear out the working tree
     pushd "$extractdir"
-    git ls-files | xargs rm -v
+    git ls-files -z --recurse-submodules | xargs -0 rm -v
     find . -type d -empty -delete
     popd
 else
@@ -132,6 +164,8 @@
 # Extract the subset of upstream we care about
 pushd "$upstreamdir"
 git checkout "$tag"
+git submodule sync --recursive
+git submodule update --recursive --init
 readonly upstream_hash="$( git rev-parse HEAD )"
 readonly upstream_hash_short="$( git rev-parse --short=8 "$upstream_hash" )"
 readonly upstream_datetime="$( git rev-list "$upstream_hash" --format='%ci' -n 1 | grep -e "^$regex_date" )"
diff --git a/Utilities/Scripts/update-zstd.bash b/Utilities/Scripts/update-zstd.bash
index a9b62f9..48ca5a1 100755
--- a/Utilities/Scripts/update-zstd.bash
+++ b/Utilities/Scripts/update-zstd.bash
@@ -8,7 +8,7 @@
 readonly ownership="zstd upstream <kwrobot@kitware.com>"
 readonly subtree="Utilities/cmzstd"
 readonly repo="https://github.com/facebook/zstd.git"
-readonly tag="v1.4.5"
+readonly tag="v1.5.0"
 readonly shortlog=false
 readonly paths="
   LICENSE
@@ -23,7 +23,7 @@
   lib/deprecated/*.h
   lib/dictBuilder/*.c
   lib/dictBuilder/*.h
-  lib/zstd.h
+  lib/*.h
 "
 
 extract_source () {
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index 32efd48..c8a970d 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeHelp_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.1...3.19 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.1...3.20 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
@@ -58,6 +58,13 @@
 set(doc_formats "")
 if(SPHINX_HTML)
   list(APPEND doc_formats html)
+
+  # we provide the path to the produced html output in the console
+  # for tools that support URI protocol schemes
+  set(html_extra_commands
+    COMMAND ${CMAKE_COMMAND} -E echo "sphinx-build html: HTML documentation generated in file://${CMAKE_CURRENT_BINARY_DIR}/html/index.html"
+  )
+
 endif()
 if(SPHINX_MAN)
   list(APPEND doc_formats man)
diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake
index bab3046..7e38df9 100644
--- a/Utilities/cmThirdPartyChecks.cmake
+++ b/Utilities/cmThirdPartyChecks.cmake
@@ -180,6 +180,7 @@
   set(HAVE_STRERROR_R 0)
   set(HAVE_STRNCMPI 0)
   set(HAVE_STRNCPY_S 1)
+  set(HAVE_STRNLEN 1)
   set(HAVE_STROPTS_H 0)
   set(HAVE__STRTOI64 1)
   set(HAVE_STRTOLL 1)
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index 2fcce1b..e418146 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -71,21 +71,15 @@
 }
 #endif
 
-/* tests for gethostbyaddr_r or gethostbyname_r */
-#if defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \
-    defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \
-    defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \
-    defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
+/* tests for gethostbyname_r */
+#if defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
     defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
     defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
 #   define _REENTRANT
     /* no idea whether _REENTRANT is always set, just invent a new flag */
 #   define TEST_GETHOSTBYFOO_REENTRANT
 #endif
-#if defined(HAVE_GETHOSTBYADDR_R_5) || \
-    defined(HAVE_GETHOSTBYADDR_R_7) || \
-    defined(HAVE_GETHOSTBYADDR_R_8) || \
-    defined(HAVE_GETHOSTBYNAME_R_3) || \
+#if defined(HAVE_GETHOSTBYNAME_R_3) || \
     defined(HAVE_GETHOSTBYNAME_R_5) || \
     defined(HAVE_GETHOSTBYNAME_R_6) || \
     defined(TEST_GETHOSTBYFOO_REENTRANT)
@@ -98,18 +92,10 @@
   int type = 0;
   struct hostent h;
   int rc = 0;
-#if defined(HAVE_GETHOSTBYADDR_R_5) || \
-    defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \
-    \
-    defined(HAVE_GETHOSTBYNAME_R_3) || \
+#if defined(HAVE_GETHOSTBYNAME_R_3) || \
     defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
   struct hostent_data hdata;
-#elif defined(HAVE_GETHOSTBYADDR_R_7) || \
-      defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \
-      defined(HAVE_GETHOSTBYADDR_R_8) || \
-      defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \
-      \
-      defined(HAVE_GETHOSTBYNAME_R_5) || \
+#elif defined(HAVE_GETHOSTBYNAME_R_5) || \
       defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
       defined(HAVE_GETHOSTBYNAME_R_6) || \
       defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
@@ -118,24 +104,6 @@
   struct hostent *hp;
 #endif
 
-#ifndef gethostbyaddr_r
-  (void)gethostbyaddr_r;
-#endif
-
-#if   defined(HAVE_GETHOSTBYADDR_R_5) || \
-      defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT)
-  rc = gethostbyaddr_r(address, length, type, &h, &hdata);
-  (void)rc;
-#elif defined(HAVE_GETHOSTBYADDR_R_7) || \
-      defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT)
-  hp = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &h_errnop);
-  (void)hp;
-#elif defined(HAVE_GETHOSTBYADDR_R_8) || \
-      defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT)
-  rc = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &hp, &h_errnop);
-  (void)rc;
-#endif
-
 #if   defined(HAVE_GETHOSTBYNAME_R_3) || \
       defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
   rc = gethostbyname_r(address, &h, &hdata);
@@ -214,37 +182,6 @@
 #include <float.h>
 int main() { return 0; }
 #endif
-#ifdef HAVE_INET_NTOA_R_DECL
-#include <arpa/inet.h>
-
-typedef void (*func_type)();
-
-int main()
-{
-#ifndef inet_ntoa_r
-  func_type func;
-  func = (func_type)inet_ntoa_r;
-  (void)func;
-#endif
-  return 0;
-}
-#endif
-#ifdef HAVE_INET_NTOA_R_DECL_REENTRANT
-#define _REENTRANT
-#include <arpa/inet.h>
-
-typedef void (*func_type)();
-
-int main()
-{
-#ifndef inet_ntoa_r
-  func_type func;
-  func = (func_type)&inet_ntoa_r;
-  (void)func;
-#endif
-  return 0;
-}
-#endif
 #ifdef HAVE_GETADDRINFO
 #include <netdb.h>
 #include <sys/types.h>
@@ -361,7 +298,7 @@
 
 /* IoctlSocket source code */
         long flags = 0;
-        if(0 != ioctlsocket(0, FIONBIO, &flags))
+        if(0 != IoctlSocket(0, FIONBIO, &flags))
           return 1;
   ;
   return 0;
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index bbd4632..7fda7ac 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
 #
 # This software is licensed as described in the file COPYING, which
 # you should have received as part of this distribution. The terms
@@ -54,6 +54,40 @@
   set(RECV_TYPE_ARG4 "int")
   set(RECV_TYPE_RETV "ssize_t")
 else()
+function(curl_cv_func_recv_run_test recv_retv recv_arg1 recv_arg2 recv_arg3 recv_arg4)
+  unset(curl_cv_func_recv_test CACHE)
+  check_c_source_compiles("
+    ${_source_epilogue}
+    #ifdef WINSOCK_API_LINKAGE
+    WINSOCK_API_LINKAGE
+    #endif
+    extern ${recv_retv} ${signature_call_conv}
+    recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4});
+    int main(void) {
+      ${recv_arg1} s=0;
+      ${recv_arg2} buf=0;
+      ${recv_arg3} len=0;
+      ${recv_arg4} flags=0;
+      ${recv_retv} res = recv(s, buf, len, flags);
+      (void) res;
+      return 0;
+    }"
+    curl_cv_func_recv_test)
+  message(STATUS
+    "Tested: ${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})")
+  if(curl_cv_func_recv_test)
+    set(curl_cv_func_recv_args
+      "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG1 "${recv_arg1}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG2 "${recv_arg2}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG3 "${recv_arg3}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG4 "${recv_arg4}" PARENT_SCOPE)
+    set(RECV_TYPE_RETV "${recv_retv}" PARENT_SCOPE)
+    set(HAVE_RECV 1 PARENT_SCOPE)
+    set(curl_cv_func_recv_done 1 PARENT_SCOPE)
+  endif()
+endfunction()
+
 check_c_source_compiles("${_source_epilogue}
 int main(void) {
     recv(0, 0, 0, 0);
@@ -61,43 +95,16 @@
 }" curl_cv_recv)
 if(curl_cv_recv)
   if(NOT DEFINED curl_cv_func_recv_args OR curl_cv_func_recv_args STREQUAL "unknown")
+    if(APPLE)
+      curl_cv_func_recv_run_test("ssize_t" "int" "void *" "size_t" "int")
+    endif()
     foreach(recv_retv "int" "ssize_t" )
       foreach(recv_arg1 "SOCKET" "int" )
         foreach(recv_arg2 "char *" "void *" )
           foreach(recv_arg3 "int" "size_t" "socklen_t" "unsigned int")
             foreach(recv_arg4 "int" "unsigned int")
               if(NOT curl_cv_func_recv_done)
-                unset(curl_cv_func_recv_test CACHE)
-                check_c_source_compiles("
-                  ${_source_epilogue}
-                  #ifdef WINSOCK_API_LINKAGE
-                  WINSOCK_API_LINKAGE
-                  #endif
-                  extern ${recv_retv} ${signature_call_conv}
-                  recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4});
-                  int main(void) {
-                    ${recv_arg1} s=0;
-                    ${recv_arg2} buf=0;
-                    ${recv_arg3} len=0;
-                    ${recv_arg4} flags=0;
-                    ${recv_retv} res = recv(s, buf, len, flags);
-                    (void) res;
-                    return 0;
-                  }"
-                  curl_cv_func_recv_test)
-                message(STATUS
-                  "Tested: ${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})")
-                if(curl_cv_func_recv_test)
-                  set(curl_cv_func_recv_args
-                    "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}")
-                  set(RECV_TYPE_ARG1 "${recv_arg1}")
-                  set(RECV_TYPE_ARG2 "${recv_arg2}")
-                  set(RECV_TYPE_ARG3 "${recv_arg3}")
-                  set(RECV_TYPE_ARG4 "${recv_arg4}")
-                  set(RECV_TYPE_RETV "${recv_retv}")
-                  set(HAVE_RECV 1)
-                  set(curl_cv_func_recv_done 1)
-                endif()
+                curl_cv_func_recv_run_test(${recv_retv} ${recv_arg1} ${recv_arg2} ${recv_arg3} ${recv_arg4})
               endif()
             endforeach()
           endforeach()
@@ -130,6 +137,42 @@
   set(SEND_TYPE_ARG4 "int")
   set(SEND_TYPE_RETV "ssize_t")
 else()
+function(curl_cv_func_send_run_test send_retv send_arg1 send_arg2 send_arg3 send_arg4)
+  unset(curl_cv_func_send_test CACHE)
+  check_c_source_compiles("
+    ${_source_epilogue}
+    #ifdef WINSOCK_API_LINKAGE
+    WINSOCK_API_LINKAGE
+    #endif
+    extern ${send_retv} ${signature_call_conv}
+    send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4});
+    int main(void) {
+      ${send_arg1} s=0;
+      ${send_arg2} buf=0;
+      ${send_arg3} len=0;
+      ${send_arg4} flags=0;
+      ${send_retv} res = send(s, buf, len, flags);
+      (void) res;
+      return 0;
+    }"
+    curl_cv_func_send_test)
+  message(STATUS
+    "Tested: ${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})")
+  if(curl_cv_func_send_test)
+    string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
+    string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
+    set(curl_cv_func_send_args
+      "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG1 "${send_arg1}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG2 "${send_arg2}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG3 "${send_arg3}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG4 "${send_arg4}" PARENT_SCOPE)
+    set(SEND_TYPE_RETV "${send_retv}" PARENT_SCOPE)
+    set(HAVE_SEND 1 PARENT_SCOPE)
+    set(curl_cv_func_send_done 1 PARENT_SCOPE)
+  endif()
+endfunction()
+
 check_c_source_compiles("${_source_epilogue}
 int main(void) {
     send(0, 0, 0, 0);
@@ -137,45 +180,16 @@
 }" curl_cv_send)
 if(curl_cv_send)
   if(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
+    if(APPLE)
+      curl_cv_func_send_run_test("ssize_t" "int" "const void *" "size_t" "int")
+    endif()
     foreach(send_retv "int" "ssize_t" )
       foreach(send_arg1 "SOCKET" "int" "ssize_t" )
         foreach(send_arg2 "const char *" "const void *" "void *" "char *")
           foreach(send_arg3 "int" "size_t" "socklen_t" "unsigned int")
             foreach(send_arg4 "int" "unsigned int")
               if(NOT curl_cv_func_send_done)
-                unset(curl_cv_func_send_test CACHE)
-                check_c_source_compiles("
-                  ${_source_epilogue}
-                  #ifdef WINSOCK_API_LINKAGE
-                  WINSOCK_API_LINKAGE
-                  #endif
-                  extern ${send_retv} ${signature_call_conv}
-                  send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4});
-                  int main(void) {
-                    ${send_arg1} s=0;
-                    ${send_arg2} buf=0;
-                    ${send_arg3} len=0;
-                    ${send_arg4} flags=0;
-                    ${send_retv} res = send(s, buf, len, flags);
-                    (void) res;
-                    return 0;
-                  }"
-                  curl_cv_func_send_test)
-                message(STATUS
-                  "Tested: ${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})")
-                if(curl_cv_func_send_test)
-                  string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
-                  string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
-                  set(curl_cv_func_send_args
-                    "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}")
-                  set(SEND_TYPE_ARG1 "${send_arg1}")
-                  set(SEND_TYPE_ARG2 "${send_arg2}")
-                  set(SEND_TYPE_ARG3 "${send_arg3}")
-                  set(SEND_TYPE_ARG4 "${send_arg4}")
-                  set(SEND_TYPE_RETV "${send_retv}")
-                  set(HAVE_SEND 1)
-                  set(curl_cv_func_send_done 1)
-                endif()
+                curl_cv_func_send_run_test("${send_retv}" "${send_arg1}" "${send_arg2}" "${send_arg3}" "${send_arg4}")
               endif()
             endforeach()
           endforeach()
@@ -223,28 +237,6 @@
   return 0;
 }" HAVE_STRUCT_TIMEVAL)
 
-set(HAVE_SIG_ATOMIC_T 1)
-set(CMAKE_REQUIRED_FLAGS)
-if(HAVE_SIGNAL_H)
-  set(CMAKE_REQUIRED_FLAGS "-DHAVE_SIGNAL_H")
-  set(CMAKE_EXTRA_INCLUDE_FILES "signal.h")
-endif()
-check_type_size("sig_atomic_t" SIZEOF_SIG_ATOMIC_T)
-if(HAVE_SIZEOF_SIG_ATOMIC_T)
-  check_c_source_compiles("
-    #ifdef HAVE_SIGNAL_H
-    #  include <signal.h>
-    #endif
-    int main(void) {
-      static volatile sig_atomic_t dummy = 0;
-      (void)dummy;
-      return 0;
-    }" HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
-  if(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
-    set(HAVE_SIG_ATOMIC_T_VOLATILE 1)
-  endif()
-endif()
-
 if(HAVE_WINDOWS_H)
   set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h)
 else()
@@ -262,6 +254,9 @@
 unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
 
 if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+  if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+  # only try this on non-macOS
+
   # if not cross-compilation...
   include(CheckCSourceRuns)
   set(CMAKE_REQUIRED_FLAGS "")
@@ -304,5 +299,6 @@
         }
         return 0;
     }" HAVE_POLL_FINE)
+  endif()
 endif()
 
diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
index c393557..dd653da 100644
--- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
+++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
@@ -46,7 +46,6 @@
     set(HAVE_PROCESS_H 1)
     set(HAVE_PWD_H 0)
     set(HAVE_SETJMP_H 1)
-    set(HAVE_SGTTY_H 0)
     set(HAVE_SIGNAL_H 1)
     set(HAVE_SOCKIO_H 0)
     set(HAVE_STDINT_H 0)
@@ -81,12 +80,8 @@
     set(HAVE_STRCASECMP 0)
     set(HAVE_STRICMP 1)
     set(HAVE_STRCMPI 1)
-    set(HAVE_GETHOSTBYADDR 1)
     set(HAVE_GETTIMEOFDAY 0)
     set(HAVE_INET_ADDR 1)
-    set(HAVE_INET_NTOA 1)
-    set(HAVE_INET_NTOA_R 0)
-    set(HAVE_PERROR 1)
     set(HAVE_CLOSESOCKET 1)
     set(HAVE_SETVBUF 0)
     set(HAVE_SIGSETJMP 0)
@@ -100,17 +95,10 @@
     set(HAVE_RAND_STATUS 0)
     set(HAVE_GMTIME_R 0)
     set(HAVE_LOCALTIME_R 0)
-    set(HAVE_GETHOSTBYADDR_R 0)
     set(HAVE_GETHOSTBYNAME_R 0)
     set(HAVE_SIGNAL_FUNC 1)
     set(HAVE_SIGNAL_MACRO 0)
 
-    set(HAVE_GETHOSTBYADDR_R_5 0)
-    set(HAVE_GETHOSTBYADDR_R_5_REENTRANT 0)
-    set(HAVE_GETHOSTBYADDR_R_7 0)
-    set(HAVE_GETHOSTBYADDR_R_7_REENTRANT 0)
-    set(HAVE_GETHOSTBYADDR_R_8 0)
-    set(HAVE_GETHOSTBYADDR_R_8_REENTRANT 0)
     set(HAVE_GETHOSTBYNAME_R_3 0)
     set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
     set(HAVE_GETHOSTBYNAME_R_5 0)
@@ -121,8 +109,6 @@
     set(TIME_WITH_SYS_TIME 0)
     set(HAVE_O_NONBLOCK 0)
     set(HAVE_IN_ADDR_T 0)
-    set(HAVE_INET_NTOA_R_DECL 0)
-    set(HAVE_INET_NTOA_R_DECL_REENTRANT 0)
     if(ENABLE_IPV6)
       set(HAVE_GETADDRINFO 1)
     else()
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 16ef037..9eef01a 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -12,20 +12,32 @@
 set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support")
 set(CURL_DISABLE_CRYPTO_AUTH OFF CACHE INTERNAL "Do not disable curl crypto auth")
 set(CURL_DISABLE_DICT ON CACHE INTERNAL "Disable curl dict protocol?")
+set(CURL_DISABLE_DOH OFF)
 set(CURL_DISABLE_FILE OFF CACHE INTERNAL "Disable curl file protocol?")
 set(CURL_DISABLE_FTP OFF CACHE INTERNAL "Disable curl ftp protocol?")
+set(CURL_DISABLE_GETOPTIONS OFF)
 set(CURL_DISABLE_GOPHER ON CACHE INTERNAL "Disable curl gopher protocol?")
 set(CURL_DISABLE_HSTS ON)
+set(CURL_DISABLE_HTTP_AUTH OFF)
 set(CURL_DISABLE_HTTP OFF CACHE INTERNAL "Disable curl http protocol?")
 set(CURL_DISABLE_IMAP ON CACHE INTERNAL "Disable curl imap protocol?")
 set(CURL_DISABLE_LDAP ON CACHE INTERNAL "Disable curl ldap protocol?")
 set(CURL_DISABLE_LDAPS ON CACHE INTERNAL "Disable curl ldaps protocol?")
+set(CURL_DISABLE_LIBCURL_OPTION OFF)
+set(CURL_DISABLE_MIME OFF)
 set(CURL_DISABLE_MQTT ON)
+set(CURL_DISABLE_NETRC OFF)
+set(CURL_DISABLE_NTLM OFF)
 set(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG OFF)
+set(CURL_DISABLE_PARSEDATE OFF)
 set(CURL_DISABLE_POP3 ON CACHE INTERNAL "Disable curl pop3 protocol?")
+set(CURL_DISABLE_PROGRESS_METER OFF)
 set(CURL_DISABLE_PROXY OFF CACHE INTERNAL "Do not disable curl proxy")
 set(CURL_DISABLE_RTSP ON CACHE INTERNAL "Disable curl rtsp protocol?")
+set(CURL_DISABLE_SHUFFLE_DNS OFF)
+set(CURL_DISABLE_SMB OFF)
 set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?")
+set(CURL_DISABLE_SOCKETPAIR OFF)
 set(CURL_DISABLE_TELNET ON CACHE INTERNAL "Disable curl telnet protocol?")
 set(CURL_DISABLE_TFTP ON CACHE INTERNAL "Disable curl tftp protocol?")
 set(CURL_DISABLE_VERBOSE_STRINGS OFF CACHE INTERNAL "Do not disable curl verbosity")
@@ -272,44 +284,78 @@
 include(CurlSymbolHiding)
 endif()
 
-option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" OFF)
-mark_as_advanced(HTTP_ONLY)
-option(CURL_DISABLE_FTP "disables FTP" OFF)
-mark_as_advanced(CURL_DISABLE_FTP)
-option(CURL_DISABLE_LDAP "disables LDAP" OFF)
-mark_as_advanced(CURL_DISABLE_LDAP)
-option(CURL_DISABLE_TELNET "disables Telnet" OFF)
-mark_as_advanced(CURL_DISABLE_TELNET)
-option(CURL_DISABLE_DICT "disables DICT" OFF)
-mark_as_advanced(CURL_DISABLE_DICT)
-option(CURL_DISABLE_FILE "disables FILE" OFF)
-mark_as_advanced(CURL_DISABLE_FILE)
-option(CURL_DISABLE_TFTP "disables TFTP" OFF)
-mark_as_advanced(CURL_DISABLE_TFTP)
-option(CURL_DISABLE_HTTP "disables HTTP" OFF)
-mark_as_advanced(CURL_DISABLE_HTTP)
-
-option(CURL_DISABLE_LDAPS "to disable LDAPS" OFF)
-mark_as_advanced(CURL_DISABLE_LDAPS)
-
-option(CURL_DISABLE_RTSP "to disable RTSP" OFF)
-mark_as_advanced(CURL_DISABLE_RTSP)
-option(CURL_DISABLE_PROXY "to disable proxy" OFF)
-mark_as_advanced(CURL_DISABLE_PROXY)
-option(CURL_DISABLE_POP3 "to disable POP3" OFF)
-mark_as_advanced(CURL_DISABLE_POP3)
-option(CURL_DISABLE_IMAP "to disable IMAP" OFF)
-mark_as_advanced(CURL_DISABLE_IMAP)
-option(CURL_DISABLE_SMTP "to disable SMTP" OFF)
-mark_as_advanced(CURL_DISABLE_SMTP)
-option(CURL_DISABLE_GOPHER "to disable Gopher" OFF)
-mark_as_advanced(CURL_DISABLE_GOPHER)
-option(CURL_DISABLE_MQTT "to disable MQTT" OFF)
-mark_as_advanced(CURL_DISABLE_MQTT)
-
 option(CURL_ENABLE_EXPORT_TARGET "to enable cmake export target" ON)
 mark_as_advanced(CURL_ENABLE_EXPORT_TARGET)
 
+option(CURL_DISABLE_ALTSVC "disables alt-svc support" OFF)
+mark_as_advanced(CURL_DISABLE_ALTSVC)
+option(CURL_DISABLE_COOKIES "disables cookies support" OFF)
+mark_as_advanced(CURL_DISABLE_COOKIES)
+option(CURL_DISABLE_CRYPTO_AUTH "disables cryptographic authentication" OFF)
+mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH)
+option(CURL_DISABLE_DICT "disables DICT" OFF)
+mark_as_advanced(CURL_DISABLE_DICT)
+option(CURL_DISABLE_DOH "disables DNS-over-HTTPS" OFF)
+mark_as_advanced(CURL_DISABLE_DOH)
+option(CURL_DISABLE_FILE "disables FILE" OFF)
+mark_as_advanced(CURL_DISABLE_FILE)
+option(CURL_DISABLE_FTP "disables FTP" OFF)
+mark_as_advanced(CURL_DISABLE_FTP)
+option(CURL_DISABLE_GETOPTIONS "disables curl_easy_options API for existing options to curl_easy_setopt" OFF)
+mark_as_advanced(CURL_DISABLE_GETOPTIONS)
+option(CURL_DISABLE_GOPHER "disables Gopher" OFF)
+mark_as_advanced(CURL_DISABLE_GOPHER)
+option(CURL_DISABLE_HSTS "disables HSTS support" OFF)
+mark_as_advanced(CURL_DISABLE_HSTS)
+option(CURL_DISABLE_HTTP "disables HTTP" OFF)
+mark_as_advanced(CURL_DISABLE_HTTP)
+option(CURL_DISABLE_HTTP_AUTH "disables all HTTP authentication methods" OFF)
+mark_as_advanced(CURL_DISABLE_HTTP_AUTH)
+option(CURL_DISABLE_IMAP "disables IMAP" OFF)
+mark_as_advanced(CURL_DISABLE_IMAP)
+option(CURL_DISABLE_LDAP "disables LDAP" OFF)
+mark_as_advanced(CURL_DISABLE_LDAP)
+option(CURL_DISABLE_LDAPS "disables LDAPS" OFF)
+mark_as_advanced(CURL_DISABLE_LDAPS)
+option(CURL_DISABLE_LIBCURL_OPTION "disables --libcurl option from the curl tool" OFF)
+mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION)
+option(CURL_DISABLE_MIME "disables MIME support" OFF)
+mark_as_advanced(CURL_DISABLE_MIME)
+option(CURL_DISABLE_MQTT "disables MQTT" OFF)
+mark_as_advanced(CURL_DISABLE_MQTT)
+option(CURL_DISABLE_NETRC "disables netrc parser" OFF)
+mark_as_advanced(CURL_DISABLE_NETRC)
+option(CURL_DISABLE_NTLM "disables NTLM support" OFF)
+mark_as_advanced(CURL_DISABLE_NTLM)
+option(CURL_DISABLE_PARSEDATE "disables date parsing" OFF)
+mark_as_advanced(CURL_DISABLE_PARSEDATE)
+option(CURL_DISABLE_POP3 "disables POP3" OFF)
+mark_as_advanced(CURL_DISABLE_POP3)
+option(CURL_DISABLE_PROGRESS_METER "disables built-in progress meter" OFF)
+mark_as_advanced(CURL_DISABLE_PROGRESS_METER)
+option(CURL_DISABLE_PROXY "disables proxy support" OFF)
+mark_as_advanced(CURL_DISABLE_PROXY)
+option(CURL_DISABLE_RTSP "disables RTSP" OFF)
+mark_as_advanced(CURL_DISABLE_RTSP)
+option(CURL_DISABLE_SHUFFLE_DNS "disables shuffle DNS feature" OFF)
+mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS)
+option(CURL_DISABLE_SMB "disables SMB" OFF)
+mark_as_advanced(CURL_DISABLE_SMB)
+option(CURL_DISABLE_SMTP "disables SMTP" OFF)
+mark_as_advanced(CURL_DISABLE_SMTP)
+option(CURL_DISABLE_SOCKETPAIR "disables use of socketpair for curl_multi_poll" OFF)
+mark_as_advanced(CURL_DISABLE_SOCKETPAIR)
+option(CURL_DISABLE_TELNET "disables Telnet" OFF)
+mark_as_advanced(CURL_DISABLE_TELNET)
+option(CURL_DISABLE_TFTP "disables TFTP" OFF)
+mark_as_advanced(CURL_DISABLE_TFTP)
+option(CURL_DISABLE_VERBOSE_STRINGS "disables verbose strings" OFF)
+mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS)
+
+# Corresponds to HTTP_ONLY in lib/curl_setup.h
+option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" OFF)
+mark_as_advanced(HTTP_ONLY)
+
 if(HTTP_ONLY)
   set(CURL_DISABLE_DICT ON)
   set(CURL_DISABLE_FILE ON)
@@ -327,16 +373,6 @@
   set(CURL_DISABLE_TFTP ON)
 endif()
 
-option(CURL_DISABLE_ALTSVC "to disable alt-svc support" OFF)
-mark_as_advanced(CURL_DISABLE_ALTSVC)
-option(CURL_DISABLE_HSTS "to disable HSTS support" OFF)
-mark_as_advanced(CURL_DISABLE_HSTS)
-option(CURL_DISABLE_COOKIES "to disable cookies support" OFF)
-mark_as_advanced(CURL_DISABLE_COOKIES)
-option(CURL_DISABLE_CRYPTO_AUTH "to disable cryptographic authentication" OFF)
-mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH)
-option(CURL_DISABLE_VERBOSE_STRINGS "to disable verbose strings" OFF)
-mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS)
 option(ENABLE_IPV6 "Define if you want to enable IPv6 support" ON)
 mark_as_advanced(ENABLE_IPV6)
 if(ENABLE_IPV6 AND NOT WIN32)
@@ -495,28 +531,29 @@
   message(FATAL_ERROR "The cmake option CMAKE_USE_DARWINSSL was renamed to CMAKE_USE_SECTRANSP.")
 endif()
 
-if(CMAKE_USE_SECTRANSP)
+if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
   find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation")
   if(NOT COREFOUNDATION_FRAMEWORK)
       message(FATAL_ERROR "CoreFoundation framework not found")
   endif()
 
-  find_library(SECURITY_FRAMEWORK "Security")
-  if(NOT SECURITY_FRAMEWORK)
-     message(FATAL_ERROR "Security framework not found")
-  endif()
-
-  set(SSL_ENABLED ON)
-  set(USE_SECTRANSP ON)
-  list(APPEND CURL_LIBS "${COREFOUNDATION_FRAMEWORK}" "${SECURITY_FRAMEWORK}")
-endif()
-
-if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
   find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
   if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
      message(FATAL_ERROR "SystemConfiguration framework not found")
   endif()
-  list(APPEND CURL_LIBS "${SYSTEMCONFIGURATION_FRAMEWORK}")
+
+  list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework SystemConfiguration")
+
+  if(CMAKE_USE_SECTRANSP)
+    find_library(SECURITY_FRAMEWORK "Security")
+    if(NOT SECURITY_FRAMEWORK)
+       message(FATAL_ERROR "Security framework not found")
+    endif()
+
+    set(SSL_ENABLED ON)
+    set(USE_SECTRANSP ON)
+    list(APPEND CURL_LIBS "-framework Security")
+  endif()
 endif()
 
 if(CMAKE_USE_OPENSSL)
@@ -856,15 +893,6 @@
     set(HAVE_LIBSSH2_H ON)
     set(CURL_INCLUDES ${CURL_INCLUDES} "${LIBSSH2_INCLUDE_DIR}/libssh2.h")
     set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -DHAVE_LIBSSH2_H")
-
-    # now check for specific libssh2 symbols as they were added in different versions
-    set(CMAKE_EXTRA_INCLUDE_FILES "libssh2.h")
-    check_function_exists(libssh2_version           HAVE_LIBSSH2_VERSION)
-    check_function_exists(libssh2_init              HAVE_LIBSSH2_INIT)
-    check_function_exists(libssh2_exit              HAVE_LIBSSH2_EXIT)
-    check_function_exists(libssh2_scp_send64        HAVE_LIBSSH2_SCP_SEND64)
-    check_function_exists(libssh2_session_handshake HAVE_LIBSSH2_SESSION_HANDSHAKE)
-    set(CMAKE_EXTRA_INCLUDE_FILES "")
     unset(CMAKE_REQUIRED_LIBRARIES)
   endif()
 endif()
@@ -947,7 +975,11 @@
 option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON)
 if(ENABLE_UNIX_SOCKETS)
   include(CheckStructHasMember)
-  check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS)
+  if(WIN32)
+    set(USE_UNIX_SOCKETS ON)
+  else()
+    check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS)
+  endif()
 else()
   unset(USE_UNIX_SOCKETS CACHE)
 endif()
@@ -1065,8 +1097,6 @@
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
 check_include_file_concat("arpa/tftp.h"      HAVE_ARPA_TFTP_H)
 check_include_file_concat("assert.h"         HAVE_ASSERT_H)
-check_include_file_concat("crypto.h"         HAVE_CRYPTO_H)
-check_include_file_concat("err.h"            HAVE_ERR_H)
 check_include_file_concat("errno.h"          HAVE_ERRNO_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("idn2.h"           HAVE_IDN2_H)
@@ -1084,9 +1114,7 @@
 check_include_file_concat("pem.h"            HAVE_PEM_H)
 check_include_file_concat("poll.h"           HAVE_POLL_H)
 check_include_file_concat("pwd.h"            HAVE_PWD_H)
-check_include_file_concat("rsa.h"            HAVE_RSA_H)
 check_include_file_concat("setjmp.h"         HAVE_SETJMP_H)
-check_include_file_concat("sgtty.h"          HAVE_SGTTY_H)
 check_include_file_concat("signal.h"         HAVE_SIGNAL_H)
 check_include_file_concat("ssl.h"            HAVE_SSL_H)
 check_include_file_concat("stdbool.h"        HAVE_STDBOOL_H)
@@ -1149,12 +1177,8 @@
 check_symbol_exists(getppid       "${CURL_INCLUDES}" HAVE_GETPPID)
 check_symbol_exists(utimes        "${CURL_INCLUDES}" HAVE_UTIMES)
 
-check_symbol_exists(gethostbyaddr "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR)
-check_symbol_exists(gethostbyaddr_r "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR_R)
 check_symbol_exists(gettimeofday  "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY)
 check_symbol_exists(inet_addr     "${CURL_INCLUDES}" HAVE_INET_ADDR)
-check_symbol_exists(inet_ntoa     "${CURL_INCLUDES}" HAVE_INET_NTOA)
-check_symbol_exists(inet_ntoa_r   "${CURL_INCLUDES}" HAVE_INET_NTOA_R)
 check_symbol_exists(closesocket   "${CURL_INCLUDES}" HAVE_CLOSESOCKET)
 check_symbol_exists(sigsetjmp     "${CURL_INCLUDES}" HAVE_SIGSETJMP)
 check_symbol_exists(getpass_r     "${CURL_INCLUDES}" HAVE_GETPASS_R)
@@ -1179,11 +1203,8 @@
 check_symbol_exists(_strtoi64      "${CURL_INCLUDES}" HAVE__STRTOI64)
 check_symbol_exists(strerror_r     "${CURL_INCLUDES}" HAVE_STRERROR_R)
 check_symbol_exists(siginterrupt   "${CURL_INCLUDES}" HAVE_SIGINTERRUPT)
-check_symbol_exists(perror         "${CURL_INCLUDES}" HAVE_PERROR)
-check_symbol_exists(fork           "${CURL_INCLUDES}" HAVE_FORK)
 check_symbol_exists(getaddrinfo    "${CURL_INCLUDES}" HAVE_GETADDRINFO)
 check_symbol_exists(freeaddrinfo   "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
-check_symbol_exists(freeifaddrs    "${CURL_INCLUDES}" HAVE_FREEIFADDRS)
 check_symbol_exists(pipe           "${CURL_INCLUDES}" HAVE_PIPE)
 check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
 check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
@@ -1250,12 +1271,6 @@
     HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
     TIME_WITH_SYS_TIME
     HAVE_O_NONBLOCK
-    HAVE_GETHOSTBYADDR_R_5
-    HAVE_GETHOSTBYADDR_R_7
-    HAVE_GETHOSTBYADDR_R_8
-    HAVE_GETHOSTBYADDR_R_5_REENTRANT
-    HAVE_GETHOSTBYADDR_R_7_REENTRANT
-    HAVE_GETHOSTBYADDR_R_8_REENTRANT
     HAVE_GETHOSTBYNAME_R_3
     HAVE_GETHOSTBYNAME_R_5
     HAVE_GETHOSTBYNAME_R_6
@@ -1265,8 +1280,6 @@
     HAVE_IN_ADDR_T
     HAVE_BOOL_T
     STDC_HEADERS
-    HAVE_INET_NTOA_R_DECL
-    HAVE_INET_NTOA_R_DECL_REENTRANT
     HAVE_GETADDRINFO
     HAVE_FILE_OFFSET_BITS
     HAVE_VARIADIC_MACROS_C99
@@ -1298,13 +1311,9 @@
 
 # Check for reentrant
 foreach(CURL_TEST
-    HAVE_GETHOSTBYADDR_R_5
-    HAVE_GETHOSTBYADDR_R_7
-    HAVE_GETHOSTBYADDR_R_8
     HAVE_GETHOSTBYNAME_R_3
     HAVE_GETHOSTBYNAME_R_5
-    HAVE_GETHOSTBYNAME_R_6
-    HAVE_INET_NTOA_R_DECL_REENTRANT)
+    HAVE_GETHOSTBYNAME_R_6)
   if(NOT ${CURL_TEST})
     if(${CURL_TEST}_REENTRANT)
       set(NEED_REENTRANT 1)
@@ -1314,9 +1323,6 @@
 
 if(NEED_REENTRANT)
   foreach(CURL_TEST
-      HAVE_GETHOSTBYADDR_R_5
-      HAVE_GETHOSTBYADDR_R_7
-      HAVE_GETHOSTBYADDR_R_8
       HAVE_GETHOSTBYNAME_R_3
       HAVE_GETHOSTBYNAME_R_5
       HAVE_GETHOSTBYNAME_R_6)
@@ -1327,11 +1333,6 @@
   endforeach()
 endif()
 
-if(HAVE_INET_NTOA_R_DECL_REENTRANT)
-  set(HAVE_INET_NTOA_R_DECL 1)
-  set(NEED_REENTRANT 1)
-endif()
-
 # Check clock_gettime(CLOCK_MONOTONIC, x) support
 curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
 
@@ -1527,8 +1528,8 @@
 
 # NTLM support requires crypto function adaptions from various SSL libs
 # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR USE_MBEDTLS OR
-                                     USE_DARWINSSL OR USE_WIN32_CRYPTO))
+if(NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
+    (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO))
   set(use_curl_ntlm_core ON)
 endif()
 
@@ -1556,10 +1557,10 @@
                         (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
 # NTLM support requires crypto function adaptions from various SSL libs
 # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-_add_if("NTLM"          NOT CURL_DISABLE_CRYPTO_AUTH AND
+_add_if("NTLM"          NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
                         (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 # TODO missing option (autoconf: --enable-ntlm-wb)
-_add_if("NTLM_WB"       NOT CURL_DISABLE_CRYPTO_AUTH AND
+_add_if("NTLM_WB"       NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
                         (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND
                         NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
 # TODO missing option (--enable-tls-srp), depends on GNUTLS_SRP/OPENSSL_SRP
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index 55a6a39..7b50b7c 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -25,9 +25,6 @@
 /*
  * If you have libcurl problems, all docs and details are found here:
  *   https://curl.se/libcurl/
- *
- * curl-library mailing list subscription and unsubscription web interface:
- *   https://cool.haxx.se/mailman/listinfo/curl-library/
  */
 
 #ifdef CURL_NO_OLDIES
@@ -74,8 +71,9 @@
 #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \
     defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \
     defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
-    defined(__CYGWIN__) || defined(AMIGA) || \
-   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000))
+    defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
+   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
+    defined(__VXWORKS__)
 #include <sys/select.h>
 #endif
 
@@ -541,7 +539,7 @@
   CURLE_OBSOLETE46,              /* 46 - NOT USED */
   CURLE_TOO_MANY_REDIRECTS,      /* 47 - catch endless re-direct loops */
   CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */
-  CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */
+  CURLE_SETOPT_OPTION_SYNTAX,    /* 49 - Malformed setopt option */
   CURLE_OBSOLETE50,              /* 50 - NOT USED */
   CURLE_OBSOLETE51,              /* 51 - NOT USED */
   CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */
@@ -636,6 +634,9 @@
 /* The following were added in 7.21.5, April 2011 */
 #define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION
 
+/* Added for 7.78.0 */
+#define CURLE_TELNET_OPTION_SYNTAX CURLE_SETOPT_OPTION_SYNTAX
+
 /* The following were added in 7.17.1 */
 /* These are scheduled to disappear by 2009 */
 #define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION
@@ -2084,13 +2085,13 @@
   /* Parameters for V4 signature */
   CURLOPT(CURLOPT_AWS_SIGV4, CURLOPTTYPE_STRINGPOINT, 305),
 
-  /* Same as CURLOPT_SSL_VERIFYPEER but for DOH (DNS-over-HTTPS) servers. */
+  /* Same as CURLOPT_SSL_VERIFYPEER but for DoH (DNS-over-HTTPS) servers. */
   CURLOPT(CURLOPT_DOH_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 306),
 
-  /* Same as CURLOPT_SSL_VERIFYHOST but for DOH (DNS-over-HTTPS) servers. */
+  /* Same as CURLOPT_SSL_VERIFYHOST but for DoH (DNS-over-HTTPS) servers. */
   CURLOPT(CURLOPT_DOH_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 307),
 
-  /* Same as CURLOPT_SSL_VERIFYSTATUS but for DOH (DNS-over-HTTPS) servers. */
+  /* Same as CURLOPT_SSL_VERIFYSTATUS but for DoH (DNS-over-HTTPS) servers. */
   CURLOPT(CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 308),
 
   /* The CA certificates as "blob" used to validate the peer certificate
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index a8d5449..9019868 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -30,13 +30,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "7.77.0"
+#define LIBCURL_VERSION "7.79.1"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 77
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_MINOR 79
+#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
@@ -57,7 +57,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 0x074d00
+#define LIBCURL_VERSION_NUM 0x074f01
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index 7343cb6..1d70880 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2018 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -79,6 +79,7 @@
 #define CURLU_GUESS_SCHEME (1<<9)       /* legacy curl-style guessing */
 #define CURLU_NO_AUTHORITY (1<<10)      /* Allow empty authority when the
                                            scheme is unknown. */
+#define CURLU_ALLOW_SPACE (1<<11)       /* Allow spaces in the URL */
 
 typedef struct Curl_URL CURLU;
 
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
index 4ab77fd..36acc3a 100644
--- a/Utilities/cmcurl/lib/altsvc.c
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -460,7 +460,7 @@
   (void)data;
 #endif
   if(result) {
-    infof(data, "Excessive alt-svc header, ignoring...\n");
+    infof(data, "Excessive alt-svc header, ignoring.");
     return CURLE_OK;
   }
 
@@ -496,7 +496,7 @@
             p++;
           len = p - hostp;
           if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
-            infof(data, "Excessive alt-svc host name, ignoring...\n");
+            infof(data, "Excessive alt-svc host name, ignoring.");
             dstalpnid = ALPN_none;
           }
           else {
@@ -513,7 +513,7 @@
           /* a port number */
           unsigned long port = strtoul(++p, &end_ptr, 10);
           if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
-            infof(data, "Unknown alt-svc port number, ignoring...\n");
+            infof(data, "Unknown alt-svc port number, ignoring.");
             dstalpnid = ALPN_none;
           }
           p = end_ptr;
@@ -579,12 +579,12 @@
             as->expires = maxage + time(NULL);
             as->persist = persist;
             Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
-            infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport,
+            infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
                   Curl_alpnid2str(dstalpnid));
           }
         }
         else {
-          infof(data, "Unknown alt-svc protocol \"%s\", skipping...\n",
+          infof(data, "Unknown alt-svc protocol \"%s\", skipping.",
                 alpnbuf);
         }
       }
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 7827847..763a4aa 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -59,7 +59,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "multiif.h"
 #include "inet_pton.h"
@@ -80,13 +79,33 @@
 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
 #endif
 
+#if ARES_VERSION >= 0x010601
+/* IPv6 supported since 1.6.1 */
+#define HAVE_CARES_IPV6 1
+#endif
+
+#if ARES_VERSION >= 0x010704
+#define HAVE_CARES_SERVERS_CSV 1
+#define HAVE_CARES_LOCAL_DEV 1
+#define HAVE_CARES_SET_LOCAL 1
+#endif
+
+#if ARES_VERSION >= 0x010b00
+#define HAVE_CARES_PORTS_CSV 1
+#endif
+
+#if ARES_VERSION >= 0x011000
+/* 1.16.0 or later has ares_getaddrinfo */
+#define HAVE_CARES_GETADDRINFO 1
+#endif
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
 struct thread_data {
-  int num_pending; /* number of ares_gethostbyname() requests */
+  int num_pending; /* number of outstanding c-ares requests */
   struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
                                     parts */
   int last_status;
@@ -206,7 +225,8 @@
  */
 void Curl_resolver_cancel(struct Curl_easy *data)
 {
-  if(data && data->state.async.resolver)
+  DEBUGASSERT(data);
+  if(data->state.async.resolver)
     ares_cancel((ares_channel)data->state.async.resolver);
   destroy_async_data(&data->state.async);
 }
@@ -489,21 +509,37 @@
   return result;
 }
 
+#ifndef HAVE_CARES_GETADDRINFO
+
 /* Connects results to the list */
 static void compound_results(struct thread_data *res,
                              struct Curl_addrinfo *ai)
 {
-  struct Curl_addrinfo *ai_tail;
   if(!ai)
     return;
-  ai_tail = ai;
 
-  while(ai_tail->ai_next)
-    ai_tail = ai_tail->ai_next;
+#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+  if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
+    /* We have results already, put the new IPv6 entries at the head of the
+       list. */
+    struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
 
-  /* Add the new results to the list of old results. */
-  ai_tail->ai_next = res->temp_ai;
-  res->temp_ai = ai;
+    while(temp_ai_tail->ai_next)
+      temp_ai_tail = temp_ai_tail->ai_next;
+
+    temp_ai_tail->ai_next = ai;
+  }
+  else
+#endif /* CURLRES_IPV6 */
+  {
+    /* Add the new results to the list of old results. */
+    struct Curl_addrinfo *ai_tail = ai;
+    while(ai_tail->ai_next)
+      ai_tail = ai_tail->ai_next;
+
+    ai_tail->ai_next = res->temp_ai;
+    res->temp_ai = ai;
+  }
 }
 
 /*
@@ -605,8 +641,99 @@
     }
   }
 }
+#else
+/* c-ares 1.16.0 or later */
 
 /*
+ * ares2addr() converts an address list provided by c-ares to an internal
+ * libcurl compatible list
+ */
+static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
+{
+  /* traverse the ares_addrinfo_node list */
+  struct ares_addrinfo_node *ai;
+  struct Curl_addrinfo *cafirst = NULL;
+  struct Curl_addrinfo *calast = NULL;
+  int error = 0;
+
+  for(ai = node; ai != NULL; ai = ai->ai_next) {
+    size_t ss_size;
+    struct Curl_addrinfo *ca;
+    /* ignore elements with unsupported address family, */
+    /* settle family-specific sockaddr structure size.  */
+    if(ai->ai_family == AF_INET)
+      ss_size = sizeof(struct sockaddr_in);
+#ifdef ENABLE_IPV6
+    else if(ai->ai_family == AF_INET6)
+      ss_size = sizeof(struct sockaddr_in6);
+#endif
+    else
+      continue;
+
+    /* ignore elements without required address info */
+    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+      continue;
+
+    /* ignore elements with bogus address size */
+    if((size_t)ai->ai_addrlen < ss_size)
+      continue;
+
+    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
+    if(!ca) {
+      error = EAI_MEMORY;
+      break;
+    }
+
+    /* copy each structure member individually, member ordering, */
+    /* size, or padding might be different for each platform.    */
+
+    ca->ai_flags     = ai->ai_flags;
+    ca->ai_family    = ai->ai_family;
+    ca->ai_socktype  = ai->ai_socktype;
+    ca->ai_protocol  = ai->ai_protocol;
+    ca->ai_addrlen   = (curl_socklen_t)ss_size;
+    ca->ai_addr      = NULL;
+    ca->ai_canonname = NULL;
+    ca->ai_next      = NULL;
+
+    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+    /* if the return list is empty, this becomes the first element */
+    if(!cafirst)
+      cafirst = ca;
+
+    /* add this element last in the return list */
+    if(calast)
+      calast->ai_next = ca;
+    calast = ca;
+  }
+
+  /* if we failed, destroy the Curl_addrinfo list */
+  if(error) {
+    Curl_freeaddrinfo(cafirst);
+    cafirst = NULL;
+  }
+
+  return cafirst;
+}
+
+static void addrinfo_cb(void *arg, int status, int timeouts,
+                        struct ares_addrinfo *result)
+{
+  struct Curl_easy *data = (struct Curl_easy *)arg;
+  struct thread_data *res = data->state.async.tdata;
+  (void)timeouts;
+  if(ARES_SUCCESS == status) {
+    res->temp_ai = ares2addr(result->nodes);
+    res->last_status = CURL_ASYNC_SUCCESS;
+    ares_freeaddrinfo(result);
+  }
+  res->num_pending--;
+}
+
+#endif
+/*
  * Curl_resolver_getaddrinfo() - when using ares
  *
  * Returns name information about the given hostname and port number. If
@@ -643,8 +770,28 @@
     /* initial status - failed */
     res->last_status = ARES_ENOTFOUND;
 
-#if ARES_VERSION >= 0x010601
-    /* IPv6 supported by c-ares since 1.6.1 */
+#ifdef HAVE_CARES_GETADDRINFO
+    {
+      struct ares_addrinfo_hints hints;
+      char service[12];
+      int pf = PF_INET;
+      memset(&hints, 0, sizeof(hints));
+#ifdef CURLRES_IPV6
+      if(Curl_ipv6works(data))
+        /* The stack seems to be IPv6-enabled */
+        pf = PF_UNSPEC;
+#endif /* CURLRES_IPV6 */
+      hints.ai_family = pf;
+      hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
+        SOCK_STREAM : SOCK_DGRAM;
+      msnprintf(service, sizeof(service), "%d", port);
+      res->num_pending = 1;
+      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
+                       service, &hints, addrinfo_cb, data);
+    }
+#else
+
+#ifdef HAVE_CARES_IPV6
     if(Curl_ipv6works(data)) {
       /* The stack seems to be IPv6-enabled */
       res->num_pending = 2;
@@ -656,7 +803,7 @@
                           PF_INET6, query_completed_cb, data);
     }
     else
-#endif /* ARES_VERSION >= 0x010601 */
+#endif
     {
       res->num_pending = 1;
 
@@ -665,7 +812,7 @@
                          hostname, PF_INET,
                          query_completed_cb, data);
     }
-
+#endif
     *waitp = 1; /* expect asynchronous response */
   }
   return NULL; /* no struct yet */
@@ -686,8 +833,8 @@
   if(!(servers && servers[0]))
     return CURLE_OK;
 
-#if (ARES_VERSION >= 0x010704)
-#if (ARES_VERSION >= 0x010b00)
+#ifdef HAVE_CARES_SERVERS_CSV
+#ifdef HAVE_CARES_PORTS_CSV
   ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
                                            servers);
 #else
@@ -717,7 +864,7 @@
 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
                                 const char *interf)
 {
-#if (ARES_VERSION >= 0x010704)
+#ifdef HAVE_CARES_LOCAL_DEV
   if(!interf)
     interf = "";
 
@@ -734,7 +881,7 @@
 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
                                 const char *local_ip4)
 {
-#if (ARES_VERSION >= 0x010704)
+#ifdef HAVE_CARES_SET_LOCAL
   struct in_addr a4;
 
   if((!local_ip4) || (local_ip4[0] == 0)) {
@@ -760,7 +907,7 @@
 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
                                 const char *local_ip6)
 {
-#if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
+#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
   unsigned char a6[INET6_ADDRSTRLEN];
 
   if((!local_ip6) || (local_ip6[0] == 0)) {
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index 36f68cb..149172a 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -68,7 +68,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "multiif.h"
 #include "inet_ntop.h"
diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c
index 81f589e..26635cd 100644
--- a/Utilities/cmcurl/lib/c-hyper.c
+++ b/Utilities/cmcurl/lib/c-hyper.c
@@ -124,6 +124,17 @@
   size_t len;
   char *headp;
   CURLcode result;
+  int writetype;
+
+  if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
+    failf(data, "Too long response header");
+    data->state.hresult = CURLE_OUT_OF_MEMORY;
+    return HYPER_ITER_BREAK;
+  }
+
+  if(!data->req.bytecount)
+    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
   Curl_dyn_reset(&data->state.headerb);
   if(name_len) {
     if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
@@ -145,7 +156,10 @@
 
   Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
 
-  result = Curl_client_write(data, CLIENTWRITE_HEADER, headp, len);
+  writetype = CLIENTWRITE_HEADER;
+  if(data->set.include_header)
+    writetype |= CLIENTWRITE_BODY;
+  result = Curl_client_write(data, writetype, headp, len);
   if(result) {
     data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
     return HYPER_ITER_BREAK;
@@ -162,13 +176,43 @@
   size_t len = hyper_buf_len(chunk);
   struct Curl_easy *data = (struct Curl_easy *)userdata;
   struct SingleRequest *k = &data->req;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
   if(0 == k->bodywrites++) {
     bool done = FALSE;
-    result = Curl_http_firstwrite(data, data->conn, &done);
+#if defined(USE_NTLM)
+    struct connectdata *conn = data->conn;
+    if(conn->bits.close &&
+       (((data->req.httpcode == 401) &&
+         (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
+        ((data->req.httpcode == 407) &&
+         (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
+      infof(data, "Connection closed while negotiating NTLM");
+      data->state.authproblem = TRUE;
+      Curl_safefree(data->req.newurl);
+    }
+#endif
+    if(data->state.expect100header) {
+      Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+      if(data->req.httpcode < 400) {
+        k->exp100 = EXP100_SEND_DATA;
+        if(data->hyp.exp100_waker) {
+          hyper_waker_wake(data->hyp.exp100_waker);
+          data->hyp.exp100_waker = NULL;
+        }
+      }
+      else { /* >= 4xx */
+        k->exp100 = EXP100_FAILED;
+      }
+    }
+    if(data->state.hconnect && (data->req.httpcode/100 != 2)) {
+      done = TRUE;
+      result = CURLE_OK;
+    }
+    else
+      result = Curl_http_firstwrite(data, data->conn, &done);
     if(result || done) {
-      infof(data, "Return early from hyper_body_chunk\n");
+      infof(data, "Return early from hyper_body_chunk");
       data->state.hresult = result;
       return HYPER_ITER_BREAK;
     }
@@ -207,11 +251,15 @@
   CURLcode result;
   size_t len;
   const char *vstr;
+  int writetype;
   vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" :
     (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0");
   conn->httpversion =
     http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
     (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
+  if(http_version == HYPER_HTTP_VERSION_1_0)
+    data->state.httpwant = CURL_HTTP_VERSION_1_0;
+
   data->req.httpcode = http_status;
 
   result = Curl_http_statusline(data, conn);
@@ -229,7 +277,10 @@
   len = Curl_dyn_len(&data->state.headerb);
   Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
              len);
-  result = Curl_client_write(data, CLIENTWRITE_HEADER,
+  writetype = CLIENTWRITE_HEADER;
+  if(data->set.include_header)
+    writetype |= CLIENTWRITE_BODY;
+  result = Curl_client_write(data, writetype,
                              Curl_dyn_ptr(&data->state.headerb), len);
   if(result) {
     data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
@@ -270,8 +321,25 @@
   const uint8_t *reasonp;
   size_t reason_len;
   CURLcode result = CURLE_OK;
+  struct SingleRequest *k = &data->req;
   (void)conn;
 
+  if(k->exp100 > EXP100_SEND_DATA) {
+    struct curltime now = Curl_now();
+    timediff_t ms = Curl_timediff(now, k->start100);
+    if(ms >= data->set.expect_100_timeout) {
+      /* we've waited long enough, continue anyway */
+      k->exp100 = EXP100_SEND_DATA;
+      k->keepon |= KEEP_SEND;
+      Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+      infof(data, "Done waiting for 100-continue");
+      if(data->hyp.exp100_waker) {
+        hyper_waker_wake(data->hyp.exp100_waker);
+        data->hyp.exp100_waker = NULL;
+      }
+    }
+  }
+
   if(select_res & CURL_CSELECT_IN) {
     if(h->read_waker)
       hyper_waker_wake(h->read_waker);
@@ -305,19 +373,22 @@
     hyper_task_free(task);
 
     if(t == HYPER_TASK_ERROR) {
-      hyper_code errnum = hyper_error_code(hypererr);
-      if(errnum == HYPERE_ABORTED_BY_CALLBACK) {
+      if(data->state.hresult) {
         /* override Hyper's view, might not even be an error */
         result = data->state.hresult;
-        infof(data, "hyperstream is done (by early callback)\n");
+        infof(data, "hyperstream is done (by early callback)");
       }
       else {
         uint8_t errbuf[256];
         size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
         hyper_code code = hyper_error_code(hypererr);
         failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
-        if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount)
+        if(code == HYPERE_ABORTED_BY_CALLBACK)
+          result = CURLE_OK;
+        else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount)
           result = CURLE_GOT_NOTHING;
+        else if(code == HYPERE_INVALID_PEER_MESSAGE)
+          result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
         else
           result = CURLE_RECV_ERROR;
       }
@@ -328,10 +399,15 @@
     else if(h->endtask == task) {
       /* end of transfer */
       *done = TRUE;
-      infof(data, "hyperstream is done!\n");
+      infof(data, "hyperstream is done!");
+      if(!k->bodywrites) {
+        /* hyper doesn't always call the body write callback */
+        bool stilldone;
+        result = Curl_http_firstwrite(data, data->conn, &stilldone);
+      }
       break;
     }
-    else if(t != HYPER_TASK_RESPONSE && t != HYPER_TASK_EMPTY) {
+    else if(t != HYPER_TASK_RESPONSE) {
       *didwhat = KEEP_RECV;
       break;
     }
@@ -472,7 +548,7 @@
 
     if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
                                       (uint8_t *)v, vlen)) {
-      failf(data, "hyper_headers_add host");
+      failf(data, "hyper refused to add header '%s'", line);
       return CURLE_OUT_OF_MEMORY;
     }
     if(data->set.verbose) {
@@ -485,7 +561,7 @@
         free(ptr);
       }
       else
-        Curl_debug(data, CURLINFO_HEADER_OUT, (char *)line, linelen);
+        Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen);
     }
     numh++;
     n += linelen;
@@ -526,12 +602,32 @@
 {
   struct Curl_easy *data = (struct Curl_easy *)userdata;
   (void)ctx;
+  if(data->req.exp100 > EXP100_SEND_DATA) {
+    if(data->req.exp100 == EXP100_FAILED)
+      return HYPER_POLL_ERROR;
+
+    /* still waiting confirmation */
+    if(data->hyp.exp100_waker)
+      hyper_waker_free(data->hyp.exp100_waker);
+    data->hyp.exp100_waker = hyper_context_waker(ctx);
+    return HYPER_POLL_PENDING;
+  }
   if(data->req.upload_done)
     *chunk = NULL; /* nothing more to deliver */
   else {
     /* send everything off in a single go */
-    *chunk = hyper_buf_copy(data->set.postfields,
-                            (size_t)data->req.p.http->postsize);
+    hyper_buf *copy = hyper_buf_copy(data->set.postfields,
+                                     (size_t)data->req.p.http->postsize);
+    if(copy)
+      *chunk = copy;
+    else {
+      data->state.hresult = CURLE_OUT_OF_MEMORY;
+      return HYPER_POLL_ERROR;
+    }
+    /* increasing the writebytecount here is a little premature but we
+       don't know exactly when the body is sent*/
+    data->req.writebytecount += (size_t)data->req.p.http->postsize;
+    Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
     data->req.upload_done = TRUE;
   }
   return HYPER_POLL_READY;
@@ -542,16 +638,41 @@
 {
   size_t fillcount;
   struct Curl_easy *data = (struct Curl_easy *)userdata;
-  CURLcode result =
-    Curl_fillreadbuffer(data, data->set.upload_buffer_size, &fillcount);
+  CURLcode result;
   (void)ctx;
-  if(result)
+
+  if(data->req.exp100 > EXP100_SEND_DATA) {
+    if(data->req.exp100 == EXP100_FAILED)
+      return HYPER_POLL_ERROR;
+
+    /* still waiting confirmation */
+    if(data->hyp.exp100_waker)
+      hyper_waker_free(data->hyp.exp100_waker);
+    data->hyp.exp100_waker = hyper_context_waker(ctx);
+    return HYPER_POLL_PENDING;
+  }
+
+  result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, &fillcount);
+  if(result) {
+    data->state.hresult = result;
     return HYPER_POLL_ERROR;
+  }
   if(!fillcount)
     /* done! */
     *chunk = NULL;
-  else
-    *chunk = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
+  else {
+    hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
+    if(copy)
+      *chunk = copy;
+    else {
+      data->state.hresult = CURLE_OUT_OF_MEMORY;
+      return HYPER_POLL_ERROR;
+    }
+    /* increasing the writebytecount here is a little premature but we
+       don't know exactly when the body is sent*/
+    data->req.writebytecount += fillcount;
+    Curl_pgrsSetUploadCounter(data, fillcount);
+  }
   return HYPER_POLL_READY;
 }
 
@@ -566,6 +687,7 @@
                          hyper_request *hyperreq,
                          Curl_HttpReq httpreq)
 {
+  struct HTTP *http = data->req.p.http;
   CURLcode result = CURLE_OK;
   struct dynbuf req;
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
@@ -598,6 +720,7 @@
       result = CURLE_OUT_OF_MEMORY;
     }
   }
+  http->sending = HTTPSEND_BODY;
   return result;
 }
 
@@ -616,6 +739,48 @@
   return result;
 }
 
+/* called on 1xx responses */
+static void http1xx_cb(void *arg, struct hyper_response *resp)
+{
+  struct Curl_easy *data = (struct Curl_easy *)arg;
+  hyper_headers *headers = NULL;
+  CURLcode result = CURLE_OK;
+  uint16_t http_status;
+  int http_version;
+  const uint8_t *reasonp;
+  size_t reason_len;
+
+  infof(data, "Got HTTP 1xx informational");
+
+  http_status = hyper_response_status(resp);
+  http_version = hyper_response_version(resp);
+  reasonp = hyper_response_reason_phrase(resp);
+  reason_len = hyper_response_reason_phrase_len(resp);
+
+  result = status_line(data, data->conn,
+                       http_status, http_version, reasonp, reason_len);
+  if(!result) {
+    headers = hyper_response_headers(resp);
+    if(!headers) {
+      failf(data, "hyperstream: couldn't get 1xx response headers");
+      result = CURLE_RECV_ERROR;
+    }
+  }
+  data->state.hresult = result;
+
+  if(!result) {
+    /* the headers are already received */
+    hyper_headers_foreach(headers, hyper_each_header, data);
+    /* this callback also sets data->state.hresult on error */
+
+    if(empty_header(data))
+      result = CURLE_OUT_OF_MEMORY;
+  }
+
+  if(data->state.hresult)
+    infof(data, "ERROR in 1xx, bail out!");
+}
+
 /*
  * Curl_http() gets called from the generic multi_do() function when a HTTP
  * request is to be performed. This creates and sends a properly constructed
@@ -633,20 +798,20 @@
   hyper_request *req = NULL;
   hyper_headers *headers = NULL;
   hyper_task *handshake = NULL;
-  hyper_error *hypererr = NULL;
   CURLcode result;
   const char *p_accept; /* Accept: string */
   const char *method;
   Curl_HttpReq httpreq;
   bool h2 = FALSE;
   const char *te = NULL; /* transfer-encoding */
+  hyper_code rc;
 
   /* Always consider the DO phase done after this function call, even if there
      may be parts of the request that is not yet sent, since we can deal with
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
 
-  infof(data, "Time for the Hyper dance\n");
+  infof(data, "Time for the Hyper dance");
   memset(h, 0, sizeof(struct hyptransfer));
 
   result = Curl_http_host(data, conn);
@@ -743,7 +908,7 @@
     goto error;
   }
 
-  if(data->state.httpwant == CURL_HTTP_VERSION_1_0) {
+  if(!Curl_use_http_1_1plus(data, conn)) {
     if(HYPERE_OK != hyper_request_set_version(req,
                                               HYPER_HTTP_VERSION_1_0)) {
       failf(data, "error setting HTTP version");
@@ -766,6 +931,10 @@
     goto error;
   }
 
+  rc = hyper_request_on_informational(req, http1xx_cb, data);
+  if(rc)
+    return CURLE_OUT_OF_MEMORY;
+
   result = Curl_http_body(data, conn, httpreq, &te);
   if(result)
     return result;
@@ -830,6 +999,15 @@
   else
     Curl_safefree(data->state.aptr.accept_encoding);
 
+#ifdef HAVE_LIBZ
+  /* we only consider transfer-encoding magic if libz support is built-in */
+  result = Curl_transferencode(data);
+  if(result)
+    return result;
+  if(Curl_hyper_header(data, headers, data->state.aptr.te))
+    goto error;
+#endif
+
   result = cookies(data, conn, headers);
   if(result)
     return result;
@@ -862,25 +1040,21 @@
 
   hyper_clientconn_free(client);
 
-  do {
-    task = hyper_executor_poll(h->exec);
-    if(task) {
-      bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
-      if(error)
-        hypererr = hyper_task_value(task);
-      hyper_task_free(task);
-      if(error)
-        goto error;
-    }
-  } while(task);
-
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
     /* HTTP GET/HEAD download */
     Curl_pgrsSetUploadSize(data, 0); /* nothing */
     Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
   }
   conn->datastream = Curl_hyper_stream;
+  if(data->state.expect100header)
+    /* Timeout count starts now since with Hyper we don't know exactly when
+       the full request has been sent. */
+    data->req.start100 = Curl_now();
 
+  /* clear userpwd and proxyuserpwd to avoid re-using old credentials
+   * from re-used connections */
+  Curl_safefree(data->state.aptr.userpwd);
+  Curl_safefree(data->state.aptr.proxyuserpwd);
   return CURLE_OK;
   error:
 
@@ -893,13 +1067,6 @@
   if(handshake)
     hyper_task_free(handshake);
 
-  if(hypererr) {
-    uint8_t errbuf[256];
-    size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
-    hyper_code code = hyper_error_code(hypererr);
-    failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
-    hyper_error_free(hypererr);
-  }
   return CURLE_OUT_OF_MEMORY;
 }
 
@@ -918,6 +1085,10 @@
     hyper_waker_free(h->write_waker);
     h->write_waker = NULL;
   }
+  if(h->exp100_waker) {
+    hyper_waker_free(h->exp100_waker);
+    h->exp100_waker = NULL;
+  }
 }
 
 #endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h
index d60ed78..d63defe 100644
--- a/Utilities/cmcurl/lib/c-hyper.h
+++ b/Utilities/cmcurl/lib/c-hyper.h
@@ -33,6 +33,7 @@
   hyper_waker *read_waker;
   const hyper_executor *exec;
   hyper_task *endtask;
+  hyper_waker *exp100_waker;
 };
 
 size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index 5453c00..f5ba8ff 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -34,6 +34,7 @@
 #include "share.h"
 #include "sigpipe.h"
 #include "connect.h"
+#include "strcase.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -161,6 +162,7 @@
 
   /* put the number first so that the hostname gets cut off if too long */
   msnprintf(buf, len, "%ld%s", port, hostname);
+  Curl_strntolower(buf, buf, len);
 }
 
 /* Returns number of connections currently held in the connection cache.
@@ -264,7 +266,7 @@
   connc->num_conn++;
 
   DEBUGF(infof(data, "Added connection %ld. "
-               "The cache now contains %zu members\n",
+               "The cache now contains %zu members",
                conn->connection_id, connc->num_conn));
 
   unlock:
@@ -298,7 +300,7 @@
     conn->bundle = NULL; /* removed from it */
     if(connc) {
       connc->num_conn--;
-      DEBUGF(infof(data, "The cache now contains %zu members\n",
+      DEBUGF(infof(data, "The cache now contains %zu members",
                    connc->num_conn));
     }
     if(lock) {
@@ -408,7 +410,7 @@
   conn->lastused = Curl_now(); /* it was used up until now */
   if(maxconnects > 0 &&
      Curl_conncache_size(data) > maxconnects) {
-    infof(data, "Connection cache is full, closing the oldest one.\n");
+    infof(data, "Connection cache is full, closing the oldest one");
 
     conn_candidate = Curl_conncache_extract_oldest(data);
     if(conn_candidate) {
@@ -464,7 +466,7 @@
     /* remove it to prevent another thread from nicking it */
     bundle_remove_conn(bundle, conn_candidate);
     data->state.conn_cache->num_conn--;
-    DEBUGF(infof(data, "The cache now contains %zu members\n",
+    DEBUGF(infof(data, "The cache now contains %zu members",
                  data->state.conn_cache->num_conn));
   }
 
@@ -526,7 +528,7 @@
     /* remove it to prevent another thread from nicking it */
     bundle_remove_conn(bundle_candidate, conn_candidate);
     connc->num_conn--;
-    DEBUGF(infof(data, "The cache now contains %zu members\n",
+    DEBUGF(infof(data, "The cache now contains %zu members",
                  connc->num_conn));
   }
   CONNCACHE_UNLOCK(data);
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index d9317f3..d61b037 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -111,7 +111,7 @@
   /* only set IDLE and INTVL if setting KEEPALIVE is successful */
   if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
         (void *)&optval, sizeof(optval)) < 0) {
-    infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
+    infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
   }
   else {
 #if defined(SIO_KEEPALIVE_VALS)
@@ -126,7 +126,7 @@
     vals.keepaliveinterval = optval;
     if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
                 NULL, 0, &dummy, NULL, NULL) != 0) {
-      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d\n",
+      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
             (int)sockfd, WSAGetLastError());
     }
 #else
@@ -135,7 +135,7 @@
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
+      infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
     }
 #endif
 #ifdef TCP_KEEPINTVL
@@ -143,7 +143,7 @@
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
+      infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
     }
 #endif
 #ifdef TCP_KEEPALIVE
@@ -152,7 +152,7 @@
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPALIVE on fd %d\n", sockfd);
+      infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
     }
 #endif
 #endif
@@ -331,7 +331,7 @@
           /*
            * We now have the numerical IP address in the 'myhost' buffer
            */
-          infof(data, "Local Interface %s is ip %s using address family %i\n",
+          infof(data, "Local Interface %s is ip %s using address family %i",
                 dev, myhost, af);
           done = 1;
           break;
@@ -364,7 +364,7 @@
       if(h) {
         /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
         Curl_printable_address(h->addr, myhost, sizeof(myhost));
-        infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
+        infof(data, "Name '%s' family %i resolved to '%s' family %i",
               dev, af, myhost, h->addr->ai_family);
         Curl_resolv_unlock(data, h);
         if(af != h->addr->ai_family) {
@@ -458,13 +458,13 @@
               error, Curl_strerror(error, buffer, sizeof(buffer)));
         return CURLE_INTERFACE_FAILED;
       }
-      infof(data, "Local port: %hu\n", port);
+      infof(data, "Local port: %hu", port);
       conn->bits.bound = TRUE;
       return CURLE_OK;
     }
 
     if(--portnum > 0) {
-      infof(data, "Bind to local port %hu failed, trying next\n", port);
+      infof(data, "Bind to local port %hu failed, trying next", port);
       port++; /* try next port */
       /* We re-use/clobber the port variable here below */
       if(sock->sa_family == AF_INET)
@@ -589,12 +589,10 @@
     struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
 
     while(ai) {
-      if(ai) {
-        result = singleipconnect(data, conn, ai, tempindex);
-        if(result == CURLE_COULDNT_CONNECT) {
-          ai = ainext(conn, tempindex, TRUE);
-          continue;
-        }
+      result = singleipconnect(data, conn, ai, tempindex);
+      if(result == CURLE_COULDNT_CONNECT) {
+        ai = ainext(conn, tempindex, TRUE);
+        continue;
       }
       break;
     }
@@ -753,10 +751,9 @@
   int local_port = -1;
 
   if(conn->transport == TRNSPRT_TCP) {
-    if(!conn->bits.reuse && !conn->bits.tcp_fastopen) {
+    if(!conn->bits.reuse && !conn->bits.tcp_fastopen)
       Curl_conninfo_remote(data, conn, sockfd);
-      Curl_conninfo_local(data, sockfd, local_ip, &local_port);
-    }
+    Curl_conninfo_local(data, sockfd, local_ip, &local_port);
   } /* end of TCP-only section */
 
   /* persist connection info in session handle */
@@ -872,15 +869,6 @@
 
   now = Curl_now();
 
-  /* figure out how long time we have left to connect */
-  allow = Curl_timeleft(data, &now, TRUE);
-
-  if(allow < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
   if(SOCKS_STATE(conn->cnnct.state)) {
     /* still doing SOCKS */
     result = connect_SOCKS(data, sockindex, connected);
@@ -930,7 +918,7 @@
       if(Curl_timediff(now, conn->connecttime) >=
          conn->timeoutms_per_addr[i]) {
         infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
-              "ms connect time, move on!\n", conn->timeoutms_per_addr[i]);
+              "ms connect time, move on!", conn->timeoutms_per_addr[i]);
         error = ETIMEDOUT;
       }
 
@@ -989,11 +977,12 @@
         char buffer[STRERROR_LEN];
         Curl_printable_address(conn->tempaddr[i], ipaddress,
                                sizeof(ipaddress));
-        infof(data, "connect to %s port %ld failed: %s\n",
+        infof(data, "connect to %s port %u failed: %s",
               ipaddress, conn->port,
               Curl_strerror(error, buffer, sizeof(buffer)));
 #endif
 
+        allow = Curl_timeleft(data, &now, TRUE);
         conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
           allow : allow / 2;
         ainext(conn, i, TRUE);
@@ -1006,6 +995,21 @@
     }
   }
 
+  /*
+   * Now that we've checked whether we are connected, check whether we've
+   * already timed out.
+   *
+   * First figure out how long time we have left to connect */
+
+  allow = Curl_timeleft(data, &now, TRUE);
+
+  if(allow < 0) {
+    /* time-out, bail out, go home */
+    failf(data, "Connection timeout after %ld ms",
+          Curl_timediff(now, data->progress.t_startsingle));
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
   if(result &&
      (conn->tempsock[0] == CURL_SOCKET_BAD) &&
      (conn->tempsock[1] == CURL_SOCKET_BAD)) {
@@ -1031,9 +1035,11 @@
     else
       hostname = conn->host.name;
 
-    failf(data, "Failed to connect to %s port %ld: %s",
-          hostname, conn->port,
-          Curl_strerror(error, buffer, sizeof(buffer)));
+    failf(data, "Failed to connect to %s port %u after "
+                "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+        hostname, conn->port,
+        Curl_timediff(now, data->progress.t_startsingle),
+        Curl_strerror(error, buffer, sizeof(buffer)));
 
     Curl_quic_disconnect(data, conn, 0);
     Curl_quic_disconnect(data, conn, 1);
@@ -1065,7 +1071,7 @@
 
   if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
                 sizeof(onoff)) < 0)
-    infof(data, "Could not set TCP_NODELAY: %s\n",
+    infof(data, "Could not set TCP_NODELAY: %s",
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
 #else
   (void)data;
@@ -1084,9 +1090,11 @@
   int onoff = 1;
   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
                 sizeof(onoff)) < 0) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
     char buffer[STRERROR_LEN];
-    infof(data, "Could not set SO_NOSIGPIPE: %s\n",
+    infof(data, "Could not set SO_NOSIGPIPE: %s",
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#endif
   }
 }
 #else
@@ -1180,7 +1188,7 @@
     Curl_closesocket(data, conn, sockfd);
     return CURLE_OK;
   }
-  infof(data, "  Trying %s:%d...\n", ipaddress, port);
+  infof(data, "  Trying %s:%d...", ipaddress, port);
 
 #ifdef ENABLE_IPV6
   is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
@@ -1271,7 +1279,7 @@
 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
       if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
                     (void *)&optval, sizeof(optval)) < 0)
-        infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd);
+        infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
 
       rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
 #elif defined(MSG_FASTOPEN) /* old Linux */
@@ -1321,7 +1329,7 @@
 
     default:
       /* unknown error, fallthrough and try another address! */
-      infof(data, "Immediate connect fail for %s: %s\n",
+      infof(data, "Immediate connect fail for %s: %s",
             ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
       data->state.os_errno = error;
 
@@ -1394,7 +1402,7 @@
 
   ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
 
-  DEBUGF(infof(data, "family0 == %s, family1 == %s\n",
+  DEBUGF(infof(data, "family0 == %s, family1 == %s",
                conn->tempfamily[0] == AF_INET ? "v4" : "v6",
                conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
 
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index 941623f..b7531f7 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -95,7 +95,6 @@
 #include "strcase.h"
 #include "curl_get_line.h"
 #include "curl_memrchr.h"
-#include "inet_pton.h"
 #include "parsedate.h"
 #include "rand.h"
 #include "rename.h"
@@ -147,31 +146,6 @@
 }
 
 /*
- * isip
- *
- * Returns true if the given string is an IPv4 or IPv6 address (if IPv6 has
- * been enabled while building libcurl, and false otherwise.
- */
-static bool isip(const char *domain)
-{
-  struct in_addr addr;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr6;
-#endif
-
-  if(Curl_inet_pton(AF_INET, domain, &addr)
-#ifdef ENABLE_IPV6
-     || Curl_inet_pton(AF_INET6, domain, &addr6)
-#endif
-    ) {
-    /* domain name given as IP address */
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-/*
  * matching cookie path and url path
  * RFC6265 5.1.4 Paths and Path-Match
  */
@@ -303,7 +277,7 @@
   const char *top;
   size_t len;
 
-  if(!domain || isip(domain))
+  if(!domain || Curl_host_is_ipnum(domain))
     return 0;
 
   top = get_top_domain(domain, &len);
@@ -366,7 +340,7 @@
          * Failure may be due to OOM or a bad cookie; both are ignored
          * but only the first should be
          */
-        infof(data, "ignoring failed cookie_init for %s\n", list->data);
+        infof(data, "ignoring failed cookie_init for %s", list->data);
       else
         data->cookies = newcookies;
       list = list->next;
@@ -397,7 +371,9 @@
  *
  * Remove expired cookies from the hash by inspecting the expires timestamp on
  * each cookie in the hash, freeing and deleting any where the timestamp is in
- * the past.
+ * the past.  If the cookiejar has recorded the next timestamp at which one or
+ * more cookies expire, then processing will exit early in case this timestamp
+ * is in the future.
  */
 static void remove_expired(struct CookieInfo *cookies)
 {
@@ -405,6 +381,20 @@
   curl_off_t now = (curl_off_t)time(NULL);
   unsigned int i;
 
+  /*
+   * If the earliest expiration timestamp in the jar is in the future we can
+   * skip scanning the whole jar and instead exit early as there won't be any
+   * cookies to evict.  If we need to evict however, reset the next_expiration
+   * counter in order to track the next one. In case the recorded first
+   * expiration is the max offset, then perform the safe fallback of checking
+   * all cookies.
+   */
+  if(now < cookies->next_expiration &&
+      cookies->next_expiration != CURL_OFF_T_MAX)
+    return;
+  else
+    cookies->next_expiration = CURL_OFF_T_MAX;
+
   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
     struct Cookie *pv = NULL;
     co = cookies->cookies[i];
@@ -421,6 +411,12 @@
         freecookie(co);
       }
       else {
+        /*
+         * If this cookie has an expiration timestamp earlier than what we've
+         * seen so far then record it for the next round of expirations.
+         */
+        if(co->expires && co->expires < cookies->next_expiration)
+          cookies->next_expiration = co->expires;
         pv = co;
       }
       co = nx;
@@ -524,7 +520,7 @@
         if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
            ((nlen + len) > MAX_NAME)) {
           freecookie(co);
-          infof(data, "oversized cookie dropped, name/val %zu + %zu bytes\n",
+          infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
                 nlen, len);
           return NULL;
         }
@@ -645,7 +641,7 @@
             domain = ":";
 #endif
 
-          is_ip = isip(domain ? domain : whatptr);
+          is_ip = Curl_host_is_ipnum(domain ? domain : whatptr);
 
           if(!domain
              || (is_ip && !strcmp(whatptr, domain))
@@ -665,7 +661,7 @@
              * not a domain to which the current host belongs. Mark as bad.
              */
             badcookie = TRUE;
-            infof(data, "skipped cookie with bad tailmatch domain: %s\n",
+            infof(data, "skipped cookie with bad tailmatch domain: %s",
                   whatptr);
           }
         }
@@ -996,7 +992,7 @@
    * must also check that the data handle isn't NULL since the psl code will
    * dereference it.
    */
-  if(data && (domain && co->domain && !isip(co->domain))) {
+  if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
     const psl_ctx_t *psl = Curl_psl_use(data);
     int acceptable;
 
@@ -1009,7 +1005,7 @@
 
     if(!acceptable) {
       infof(data, "cookie '%s' dropped, domain '%s' must not "
-                  "set cookies for '%s'\n", co->name, domain, co->domain);
+                  "set cookies for '%s'", co->name, domain, co->domain);
       freecookie(co);
       return NULL;
     }
@@ -1121,7 +1117,7 @@
   if(c->running)
     /* Only show this when NOT reading the cookies from a file */
     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
-          "expire %" CURL_FORMAT_CURL_OFF_T "\n",
+          "expire %" CURL_FORMAT_CURL_OFF_T,
           replace_old?"Replaced":"Added", co->name, co->value,
           co->domain, co->path, co->expires);
 
@@ -1134,6 +1130,13 @@
     c->numcookies++; /* one more cookie in the jar */
   }
 
+  /*
+   * Now that we've added a new cookie to the jar, update the expiration
+   * tracker in case it is the next one to expire.
+   */
+  if(co->expires && (co->expires < c->next_expiration))
+    c->next_expiration = co->expires;
+
   return co;
 }
 
@@ -1169,6 +1172,11 @@
     c->filename = strdup(file?file:"none"); /* copy the name just in case */
     if(!c->filename)
       goto fail; /* failed to get memory */
+    /*
+     * Initialize the next_expiration time to signal that we don't have enough
+     * information yet.
+     */
+    c->next_expiration = CURL_OFF_T_MAX;
   }
   else {
     /* we got an already existing one, use that */
@@ -1215,7 +1223,7 @@
 
     /*
      * Remove expired cookies from the hash. We must make sure to run this
-     * after reading the file, and not not on every cookie.
+     * after reading the file, and not on every cookie.
      */
     remove_expired(c);
 
@@ -1247,7 +1255,8 @@
  *
  * Helper function to sort cookies such that the longest path gets before the
  * shorter path. Path, domain and name lengths are considered in that order,
- * with tge creationtime as the tiebreaker.
+ * with the creationtime as the tiebreaker. The creationtime is guaranteed to
+ * be unique per cookie, so we know we will get an ordering at that point.
  */
 static int cookie_sort(const void *p1, const void *p2)
 {
@@ -1355,7 +1364,7 @@
   remove_expired(c);
 
   /* check if host is an IP(v4|v6) address */
-  is_ip = isip(host);
+  is_ip = Curl_host_is_ipnum(host);
 
   co = c->cookies[myhash];
 
@@ -1734,7 +1743,7 @@
     /* if we have a destination file for all the cookies to get dumped to */
     res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
     if(res)
-      infof(data, "WARNING: failed to save cookies in %s: %s\n",
+      infof(data, "WARNING: failed to save cookies in %s: %s",
             data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
   }
   else {
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h
index 460bbb1..0ffe08e 100644
--- a/Utilities/cmcurl/lib/cookie.h
+++ b/Utilities/cmcurl/lib/cookie.h
@@ -65,6 +65,7 @@
   bool running;    /* state info, for cookie adding information */
   bool newsession; /* new session, discard session cookies on load */
   int lastct;      /* last creation-time used in the jar */
+  curl_off_t next_expiration; /* the next time at which expiration happens */
 };
 
 /* This is the maximum line length we accept for a cookie line. RFC 2109
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c
index 1d5067b..842fd7f 100644
--- a/Utilities/cmcurl/lib/curl_addrinfo.c
+++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -50,12 +50,6 @@
 #  define in_addr_t unsigned long
 #endif
 
-#if defined(USE_UNIX_SOCKETS) && defined(WINAPI_FAMILY) && \
-    (WINAPI_FAMILY == WINAPI_FAMILY_APP)
-   /* Required for sockaddr_un type */
-#  include <afunix.h>
-#endif
-
 #include <stddef.h>
 
 #include "curl_addrinfo.h"
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index 4fdc672..de03550 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -26,61 +26,91 @@
 /* when building libcurl itself */
 #cmakedefine BUILDING_LIBCURL 1
 
-/* to disable cookies support */
+/* to disable alt-svc */
+#cmakedefine CURL_DISABLE_ALTSVC 1
+
+/* disables cookies support */
 #cmakedefine CURL_DISABLE_COOKIES 1
 
-/* to disable cryptographic authentication */
+/* disables cryptographic authentication */
 #cmakedefine CURL_DISABLE_CRYPTO_AUTH 1
 
-/* to disable DICT */
+/* disables DICT */
 #cmakedefine CURL_DISABLE_DICT 1
 
-/* to disable FILE */
+/* disables DNS-over-HTTPS */
+#cmakedefine CURL_DISABLE_DOH 1
+
+/* disables FILE */
 #cmakedefine CURL_DISABLE_FILE 1
 
-/* to disable FTP */
+/* disables FTP */
 #cmakedefine CURL_DISABLE_FTP 1
 
-/* to disable GOPHER */
+/* disables GOPHER */
 #cmakedefine CURL_DISABLE_GOPHER 1
 
-/* to disable IMAP */
-#cmakedefine CURL_DISABLE_IMAP 1
+/* disables HSTS support */
+#cmakedefine CURL_DISABLE_HSTS 1
 
-/* to disable HTTP */
+/* disables HTTP */
 #cmakedefine CURL_DISABLE_HTTP 1
 
-/* to disable LDAP */
+/* disables IMAP */
+#cmakedefine CURL_DISABLE_IMAP 1
+
+/* disables LDAP */
 #cmakedefine CURL_DISABLE_LDAP 1
 
-/* to disable LDAPS */
+/* disables LDAPS */
 #cmakedefine CURL_DISABLE_LDAPS 1
 
-/* to disable MQTT */
+/* disables --libcurl option from the curl tool */
+#cmakedefine CURL_DISABLE_LIBCURL_OPTION 1
+
+/* disables MIME support */
+#cmakedefine CURL_DISABLE_MIME 1
+
+/* disables MQTT */
 #cmakedefine CURL_DISABLE_MQTT 1
 
-/* to disable POP3 */
+/* disables netrc parser */
+#cmakedefine CURL_DISABLE_NETRC 1
+
+/* disables NTLM support */
+#cmakedefine CURL_DISABLE_NTLM 1
+
+/* disables date parsing */
+#cmakedefine CURL_DISABLE_PARSEDATE 1
+
+/* disables POP3 */
 #cmakedefine CURL_DISABLE_POP3 1
 
-/* to disable proxies */
+/* disables built-in progress meter */
+#cmakedefine CURL_DISABLE_PROGRESS_METER 1
+
+/* disables proxies */
 #cmakedefine CURL_DISABLE_PROXY 1
 
-/* to disable RTSP */
+/* disables RTSP */
 #cmakedefine CURL_DISABLE_RTSP 1
 
-/* to disable SMB */
+/* disables SMB */
 #cmakedefine CURL_DISABLE_SMB 1
 
-/* to disable SMTP */
+/* disables SMTP */
 #cmakedefine CURL_DISABLE_SMTP 1
 
-/* to disable TELNET */
+/* disables use of socketpair for curl_multi_poll */
+#cmakedefine CURL_DISABLE_SOCKETPAIR 1
+
+/* disables TELNET */
 #cmakedefine CURL_DISABLE_TELNET 1
 
-/* to disable TFTP */
+/* disables TFTP */
 #cmakedefine CURL_DISABLE_TFTP 1
 
-/* to disable verbose strings */
+/* disables verbose strings */
 #cmakedefine CURL_DISABLE_VERBOSE_STRINGS 1
 
 /* to make a symbol visible */
@@ -105,12 +135,6 @@
 /* Define if you want to enable IPv6 support */
 #cmakedefine ENABLE_IPV6 1
 
-/* Specifies the number of arguments to getservbyport_r */
-#cmakedefine GETSERVBYPORT_R_ARGS ${GETSERVBYPORT_R_ARGS}
-
-/* Specifies the size of the buffer to pass to getservbyport_r */
-#cmakedefine GETSERVBYPORT_R_BUFSIZE ${GETSERVBYPORT_R_BUFSIZE}
-
 /* Define to 1 if you have the alarm function. */
 #cmakedefine HAVE_ALARM 1
 
@@ -144,18 +168,12 @@
 /* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
 #cmakedefine HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1
 
-/* Define to 1 if you have the <crypto.h> header file. */
-#cmakedefine HAVE_CRYPTO_H 1
-
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #cmakedefine HAVE_DLFCN_H 1
 
 /* Define to 1 if you have the <errno.h> header file. */
 #cmakedefine HAVE_ERRNO_H 1
 
-/* Define to 1 if you have the <err.h> header file. */
-#cmakedefine HAVE_ERR_H 1
-
 /* Define to 1 if you have the fcntl function. */
 #cmakedefine HAVE_FCNTL 1
 
@@ -165,18 +183,9 @@
 /* Define to 1 if you have a working fcntl O_NONBLOCK function. */
 #cmakedefine HAVE_FCNTL_O_NONBLOCK 1
 
-/* Define to 1 if you have the fdopen function. */
-#cmakedefine HAVE_FDOPEN 1
-
-/* Define to 1 if you have the `fork' function. */
-#cmakedefine HAVE_FORK 1
-
 /* Define to 1 if you have the freeaddrinfo function. */
 #cmakedefine HAVE_FREEADDRINFO 1
 
-/* Define to 1 if you have the freeifaddrs function. */
-#cmakedefine HAVE_FREEIFADDRS 1
-
 /* Define to 1 if you have the ftruncate function. */
 #cmakedefine HAVE_FTRUNCATE 1
 
@@ -189,21 +198,6 @@
 /* Define to 1 if you have the `getppid' function. */
 #cmakedefine HAVE_GETPPID 1
 
-/* Define to 1 if you have the gethostbyaddr function. */
-#cmakedefine HAVE_GETHOSTBYADDR 1
-
-/* Define to 1 if you have the gethostbyaddr_r function. */
-#cmakedefine HAVE_GETHOSTBYADDR_R 1
-
-/* gethostbyaddr_r() takes 5 args */
-#cmakedefine HAVE_GETHOSTBYADDR_R_5 1
-
-/* gethostbyaddr_r() takes 7 args */
-#cmakedefine HAVE_GETHOSTBYADDR_R_7 1
-
-/* gethostbyaddr_r() takes 8 args */
-#cmakedefine HAVE_GETHOSTBYADDR_R_8 1
-
 /* Define to 1 if you have the gethostbyname function. */
 #cmakedefine HAVE_GETHOSTBYNAME 1
 
@@ -252,9 +246,6 @@
 /* Define to 1 if you have the `getrlimit' function. */
 #cmakedefine HAVE_GETRLIMIT 1
 
-/* Define to 1 if you have the getservbyport_r function. */
-#cmakedefine HAVE_GETSERVBYPORT_R 1
-
 /* Define to 1 if you have the `gettimeofday' function. */
 #cmakedefine HAVE_GETTIMEOFDAY 1
 
@@ -388,21 +379,6 @@
 /* Define to 1 if you have the `ssh2' library (-lssh2). */
 #cmakedefine HAVE_LIBSSH2 1
 
-/* Define to 1 if libssh2 provides `libssh2_version'. */
-#cmakedefine HAVE_LIBSSH2_VERSION 1
-
-/* Define to 1 if libssh2 provides `libssh2_init'. */
-#cmakedefine HAVE_LIBSSH2_INIT 1
-
-/* Define to 1 if libssh2 provides `libssh2_exit'. */
-#cmakedefine HAVE_LIBSSH2_EXIT 1
-
-/* Define to 1 if libssh2 provides `libssh2_scp_send64'. */
-#cmakedefine HAVE_LIBSSH2_SCP_SEND64 1
-
-/* Define to 1 if libssh2 provides `libssh2_session_handshake'. */
-#cmakedefine HAVE_LIBSSH2_SESSION_HANDSHAKE 1
-
 /* Define to 1 if you have the <libssh2.h> header file. */
 #cmakedefine HAVE_LIBSSH2_H 1
 
@@ -519,9 +495,6 @@
 /* Define to 1 if you have the recvfrom function. */
 #cmakedefine HAVE_RECVFROM 1
 
-/* Define to 1 if you have the <rsa.h> header file. */
-#cmakedefine HAVE_RSA_H 1
-
 /* Define to 1 if you have the select function. */
 #cmakedefine HAVE_SELECT 1
 
@@ -555,9 +528,6 @@
 /* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */
 #cmakedefine HAVE_SETSOCKOPT_SO_NONBLOCK 1
 
-/* Define to 1 if you have the <sgtty.h> header file. */
-#cmakedefine HAVE_SGTTY_H 1
-
 /* Define to 1 if you have the sigaction function. */
 #cmakedefine HAVE_SIGACTION 1
 
@@ -573,12 +543,6 @@
 /* Define to 1 if you have the sigsetjmp function or macro. */
 #cmakedefine HAVE_SIGSETJMP 1
 
-/* Define to 1 if sig_atomic_t is an available typedef. */
-#cmakedefine HAVE_SIG_ATOMIC_T 1
-
-/* Define to 1 if sig_atomic_t is already defined as volatile. */
-#cmakedefine HAVE_SIG_ATOMIC_T_VOLATILE 1
-
 /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
 #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
 
@@ -994,9 +958,6 @@
 /* if Unix domain sockets are enabled  */
 #cmakedefine USE_UNIX_SOCKETS
 
-/* to disable alt-svc */
-#cmakedefine CURL_DISABLE_ALTSVC 1
-
 /* to enable SSPI support */
 #cmakedefine USE_WINDOWS_SSPI 1
 
diff --git a/Utilities/cmcurl/lib/curl_endian.c b/Utilities/cmcurl/lib/curl_endian.c
index b6f107e..ecde74b 100644
--- a/Utilities/cmcurl/lib/curl_endian.c
+++ b/Utilities/cmcurl/lib/curl_endian.c
@@ -80,45 +80,3 @@
   return (unsigned short)(((unsigned short)buf[0] << 8) |
                           ((unsigned short)buf[1]));
 }
-
-#if (SIZEOF_CURL_OFF_T > 4)
-/*
- * write32_le()
- *
- * This function converts a 32-bit integer from the native endian format,
- * to little endian format ready for sending down the wire.
- *
- * Parameters:
- *
- * value    [in]     - The 32-bit integer value.
- * buffer   [in]     - A pointer to the output buffer.
- */
-static void write32_le(const int value, unsigned char *buffer)
-{
-  buffer[0] = (char)(value & 0x000000FF);
-  buffer[1] = (char)((value & 0x0000FF00) >> 8);
-  buffer[2] = (char)((value & 0x00FF0000) >> 16);
-  buffer[3] = (char)((value & 0xFF000000) >> 24);
-}
-
-/*
- * Curl_write64_le()
- *
- * This function converts a 64-bit integer from the native endian format,
- * to little endian format ready for sending down the wire.
- *
- * Parameters:
- *
- * value    [in]     - The 64-bit integer value.
- * buffer   [in]     - A pointer to the output buffer.
- */
-#if defined(HAVE_LONGLONG)
-void Curl_write64_le(const long long value, unsigned char *buffer)
-#else
-void Curl_write64_le(const __int64 value, unsigned char *buffer)
-#endif
-{
-  write32_le((int)value, buffer);
-  write32_le((int)(value >> 32), buffer + 4);
-}
-#endif /* SIZEOF_CURL_OFF_T > 4 */
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c
index acaaa1c..5810dad 100644
--- a/Utilities/cmcurl/lib/curl_gssapi.c
+++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -59,7 +59,7 @@
     req_flags |= GSS_C_DELEG_POLICY_FLAG;
 #else
     infof(data, "warning: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
-        "compiled in\n");
+        "compiled in");
 #endif
   }
 
@@ -130,7 +130,7 @@
 
   display_gss_error(minor, GSS_C_MECH_CODE, buf, len);
 
-  infof(data, "%s%s\n", prefix, buf);
+  infof(data, "%s%s", prefix, buf);
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
   (void)prefix;
diff --git a/Utilities/cmcurl/lib/curl_multibyte.c b/Utilities/cmcurl/lib/curl_multibyte.c
index 16418be..e9d2a8c 100644
--- a/Utilities/cmcurl/lib/curl_multibyte.c
+++ b/Utilities/cmcurl/lib/curl_multibyte.c
@@ -102,14 +102,16 @@
   va_end(param);
 
 #ifdef _UNICODE
-  if(filename_w)
+  if(filename_w) {
     result = _wopen(filename_w, oflag, pmode);
-  free(filename_w);
-  if(result != -1)
-    return result;
-#endif
-
+    free(filename_w);
+  }
+  else
+    errno = EINVAL;
+  return result;
+#else
   return (_open)(filename, oflag, pmode);
+#endif
 }
 
 FILE *curlx_win32_fopen(const char *filename, const char *mode)
@@ -120,19 +122,20 @@
   wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
   if(filename_w && mode_w)
     result = _wfopen(filename_w, mode_w);
+  else
+    errno = EINVAL;
   free(filename_w);
   free(mode_w);
-  if(result)
-    return result;
-#endif
-
+  return result;
+#else
   return (fopen)(filename, mode);
+#endif
 }
 
 int curlx_win32_stat(const char *path, struct_stat *buffer)
 {
-  int result = -1;
 #ifdef _UNICODE
+  int result = -1;
   wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
   if(path_w) {
 #if defined(USE_WIN32_SMALL_FILES)
@@ -141,31 +144,34 @@
     result = _wstati64(path_w, buffer);
 #endif
     free(path_w);
-    if(result != -1)
-      return result;
   }
-#endif /* _UNICODE */
-
-#if defined(USE_WIN32_SMALL_FILES)
-  result = _stat(path, buffer);
-#else
-  result = _stati64(path, buffer);
-#endif
+  else
+    errno = EINVAL;
   return result;
+#else
+#if defined(USE_WIN32_SMALL_FILES)
+  return _stat(path, buffer);
+#else
+  return _stati64(path, buffer);
+#endif
+#endif
 }
 
 int curlx_win32_access(const char *path, int mode)
 {
 #if defined(_UNICODE)
+  int result = -1;
   wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
   if(path_w) {
-    int result = _waccess(path_w, mode);
+    result = _waccess(path_w, mode);
     free(path_w);
-    if(result != -1)
-      return result;
   }
-#endif /* _UNICODE */
+  else
+    errno = EINVAL;
+  return result;
+#else
   return _access(path, mode);
+#endif
 }
 
 #endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index 89d4ec8..749b44e 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -86,7 +86,6 @@
 #elif defined(USE_MBEDTLS)
 
 #  include <mbedtls/des.h>
-#  include "curl_md4.h"
 
 #elif defined(USE_SECTRANSP)
 
@@ -498,17 +497,14 @@
    * network encoding not the host encoding.
    */
   result = Curl_convert_to_network(data, (char *)pw, len * 2);
-  if(result)
-    return result;
-
-  /* Create NT hashed password. */
-  Curl_md4it(ntbuffer, pw, 2 * len);
-
-  memset(ntbuffer + 16, 0, 21 - 16);
-
+  if(!result) {
+    /* Create NT hashed password. */
+    Curl_md4it(ntbuffer, pw, 2 * len);
+    memset(ntbuffer + 16, 0, 21 - 16);
+  }
   free(pw);
 
-  return CURLE_OK;
+  return result;
 }
 
 #if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI)
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c
index 9af79fd..5a3bc3c 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c
@@ -355,17 +355,17 @@
   }
   else {
     if(*state == NTLMSTATE_LAST) {
-      infof(data, "NTLM auth restarted\n");
+      infof(data, "NTLM auth restarted");
       Curl_http_auth_cleanup_ntlm_wb(conn);
     }
     else if(*state == NTLMSTATE_TYPE3) {
-      infof(data, "NTLM handshake rejected\n");
+      infof(data, "NTLM handshake rejected");
       Curl_http_auth_cleanup_ntlm_wb(conn);
       *state = NTLMSTATE_NONE;
       return CURLE_REMOTE_ACCESS_DENIED;
     }
     else if(*state >= NTLMSTATE_TYPE1) {
-      infof(data, "NTLM handshake failure (internal error)\n");
+      infof(data, "NTLM handshake failure (internal error)");
       return CURLE_REMOTE_ACCESS_DENIED;
     }
 
@@ -426,7 +426,8 @@
     /* Use Samba's 'winbind' daemon to support NTLM authentication,
      * by delegating the NTLM challenge/response protocol to a helper
      * in ntlm_auth.
-     * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+     * https://web.archive.org/web/20190925164737
+     * /devel.squid-cache.org/ntlm/squid_helper_protocol.html
      * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
      * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
      * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
diff --git a/Utilities/cmcurl/lib/curl_range.c b/Utilities/cmcurl/lib/curl_range.c
index f7fb7c0..24bdb30 100644
--- a/Utilities/cmcurl/lib/curl_range.c
+++ b/Utilities/cmcurl/lib/curl_range.c
@@ -53,14 +53,14 @@
     if((to_t == CURL_OFFT_INVAL) && !from_t) {
       /* X - */
       data->state.resume_from = from;
-      DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
+      DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file",
                    from));
     }
     else if((from_t == CURL_OFFT_INVAL) && !to_t) {
       /* -Y */
       data->req.maxdownload = to;
       data->state.resume_from = -to;
-      DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
+      DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes",
                    to));
     }
     else {
@@ -78,12 +78,12 @@
       data->req.maxdownload = totalsize + 1; /* include last byte */
       data->state.resume_from = from;
       DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
-                   " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
+                   " getting %" CURL_FORMAT_CURL_OFF_T " bytes",
                    from, data->req.maxdownload));
     }
     DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
                  " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
-                 CURL_FORMAT_CURL_OFF_T " bytes\n",
+                 CURL_FORMAT_CURL_OFF_T " bytes",
                  from, to, data->req.maxdownload));
   }
   else
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index a4d1059..4a24887 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -234,7 +234,7 @@
   };
 
   if(sasl->state != newstate)
-    infof(data, "SASL %p state change from %s to %s\n",
+    infof(data, "SASL %p state change from %s to %s",
           (void *)sasl, names[sasl->state], names[newstate]);
 #else
   (void) data;
@@ -630,7 +630,9 @@
       }
       else
         /* Decode the security challenge and create the response message */
-        result = Curl_auth_create_gssapi_security_message(data, &serverdata,
+        result = Curl_auth_create_gssapi_security_message(data,
+                                                          conn->sasl_authzid,
+                                                          &serverdata,
                                                           &conn->krb5,
                                                           &resp);
     }
@@ -639,7 +641,9 @@
     /* Decode the security challenge and create the response message */
     result = get_server_message(sasl, data, &serverdata);
     if(!result)
-      result = Curl_auth_create_gssapi_security_message(data, &serverdata,
+      result = Curl_auth_create_gssapi_security_message(data,
+                                                        conn->sasl_authzid,
+                                                        &serverdata,
                                                         &conn->krb5,
                                                         &resp);
     break;
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 2d13a40..554dcc1 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -170,42 +170,48 @@
  */
 
 #ifdef HTTP_ONLY
-#  ifndef CURL_DISABLE_TFTP
-#    define CURL_DISABLE_TFTP
-#  endif
-#  ifndef CURL_DISABLE_FTP
-#    define CURL_DISABLE_FTP
-#  endif
-#  ifndef CURL_DISABLE_LDAP
-#    define CURL_DISABLE_LDAP
-#  endif
-#  ifndef CURL_DISABLE_TELNET
-#    define CURL_DISABLE_TELNET
-#  endif
 #  ifndef CURL_DISABLE_DICT
 #    define CURL_DISABLE_DICT
 #  endif
 #  ifndef CURL_DISABLE_FILE
 #    define CURL_DISABLE_FILE
 #  endif
-#  ifndef CURL_DISABLE_RTSP
-#    define CURL_DISABLE_RTSP
-#  endif
-#  ifndef CURL_DISABLE_POP3
-#    define CURL_DISABLE_POP3
-#  endif
-#  ifndef CURL_DISABLE_IMAP
-#    define CURL_DISABLE_IMAP
-#  endif
-#  ifndef CURL_DISABLE_SMTP
-#    define CURL_DISABLE_SMTP
+#  ifndef CURL_DISABLE_FTP
+#    define CURL_DISABLE_FTP
 #  endif
 #  ifndef CURL_DISABLE_GOPHER
 #    define CURL_DISABLE_GOPHER
 #  endif
+#  ifndef CURL_DISABLE_IMAP
+#    define CURL_DISABLE_IMAP
+#  endif
+#  ifndef CURL_DISABLE_LDAP
+#    define CURL_DISABLE_LDAP
+#  endif
+#  ifndef CURL_DISABLE_LDAPS
+#    define CURL_DISABLE_LDAPS
+#  endif
+#  ifndef CURL_DISABLE_MQTT
+#    define CURL_DISABLE_MQTT
+#  endif
+#  ifndef CURL_DISABLE_POP3
+#    define CURL_DISABLE_POP3
+#  endif
+#  ifndef CURL_DISABLE_RTSP
+#    define CURL_DISABLE_RTSP
+#  endif
 #  ifndef CURL_DISABLE_SMB
 #    define CURL_DISABLE_SMB
 #  endif
+#  ifndef CURL_DISABLE_SMTP
+#    define CURL_DISABLE_SMTP
+#  endif
+#  ifndef CURL_DISABLE_TELNET
+#    define CURL_DISABLE_TELNET
+#  endif
+#  ifndef CURL_DISABLE_TFTP
+#    define CURL_DISABLE_TFTP
+#  endif
 #endif
 
 /*
@@ -471,6 +477,15 @@
 #endif
 #endif
 
+#ifndef SSIZE_T_MAX
+/* some limits.h headers have this defined, some don't */
+#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
+#define SSIZE_T_MAX 9223372036854775807
+#else
+#define SSIZE_T_MAX 2147483647
+#endif
+#endif
+
 /*
  * Arg 2 type for gethostname in case it hasn't been defined in config file.
  */
@@ -659,24 +674,16 @@
 #endif
 
 /* Single point where USE_NTLM definition might be defined */
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-#if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                     \
-  defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) ||  \
-  defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
-  (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
-
-#define USE_CURL_NTLM_CORE
-
-#  if defined(USE_MBEDTLS)
-/* Get definition of MBEDTLS_MD4_C */
-#  include <mbedtls/md4.h>
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM)
+#  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                       \
+      defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) ||  \
+      defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
+      (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
+#    define USE_CURL_NTLM_CORE
 #  endif
-
-#endif
-
-#if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
-#define USE_NTLM
-#endif
+#  if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
+#    define USE_NTLM
+#  endif
 #endif
 
 #ifdef CURL_WANTS_CA_BUNDLE_ENV
@@ -834,4 +841,20 @@
 #define ENABLE_QUIC
 #endif
 
+#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
+#  if defined(__MINGW32__) && !defined(LUP_SECURE)
+     typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */
+#  endif
+#  if !defined(UNIX_PATH_MAX)
+     /* Replicating logic present in afunix.h
+        (distributed with newer Windows 10 SDK versions only) */
+#    define UNIX_PATH_MAX 108
+     /* !checksrc! disable TYPEDEFSTRUCT 1 */
+     typedef struct sockaddr_un {
+       ADDRESS_FAMILY sun_family;
+       char sun_path[UNIX_PATH_MAX];
+     } SOCKADDR_UN, *PSOCKADDR_UN;
+#  endif
+#endif
+
 #endif /* HEADER_CURL_SETUP_H */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h
index 22d0a06..38018d2 100644
--- a/Utilities/cmcurl/lib/curl_setup_once.h
+++ b/Utilities/cmcurl/lib/curl_setup_once.h
@@ -323,26 +323,6 @@
 
 #include "curl_ctype.h"
 
-/*
- * Typedef to 'int' if sig_atomic_t is not an available 'typedefed' type.
- */
-
-#ifndef HAVE_SIG_ATOMIC_T
-typedef int sig_atomic_t;
-#define HAVE_SIG_ATOMIC_T
-#endif
-
-
-/*
- * Convenience SIG_ATOMIC_T definition
- */
-
-#ifdef HAVE_SIG_ATOMIC_T_VOLATILE
-#define SIG_ATOMIC_T static sig_atomic_t
-#else
-#define SIG_ATOMIC_T static volatile sig_atomic_t
-#endif
-
 
 /*
  * Macro used to include code only in debug builds.
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c
index 625b057..5d53b8f 100644
--- a/Utilities/cmcurl/lib/dict.c
+++ b/Utilities/cmcurl/lib/dict.c
@@ -216,7 +216,7 @@
     }
 
     if(!word || (*word == (char)0)) {
-      infof(data, "lookup word is missing\n");
+      infof(data, "lookup word is missing");
       word = (char *)"default";
     }
     if(!database || (*database == (char)0)) {
@@ -267,7 +267,7 @@
     }
 
     if(!word || (*word == (char)0)) {
-      infof(data, "lookup word is missing\n");
+      infof(data, "lookup word is missing");
       word = (char *)"default";
     }
     if(!database || (*database == (char)0)) {
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index 36f8cd5..de0c902 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -186,19 +186,19 @@
   return realsize;
 }
 
-/* called from multi.c when this DOH transfer is complete */
+/* called from multi.c when this DoH transfer is complete */
 static int doh_done(struct Curl_easy *doh, CURLcode result)
 {
   struct Curl_easy *data = doh->set.dohfor;
   struct dohdata *dohp = data->req.doh;
-  /* so one of the DOH request done for the 'data' transfer is now complete! */
+  /* so one of the DoH request done for the 'data' transfer is now complete! */
   dohp->pending--;
-  infof(data, "a DOH request is completed, %u to go\n", dohp->pending);
+  infof(data, "a DoH request is completed, %u to go", dohp->pending);
   if(result)
-    infof(data, "DOH request %s\n", curl_easy_strerror(result));
+    infof(data, "DoH request %s", curl_easy_strerror(result));
 
   if(!dohp->pending) {
-    /* DOH completed */
+    /* DoH completed */
     curl_slist_free_all(dohp->headers);
     dohp->headers = NULL;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
@@ -228,7 +228,7 @@
   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
                          &p->dohlen);
   if(d) {
-    failf(data, "Failed to encode DOH packet [%d]", d);
+    failf(data, "Failed to encode DoH packet [%d]", d);
     return CURLE_OUT_OF_MEMORY;
   }
 
@@ -302,7 +302,7 @@
     /* Inherit *some* SSL options from the user's transfer. This is a
        best-guess as to which options are needed for compatibility. #3661
 
-       Note DOH does not inherit the user's proxy server so proxy SSL settings
+       Note DoH does not inherit the user's proxy server so proxy SSL settings
        have no effect and are not inherited. If that changes then two new
        options should be added to check doh proxy insecure separately,
        CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
@@ -359,17 +359,17 @@
         (data->set.ssl.auto_client_cert ?
          CURLSSLOPT_AUTO_CLIENT_CERT : 0);
 
-      curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
+      (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
     }
 
     doh->set.fmultidone = doh_done;
     doh->set.dohfor = data; /* identify for which transfer this is done */
     p->easy = doh;
 
-    /* DOH private_data must be null because the user must have a way to
-       distinguish their transfer's handle from DOH handles in user
+    /* DoH private_data must be null because the user must have a way to
+       distinguish their transfer's handle from DoH handles in user
        callbacks (ie SSL CTX callback). */
-    DEBUGASSERT(!data->set.private_data);
+    DEBUGASSERT(!doh->set.private_data);
 
     if(curl_multi_add_handle(multi, doh))
       goto error;
@@ -386,7 +386,7 @@
 }
 
 /*
- * Curl_doh() resolves a name using DOH. It resolves a name and returns a
+ * Curl_doh() resolves a name using DoH. It resolves a name and returns a
  * 'Curl_addrinfo *' with the address information.
  */
 
@@ -420,7 +420,7 @@
   if(!dohp->headers)
     goto error;
 
-  /* create IPv4 DOH request */
+  /* create IPv4 DoH request */
   result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
                     DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
                     data->multi, dohp->headers);
@@ -429,7 +429,7 @@
   dohp->pending++;
 
   if(Curl_ipv6works(data)) {
-    /* create IPv6 DOH request */
+    /* create IPv6 DoH request */
     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
                       data->multi, dohp->headers);
@@ -764,11 +764,11 @@
                     const struct dohentry *d)
 {
   int i;
-  infof(data, "TTL: %u seconds\n", d->ttl);
+  infof(data, "TTL: %u seconds", d->ttl);
   for(i = 0; i < d->numaddr; i++) {
     const struct dohaddr *a = &d->addr[i];
     if(a->type == DNS_TYPE_A) {
-      infof(data, "DOH A: %u.%u.%u.%u\n",
+      infof(data, "DoH A: %u.%u.%u.%u",
             a->ip.v4[0], a->ip.v4[1],
             a->ip.v4[2], a->ip.v4[3]);
     }
@@ -777,7 +777,7 @@
       char buffer[128];
       char *ptr;
       size_t len;
-      msnprintf(buffer, 128, "DOH AAAA: ");
+      msnprintf(buffer, 128, "DoH AAAA: ");
       ptr = &buffer[10];
       len = 118;
       for(j = 0; j < 16; j += 2) {
@@ -788,11 +788,11 @@
         len -= l;
         ptr += l;
       }
-      infof(data, "%s\n", buffer);
+      infof(data, "%s", buffer);
     }
   }
   for(i = 0; i < d->numcname; i++) {
-    infof(data, "CNAME: %s\n", Curl_dyn_ptr(&d->cname[i]));
+    infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
   }
 }
 #else
@@ -803,7 +803,7 @@
  * doh2ai()
  *
  * This function returns a pointer to the first element of a newly allocated
- * Curl_addrinfo struct linked list filled with the data from a set of DOH
+ * Curl_addrinfo struct linked list filled with the data from a set of DoH
  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
  *
@@ -931,7 +931,7 @@
 
   if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
      !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
-    failf(data, "Could not DOH-resolve: %s", data->state.async.hostname);
+    failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
     return data->conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
       CURLE_COULDNT_RESOLVE_HOST;
   }
@@ -941,7 +941,7 @@
     };
     struct dohentry de;
     int slot;
-    /* remove DOH handles from multi handle and close them */
+    /* remove DoH handles from multi handle and close them */
     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
       curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
       Curl_close(&dohp->probe[slot].easy);
@@ -958,7 +958,7 @@
                             &de);
       Curl_dyn_free(&p->serverdoh);
       if(rc[slot]) {
-        infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]),
+        infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
               type2name(p->dnstype), dohp->host);
       }
     } /* next slot */
@@ -969,7 +969,7 @@
       struct Curl_dns_entry *dns;
       struct Curl_addrinfo *ai;
 
-      infof(data, "DOH Host name: %s\n", dohp->host);
+      infof(data, "DoH Host name: %s", dohp->host);
       showdoh(data, &de);
 
       ai = doh2ai(&de, dohp->host, dohp->port);
@@ -1007,7 +1007,7 @@
 
   } /* !dohp->pending */
 
-  /* else wait for pending DOH transactions to complete */
+  /* else wait for pending DoH transactions to complete */
   return CURLE_OK;
 }
 
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h
index b3584d1..70e96e0 100644
--- a/Utilities/cmcurl/lib/doh.h
+++ b/Utilities/cmcurl/lib/doh.h
@@ -101,7 +101,7 @@
 void de_cleanup(struct dohentry *d);
 #endif
 
-#else /* if DOH is disabled */
+#else /* if DoH is disabled */
 #define Curl_doh(a,b,c,d) NULL
 #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
 #endif
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index 530b7c7..2aca938 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -117,7 +117,7 @@
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
 #if defined(WIN32) && defined(UNICODE)
-curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
+curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
 #endif
 
 #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
@@ -417,13 +417,13 @@
           ev->list = nxt;
         free(m);
         m = nxt;
-        infof(easy, "socket cb: socket %d REMOVED\n", s);
+        infof(easy, "socket cb: socket %d REMOVED", s);
       }
       else {
         /* The socket 's' is already being monitored, update the activity
            mask. Convert from libcurl bitmask to the poll one. */
         m->socket.events = socketcb2poll(what);
-        infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s,
+        infof(easy, "socket cb: socket %d UPDATED as %s%s", s,
               (what&CURL_POLL_IN)?"IN":"",
               (what&CURL_POLL_OUT)?"OUT":"");
       }
@@ -447,7 +447,7 @@
         m->socket.events = socketcb2poll(what);
         m->socket.revents = 0;
         ev->list = m;
-        infof(easy, "socket cb: socket %d ADDED as %s%s\n", s,
+        infof(easy, "socket cb: socket %d ADDED as %s%s", s,
               (what&CURL_POLL_IN)?"IN":"",
               (what&CURL_POLL_OUT)?"OUT":"");
       }
@@ -532,7 +532,7 @@
         if(fds[i].revents) {
           /* socket activity, tell libcurl */
           int act = poll2cselect(fds[i].revents); /* convert */
-          infof(multi->easyp, "call curl_multi_socket_action(socket %d)\n",
+          infof(multi->easyp, "call curl_multi_socket_action(socket %d)",
                 fds[i].fd);
           mcode = curl_multi_socket_action(multi, fds[i].fd, act,
                                            &ev->running_handles);
@@ -1027,7 +1027,7 @@
 
   if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) {
     /* Not changing any pause state, return */
-    DEBUGF(infof(data, "pause: no change, early return\n"));
+    DEBUGF(infof(data, "pause: no change, early return"));
     return CURLE_OK;
   }
 
@@ -1213,9 +1213,16 @@
   /* Param is unused. */
   (void)param;
 
-  if(conn->handler->connection_check)
+  if(conn->handler->connection_check) {
+    /* briefly attach the connection to this transfer for the purpose of
+       checking it */
+    Curl_attach_connnection(data, conn);
+
     /* Do a protocol-specific keepalive check on the connection. */
     conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
+    /* detach the connection again */
+    Curl_detach_connnection(data);
+  }
 
   return 0; /* continue iteration */
 }
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index 769f06a..ac7a000 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -865,8 +865,6 @@
 
         if(post->flags & CURL_HTTPPOST_LARGE)
           clen = post->contentlen;
-        if(!clen)
-          clen = -1;
 
         if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) {
           if(!strcmp(file->contents, "-")) {
@@ -888,13 +886,21 @@
         else if(post->flags & HTTPPOST_BUFFER)
           result = curl_mime_data(part, post->buffer,
                                   post->bufferlength? post->bufferlength: -1);
-        else if(post->flags & HTTPPOST_CALLBACK)
+        else if(post->flags & HTTPPOST_CALLBACK) {
           /* the contents should be read with the callback and the size is set
              with the contentslength */
+          if(!clen)
+            clen = -1;
           result = curl_mime_data_cb(part, clen,
                                      fread_func, NULL, NULL, post->userp);
+        }
         else {
-          result = curl_mime_data(part, post->contents, (ssize_t) clen);
+          size_t uclen;
+          if(!clen)
+            uclen = CURL_ZERO_TERMINATED;
+          else
+            uclen = (size_t)clen;
+          result = curl_mime_data(part, post->contents, uclen);
 #ifdef CURL_DOES_CONVERSIONS
           /* Convert textual contents now. */
           if(!result && data && part->datasize)
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 425b0af..cad584f 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -289,7 +289,7 @@
     failf(data, "Error accept()ing server connect");
     return CURLE_FTP_PORT_FAILED;
   }
-  infof(data, "Connection accepted from server\n");
+  infof(data, "Connection accepted from server");
   /* when this happens within the DO state it is important that we mark us as
      not needing DO_MORE anymore */
   conn->bits.do_more = FALSE;
@@ -380,7 +380,7 @@
   *received = FALSE;
 
   timeout_ms = ftp_timeleft_accept(data);
-  infof(data, "Checking for server connect\n");
+  infof(data, "Checking for server connect");
   if(timeout_ms < 0) {
     /* if a timeout was already reached, bail out */
     failf(data, "Accept timeout occurred while waiting server connect");
@@ -390,7 +390,7 @@
   /* First check whether there is a cached response from server */
   if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
     /* Data connection could not be established, let's return */
-    infof(data, "There is negative response in cache while serv connect\n");
+    infof(data, "There is negative response in cache while serv connect");
     (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
     return CURLE_FTP_ACCEPT_FAILED;
   }
@@ -408,11 +408,11 @@
   default:
 
     if(result & CURL_CSELECT_IN2) {
-      infof(data, "Ready to accept data connection from server\n");
+      infof(data, "Ready to accept data connection from server");
       *received = TRUE;
     }
     else if(result & CURL_CSELECT_IN) {
-      infof(data, "Ctrl conn has data while waiting for data conn\n");
+      infof(data, "Ctrl conn has data while waiting for data conn");
       (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
 
       if(ftpcode/100 > 3)
@@ -444,7 +444,7 @@
   if(conn->bits.ftp_use_data_ssl) {
     /* since we only have a plaintext TCP connection here, we must now
      * do the TLS stuff */
-    infof(data, "Doing the SSL/TLS handshake on the data stream\n");
+    infof(data, "Doing the SSL/TLS handshake on the data stream");
     result = Curl_ssl_connect(data, conn, SECONDARYSOCKET);
     if(result)
       return result;
@@ -487,7 +487,7 @@
   CURLcode result = CURLE_OK;
 
   *connected = FALSE;
-  infof(data, "Preparing for accepting server on data port\n");
+  infof(data, "Preparing for accepting server on data port");
 
   /* Save the time we start accepting server connect */
   Curl_pgrsTime(data, TIMER_STARTACCEPT);
@@ -592,7 +592,7 @@
      * This response code can come at any point so having it treated
      * generically is a good idea.
      */
-    infof(data, "We got a 421 - timeout!\n");
+    infof(data, "We got a 421 - timeout!");
     state(data, FTP_STOP);
     return CURLE_OPERATION_TIMEDOUT;
   }
@@ -768,7 +768,7 @@
   (void) lineno;
 #else
   if(ftpc->state != newstate)
-    infof(data, "FTP %p (line %d) state change from %s to %s\n",
+    infof(data, "FTP %p (line %d) state change from %s to %s",
           (void *)ftpc, lineno, ftp_state_names[ftpc->state],
           ftp_state_names[newstate]);
 #endif
@@ -1139,7 +1139,7 @@
         /* The requested bind address is not local.  Use the address used for
          * the control connection instead and restart the port loop
          */
-        infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
+        infof(data, "bind(port=%hu) on non-local address failed: %s", port,
               Curl_strerror(error, buffer, sizeof(buffer)));
 
         sslen = sizeof(ss);
@@ -1341,7 +1341,7 @@
   if(!result) {
     ftpc->count1 = modeoff;
     state(data, FTP_PASV);
-    infof(data, "Connect data stream passively\n");
+    infof(data, "Connect data stream passively");
   }
   return result;
 }
@@ -1648,7 +1648,7 @@
       data->state.infilesize -= data->state.resume_from;
 
       if(data->state.infilesize <= 0) {
-        infof(data, "File already completely uploaded\n");
+        infof(data, "File already completely uploaded");
 
         /* no data to transfer */
         Curl_setup_transfer(data, -1, -1, FALSE, -1);
@@ -1802,7 +1802,7 @@
     return CURLE_WEIRD_SERVER_REPLY;
   }
 
-  infof(data, "Failed EPSV attempt. Disabling EPSV\n");
+  infof(data, "Failed EPSV attempt. Disabling EPSV");
   /* disable it for next transfer */
   conn->bits.ftp_use_epsv = FALSE;
   data->state.errorbuf = FALSE; /* allow error message to get
@@ -1921,7 +1921,7 @@
     if(data->set.ftp_skip_ip) {
       /* told to ignore the remotely given IP but instead use the host we used
          for the control connection */
-      infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead\n",
+      infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead",
             ip[0], ip[1], ip[2], ip[3],
             conn->host.name);
       ftpc->newhost = strdup(control_address(conn));
@@ -2044,7 +2044,7 @@
     /* the command failed */
 
     if(EPRT == fcmd) {
-      infof(data, "disabling EPRT usage\n");
+      infof(data, "disabling EPRT usage");
       conn->bits.ftp_use_eprt = FALSE;
     }
     fcmd++;
@@ -2058,7 +2058,7 @@
       result = ftp_state_use_port(data, fcmd);
   }
   else {
-    infof(data, "Connect data stream actively\n");
+    infof(data, "Connect data stream actively");
     state(data, FTP_STOP); /* end of DO phase */
     result = ftp_dophase_done(data, FALSE);
   }
@@ -2128,7 +2128,7 @@
     }
     break;
   default:
-    infof(data, "unsupported MDTM reply format\n");
+    infof(data, "unsupported MDTM reply format");
     break;
   case 550: /* "No such file or directory" */
     failf(data, "Given file does not exist");
@@ -2142,7 +2142,7 @@
       case CURL_TIMECOND_IFMODSINCE:
       default:
         if(data->info.filetime <= data->set.timevalue) {
-          infof(data, "The requested document is not new enough\n");
+          infof(data, "The requested document is not new enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
           state(data, FTP_STOP);
@@ -2151,7 +2151,7 @@
         break;
       case CURL_TIMECOND_IFUNMODSINCE:
         if(data->info.filetime > data->set.timevalue) {
-          infof(data, "The requested document is not old enough\n");
+          infof(data, "The requested document is not old enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
           state(data, FTP_STOP);
@@ -2161,7 +2161,7 @@
       } /* switch */
     }
     else {
-      infof(data, "Skipping time comparison\n");
+      infof(data, "Skipping time comparison");
     }
   }
 
@@ -2186,7 +2186,7 @@
     return CURLE_FTP_COULDNT_SET_TYPE;
   }
   if(ftpcode != 200)
-    infof(data, "Got a %03d response code instead of the assumed 200\n",
+    infof(data, "Got a %03d response code instead of the assumed 200",
           ftpcode);
 
   if(instate == FTP_TYPE)
@@ -2219,7 +2219,7 @@
     /* We always (attempt to) get the size of downloads, so it is done before
        this even when not doing resumes. */
     if(filesize == -1) {
-      infof(data, "ftp server doesn't support SIZE\n");
+      infof(data, "ftp server doesn't support SIZE");
       /* We couldn't get the size and therefore we can't know if there really
          is a part of the file left to get, although the server will just
          close the connection when we start the connection so it won't cause
@@ -2256,7 +2256,7 @@
     if(ftp->downloadsize == 0) {
       /* no data to transfer */
       Curl_setup_transfer(data, -1, -1, FALSE, -1);
-      infof(data, "File already completely downloaded\n");
+      infof(data, "File already completely downloaded");
 
       /* Set ->transfer so that we won't get any error in ftp_done()
        * because we didn't transfer the any file */
@@ -2267,7 +2267,7 @@
 
     /* Set resume file transfer offset */
     infof(data, "Instructs server to resume from offset %"
-          CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
+          CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
 
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
                            data->state.resume_from);
@@ -2413,7 +2413,7 @@
 
     if(!connected) {
       struct ftp_conn *ftpc = &conn->proto.ftpc;
-      infof(data, "Data conn was not available immediately\n");
+      infof(data, "Data conn was not available immediately");
       ftpc->wait_data_conn = TRUE;
     }
 
@@ -2506,11 +2506,11 @@
     else if((instate != FTP_LIST) && (data->state.prefer_ascii))
       size = -1; /* kludge for servers that understate ASCII mode file size */
 
-    infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n",
+    infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T,
           data->req.maxdownload);
 
     if(instate != FTP_LIST)
-      infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T "\n",
+      infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
             size);
 
     /* FTP download: */
@@ -2526,7 +2526,7 @@
 
       if(!connected) {
         struct ftp_conn *ftpc = &conn->proto.ftpc;
-        infof(data, "Data conn was not available immediately\n");
+        infof(data, "Data conn was not available immediately");
         state(data, FTP_STOP);
         ftpc->wait_data_conn = TRUE;
       }
@@ -2681,9 +2681,12 @@
     /* we have now received a full FTP server response */
     switch(ftpc->state) {
     case FTP_WAIT220:
-      if(ftpcode == 230)
-        /* 230 User logged in - already! */
-        return ftp_state_user_resp(data, ftpcode, ftpc->state);
+      if(ftpcode == 230) {
+        /* 230 User logged in - already! Take as 220 if TLS required. */
+        if(data->set.use_ssl <= CURLUSESSL_TRY ||
+           conn->bits.ftp_use_control_ssl)
+          return ftp_state_user_resp(data, ftpcode, ftpc->state);
+      }
       else if(ftpcode != 220) {
         failf(data, "Got a %03d ftp-server response when 220 was expected",
               ftpcode);
@@ -2702,9 +2705,9 @@
         Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
 
         if(Curl_sec_login(data, conn))
-          infof(data, "Logging in with password in cleartext!\n");
+          infof(data, "Logging in with password in cleartext!");
         else
-          infof(data, "Authentication successful\n");
+          infof(data, "Authentication successful");
       }
 #endif
 
@@ -2740,6 +2743,9 @@
     case FTP_AUTH:
       /* we have gotten the response to a previous AUTH command */
 
+      if(pp->cache_size)
+        return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
+
       /* RFC2228 (page 5) says:
        *
        * If the server is willing to accept the named security mechanism,
@@ -2895,7 +2901,7 @@
             }
             Curl_safefree(ftpc->entrypath);
             ftpc->entrypath = dir; /* remember this */
-            infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+            infof(data, "Entry path is '%s'", ftpc->entrypath);
             /* also save it where getinfo can access it: */
             data->state.most_recent_ftp_entrypath = ftpc->entrypath;
             state(data, FTP_SYST);
@@ -2904,18 +2910,18 @@
 
           Curl_safefree(ftpc->entrypath);
           ftpc->entrypath = dir; /* remember this */
-          infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+          infof(data, "Entry path is '%s'", ftpc->entrypath);
           /* also save it where getinfo can access it: */
           data->state.most_recent_ftp_entrypath = ftpc->entrypath;
         }
         else {
           /* couldn't get the path */
           free(dir);
-          infof(data, "Failed to figure out path\n");
+          infof(data, "Failed to figure out path");
         }
       }
       state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
     case FTP_SYST:
@@ -2962,7 +2968,7 @@
       }
 
       state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
     case FTP_NAMEFMT:
@@ -2973,7 +2979,7 @@
       }
 
       state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
     case FTP_QUOTE:
@@ -3272,7 +3278,7 @@
     }
 
     if(ftpc->prevpath)
-      infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
+      infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
   }
 
   /* free the dir tree and file parts */
@@ -3338,7 +3344,7 @@
     if(ftpc->dont_check && data->req.maxdownload > 0) {
       /* we have just sent ABOR and there is no reliable way to check if it was
        * successful or not; we have to close the connection now */
-      infof(data, "partial download completed, closing connection\n");
+      infof(data, "partial download completed, closing connection");
       connclose(conn, "Partial download with no ability to check");
       return result;
     }
@@ -3529,7 +3535,7 @@
 {
   char buf[256];
   Curl_printable_address(ai, buf, sizeof(buf));
-  infof(data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
+  infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
 }
 #endif
 
@@ -3569,7 +3575,7 @@
 
     /* Ready to do more? */
     if(connected) {
-      DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
+      DEBUGF(infof(data, "DO-MORE connected phase starts"));
     }
     else {
       if(result && (ftpc->count1 == 0)) {
@@ -3644,8 +3650,13 @@
         return result;
 
       result = ftp_multi_statemach(data, &complete);
-      /* ftpc->wait_data_conn is always false here */
-      *completep = (int)complete;
+      if(ftpc->wait_data_conn)
+        /* if we reach the end of the FTP state machine here, *complete will be
+           TRUE but so is ftpc->wait_data_conn, which says we need to wait for
+           the data connection and therefore we're not actually complete */
+        *completep = 0;
+      else
+        *completep = (int)complete;
     }
     else {
       /* download */
@@ -3692,7 +3703,7 @@
   if(!ftpc->wait_data_conn) {
     /* no waiting for the data connection so this is now complete */
     *completep = 1;
-    DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
+    DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
   }
 
   return result;
@@ -3717,7 +3728,7 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* requested no body means no transfer... */
@@ -3737,10 +3748,10 @@
 
   *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
 
-  infof(data, "ftp_perform ends with SECONDARY: %d\n", *connected);
+  infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete1\n"));
+    DEBUGF(infof(data, "DO phase is complete1"));
   }
 
   return result;
@@ -3835,7 +3846,7 @@
   /* let the writefunc callback know the transfer */
   data->set.out = data;
 
-  infof(data, "Wildcard - Parsing started\n");
+  infof(data, "Wildcard - Parsing started");
   return CURLE_OK;
 
   fail:
@@ -3902,7 +3913,7 @@
       free(ftp->pathalloc);
       ftp->pathalloc = ftp->path = tmp_path;
 
-      infof(data, "Wildcard - START of \"%s\"\n", finfo->filename);
+      infof(data, "Wildcard - START of \"%s\"", finfo->filename);
       if(data->set.chunk_bgn) {
         long userresponse;
         Curl_set_in_callback(data, true);
@@ -3911,7 +3922,7 @@
         Curl_set_in_callback(data, false);
         switch(userresponse) {
         case CURL_CHUNK_BGN_FUNC_SKIP:
-          infof(data, "Wildcard - \"%s\" skipped by user\n",
+          infof(data, "Wildcard - \"%s\" skipped by user",
                 finfo->filename);
           wildcard->state = CURLWC_SKIP;
           continue;
@@ -4234,7 +4245,7 @@
         n -= ftpc->file?strlen(ftpc->file):0;
 
       if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
-        infof(data, "Request has same path as previous transfer\n");
+        infof(data, "Request has same path as previous transfer");
         ftpc->cwddone = TRUE;
       }
     }
@@ -4280,11 +4291,11 @@
   CURLcode result = ftp_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = ftp_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete2\n"));
+    DEBUGF(infof(data, "DO phase is complete2"));
   }
   return result;
 }
diff --git a/Utilities/cmcurl/lib/hostasyn.c b/Utilities/cmcurl/lib/hostasyn.c
index b25de1d..f7d99ce 100644
--- a/Utilities/cmcurl/lib/hostasyn.c
+++ b/Utilities/cmcurl/lib/hostasyn.c
@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "curl_memory.h"
 /* The last #include file should be: */
diff --git a/Utilities/cmcurl/lib/hostcheck.c b/Utilities/cmcurl/lib/hostcheck.c
index 49dbab3..cf267a7 100644
--- a/Utilities/cmcurl/lib/hostcheck.c
+++ b/Utilities/cmcurl/lib/hostcheck.c
@@ -36,7 +36,7 @@
 
 #include "hostcheck.h"
 #include "strcase.h"
-#include "inet_pton.h"
+#include "hostip.h"
 
 #include "curl_memory.h"
 /* The last #include file should be: */
@@ -67,10 +67,6 @@
   const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
   int wildcard_enabled;
   size_t prefixlen, suffixlen;
-  struct in_addr ignored;
-#ifdef ENABLE_IPV6
-  struct sockaddr_in6 si6;
-#endif
 
   /* normalize pattern and hostname by stripping off trailing dots */
   size_t len = strlen(hostname);
@@ -86,12 +82,8 @@
       CURL_HOST_MATCH : CURL_HOST_NOMATCH;
 
   /* detect IP address as hostname and fail the match if so */
-  if(Curl_inet_pton(AF_INET, hostname, &ignored) > 0)
+  if(Curl_host_is_ipnum(hostname))
     return CURL_HOST_NOMATCH;
-#ifdef ENABLE_IPV6
-  if(Curl_inet_pton(AF_INET6, hostname, &si6.sin6_addr) > 0)
-    return CURL_HOST_NOMATCH;
-#endif
 
   /* We require at least 2 dots in pattern to avoid too wide wildcard
      match. */
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index e0e3cfc..117caa2 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -56,13 +56,13 @@
 #include "hash.h"
 #include "rand.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "inet_ntop.h"
 #include "inet_pton.h"
 #include "multiif.h"
 #include "doh.h"
 #include "warnless.h"
+#include "strcase.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -289,7 +289,7 @@
     user.cache_timeout = data->set.dns_cache_timeout;
 
     if(hostcache_timestamp_remove(&user, dns)) {
-      infof(data, "Hostname in DNS cache was stale, zapped\n");
+      infof(data, "Hostname in DNS cache was stale, zapped");
       dns = NULL; /* the memory deallocation is being handled by the hash */
       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
     }
@@ -460,6 +460,127 @@
   return dns;
 }
 
+#ifdef ENABLE_IPV6
+/* return a static IPv6 resolve for 'localhost' */
+static struct Curl_addrinfo *get_localhost6(int port)
+{
+  struct Curl_addrinfo *ca;
+  const size_t ss_size = sizeof(struct sockaddr_in6);
+  const size_t hostlen = strlen("localhost");
+  struct sockaddr_in6 sa6;
+  unsigned char ipv6[16];
+  unsigned short port16 = (unsigned short)(port & 0xffff);
+  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  if(!ca)
+    return NULL;
+
+  sa6.sin6_family = AF_INET6;
+  sa6.sin6_port = htons(port16);
+  sa6.sin6_flowinfo = 0;
+  sa6.sin6_scope_id = 0;
+  if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
+    return NULL;
+  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
+
+  ca->ai_flags     = 0;
+  ca->ai_family    = AF_INET6;
+  ca->ai_socktype  = SOCK_STREAM;
+  ca->ai_protocol  = IPPROTO_TCP;
+  ca->ai_addrlen   = (curl_socklen_t)ss_size;
+  ca->ai_next      = NULL;
+  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+  memcpy(ca->ai_addr, &sa6, ss_size);
+  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
+  strcpy(ca->ai_canonname, "localhost");
+  return ca;
+}
+#else
+#define get_localhost6(x) NULL
+#endif
+
+/* return a static IPv4 resolve for 'localhost' */
+static struct Curl_addrinfo *get_localhost(int port)
+{
+  struct Curl_addrinfo *ca;
+  const size_t ss_size = sizeof(struct sockaddr_in);
+  const size_t hostlen = strlen("localhost");
+  struct sockaddr_in sa;
+  unsigned int ipv4;
+  unsigned short port16 = (unsigned short)(port & 0xffff);
+  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  if(!ca)
+    return NULL;
+
+  /* memset to clear the sa.sin_zero field */
+  memset(&sa, 0, sizeof(sa));
+  sa.sin_family = AF_INET;
+  sa.sin_port = htons(port16);
+  if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
+    return NULL;
+  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
+
+  ca->ai_flags     = 0;
+  ca->ai_family    = AF_INET;
+  ca->ai_socktype  = SOCK_STREAM;
+  ca->ai_protocol  = IPPROTO_TCP;
+  ca->ai_addrlen   = (curl_socklen_t)ss_size;
+  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+  memcpy(ca->ai_addr, &sa, ss_size);
+  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
+  strcpy(ca->ai_canonname, "localhost");
+  ca->ai_next = get_localhost6(port);
+  return ca;
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Curl_ipv6works() returns TRUE if IPv6 seems to work.
+ */
+bool Curl_ipv6works(struct Curl_easy *data)
+{
+  if(data) {
+    /* the nature of most system is that IPv6 status doesn't come and go
+       during a program's lifetime so we only probe the first time and then we
+       have the info kept for fast re-use */
+    DEBUGASSERT(data);
+    DEBUGASSERT(data->multi);
+    return data->multi->ipv6_works;
+  }
+  else {
+    int ipv6_works = -1;
+    /* probe to see if we have a working IPv6 stack */
+    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+    if(s == CURL_SOCKET_BAD)
+      /* an IPv6 address was requested but we can't get/use one */
+      ipv6_works = 0;
+    else {
+      ipv6_works = 1;
+      sclose(s);
+    }
+    return (ipv6_works>0)?TRUE:FALSE;
+  }
+}
+#endif /* ENABLE_IPV6 */
+
+/*
+ * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
+ * (or IPv6 if supported) address.
+ */
+bool Curl_host_is_ipnum(const char *hostname)
+{
+  struct in_addr in;
+#ifdef ENABLE_IPV6
+  struct in6_addr in6;
+#endif
+  if(Curl_inet_pton(AF_INET, hostname, &in) > 0
+#ifdef ENABLE_IPV6
+     || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
+#endif
+    )
+    return TRUE;
+  return FALSE;
+}
+
 /*
  * Curl_resolv() is the main name resolve function within libcurl. It resolves
  * a name and returns a pointer to the entry in the 'entry' argument (if one
@@ -487,7 +608,6 @@
   CURLcode result;
   enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
   struct connectdata *conn = data->conn;
-
   *entry = NULL;
   conn->bits.doh = FALSE; /* default is not */
 
@@ -497,7 +617,7 @@
   dns = fetch_addr(data, hostname, port);
 
   if(dns) {
-    infof(data, "Hostname %s was found in DNS cache\n", hostname);
+    infof(data, "Hostname %s was found in DNS cache", hostname);
     dns->inuse++; /* we use it! */
     rc = CURLRESOLV_RESOLVED;
   }
@@ -534,16 +654,20 @@
     }
 
 #if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
-    /*
-     * The automagic conversion from IPv4 literals to IPv6 literals only works
-     * if the SCDynamicStoreCopyProxies system function gets called first. As
-     * Curl currently doesn't support system-wide HTTP proxies, we therefore
-     * don't use any value this function might return.
-     *
-     * This function is only available on a macOS and is not needed for
-     * IPv4-only builds, hence the conditions above.
-     */
-    SCDynamicStoreCopyProxies(NULL);
+    {
+      /*
+       * The automagic conversion from IPv4 literals to IPv6 literals only
+       * works if the SCDynamicStoreCopyProxies system function gets called
+       * first. As Curl currently doesn't support system-wide HTTP proxies, we
+       * therefore don't use any value this function might return.
+       *
+       * This function is only available on a macOS and is not needed for
+       * IPv4-only builds, hence the conditions above.
+       */
+      CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+      if(dict)
+        CFRelease(dict);
+    }
 #endif
 
 #ifndef USE_RESOLVE_ON_IPS
@@ -579,15 +703,18 @@
 #endif /* !USE_RESOLVE_ON_IPS */
 
     if(!addr) {
-      /* Check what IP specifics the app has requested and if we can provide
-       * it. If not, bail out. */
-      if(!Curl_ipvalid(data, conn))
+      if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
         return CURLRESOLV_ERROR;
 
-      if(allowDOH && data->set.doh && !ipnum) {
+      if(strcasecompare(hostname, "localhost"))
+        addr = get_localhost(port);
+      else if(allowDOH && data->set.doh && !ipnum)
         addr = Curl_doh(data, hostname, port, &respwait);
-      }
       else {
+        /* Check what IP specifics the app has requested and if we can provide
+         * it. If not, bail out. */
+        if(!Curl_ipvalid(data, conn))
+          return CURLRESOLV_ERROR;
         /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
            non-zero value indicating that we need to wait for the response to
            the resolve call */
@@ -757,7 +884,7 @@
 #else
 #ifndef CURLRES_ASYNCH
   if(timeoutms)
-    infof(data, "timeout on name lookup is not supported\n");
+    infof(data, "timeout on name lookup is not supported");
 #else
   (void)timeoutms; /* timeoutms not used with an async resolver */
 #endif
@@ -895,7 +1022,7 @@
       size_t entry_len;
 
       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
-        infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
+        infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'",
               hostp->data);
         continue;
       }
@@ -984,7 +1111,7 @@
 
 #ifndef ENABLE_IPV6
         if(strchr(address, ':')) {
-          infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
+          infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
                 address);
           continue;
         }
@@ -992,7 +1119,7 @@
 
         ai = Curl_str2addr(address, port);
         if(!ai) {
-          infof(data, "Resolve address '%s' found illegal!\n", address);
+          infof(data, "Resolve address '%s' found illegal!", address);
           goto err;
         }
 
@@ -1011,10 +1138,10 @@
       error = false;
    err:
       if(error) {
-        infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
+        failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!",
               hostp->data);
         Curl_freeaddrinfo(head);
-        continue;
+        return CURLE_SETOPT_OPTION_SYNTAX;
       }
 
       /* Create an entry id, based upon the hostname and port */
@@ -1028,7 +1155,7 @@
       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
 
       if(dns) {
-        infof(data, "RESOLVE %s:%d is - old addresses discarded!\n",
+        infof(data, "RESOLVE %s:%d is - old addresses discarded!",
                 hostname, port);
         /* delete old entry, there are two reasons for this
          1. old entry may have different addresses.
@@ -1061,12 +1188,12 @@
         Curl_freeaddrinfo(head);
         return CURLE_OUT_OF_MEMORY;
       }
-      infof(data, "Added %s:%d:%s to DNS cache%s\n",
+      infof(data, "Added %s:%d:%s to DNS cache%s",
             hostname, port, addresses, permanent ? "" : " (non-permanent)");
 
       /* Wildcard hostname */
       if(hostname[0] == '*' && hostname[1] == '\0') {
-        infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n",
+        infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks",
               hostname, port);
         data->state.wildcard_resolve = true;
       }
@@ -1094,7 +1221,7 @@
 {
 #ifdef CURLRES_ASYNCH
   if(data->conn->bits.doh)
-    /* nothing to wait for during DOH resolve, those handles have their own
+    /* nothing to wait for during DoH resolve, those handles have their own
        sockets */
     return GETSOCK_BLANK;
   return Curl_resolver_getsock(data, socks);
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index d178976..67a688a 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -71,6 +71,8 @@
   long inuse;
 };
 
+bool Curl_host_is_ipnum(const char *hostname);
+
 /*
  * Curl_resolv() returns an entry with the info for the specified host
  * and port.
@@ -95,7 +97,7 @@
                                    struct Curl_dns_entry **dnsentry,
                                    timediff_t timeoutms);
 
-#ifdef CURLRES_IPV6
+#ifdef ENABLE_IPV6
 /*
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  */
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c
index d0754af..1fd7910 100644
--- a/Utilities/cmcurl/lib/hostip4.c
+++ b/Utilities/cmcurl/lib/hostip4.c
@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -104,7 +103,7 @@
 
   ai = Curl_ipv4_resolve_r(hostname, port);
   if(!ai)
-    infof(data, "Curl_ipv4_resolve_r failed for %s\n", hostname);
+    infof(data, "Curl_ipv4_resolve_r failed for %s", hostname);
 
   return ai;
 }
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c
index 9791d86..c2d5f08 100644
--- a/Utilities/cmcurl/lib/hostip6.c
+++ b/Utilities/cmcurl/lib/hostip6.c
@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "inet_pton.h"
 #include "connect.h"
@@ -60,34 +59,6 @@
 #include "memdebug.h"
 
 /*
- * Curl_ipv6works() returns TRUE if IPv6 seems to work.
- */
-bool Curl_ipv6works(struct Curl_easy *data)
-{
-  if(data) {
-    /* the nature of most system is that IPv6 status doesn't come and go
-       during a program's lifetime so we only probe the first time and then we
-       have the info kept for fast re-use */
-    DEBUGASSERT(data);
-    DEBUGASSERT(data->multi);
-    return data->multi->ipv6_works;
-  }
-  else {
-    int ipv6_works = -1;
-    /* probe to see if we have a working IPv6 stack */
-    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
-    if(s == CURL_SOCKET_BAD)
-      /* an IPv6 address was requested but we can't get/use one */
-      ipv6_works = 0;
-    else {
-      ipv6_works = 1;
-      sclose(s);
-    }
-    return (ipv6_works>0)?TRUE:FALSE;
-  }
-}
-
-/*
  * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
  * been set and returns TRUE if they are OK.
  */
@@ -172,7 +143,7 @@
 
   error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
   if(error) {
-    infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
+    infof(data, "getaddrinfo(3) failed for %s:%d", hostname, port);
     return NULL;
   }
 
diff --git a/Utilities/cmcurl/lib/hostsyn.c b/Utilities/cmcurl/lib/hostsyn.c
index 550b43a..c00c274 100644
--- a/Utilities/cmcurl/lib/hostsyn.c
+++ b/Utilities/cmcurl/lib/hostsyn.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "curl_memory.h"
 /* The last #include file should be: */
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c
index ef166f1..052dc11 100644
--- a/Utilities/cmcurl/lib/hsts.c
+++ b/Utilities/cmcurl/lib/hsts.c
@@ -49,6 +49,7 @@
 #define MAX_HSTS_HOSTLENSTR "256"
 #define MAX_HSTS_DATELEN 64
 #define MAX_HSTS_DATELENSTR "64"
+#define UNLIMITED "unlimited"
 
 #ifdef DEBUGBUILD
 /* to play well with debug builds, we can *set* a fixed time this will
@@ -138,6 +139,11 @@
   struct stsentry *sts;
   time_t now = time(NULL);
 
+  if(Curl_host_is_ipnum(hostname))
+    /* "explicit IP address identification of all forms is excluded."
+       / RFC 6797 */
+    return CURLE_OK;
+
   do {
     while(*p && ISSPACE(*p))
       p++;
@@ -278,13 +284,17 @@
   e.namelen = strlen(sts->host);
   e.includeSubDomains = sts->includeSubDomains;
 
-  result = Curl_gmtime((time_t)sts->expires, &stamp);
-  if(result)
-    return result;
+  if(sts->expires != TIME_T_MAX) {
+    result = Curl_gmtime((time_t)sts->expires, &stamp);
+    if(result)
+      return result;
 
-  msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
-            stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
-            stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+    msnprintf(e.expire, sizeof(e.expire), "%d%02d%02d %02d:%02d:%02d",
+              stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+              stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+  }
+  else
+    strcpy(e.expire, UNLIMITED);
 
   sc = data->set.hsts_write(data, &e, i,
                             data->set.hsts_write_userp);
@@ -298,14 +308,18 @@
 static CURLcode hsts_out(struct stsentry *sts, FILE *fp)
 {
   struct tm stamp;
-  CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp);
-  if(result)
-    return result;
-
-  fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
-          sts->includeSubDomains ? ".": "", sts->host,
-          stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
-          stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+  if(sts->expires != TIME_T_MAX) {
+    CURLcode result = Curl_gmtime((time_t)sts->expires, &stamp);
+    if(result)
+      return result;
+    fprintf(fp, "%s%s \"%d%02d%02d %02d:%02d:%02d\"\n",
+            sts->includeSubDomains ? ".": "", sts->host,
+            stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+            stamp.tm_hour, stamp.tm_min, stamp.tm_sec);
+  }
+  else
+    fprintf(fp, "%s%s \"%s\"\n",
+            sts->includeSubDomains ? ".": "", sts->host, UNLIMITED);
   return CURLE_OK;
 }
 
@@ -398,7 +412,8 @@
               "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"",
               host, date);
   if(2 == rc) {
-    time_t expires = Curl_getdate_capped(date);
+    time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
+      TIME_T_MAX;
     CURLcode result;
     char *p = host;
     bool subdomain = FALSE;
@@ -451,7 +466,7 @@
           return result;
       }
       else if(sc == CURLSTS_FAIL)
-        return CURLE_BAD_FUNCTION_ARGUMENT;
+        return CURLE_ABORTED_BY_CALLBACK;
     } while(sc == CURLSTS_OK);
   }
   return CURLE_OK;
@@ -521,7 +536,9 @@
  */
 CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
 {
-  return hsts_pull(data, h);
+  if(h)
+    return hsts_pull(data, h);
+  return CURLE_OK;
 }
 
 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h
index baa5828..653c053 100644
--- a/Utilities/cmcurl/lib/hsts.h
+++ b/Utilities/cmcurl/lib/hsts.h
@@ -59,7 +59,7 @@
                           struct hsts *h);
 #else
 #define Curl_hsts_cleanup(x)
-#define Curl_hsts_loadcb(x,y)
+#define Curl_hsts_loadcb(x,y) CURLE_OK
 #define Curl_hsts_save(x,y,z)
 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */
 #endif /* HEADER_CURL_HSTS_H */
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 628dd73..648583c 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -504,7 +504,7 @@
         /* rewind data when completely done sending! */
         if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
           conn->bits.rewindaftersend = TRUE;
-          infof(data, "Rewind stream after send\n");
+          infof(data, "Rewind stream after send");
         }
 
         return CURLE_OK;
@@ -515,7 +515,7 @@
         return CURLE_OK;
 
       infof(data, "NTLM send, close instead of sending %"
-            CURL_FORMAT_CURL_OFF_T " bytes\n",
+            CURL_FORMAT_CURL_OFF_T " bytes",
             (curl_off_t)(expectsend - bytessent));
     }
 #endif
@@ -532,7 +532,7 @@
         /* rewind data when completely done sending! */
         if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
           conn->bits.rewindaftersend = TRUE;
-          infof(data, "Rewind stream after send\n");
+          infof(data, "Rewind stream after send");
         }
 
         return CURLE_OK;
@@ -543,7 +543,7 @@
         return CURLE_OK;
 
       infof(data, "NEGOTIATE send, close instead of sending %"
-        CURL_FORMAT_CURL_OFF_T " bytes\n",
+        CURL_FORMAT_CURL_OFF_T " bytes",
         (curl_off_t)(expectsend - bytessent));
     }
 #endif
@@ -756,14 +756,14 @@
 
   if(auth) {
 #ifndef CURL_DISABLE_PROXY
-    infof(data, "%s auth using %s with user '%s'\n",
+    infof(data, "%s auth using %s with user '%s'",
           proxy ? "Proxy" : "Server", auth,
           proxy ? (data->state.aptr.proxyuser ?
                    data->state.aptr.proxyuser : "") :
           (data->state.aptr.user ?
            data->state.aptr.user : ""));
 #else
-    infof(data, "Server auth using %s with user '%s'\n",
+    infof(data, "Server auth using %s with user '%s'",
           auth, data->state.aptr.user ?
           data->state.aptr.user : "");
 #endif
@@ -850,7 +850,9 @@
   /* To prevent the user+password to get sent to other than the original
      host due to a location-follow, we do some weirdo checks here */
   if(!data->state.this_is_a_follow ||
+#ifndef CURL_DISABLE_NETRC
      conn->bits.netrc ||
+#endif
      !data->state.first_host ||
      data->set.allow_auth_to_other_hosts ||
      strcasecompare(data->state.first_host, conn->host.name)) {
@@ -995,14 +997,14 @@
 
                 result = Curl_input_ntlm_wb(data, conn, proxy, auth);
                 if(result) {
-                  infof(data, "Authentication problem. Ignoring this.\n");
+                  infof(data, "Authentication problem. Ignoring this.");
                   data->state.authproblem = TRUE;
                 }
               }
 #endif
             }
             else {
-              infof(data, "Authentication problem. Ignoring this.\n");
+              infof(data, "Authentication problem. Ignoring this.");
               data->state.authproblem = TRUE;
             }
           }
@@ -1013,7 +1015,7 @@
 #ifndef CURL_DISABLE_CRYPTO_AUTH
         if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
           if((authp->avail & CURLAUTH_DIGEST) != 0)
-            infof(data, "Ignoring duplicate digest auth header.\n");
+            infof(data, "Ignoring duplicate digest auth header.");
           else if(Curl_auth_is_digest_supported()) {
             CURLcode result;
 
@@ -1026,7 +1028,7 @@
              * Digest */
             result = Curl_input_digest(data, proxy, auth);
             if(result) {
-              infof(data, "Authentication problem. Ignoring this.\n");
+              infof(data, "Authentication problem. Ignoring this.");
               data->state.authproblem = TRUE;
             }
           }
@@ -1042,7 +1044,7 @@
                  anyway, which basically means our name+password isn't
                  valid. */
               authp->avail = CURLAUTH_NONE;
-              infof(data, "Authentication problem. Ignoring this.\n");
+              infof(data, "Authentication problem. Ignoring this.");
               data->state.authproblem = TRUE;
             }
           }
@@ -1055,7 +1057,7 @@
                 /* We asked for Bearer authentication but got a 40X back
                   anyway, which basically means our token isn't valid. */
                 authp->avail = CURLAUTH_NONE;
-                infof(data, "Authentication problem. Ignoring this.\n");
+                infof(data, "Authentication problem. Ignoring this.");
                 data->state.authproblem = TRUE;
               }
             }
@@ -1177,6 +1179,7 @@
   data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
 
   if(data->set.max_send_speed &&
+     (data->set.max_send_speed < (curl_off_t)fullsize) &&
      (data->set.max_send_speed < http->postsize))
     /* speed limit */
     fullsize = (size_t)data->set.max_send_speed;
@@ -1537,38 +1540,35 @@
 #ifndef CURL_DISABLE_PROXY
 static CURLcode add_haproxy_protocol_header(struct Curl_easy *data)
 {
-  char proxy_header[128];
   struct dynbuf req;
   CURLcode result;
-  char tcp_version[5];
+  const char *tcp_version;
   DEBUGASSERT(data->conn);
-
-  /* Emit the correct prefix for IPv6 */
-  if(data->conn->bits.ipv6) {
-    strcpy(tcp_version, "TCP6");
-  }
-  else {
-    strcpy(tcp_version, "TCP4");
-  }
-
-  msnprintf(proxy_header,
-            sizeof(proxy_header),
-            "PROXY %s %s %s %i %i\r\n",
-            tcp_version,
-            data->info.conn_local_ip,
-            data->info.conn_primary_ip,
-            data->info.conn_local_port,
-            data->info.conn_primary_port);
-
   Curl_dyn_init(&req, DYN_HAXPROXY);
 
-  result = Curl_dyn_add(&req, proxy_header);
-  if(result)
-    return result;
+#ifdef USE_UNIX_SOCKETS
+  if(data->conn->unix_domain_socket)
+    /* the buffer is large enough to hold this! */
+    result = Curl_dyn_add(&req, "PROXY UNKNOWN\r\n");
+  else {
+#endif
+  /* Emit the correct prefix for IPv6 */
+  tcp_version = data->conn->bits.ipv6 ? "TCP6" : "TCP4";
 
-  result = Curl_buffer_send(&req, data, &data->info.request_size,
-                            0, FIRSTSOCKET);
+  result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
+                         tcp_version,
+                         data->info.conn_local_ip,
+                         data->info.conn_primary_ip,
+                         data->info.conn_local_port,
+                         data->info.conn_primary_port);
 
+#ifdef USE_UNIX_SOCKETS
+  }
+#endif
+
+  if(!result)
+    result = Curl_buffer_send(&req, data, &data->info.request_size,
+                              0, FIRSTSOCKET);
   return result;
 }
 #endif
@@ -1588,7 +1588,7 @@
 #endif
 
   /* perform SSL initialization for this socket */
-  result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET, done);
+  result = Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, done);
   if(result)
     connclose(conn, "Failed HTTPS connection");
 
@@ -1669,8 +1669,8 @@
  * - if any server previously contacted to handle this request only supports
  * 1.0.
  */
-static bool use_http_1_1plus(const struct Curl_easy *data,
-                             const struct connectdata *conn)
+bool Curl_use_http_1_1plus(const struct Curl_easy *data,
+                           const struct connectdata *conn)
 {
   if((data->state.httpversion == 10) || (conn->httpversion == 10))
     return FALSE;
@@ -1696,7 +1696,7 @@
     return "2";
 #endif
 
-  if(use_http_1_1plus(data, conn))
+  if(Curl_use_http_1_1plus(data, conn))
     return "1.1";
 
   return "1.0";
@@ -1711,7 +1711,7 @@
   CURLcode result = CURLE_OK;
   data->state.expect100header = FALSE; /* default to false unless it is set
                                           to TRUE below */
-  if(!data->state.disableexpect && use_http_1_1plus(data, conn) &&
+  if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
      (conn->httpversion < 20)) {
     /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
        Expect: 100-continue to the headers which actually speeds up post
@@ -2348,7 +2348,7 @@
       if(conn->bits.authneg)
         /* don't enable chunked during auth neg */
         ;
-      else if(use_http_1_1plus(data, conn)) {
+      else if(Curl_use_http_1_1plus(data, conn)) {
         if(conn->httpversion < 20)
           /* HTTP, upload, unknown file size and not HTTP 1.0 */
           data->req.upload_chunky = TRUE;
@@ -2711,14 +2711,16 @@
     int count = 0;
 
     if(data->cookies && data->state.cookie_engine) {
+      const char *host = data->state.aptr.cookiehost ?
+        data->state.aptr.cookiehost : conn->host.name;
+      const bool secure_context =
+        conn->handler->protocol&CURLPROTO_HTTPS ||
+        strcasecompare("localhost", host) ||
+        !strcmp(host, "127.0.0.1") ||
+        !strcmp(host, "[::1]") ? TRUE : FALSE;
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-      co = Curl_cookie_getlist(data->cookies,
-                               data->state.aptr.cookiehost?
-                               data->state.aptr.cookiehost:
-                               conn->host.name,
-                               data->state.up.path,
-                               (conn->handler->protocol&CURLPROTO_HTTPS)?
-                               TRUE:FALSE);
+      co = Curl_cookie_getlist(data->cookies, host, data->state.up.path,
+                               secure_context);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
     }
     if(co) {
@@ -2901,6 +2903,20 @@
 {
   struct SingleRequest *k = &data->req;
   DEBUGASSERT(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP));
+  if(data->req.ignore_cl) {
+    k->size = k->maxdownload = -1;
+  }
+  else if(k->size != -1) {
+    /* We wait until after all headers have been received to set this so that
+       we know for sure Content-Length is valid. */
+    if(data->set.max_filesize &&
+       k->size > data->set.max_filesize) {
+      failf(data, "Maximum file size exceeded");
+      return CURLE_FILESIZE_EXCEEDED;
+    }
+    Curl_pgrsSetDownloadSize(data, k->size);
+  }
+
   if(data->req.newurl) {
     if(conn->bits.close) {
       /* Abort after the headers if "follow Location" is set
@@ -2912,7 +2928,7 @@
     /* We have a new url to load, but since we want to be able to re-use this
        connection properly, we read the full response in "ignore more" */
     k->ignorebody = TRUE;
-    infof(data, "Ignoring the response-body\n");
+    infof(data, "Ignoring the response-body");
   }
   if(data->state.resume_from && !k->content_range &&
      (data->state.httpreq == HTTPREQ_GET) &&
@@ -2947,7 +2963,7 @@
       /* We're simulating a http 304 from server so we return
          what should have been returned from the server */
       data->info.httpcode = 304;
-      infof(data, "Simulate a HTTP 304 response!\n");
+      infof(data, "Simulate a HTTP 304 response!");
       /* we abort the transfer before it is completed == we ruin the
          re-use ability. Close the connection */
       connclose(conn, "Simulated 304 handling");
@@ -2958,6 +2974,39 @@
   return CURLE_OK;
 }
 
+#ifdef HAVE_LIBZ
+CURLcode Curl_transferencode(struct Curl_easy *data)
+{
+  if(!Curl_checkheaders(data, "TE") &&
+     data->set.http_transfer_encoding) {
+    /* When we are to insert a TE: header in the request, we must also insert
+       TE in a Connection: header, so we need to merge the custom provided
+       Connection: header and prevent the original to get sent. Note that if
+       the user has inserted his/her own TE: header we don't do this magic
+       but then assume that the user will handle it all! */
+    char *cptr = Curl_checkheaders(data, "Connection");
+#define TE_HEADER "TE: gzip\r\n"
+
+    Curl_safefree(data->state.aptr.te);
+
+    if(cptr) {
+      cptr = Curl_copy_header_value(cptr);
+      if(!cptr)
+        return CURLE_OUT_OF_MEMORY;
+    }
+
+    /* Create the (updated) Connection: header */
+    data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
+                                cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
+
+    free(cptr);
+    if(!data->state.aptr.te)
+      return CURLE_OUT_OF_MEMORY;
+  }
+  return CURLE_OK;
+}
+#endif
+
 #ifndef USE_HYPER
 /*
  * Curl_http() gets called from the generic multi_do() function when a HTTP
@@ -3004,11 +3053,11 @@
           if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
             /* We don't support HTTP/2 proxies yet. Also it's debatable
                whether or not this setting should apply to HTTP/2 proxies. */
-            infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n");
+            infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
             break;
           }
 #endif
-          DEBUGF(infof(data, "HTTP/2 over clean TCP\n"));
+          DEBUGF(infof(data, "HTTP/2 over clean TCP"));
           conn->httpversion = 20;
 
           result = Curl_http2_switched(data, NULL, 0);
@@ -3074,33 +3123,9 @@
 
 #ifdef HAVE_LIBZ
   /* we only consider transfer-encoding magic if libz support is built-in */
-
-  if(!Curl_checkheaders(data, "TE") &&
-     data->set.http_transfer_encoding) {
-    /* When we are to insert a TE: header in the request, we must also insert
-       TE in a Connection: header, so we need to merge the custom provided
-       Connection: header and prevent the original to get sent. Note that if
-       the user has inserted his/her own TE: header we don't do this magic
-       but then assume that the user will handle it all! */
-    char *cptr = Curl_checkheaders(data, "Connection");
-#define TE_HEADER "TE: gzip\r\n"
-
-    Curl_safefree(data->state.aptr.te);
-
-    if(cptr) {
-      cptr = Curl_copy_header_value(cptr);
-      if(!cptr)
-        return CURLE_OUT_OF_MEMORY;
-    }
-
-    /* Create the (updated) Connection: header */
-    data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
-                                cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
-
-    free(cptr);
-    if(!data->state.aptr.te)
-      return CURLE_OUT_OF_MEMORY;
-  }
+  result = Curl_transferencode(data);
+  if(result)
+    return result;
 #endif
 
   result = Curl_http_body(data, conn, httpreq, &te);
@@ -3253,7 +3278,7 @@
       /* already sent the entire request body, mark the "upload" as
          complete */
       infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
-            " out of %" CURL_FORMAT_CURL_OFF_T " bytes\n",
+            " out of %" CURL_FORMAT_CURL_OFF_T " bytes",
             data->req.writebytecount, http->postsize);
       data->req.upload_done = TRUE;
       data->req.keepon &= ~KEEP_SEND; /* we're done writing */
@@ -3392,17 +3417,8 @@
                                     NULL, 10, &contentlength);
 
     if(offt == CURL_OFFT_OK) {
-      if(data->set.max_filesize &&
-         contentlength > data->set.max_filesize) {
-        failf(data, "Maximum file size exceeded");
-        return CURLE_FILESIZE_EXCEEDED;
-      }
       k->size = contentlength;
       k->maxdownload = k->size;
-      /* we set the progress download size already at this point
-         just to make it easier for apps/callbacks to extract this
-         info as soon as possible */
-      Curl_pgrsSetDownloadSize(data, k->size);
     }
     else if(offt == CURL_OFFT_FLOW) {
       /* out of range */
@@ -3411,7 +3427,7 @@
         return CURLE_FILESIZE_EXCEEDED;
       }
       streamclose(conn, "overflow content-length");
-      infof(data, "Overflow Content-Length: value!\n");
+      infof(data, "Overflow Content-Length: value!");
     }
     else {
       /* negative or just rubbish - bad HTTP */
@@ -3443,7 +3459,7 @@
      * Default action for 1.0 is to close.
      */
     connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */
-    infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
+    infof(data, "HTTP/1.0 proxy connection set to keep alive!");
   }
   else if((conn->httpversion == 11) &&
           conn->bits.httpproxy &&
@@ -3453,7 +3469,7 @@
      * close down after this transfer.
      */
     connclose(conn, "Proxy-Connection: asked to close after done");
-    infof(data, "HTTP/1.1 proxy connection set close!\n");
+    infof(data, "HTTP/1.1 proxy connection set close!");
   }
 #endif
   else if((conn->httpversion == 10) &&
@@ -3465,7 +3481,7 @@
      *
      * [RFC2068, section 19.7.1] */
     connkeep(conn, "Connection keep-alive");
-    infof(data, "HTTP/1.0 connection set to keep alive!\n");
+    infof(data, "HTTP/1.0 connection set to keep alive!");
   }
   else if(Curl_compareheader(headp, "Connection:", "close")) {
     /*
@@ -3493,6 +3509,12 @@
                                          TRUE);
     if(result)
       return result;
+    if(!k->chunk) {
+      /* if this isn't chunked, only close can signal the end of this transfer
+         as Content-Length is said not to be trusted for transfer-encoding! */
+      connclose(conn, "HTTP/1.1 transfer-encoding without chunks");
+      k->ignore_cl = TRUE;
+    }
   }
   else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) &&
           data->set.str[STRING_ENCODING]) {
@@ -3555,18 +3577,21 @@
 #if !defined(CURL_DISABLE_COOKIES)
   else if(data->cookies && data->state.cookie_engine &&
           checkprefix("Set-Cookie:", headp)) {
+    /* If there is a custom-set Host: name, use it here, or else use real peer
+       host name. */
+    const char *host = data->state.aptr.cookiehost?
+      data->state.aptr.cookiehost:conn->host.name;
+    const bool secure_context =
+      conn->handler->protocol&CURLPROTO_HTTPS ||
+      strcasecompare("localhost", host) ||
+      !strcmp(host, "127.0.0.1") ||
+      !strcmp(host, "[::1]") ? TRUE : FALSE;
+
     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
                     CURL_LOCK_ACCESS_SINGLE);
-    Curl_cookie_add(data,
-                    data->cookies, TRUE, FALSE,
-                    headp + strlen("Set-Cookie:"),
-                    /* If there is a custom-set Host: name, use it
-                       here, or else use real peer host name. */
-                    data->state.aptr.cookiehost?
-                    data->state.aptr.cookiehost:conn->host.name,
-                    data->state.up.path,
-                    (conn->handler->protocol&CURLPROTO_HTTPS)?
-                    TRUE:FALSE);
+    Curl_cookie_add(data, data->cookies, TRUE, FALSE,
+                    headp + strlen("Set-Cookie:"), host,
+                    data->state.up.path, secure_context);
     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
   }
 #endif
@@ -3646,10 +3671,10 @@
       Curl_hsts_parse(data->hsts, data->state.up.hostname,
                       headp + strlen("Strict-Transport-Security:"));
     if(check)
-      infof(data, "Illegal STS header skipped\n");
+      infof(data, "Illegal STS header skipped");
 #ifdef DEBUGBUILD
     else
-      infof(data, "Parsed STS header fine (%zu entries)\n",
+      infof(data, "Parsed STS header fine (%zu entries)",
             data->hsts->list.size);
 #endif
   }
@@ -3719,12 +3744,12 @@
     /* Default action for HTTP/1.0 must be to close, unless
        we get one of those fancy headers that tell us the
        server keeps it open for us! */
-    infof(data, "HTTP 1.0, assume close after body\n");
+    infof(data, "HTTP 1.0, assume close after body");
     connclose(conn, "HTTP/1.0 close after body");
   }
   else if(conn->httpversion == 20 ||
           (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) {
-    DEBUGF(infof(data, "HTTP/2 found, allow multiplexing\n"));
+    DEBUGF(infof(data, "HTTP/2 found, allow multiplexing"));
     /* HTTP/2 cannot avoid multiplexing since it is a core functionality
        of the protocol */
     conn->bundle->multiuse = BUNDLE_MULTIPLEX;
@@ -3733,7 +3758,7 @@
           !conn->bits.close) {
     /* If HTTP version is >= 1.1 and connection is persistent */
     DEBUGF(infof(data,
-                 "HTTP 1.1 or later with persistent connection\n"));
+                 "HTTP 1.1 or later with persistent connection"));
   }
 
   k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200;
@@ -3911,7 +3936,7 @@
           /* Switching Protocols */
           if(k->upgr101 == UPGR101_REQUESTED) {
             /* Switching to HTTP/2 */
-            infof(data, "Received 101\n");
+            infof(data, "Received 101");
             k->upgr101 = UPGR101_RECEIVED;
 
             /* we'll get more headers (HTTP/2 response) */
@@ -3951,7 +3976,7 @@
              assume that the server will close the connection to
              signal the end of the document. */
           infof(data, "no chunk, no close, no size. Assume close to "
-                "signal end\n");
+                "signal end");
           streamclose(conn, "HTTP: No end-of-message indicator");
         }
       }
@@ -3964,7 +3989,7 @@
            (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
           ((data->req.httpcode == 407) &&
            (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
-        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
+        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
         data->state.authproblem = TRUE;
       }
 #endif
@@ -3974,7 +3999,7 @@
           (conn->http_negotiate_state == GSS_AUTHRECV)) ||
          ((data->req.httpcode == 407) &&
           (conn->proxy_negotiate_state == GSS_AUTHRECV)))) {
-        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
+        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
         data->state.authproblem = TRUE;
       }
       if((conn->http_negotiate_state == GSS_AUTHDONE) &&
@@ -4054,21 +4079,21 @@
               if((k->httpcode == 417) && data->state.expect100header) {
                 /* 417 Expectation Failed - try again without the Expect
                    header */
-                infof(data, "Got 417 while waiting for a 100\n");
+                infof(data, "Got 417 while waiting for a 100");
                 data->state.disableexpect = TRUE;
                 DEBUGASSERT(!data->req.newurl);
                 data->req.newurl = strdup(data->state.url);
                 Curl_done_sending(data, k);
               }
               else if(data->set.http_keep_sending_on_error) {
-                infof(data, "HTTP error before end of send, keep sending\n");
+                infof(data, "HTTP error before end of send, keep sending");
                 if(k->exp100 > EXP100_SEND_DATA) {
                   k->exp100 = EXP100_SEND_DATA;
                   k->keepon |= KEEP_SEND;
                 }
               }
               else {
-                infof(data, "HTTP error before end of send, stop sending\n");
+                infof(data, "HTTP error before end of send, stop sending");
                 streamclose(conn, "Stop sending data before everything sent");
                 result = Curl_done_sending(data, k);
                 if(result)
@@ -4088,7 +4113,7 @@
         if(conn->bits.rewindaftersend) {
           /* We rewind after a complete send, so thus we continue
              sending now */
-          infof(data, "Keep sending data to get tossed away!\n");
+          infof(data, "Keep sending data to get tossed away!");
           k->keepon |= KEEP_SEND;
         }
       }
@@ -4201,18 +4226,20 @@
          * https://tools.ietf.org/html/rfc7230#section-3.1.2
          *
          * The response code is always a three-digit number in HTTP as the spec
-         * says. We try to allow any number here, but we cannot make
+         * says. We allow any three-digit number here, but we cannot make
          * guarantees on future behaviors since it isn't within the protocol.
          */
         char separator;
         char twoorthree[2];
         int httpversion = 0;
+        char digit4 = 0;
         nc = sscanf(HEADER1,
-                    " HTTP/%1d.%1d%c%3d",
+                    " HTTP/%1d.%1d%c%3d%c",
                     &httpversion_major,
                     &httpversion,
                     &separator,
-                    &k->httpcode);
+                    &k->httpcode,
+                    &digit4);
 
         if(nc == 1 && httpversion_major >= 2 &&
            2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) {
@@ -4221,7 +4248,15 @@
           separator = ' ';
         }
 
-        if((nc == 4) && (' ' == separator)) {
+        /* There can only be a 4th response code digit stored in 'digit4' if
+           all the other fields were parsed and stored first, so nc is 5 when
+           digit4 a digit */
+        else if(ISDIGIT(digit4)) {
+          failf(data, "Unsupported response code in HTTP response");
+          return CURLE_UNSUPPORTED_PROTOCOL;
+        }
+
+        if((nc >= 4) && (' ' == separator)) {
           httpversion += 10 * httpversion_major;
           switch(httpversion) {
           case 10:
@@ -4243,11 +4278,11 @@
           if(k->upgr101 == UPGR101_RECEIVED) {
             /* supposedly upgraded to http2 now */
             if(conn->httpversion != 20)
-              infof(data, "Lying server, not serving HTTP/2\n");
+              infof(data, "Lying server, not serving HTTP/2");
           }
           if(conn->httpversion < 20) {
             conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
-            infof(data, "Mark bundle as not supporting multiuse\n");
+            infof(data, "Mark bundle as not supporting multiuse");
           }
         }
         else if(!nc) {
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index 2a3834a..e4ab466 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -93,11 +93,14 @@
                               struct connectdata *conn);
 CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
                           char *headp);
+CURLcode Curl_transferencode(struct Curl_easy *data);
 CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
                         Curl_HttpReq httpreq,
                         const char **teep);
 CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
                             struct dynbuf *r, Curl_HttpReq httpreq);
+bool Curl_use_http_1_1plus(const struct Curl_easy *data,
+                           const struct connectdata *conn);
 #ifndef CURL_DISABLE_COOKIES
 CURLcode Curl_http_cookies(struct Curl_easy *data,
                            struct connectdata *conn,
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index f194c18..6d63f43 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -146,12 +146,12 @@
   (void)data;
 #endif
 
-  H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now\n"));
+  H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
 
   nghttp2_session_del(c->h2);
   Curl_safefree(c->inbuf);
 
-  H2BUGF(infof(data, "HTTP/2 DISCONNECT done\n"));
+  H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
 
   return CURLE_OK;
 }
@@ -196,11 +196,13 @@
           data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
       if(nread != -1) {
         infof(data,
-              "%d bytes stray data read before trying h2 connection\n",
+              "%d bytes stray data read before trying h2 connection",
               (int)nread);
         httpc->nread_inbuf = 0;
         httpc->inbuflen = nread;
-        (void)h2_process_pending_input(data, httpc, &result);
+        if(h2_process_pending_input(data, httpc, &result) < 0)
+          /* immediate error, considered dead */
+          dead = TRUE;
       }
       else
         /* the read failed so let's say this is dead anyway */
@@ -350,13 +352,12 @@
 };
 
 /*
- * Store nghttp2 version info in this buffer, Prefix with a space.  Return
- * total length written.
+ * Store nghttp2 version info in this buffer.
  */
-int Curl_http2_ver(char *p, size_t len)
+void Curl_http2_ver(char *p, size_t len)
 {
   nghttp2_info *h2 = nghttp2_version(0);
-  return msnprintf(p, len, "nghttp2/%s", h2->version_str);
+  (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
 }
 
 /*
@@ -551,7 +552,7 @@
                         const nghttp2_push_promise *frame)
 {
   int rv; /* one of the CURL_PUSH_* defines */
-  H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
+  H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!",
                frame->promised_stream_id));
   if(data->multi->push_cb) {
     struct HTTP *stream;
@@ -563,7 +564,7 @@
     /* clone the parent */
     struct Curl_easy *newhandle = duphandle(data);
     if(!newhandle) {
-      infof(data, "failed to duplicate handle\n");
+      infof(data, "failed to duplicate handle");
       rv = CURL_PUSH_DENY; /* FAIL HARD */
       goto fail;
     }
@@ -571,7 +572,7 @@
     heads.data = data;
     heads.frame = frame;
     /* ask the application */
-    H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
+    H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!"));
 
     stream = data->req.p.http;
     if(!stream) {
@@ -619,7 +620,7 @@
        state with the given connection !*/
     rc = Curl_multi_add_perform(data->multi, newhandle, conn);
     if(rc) {
-      infof(data, "failed to add handle to multi\n");
+      infof(data, "failed to add handle to multi");
       http2_stream_free(newhandle->req.p.http);
       newhandle->req.p.http = NULL;
       Curl_close(&newhandle);
@@ -632,15 +633,17 @@
                                               frame->promised_stream_id,
                                               newhandle);
     if(rv) {
-      infof(data, "failed to set user_data for stream %d\n",
+      infof(data, "failed to set user_data for stream %d",
             frame->promised_stream_id);
       DEBUGASSERT(0);
       rv = CURL_PUSH_DENY;
       goto fail;
     }
+    Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS);
+    Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
   }
   else {
-    H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
+    H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!"));
     rv = CURL_PUSH_DENY;
   }
   fail:
@@ -676,21 +679,21 @@
     /* stream ID zero is for connection-oriented stuff */
     if(frame->hd.type == NGHTTP2_SETTINGS) {
       uint32_t max_conn = httpc->settings.max_concurrent_streams;
-      H2BUGF(infof(data, "Got SETTINGS\n"));
+      H2BUGF(infof(data, "Got SETTINGS"));
       httpc->settings.max_concurrent_streams =
         nghttp2_session_get_remote_settings(
           session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
       httpc->settings.enable_push =
         nghttp2_session_get_remote_settings(
           session, NGHTTP2_SETTINGS_ENABLE_PUSH);
-      H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d\n",
+      H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
                    httpc->settings.max_concurrent_streams));
-      H2BUGF(infof(data, "ENABLE_PUSH == %s\n",
+      H2BUGF(infof(data, "ENABLE_PUSH == %s",
                    httpc->settings.enable_push?"TRUE":"false"));
       if(max_conn != httpc->settings.max_concurrent_streams) {
         /* only signal change if the value actually changed */
         infof(data,
-              "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!\n",
+              "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
               httpc->settings.max_concurrent_streams);
         multi_connchanged(data->multi);
       }
@@ -700,19 +703,19 @@
   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
   if(!data_s) {
     H2BUGF(infof(data,
-                 "No Curl_easy associated with stream: %x\n",
+                 "No Curl_easy associated with stream: %x",
                  stream_id));
     return 0;
   }
 
   stream = data_s->req.p.http;
   if(!stream) {
-    H2BUGF(infof(data_s, "No proto pointer for stream: %x\n",
+    H2BUGF(infof(data_s, "No proto pointer for stream: %x",
                  stream_id));
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
-  H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
+  H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x",
                frame->hd.type, stream_id));
 
   switch(frame->hd.type) {
@@ -760,7 +763,8 @@
            ncopy);
     stream->nread_header_recvbuf += ncopy;
 
-    H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
+    DEBUGASSERT(stream->mem);
+    H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
                  ncopy, stream_id, stream->mem));
 
     stream->len -= ncopy;
@@ -782,13 +786,13 @@
       if(nghttp2_is_fatal(h2))
         return NGHTTP2_ERR_CALLBACK_FAILURE;
       else if(rv == CURL_PUSH_ERROROUT) {
-        DEBUGF(infof(data_s, "Fail the parent stream (too)\n"));
+        DEBUGF(infof(data_s, "Fail the parent stream (too)"));
         return NGHTTP2_ERR_CALLBACK_FAILURE;
       }
     }
     break;
   default:
-    H2BUGF(infof(data_s, "Got frame type %x for stream %u!\n",
+    H2BUGF(infof(data_s, "Got frame type %x for stream %u!",
                  frame->hd.type, stream_id));
     break;
   }
@@ -833,7 +837,7 @@
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
   H2BUGF(infof(data_s, "%zu data received for stream %u "
-               "(%zu left in buffer %p, total %zu)\n",
+               "(%zu left in buffer %p, total %zu)",
                nread, stream_id,
                stream->len, stream->mem,
                stream->memlen));
@@ -842,7 +846,7 @@
     stream->pausedata = mem + nread;
     stream->pauselen = len - nread;
     H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
-                 ", stream %u\n",
+                 ", stream %u",
                  len - nread, stream_id));
     data_s->conn->proto.httpc.pause_stream_id = stream_id;
 
@@ -880,7 +884,7 @@
          decided to reject stream (e.g., PUSH_PROMISE). */
       return 0;
     }
-    H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
+    H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
                  nghttp2_http2_strerror(error_code), error_code, stream_id));
     stream = data_s->req.p.http;
     if(!stream)
@@ -895,15 +899,15 @@
     /* remove the entry from the hash as the stream is now gone */
     rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
     if(rv) {
-      infof(data_s, "http/2: failed to clear user_data for stream %d!\n",
+      infof(data_s, "http/2: failed to clear user_data for stream %d!",
             stream_id);
       DEBUGASSERT(0);
     }
     if(stream_id == httpc->pause_stream_id) {
-      H2BUGF(infof(data_s, "Stopped the pause stream!\n"));
+      H2BUGF(infof(data_s, "Stopped the pause stream!"));
       httpc->pause_stream_id = 0;
     }
-    H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
+    H2BUGF(infof(data_s, "Removed stream %u hash!", stream_id));
     stream->stream_id = 0; /* cleared */
   }
   return 0;
@@ -921,7 +925,7 @@
     return 0;
   }
 
-  H2BUGF(infof(data_s, "on_begin_headers() was called\n"));
+  H2BUGF(infof(data_s, "on_begin_headers() was called"));
 
   if(frame->hd.type != NGHTTP2_HEADERS) {
     return 0;
@@ -1049,7 +1053,7 @@
 
   if(stream->bodystarted) {
     /* This is a trailer */
-    H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
+    H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
                  value));
     result = Curl_dyn_addf(&stream->trailer_recvbuf,
                            "%.*s: %.*s\r\n", namelen, name,
@@ -1082,7 +1086,7 @@
     if(get_transfer(httpc) != data_s)
       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
-    H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
+    H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
                  stream->status_code, data_s));
     return 0;
   }
@@ -1106,7 +1110,7 @@
   if(get_transfer(httpc) != data_s)
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
-  H2BUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
+  H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
                value));
 
   return 0; /* 0 is successful */
@@ -1156,7 +1160,7 @@
     return NGHTTP2_ERR_DEFERRED;
 
   H2BUGF(infof(data_s, "data_source_read_callback: "
-               "returns %zu bytes stream %u\n",
+               "returns %zu bytes stream %u",
                nread, stream_id));
 
   return nread;
@@ -1223,7 +1227,7 @@
       (void)nghttp2_session_send(httpc->h2);
 
     if(http->stream_id == httpc->pause_stream_id) {
-      infof(data, "stopped the pause stream!\n");
+      infof(data, "stopped the pause stream!");
       httpc->pause_stream_id = 0;
     }
   }
@@ -1236,7 +1240,7 @@
     int rv = nghttp2_session_set_stream_user_data(httpc->h2,
                                                   http->stream_id, 0);
     if(rv) {
-      infof(data, "http/2: failed to clear user_data for stream %d!\n",
+      infof(data, "http/2: failed to clear user_data for stream %d!",
             http->stream_id);
       DEBUGASSERT(0);
     }
@@ -1383,7 +1387,7 @@
   if(nread == rv) {
     H2BUGF(infof(data,
                  "h2_process_pending_input: All data in connection buffer "
-                 "processed\n"));
+                 "processed"));
     httpc->inbuflen = 0;
     httpc->nread_inbuf = 0;
   }
@@ -1391,7 +1395,7 @@
     httpc->nread_inbuf += rv;
     H2BUGF(infof(data,
                  "h2_process_pending_input: %zu bytes left in connection "
-                 "buffer\n",
+                 "buffer",
                  httpc->inbuflen - httpc->nread_inbuf));
   }
 
@@ -1412,7 +1416,7 @@
   if(should_close_session(httpc)) {
     struct HTTP *stream = data->req.p.http;
     H2BUGF(infof(data,
-                 "h2_process_pending_input: nothing to do in this session\n"));
+                 "h2_process_pending_input: nothing to do in this session"));
     if(stream->error)
       *err = CURLE_HTTP2;
     else {
@@ -1456,7 +1460,7 @@
       struct SingleRequest *k = &data->req;
       int rv;
 
-      H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)\n", data));
+      H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
 
       /* and attempt to send the pending frames */
       rv = h2_session_send(data, h2);
@@ -1495,7 +1499,7 @@
   /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
   stream->closed = FALSE;
   if(stream->error == NGHTTP2_REFUSED_STREAM) {
-    H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!\n",
+    H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!",
                  stream->stream_id));
     connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
     data->state.refused_stream = TRUE;
@@ -1544,7 +1548,7 @@
 
   stream->close_handled = TRUE;
 
-  H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
+  H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
   return 0;
 }
 
@@ -1587,7 +1591,7 @@
 
     h2_pri_spec(data, &pri_spec);
 
-    H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
+    H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
                  stream->stream_id, data));
     DEBUGASSERT(stream->stream_id != -1);
     rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
@@ -1611,7 +1615,7 @@
 
   if(should_close_session(httpc)) {
     H2BUGF(infof(data,
-                 "http2_recv: nothing to do in this session\n"));
+                 "http2_recv: nothing to do in this session"));
     if(conn->bits.close) {
       /* already marked for closure, return OK and we're done */
       *err = CURLE_OK;
@@ -1621,10 +1625,6 @@
     return -1;
   }
 
-  if(stream->closed)
-    /* closed overrides paused */
-    return http2_handle_stream_close(conn, data, stream, err);
-
   /* Nullify here because we call nghttp2_session_send() and they
      might refer to the old buffer. */
   stream->upload_mem = NULL;
@@ -1645,12 +1645,12 @@
            stream->nread_header_recvbuf, ncopy);
     stream->nread_header_recvbuf += ncopy;
 
-    H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
+    H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
                  (int)ncopy));
     return ncopy;
   }
 
-  H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u\n",
+  H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
                data, stream->stream_id,
                nghttp2_session_get_local_window_size(httpc->h2),
                nghttp2_session_get_stream_local_window_size(httpc->h2,
@@ -1658,7 +1658,7 @@
            ));
 
   if((data->state.drain) && stream->memlen) {
-    H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
+    H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)",
                  stream->memlen, stream->stream_id,
                  stream->mem, mem));
     if(mem != stream->mem) {
@@ -1686,7 +1686,7 @@
     stream->pauselen -= nread;
 
     if(stream->pauselen == 0) {
-      H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
+      H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
       DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
       httpc->pause_stream_id = 0;
 
@@ -1704,7 +1704,7 @@
         return -1;
       }
     }
-    H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
+    H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
                  nread, stream->stream_id));
     return nread;
   }
@@ -1720,7 +1720,7 @@
     if(stream->closed)
       /* closed overrides paused */
       return 0;
-    H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
+    H2BUGF(infof(data, "stream %x is paused, pause id: %x",
                  stream->stream_id, httpc->pause_stream_id));
     *err = CURLE_AGAIN;
     return -1;
@@ -1758,12 +1758,12 @@
           return -1;
         }
 
-        H2BUGF(infof(data, "end of stream\n"));
+        H2BUGF(infof(data, "end of stream"));
         *err = CURLE_OK;
         return 0;
       }
 
-      H2BUGF(infof(data, "nread=%zd\n", nread));
+      H2BUGF(infof(data, "nread=%zd", nread));
 
       httpc->inbuflen = nread;
 
@@ -1772,7 +1772,7 @@
     else {
       nread = httpc->inbuflen - httpc->nread_inbuf;
       (void)nread;  /* silence warning, used in debug */
-      H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
+      H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
                    nread));
     }
 
@@ -1781,14 +1781,14 @@
   }
   if(stream->memlen) {
     ssize_t retlen = stream->memlen;
-    H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
+    H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
                  retlen, stream->stream_id));
     stream->memlen = 0;
 
     if(httpc->pause_stream_id == stream->stream_id) {
       /* data for this stream is returned now, but this stream caused a pause
          already so we need it called again asap */
-      H2BUGF(infof(data, "Data returned for PAUSED stream %u\n",
+      H2BUGF(infof(data, "Data returned for PAUSED stream %u",
                    stream->stream_id));
     }
     else if(!stream->closed) {
@@ -1803,7 +1803,7 @@
   if(stream->closed)
     return http2_handle_stream_close(conn, data, stream, err);
   *err = CURLE_AGAIN;
-  H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
+  H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
                stream->stream_id));
   return -1;
 }
@@ -1907,11 +1907,11 @@
 
   (void)sockindex;
 
-  H2BUGF(infof(data, "http2_send len=%zu\n", len));
+  H2BUGF(infof(data, "http2_send len=%zu", len));
 
   if(stream->stream_id != -1) {
     if(stream->close_handled) {
-      infof(data, "stream %d closed\n", stream->stream_id);
+      infof(data, "stream %d closed", stream->stream_id);
       *err = CURLE_HTTP2_STREAM;
       return -1;
     }
@@ -1940,7 +1940,7 @@
     stream->upload_len = 0;
 
     if(should_close_session(httpc)) {
-      H2BUGF(infof(data, "http2_send: nothing to do in this session\n"));
+      H2BUGF(infof(data, "http2_send: nothing to do in this session"));
       *err = CURLE_HTTP2;
       return -1;
     }
@@ -1953,7 +1953,7 @@
       nghttp2_session_resume_data(h2, stream->stream_id);
     }
 
-    H2BUGF(infof(data, "http2_send returns %zu for stream %u\n", len,
+    H2BUGF(infof(data, "http2_send returns %zu for stream %u", len,
                  stream->stream_id));
     return len;
   }
@@ -2116,7 +2116,7 @@
     for(i = 0; i < nheader; ++i) {
       acc += nva[i].namelen + nva[i].valuelen;
 
-      H2BUGF(infof(data, "h2 header: %.*s:%.*s\n",
+      H2BUGF(infof(data, "h2 header: %.*s:%.*s",
                    nva[i].namelen, nva[i].name,
                    nva[i].valuelen, nva[i].value));
     }
@@ -2124,13 +2124,13 @@
     if(acc > MAX_ACC) {
       infof(data, "http2_send: Warning: The cumulative length of all "
             "headers exceeds %d bytes and that could cause the "
-            "stream to be rejected.\n", MAX_ACC);
+            "stream to be rejected.", MAX_ACC);
     }
   }
 
   h2_pri_spec(data, &pri_spec);
 
-  H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)\n",
+  H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
                nghttp2_session_check_request_allowed(h2), (void *)data));
 
   switch(data->state.httpreq) {
@@ -2158,20 +2158,20 @@
 
   if(stream_id < 0) {
     H2BUGF(infof(data,
-                 "http2_send() nghttp2_submit_request error (%s)%d\n",
+                 "http2_send() nghttp2_submit_request error (%s)%d",
                  nghttp2_strerror(stream_id), stream_id));
     *err = CURLE_SEND_ERROR;
     return -1;
   }
 
-  infof(data, "Using Stream ID: %x (easy handle %p)\n",
+  infof(data, "Using Stream ID: %x (easy handle %p)",
         stream_id, (void *)data);
   stream->stream_id = stream_id;
 
   rv = h2_session_send(data, h2);
   if(rv) {
     H2BUGF(infof(data,
-                 "http2_send() nghttp2_session_send error (%s)%d\n",
+                 "http2_send() nghttp2_session_send error (%s)%d",
                  nghttp2_strerror(rv), rv));
 
     *err = CURLE_SEND_ERROR;
@@ -2179,7 +2179,7 @@
   }
 
   if(should_close_session(httpc)) {
-    H2BUGF(infof(data, "http2_send: nothing to do in this session\n"));
+    H2BUGF(infof(data, "http2_send: nothing to do in this session"));
     *err = CURLE_HTTP2;
     return -1;
   }
@@ -2215,6 +2215,16 @@
   Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
   Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
 
+  stream->upload_left = 0;
+  stream->upload_mem = NULL;
+  stream->upload_len = 0;
+  stream->mem = data->state.buffer;
+  stream->len = data->set.buffer_size;
+
+  multi_connchanged(data->multi);
+  /* below this point only connection related inits are done, which only needs
+     to be done once per connection */
+
   if((conn->handler == &Curl_handler_http2_ssl) ||
      (conn->handler == &Curl_handler_http2))
     return CURLE_OK; /* already done */
@@ -2230,12 +2240,11 @@
     return result;
   }
 
-  infof(data, "Using HTTP2, server supports multi-use\n");
-  stream->upload_left = 0;
-  stream->upload_mem = NULL;
-  stream->upload_len = 0;
-  stream->mem = data->state.buffer;
-  stream->len = data->set.buffer_size;
+  infof(data, "Using HTTP2, server supports multiplexing");
+
+  conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+  conn->httpversion = 20;
+  conn->bundle->multiuse = BUNDLE_MULTIPLEX;
 
   httpc->inbuflen = 0;
   httpc->nread_inbuf = 0;
@@ -2243,12 +2252,7 @@
   httpc->pause_stream_id = 0;
   httpc->drain_total = 0;
 
-  conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-  conn->httpversion = 20;
-  conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-
-  infof(data, "Connection state changed (HTTP/2 confirmed)\n");
-  multi_connchanged(data->multi);
+  infof(data, "Connection state changed (HTTP/2 confirmed)");
 
   return CURLE_OK;
 }
@@ -2287,7 +2291,7 @@
                                               stream->stream_id,
                                               data);
     if(rv) {
-      infof(data, "http/2: failed to set user_data for stream %d!\n",
+      infof(data, "http/2: failed to set user_data for stream %d!",
             stream->stream_id);
       DEBUGASSERT(0);
     }
@@ -2327,7 +2331,7 @@
   }
 
   infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
-        " after upgrade: len=%zu\n",
+        " after upgrade: len=%zu",
         nread);
 
   if(nread)
@@ -2337,15 +2341,8 @@
 
   DEBUGASSERT(httpc->nread_inbuf == 0);
 
-  /* Good enough to call it an end once the remaining payload is copied to the
-   * connection buffer.
-   * Some servers (e.g. nghttpx v1.43.0) may fulfill stream 1 immediately
-   * following the protocol switch other than waiting for the client-side
-   * connection preface. If h2_process_pending_input is invoked here to parse
-   * the remaining payload, stream 1 would be marked as closed too early and
-   * thus ignored in http2_recv (following 252790c53).
-   * The logic in lib/http.c and lib/transfer.c guarantees a following
-   * http2_recv would be invoked very soon. */
+  if(-1 == h2_process_pending_input(data, httpc, &result))
+    return CURLE_HTTP2;
 
   return CURLE_OK;
 }
@@ -2378,7 +2375,7 @@
     if(rv)
       return CURLE_SEND_ERROR;
 
-    DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u\n",
+    DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
                  window, stream->stream_id));
 
 #ifdef DEBUGBUILD
@@ -2387,7 +2384,7 @@
       uint32_t window2 =
         nghttp2_session_get_stream_local_window_size(httpc->h2,
                                                      stream->stream_id);
-      DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u\n",
+      DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
                    window2, stream->stream_id));
     }
 #endif
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h
index 21e2c08..d6986d9 100644
--- a/Utilities/cmcurl/lib/http2.h
+++ b/Utilities/cmcurl/lib/http2.h
@@ -32,10 +32,9 @@
 #define DEFAULT_MAX_CONCURRENT_STREAMS 100
 
 /*
- * Store nghttp2 version info in this buffer, Prefix with a space.  Return
- * total length written.
+ * Store nghttp2 version info in this buffer.
  */
-int Curl_http2_ver(char *p, size_t len);
+void Curl_http2_ver(char *p, size_t len);
 
 const char *Curl_http2_strerror(uint32_t err);
 
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index a04b46a..02663ab 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -126,7 +126,7 @@
   tmp1 = strchr(tmp0, ':');
   len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
   if(len < 1) {
-    infof(data, "first provider can't be empty\n");
+    infof(data, "first provider can't be empty");
     ret = CURLE_BAD_FUNCTION_ARGUMENT;
     goto fail;
   }
@@ -145,7 +145,7 @@
     tmp1 = strchr(tmp0, ':');
     len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
     if(len < 1) {
-      infof(data, "second provider can't be empty\n");
+      infof(data, "second provider can't be empty");
       ret = CURLE_BAD_FUNCTION_ARGUMENT;
       goto fail;
     }
@@ -165,7 +165,7 @@
       tmp1 = strchr(tmp0, ':');
       len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
       if(len < 1) {
-        infof(data, "region can't be empty\n");
+        infof(data, "region can't be empty");
         ret = CURLE_BAD_FUNCTION_ARGUMENT;
         goto fail;
       }
@@ -182,7 +182,7 @@
           goto fail;
         }
         if(strlen(service) < 1) {
-          infof(data, "service can't be empty\n");
+          infof(data, "service can't be empty");
           ret = CURLE_BAD_FUNCTION_ARGUMENT;
           goto fail;
         }
@@ -203,7 +203,7 @@
     tmp1 = strchr(tmp0, '.');
     len = tmp1 - tmp0;
     if(!tmp1 || len < 1) {
-      infof(data, "service missing in parameters or hostname\n");
+      infof(data, "service missing in parameters or hostname");
       ret = CURLE_URL_MALFORMAT;
       goto fail;
     }
@@ -218,7 +218,7 @@
       tmp1 = strchr(tmp0, '.');
       len = tmp1 - tmp0;
       if(!tmp1 || len < 1) {
-        infof(data, "region missing in parameters or hostname\n");
+        infof(data, "region missing in parameters or hostname");
         ret = CURLE_URL_MALFORMAT;
         goto fail;
       }
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c
index 049b232..34bb5a8 100644
--- a/Utilities/cmcurl/lib/http_digest.c
+++ b/Utilities/cmcurl/lib/http_digest.c
@@ -146,7 +146,8 @@
     tmp = strchr((char *)uripath, '?');
     if(tmp) {
       size_t urilen = tmp - (char *)uripath;
-      path = (unsigned char *) aprintf("%.*s", urilen, uripath);
+      /* typecast is fine here since the value is always less than 32 bits */
+      path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath);
     }
   }
   if(!tmp)
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c
index 68cce1b..5f764dc 100644
--- a/Utilities/cmcurl/lib/http_negotiate.c
+++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -89,7 +89,7 @@
   neg_ctx->havenegdata = len != 0;
   if(!len) {
     if(state == GSS_AUTHSUCC) {
-      infof(data, "Negotiate auth restarted\n");
+      infof(data, "Negotiate auth restarted");
       Curl_http_auth_cleanup_negotiate(conn);
     }
     else if(state != GSS_AUTHNONE) {
@@ -142,11 +142,11 @@
   }
 
   if(neg_ctx->noauthpersist ||
-    (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
+     (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
 
     if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
       infof(data, "Curl_output_negotiate, "
-       "no persistent authentication: cleanup existing context");
+            "no persistent authentication: cleanup existing context");
       Curl_http_auth_cleanup_negotiate(conn);
     }
     if(!neg_ctx->context) {
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c
index e200fdb..627a11c 100644
--- a/Utilities/cmcurl/lib/http_ntlm.c
+++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -100,17 +100,17 @@
     }
     else {
       if(*state == NTLMSTATE_LAST) {
-        infof(data, "NTLM auth restarted\n");
+        infof(data, "NTLM auth restarted");
         Curl_http_auth_cleanup_ntlm(conn);
       }
       else if(*state == NTLMSTATE_TYPE3) {
-        infof(data, "NTLM handshake rejected\n");
+        infof(data, "NTLM handshake rejected");
         Curl_http_auth_cleanup_ntlm(conn);
         *state = NTLMSTATE_NONE;
         return CURLE_REMOTE_ACCESS_DENIED;
       }
       else if(*state >= NTLMSTATE_TYPE1) {
-        infof(data, "NTLM handshake failure (internal error)\n");
+        infof(data, "NTLM handshake failure (internal error)");
         return CURLE_REMOTE_ACCESS_DENIED;
       }
 
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index a3a62c1..58489ab 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -61,7 +61,7 @@
   if(!conn->bits.proxy_ssl_connected[sockindex]) {
     /* perform SSL initialization for this socket */
     result =
-      Curl_ssl_connect_nonblocking(data, conn, sockindex,
+      Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex,
                                    &conn->bits.proxy_ssl_connected[sockindex]);
     if(result)
       /* a failed connection is marked for closure to prevent (bad) re-use or
@@ -129,13 +129,13 @@
 bool Curl_connect_complete(struct connectdata *conn)
 {
   return !conn->connect_state ||
-    (conn->connect_state->tunnel_state == TUNNEL_COMPLETE);
+    (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE);
 }
 
 bool Curl_connect_ongoing(struct connectdata *conn)
 {
   return conn->connect_state &&
-    (conn->connect_state->tunnel_state != TUNNEL_COMPLETE);
+    (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE);
 }
 
 /* when we've sent a CONNECT to a proxy, we should rather either wait for the
@@ -148,7 +148,7 @@
   DEBUGASSERT(conn->connect_state);
   http = &conn->connect_state->http_proxy;
 
-  if(http->sending)
+  if(http->sending == HTTPSEND_REQUEST)
     return GETSOCK_WRITESOCK(0);
 
   return GETSOCK_READSOCK(0);
@@ -169,7 +169,7 @@
     s = calloc(1, sizeof(struct http_connect_state));
     if(!s)
       return CURLE_OUT_OF_MEMORY;
-    infof(data, "allocate connect buffer!\n");
+    infof(data, "allocate connect buffer!");
     conn->connect_state = s;
     Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
 
@@ -202,13 +202,16 @@
 {
   struct connectdata *conn = data->conn;
   struct http_connect_state *s = conn->connect_state;
-  s->tunnel_state = TUNNEL_COMPLETE;
-  Curl_dyn_free(&s->rcvbuf);
-  Curl_dyn_free(&s->req);
+  if(s->tunnel_state != TUNNEL_EXIT) {
+    s->tunnel_state = TUNNEL_EXIT;
+    Curl_dyn_free(&s->rcvbuf);
+    Curl_dyn_free(&s->req);
 
-  /* retore the protocol pointer */
-  data->req.p.http = s->prot_save;
-  infof(data, "CONNECT phase completed!\n");
+    /* retore the protocol pointer */
+    data->req.p.http = s->prot_save;
+    s->prot_save = NULL;
+    infof(data, "CONNECT phase completed!");
+  }
 }
 
 static CURLcode CONNECT_host(struct Curl_easy *data,
@@ -243,11 +246,11 @@
   return CURLE_OK;
 }
 
+#ifndef USE_HYPER
 static CURLcode CONNECT(struct Curl_easy *data,
                         int sockindex,
                         const char *hostname,
                         int remote_port)
-#ifndef USE_HYPER
 {
   int subversion = 0;
   struct SingleRequest *k = &data->req;
@@ -275,7 +278,7 @@
       char *hostheader = NULL;
       char *host = NULL;
 
-      infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
+      infof(data, "Establish HTTP proxy tunnel to %s:%d",
             hostname, remote_port);
 
         /* This only happens if we've looped here due to authentication
@@ -297,32 +300,27 @@
                                      hostheader, TRUE);
 
       if(!result) {
-        const char *proxyconn = "";
-        const char *useragent = "";
         const char *httpv =
           (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
 
-        if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection"))
-          proxyconn = "Proxy-Connection: Keep-Alive\r\n";
-
-        if(!Curl_checkProxyheaders(data, conn, "User-Agent") &&
-           data->set.str[STRING_USERAGENT])
-          useragent = data->state.aptr.uagent;
-
         result =
           Curl_dyn_addf(req,
                         "CONNECT %s HTTP/%s\r\n"
                         "%s"  /* Host: */
-                        "%s"  /* Proxy-Authorization */
-                        "%s"  /* User-Agent */
-                        "%s", /* Proxy-Connection */
+                        "%s", /* Proxy-Authorization */
                         hostheader,
                         httpv,
                         host?host:"",
                         data->state.aptr.proxyuserpwd?
-                        data->state.aptr.proxyuserpwd:"",
-                        useragent,
-                        proxyconn);
+                        data->state.aptr.proxyuserpwd:"");
+
+        if(!result && !Curl_checkProxyheaders(data, conn, "User-Agent") &&
+           data->set.str[STRING_USERAGENT])
+          result = Curl_dyn_addf(req, "User-Agent: %s\r\n",
+                                 data->set.str[STRING_USERAGENT]);
+
+        if(!result && !Curl_checkProxyheaders(data, conn, "Proxy-Connection"))
+          result = Curl_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
 
         if(!result)
           result = Curl_add_custom_headers(data, TRUE, req);
@@ -387,6 +385,7 @@
         k->upload_fromhere += bytes_written;
         return result;
       }
+      http->sending = HTTPSEND_NADA;
       /* if nothing left to send, continue */
     }
     { /* READING RESPONSE PHASE */
@@ -416,7 +415,7 @@
             /* proxy auth was requested and there was proxy auth available,
                then deem this as "mere" proxy disconnect */
             conn->bits.proxy_connect_closed = TRUE;
-            infof(data, "Proxy CONNECT connection closed\n");
+            infof(data, "Proxy CONNECT connection closed");
           }
           else {
             error = SELECT_ERROR;
@@ -451,7 +450,7 @@
             r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
             if(r == CHUNKE_STOP) {
               /* we're done reading chunks! */
-              infof(data, "chunk reading DONE\n");
+              infof(data, "chunk reading DONE");
               s->keepon = KEEPON_DONE;
               /* we did the full CONNECT treatment, go COMPLETE */
               s->tunnel_state = TUNNEL_COMPLETE;
@@ -510,13 +509,13 @@
 
             if(s->cl) {
               infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
-                    " bytes of response-body\n", s->cl);
+                    " bytes of response-body", s->cl);
             }
             else if(s->chunked_encoding) {
               CHUNKcode r;
               CURLcode extra;
 
-              infof(data, "Ignore chunked response-body\n");
+              infof(data, "Ignore chunked response-body");
 
               /* We set ignorebody true here since the chunked decoder
                  function will acknowledge that. Pay attention so that this is
@@ -533,7 +532,7 @@
                                       &extra);
               if(r == CHUNKE_STOP) {
                 /* we're done reading chunks! */
-                infof(data, "chunk reading DONE\n");
+                infof(data, "chunk reading DONE");
                 s->keepon = KEEPON_DONE;
                 /* we did the full CONNECT treatment, go to COMPLETE */
                 s->tunnel_state = TUNNEL_COMPLETE;
@@ -579,7 +578,7 @@
             /* A client MUST ignore any Content-Length or Transfer-Encoding
                header fields received in a successful response to CONNECT.
                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
-            infof(data, "Ignoring Content-Length in CONNECT %03d response\n",
+            infof(data, "Ignoring Content-Length in CONNECT %03d response",
                   k->httpcode);
           }
           else {
@@ -595,11 +594,11 @@
                header fields received in a successful response to CONNECT.
                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
             infof(data, "Ignoring Transfer-Encoding in "
-                  "CONNECT %03d response\n", k->httpcode);
+                  "CONNECT %03d response", k->httpcode);
           }
           else if(Curl_compareheader(linep,
                                      "Transfer-Encoding:", "chunked")) {
-            infof(data, "CONNECT responded chunked\n");
+            infof(data, "CONNECT responded chunked");
             s->chunked_encoding = TRUE;
             /* init our chunky engine */
             Curl_httpchunk_init(data);
@@ -657,7 +656,7 @@
   if(data->info.httpproxycode/100 != 2) {
     if(s->close_connection && data->req.newurl) {
       conn->bits.proxy_connect_closed = TRUE;
-      infof(data, "Connect me again please\n");
+      infof(data, "Connect me again please");
       connect_done(data);
     }
     else {
@@ -692,7 +691,7 @@
   data->state.authproxy.done = TRUE;
   data->state.authproxy.multipass = FALSE;
 
-  infof(data, "Proxy replied %d to CONNECT request\n",
+  infof(data, "Proxy replied %d to CONNECT request",
         data->info.httpproxycode);
   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
@@ -702,6 +701,10 @@
 }
 #else
 /* The Hyper version of CONNECT */
+static CURLcode CONNECT(struct Curl_easy *data,
+                        int sockindex,
+                        const char *hostname,
+                        int remote_port)
 {
   struct connectdata *conn = data->conn;
   struct hyptransfer *h = &data->hyp;
@@ -740,6 +743,8 @@
       hyper_io_set_write(io, Curl_hyper_send);
       conn->sockfd = tunnelsocket;
 
+      data->state.hconnect = TRUE;
+
       /* create an executor to poll futures */
       if(!h->exec) {
         h->exec = hyper_executor_new();
@@ -830,16 +835,26 @@
          Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd))
         goto error;
 
-      if(data->set.str[STRING_USERAGENT] &&
-         *data->set.str[STRING_USERAGENT] &&
-         data->state.aptr.uagent &&
-         Curl_hyper_header(data, headers, data->state.aptr.uagent))
-        goto error;
+      if(!Curl_checkProxyheaders(data, conn, "User-Agent") &&
+         data->set.str[STRING_USERAGENT]) {
+        struct dynbuf ua;
+        Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
+        result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
+                               data->set.str[STRING_USERAGENT]);
+        if(result)
+          goto error;
+        if(Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)))
+          goto error;
+        Curl_dyn_free(&ua);
+      }
 
       if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection") &&
          Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"))
         goto error;
 
+      if(Curl_add_custom_headers(data, TRUE, headers))
+        goto error;
+
       sendtask = hyper_clientconn_send(client, req);
       if(!sendtask) {
         failf(data, "hyper_clientconn_send");
@@ -875,7 +890,6 @@
         goto error;
       if(!done)
         break;
-      fprintf(stderr, "done\n");
       s->tunnel_state = TUNNEL_COMPLETE;
       if(h->exec) {
         hyper_executor_free(h->exec);
@@ -897,6 +911,33 @@
   } while(data->req.newurl);
 
   result = CURLE_OK;
+  if(s->tunnel_state == TUNNEL_COMPLETE) {
+    data->info.httpproxycode = data->req.httpcode;
+    if(data->info.httpproxycode/100 != 2) {
+      if(conn->bits.close && data->req.newurl) {
+        conn->bits.proxy_connect_closed = TRUE;
+        infof(data, "Connect me again please");
+        connect_done(data);
+      }
+      else {
+        free(data->req.newurl);
+        data->req.newurl = NULL;
+        /* failure, close this connection to avoid re-use */
+        streamclose(conn, "proxy CONNECT failure");
+        Curl_closesocket(data, conn, conn->sock[sockindex]);
+        conn->sock[sockindex] = CURL_SOCKET_BAD;
+      }
+
+      /* to back to init state */
+      s->tunnel_state = TUNNEL_INIT;
+
+      if(!conn->bits.proxy_connect_closed) {
+        failf(data, "Received HTTP code %d from proxy after CONNECT",
+              data->req.httpcode);
+        result = CURLE_RECV_ERROR;
+      }
+    }
+  }
   error:
   free(host);
   free(hostheader);
@@ -917,7 +958,6 @@
   }
   return result;
 }
-
 #endif
 
 void Curl_connect_free(struct Curl_easy *data)
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h
index f5a4cb0..cdf8de4 100644
--- a/Utilities/cmcurl/lib/http_proxy.h
+++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -65,9 +65,10 @@
   } keepon;
   curl_off_t cl; /* size of content to read and ignore */
   enum {
-    TUNNEL_INIT,    /* init/default/no tunnel state */
-    TUNNEL_CONNECT, /* CONNECT has been sent off */
-    TUNNEL_COMPLETE /* CONNECT response received completely */
+    TUNNEL_INIT,     /* init/default/no tunnel state */
+    TUNNEL_CONNECT,  /* CONNECT has been sent off */
+    TUNNEL_COMPLETE, /* CONNECT response received completely */
+    TUNNEL_EXIT
   } tunnel_state;
   BIT(chunked_encoding);
   BIT(close_connection);
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index d85bcc3..6163899 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -74,7 +74,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
@@ -414,7 +413,7 @@
   };
 
   if(imapc->state != newstate)
-    infof(data, "IMAP %p state change from %s to %s\n",
+    infof(data, "IMAP %p state change from %s to %s",
           (void *)imapc, names[imapc->state], names[newstate]);
 #endif
 
@@ -475,8 +474,8 @@
 {
   /* Start the SSL connection */
   struct imap_conn *imapc = &conn->proto.imapc;
-  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
-                                                 &imapc->ssldone);
+  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                                 FIRSTSOCKET, &imapc->ssldone);
 
   if(!result) {
     if(imapc->state != IMAP_UPGRADETLS)
@@ -606,7 +605,7 @@
       result = imap_perform_login(data, conn);
     else {
       /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported!\n");
+      infof(data, "No known authentication mechanisms supported!");
       result = CURLE_LOGIN_DENIED;
     }
   }
@@ -861,7 +860,7 @@
     /* PREAUTH */
     struct imap_conn *imapc = &conn->proto.imapc;
     imapc->preauth = TRUE;
-    infof(data, "PREAUTH connection, already authenticated!\n");
+    infof(data, "PREAUTH connection, already authenticated!");
   }
   else if(imapcode != IMAP_RESP_OK) {
     failf(data, "Got unexpected imap-server response");
@@ -935,22 +934,18 @@
       line += wordlen;
     }
   }
-  else if(imapcode == IMAP_RESP_OK) {
-    if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-      /* We don't have a SSL/TLS connection yet, but SSL is requested */
-      if(imapc->tls_supported)
-        /* Switch to TLS connection now */
-        result = imap_perform_starttls(data, conn);
-      else if(data->set.use_ssl == CURLUSESSL_TRY)
-        /* Fallback and carry on with authentication */
-        result = imap_perform_authentication(data, conn);
-      else {
-        failf(data, "STARTTLS not supported.");
-        result = CURLE_USE_SSL_FAILED;
-      }
+  else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+    /* PREAUTH is not compatible with STARTTLS. */
+    if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
+      /* Switch to TLS connection now */
+      result = imap_perform_starttls(data, conn);
     }
-    else
+    else if(data->set.use_ssl <= CURLUSESSL_TRY)
       result = imap_perform_authentication(data, conn);
+    else {
+      failf(data, "STARTTLS not available.");
+      result = CURLE_USE_SSL_FAILED;
+    }
   }
   else
     result = imap_perform_authentication(data, conn);
@@ -968,6 +963,10 @@
 
   (void)instate; /* no use for this yet */
 
+  /* Pipelining in response is forbidden. */
+  if(data->conn->proto.imapc.pp.cache_size)
+    return CURLE_WEIRD_SERVER_REPLY;
+
   if(imapcode != IMAP_RESP_OK) {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
       failf(data, "STARTTLS denied");
@@ -1143,7 +1142,7 @@
   }
 
   if(parsed) {
-    infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download\n",
+    infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download",
           size);
     Curl_pgrsSetDownloadSize(data, size);
 
@@ -1169,7 +1168,7 @@
       data->req.bytecount += chunk;
 
       infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
-            " bytes are left for transfer\n", chunk, size - chunk);
+            " bytes are left for transfer", chunk, size - chunk);
 
       /* Have we used the entire cache or just part of it?*/
       if(pp->cache_size > chunk) {
@@ -1369,7 +1368,7 @@
   struct imap_conn *imapc = &conn->proto.imapc;
 
   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &imapc->ssldone);
     if(result || !imapc->ssldone)
       return result;
@@ -1543,7 +1542,7 @@
   struct imap_conn *imapc = &conn->proto.imapc;
   bool selected = FALSE;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* Requested no body means no transfer */
@@ -1590,7 +1589,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
@@ -1682,11 +1681,11 @@
   CURLcode result = imap_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = imap_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -2017,7 +2016,7 @@
       return result;
     }
 
-    DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'\n", name, value));
+    DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value));
 
     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
        PARTIAL) stripping of the trailing slash character if it is present.
diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c
index 4c3e9e4..b5f9b80 100644
--- a/Utilities/cmcurl/lib/inet_ntop.c
+++ b/Utilities/cmcurl/lib/inet_ntop.c
@@ -40,7 +40,7 @@
 #define INT16SZ          2
 
 /*
- * Format an IPv4 address, more or less like inet_ntoa().
+ * Format an IPv4 address, more or less like inet_ntop().
  *
  * Returns `dst' (as a const)
  * Note:
diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c
index c8c4b66..659947d 100644
--- a/Utilities/cmcurl/lib/krb5.c
+++ b/Utilities/cmcurl/lib/krb5.c
@@ -263,7 +263,7 @@
     }
     /* We pass NULL as |output_name_type| to avoid a leak. */
     gss_display_name(&min, gssname, &output_buffer, NULL);
-    Curl_infof(data, "Trying against %s\n", output_buffer.value);
+    infof(data, "Trying against %s", output_buffer.value);
     gssresp = GSS_C_NO_BUFFER;
     *context = GSS_C_NO_CONTEXT;
 
@@ -290,7 +290,7 @@
       }
 
       if(GSS_ERROR(maj)) {
-        Curl_infof(data, "Error creating security context\n");
+        infof(data, "Error creating security context");
         ret = AUTH_ERROR;
         break;
       }
@@ -301,8 +301,7 @@
         result = Curl_base64_encode(data, (char *)output_buffer.value,
                                     output_buffer.length, &p, &base64_sz);
         if(result) {
-          Curl_infof(data, "base64-encoding: %s\n",
-                     curl_easy_strerror(result));
+          infof(data, "base64-encoding: %s", curl_easy_strerror(result));
           ret = AUTH_ERROR;
           break;
         }
@@ -327,7 +326,7 @@
         }
 
         if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
-          Curl_infof(data, "Server didn't accept auth data\n");
+          infof(data, "Server didn't accept auth data");
           ret = AUTH_ERROR;
           break;
         }
@@ -629,7 +628,7 @@
 
       socket_write(data, fd, cmd_buffer, cmd_size);
       socket_write(data, fd, "\r\n", 2);
-      infof(data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic,
+      infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic,
             cmd_buffer);
       free(cmd_buffer);
     }
@@ -738,7 +737,7 @@
 
   if(!conn->sec_complete) {
     infof(data, "Trying to change the protection level after the"
-                " completion of the data exchange.\n");
+                " completion of the data exchange.");
     return -1;
   }
 
@@ -815,13 +814,13 @@
   if(mech->init) {
     ret = mech->init(conn->app_data);
     if(ret) {
-      infof(data, "Failed initialization for %s. Skipping it.\n",
+      infof(data, "Failed initialization for %s. Skipping it.",
             mech->name);
       return CURLE_FAILED_INIT;
     }
   }
 
-  infof(data, "Trying mechanism %s...\n", mech->name);
+  infof(data, "Trying mechanism %s...", mech->name);
   ret = ftp_send_command(data, "AUTH %s", mech->name);
   if(ret < 0)
     return CURLE_COULDNT_CONNECT;
@@ -830,15 +829,15 @@
     switch(ret) {
     case 504:
       infof(data, "Mechanism %s is not supported by the server (server "
-            "returned ftp code: 504).\n", mech->name);
+            "returned ftp code: 504).", mech->name);
       break;
     case 534:
       infof(data, "Mechanism %s was rejected by the server (server returned "
-            "ftp code: 534).\n", mech->name);
+            "ftp code: 534).", mech->name);
       break;
     default:
       if(ret/100 == 5) {
-        infof(data, "server does not support the security extensions\n");
+        infof(data, "server does not support the security extensions");
         return CURLE_USE_SSL_FAILED;
       }
       break;
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index ed16423..1d9e44c 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -296,9 +296,9 @@
   char *passwd = NULL;
 
   *done = TRUE; /* unconditionally */
-  infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
+  infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
           LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
-  infof(data, "LDAP local: %s\n", data->state.url);
+  infof(data, "LDAP local: %s", data->state.url);
 
 #ifdef HAVE_LDAP_URL_PARSE
   rc = ldap_url_parse(data->state.url, &ludp);
@@ -314,7 +314,7 @@
   /* Get the URL scheme (either ldap or ldaps) */
   if(conn->given->flags & PROTOPT_SSL)
     ldap_ssl = 1;
-  infof(data, "LDAP local: trying to establish %s connection\n",
+  infof(data, "LDAP local: trying to establish %s connection",
           ldap_ssl ? "encrypted" : "cleartext");
 
 #if defined(USE_WIN32_LDAP)
@@ -366,14 +366,14 @@
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
       }
-      infof(data, "LDAP local: using %s CA cert '%s'\n",
-              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-              ldap_ca);
+      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));
+              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+              ldap_err2string(rc));
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
       }
@@ -409,7 +409,7 @@
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
       }
-      infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
+      infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca);
       rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
       if(rc != LDAP_SUCCESS) {
         failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
@@ -718,7 +718,7 @@
     LDAP_TRACE(("Received %d entries\n", num));
   }
   if(rc == LDAP_SIZELIMIT_EXCEEDED)
-    infof(data, "There are more than %d entries\n", num);
+    infof(data, "There are more than %d entries", num);
   if(ludp)
     ldap_free_urldesc(ludp);
   if(server)
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index c651ddf..e7a428f 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -36,8 +36,12 @@
 #endif /* USE_OPENSSL */
 
 #ifdef USE_MBEDTLS
-#include <mbedtls/config.h>
 #include <mbedtls/version.h>
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#include <mbedtls/mbedtls_config.h>
+#else
+#include <mbedtls/config.h>
+#endif
 
 #if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c
index 7a24fd8..983ed97 100644
--- a/Utilities/cmcurl/lib/md5.c
+++ b/Utilities/cmcurl/lib/md5.c
@@ -33,7 +33,8 @@
 #ifdef USE_MBEDTLS
 #include <mbedtls/version.h>
 
-#if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+   (MBEDTLS_VERSION_NUMBER < 0x03000000)
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
 #endif
 #endif /* USE_MBEDTLS */
@@ -85,7 +86,7 @@
 static void MD5_Init(MD5_CTX *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_md5_starts(ctx);
+  (void) mbedtls_md5_starts(ctx);
 #else
   (void) mbedtls_md5_starts_ret(ctx);
 #endif
@@ -96,7 +97,7 @@
                        unsigned int length)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_md5_update(ctx, data, length);
+  (void) mbedtls_md5_update(ctx, data, length);
 #else
   (void) mbedtls_md5_update_ret(ctx, data, length);
 #endif
@@ -105,7 +106,7 @@
 static void MD5_Final(unsigned char *digest, MD5_CTX *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_md5_finish(ctx, digest);
+  (void) mbedtls_md5_finish(ctx, digest);
 #else
   (void) mbedtls_md5_finish_ret(ctx, digest);
 #endif
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c
index 5292026..7a1aec5 100644
--- a/Utilities/cmcurl/lib/mprintf.c
+++ b/Utilities/cmcurl/lib/mprintf.c
@@ -1017,9 +1017,11 @@
   retcode = dprintf_formatf(&info, addbyter, format, ap_save);
   if((retcode != -1) && info.max) {
     /* we terminate this with a zero byte */
-    if(info.max == info.length)
+    if(info.max == info.length) {
       /* we're at maximum, scrap the last letter */
       info.buffer[-1] = 0;
+      retcode--; /* don't count the nul byte */
+    }
     else
       info.buffer[0] = 0;
   }
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c
index d88fa73..fcd40b4 100644
--- a/Utilities/cmcurl/lib/mqtt.c
+++ b/Utilities/cmcurl/lib/mqtt.c
@@ -128,6 +128,10 @@
     mq->sendleftovers = sendleftovers;
     mq->nsend = nsend;
   }
+  else {
+    mq->sendleftovers = NULL;
+    mq->nsend = 0;
+  }
   return result;
 }
 
@@ -143,32 +147,197 @@
   return GETSOCK_READSOCK(FIRSTSOCKET);
 }
 
+static int mqtt_encode_len(char *buf, size_t len)
+{
+  unsigned char encoded;
+  int i;
+
+  for(i = 0; (len > 0) && (i<4); i++) {
+    encoded = len % 0x80;
+    len /= 0x80;
+    if(len)
+      encoded |= 0x80;
+    buf[i] = encoded;
+  }
+
+  return i;
+}
+
+/* add the passwd to the CONNECT packet */
+static int add_passwd(const char *passwd, const size_t plen,
+                       char *pkt, const size_t start, int remain_pos)
+{
+  /* magic number that need to be set properly */
+  const size_t conn_flags_pos = remain_pos + 8;
+  if(plen > 0xffff)
+    return 1;
+
+  /* set password flag */
+  pkt[conn_flags_pos] |= 0x40;
+
+  /* length of password provided */
+  pkt[start] = (char)((plen >> 8) & 0xFF);
+  pkt[start + 1] = (char)(plen & 0xFF);
+  memcpy(&pkt[start + 2], passwd, plen);
+  return 0;
+}
+
+/* add user to the CONN packet */
+static int add_user(const char *username, const size_t ulen,
+                    unsigned char *pkt, const size_t start, int remain_pos)
+{
+  /* magic number that need to be set properly */
+  const size_t conn_flags_pos = remain_pos + 8;
+  if(ulen > 0xffff)
+    return 1;
+
+  /* set username flag */
+  pkt[conn_flags_pos] |= 0x80;
+  /* length of username provided */
+  pkt[start] = (unsigned char)((ulen >> 8) & 0xFF);
+  pkt[start + 1] = (unsigned char)(ulen & 0xFF);
+  memcpy(&pkt[start + 2], username, ulen);
+  return 0;
+}
+
+/* add client ID to the CONN packet */
+static int add_client_id(const char *client_id, const size_t client_id_len,
+                         char *pkt, const size_t start)
+{
+  if(client_id_len != MQTT_CLIENTID_LEN)
+    return 1;
+  pkt[start] = 0x00;
+  pkt[start + 1] = MQTT_CLIENTID_LEN;
+  memcpy(&pkt[start + 2], client_id, MQTT_CLIENTID_LEN);
+  return 0;
+}
+
+/* Set initial values of CONN packet */
+static int init_connpack(char *packet, char *remain, int remain_pos)
+{
+  /* Fixed header starts */
+  /* packet type */
+  packet[0] = MQTT_MSG_CONNECT;
+  /* remaining length field */
+  memcpy(&packet[1], remain, remain_pos);
+  /* Fixed header ends */
+
+  /* Variable header starts */
+  /* protocol length */
+  packet[remain_pos + 1] = 0x00;
+  packet[remain_pos + 2] = 0x04;
+  /* protocol name */
+  packet[remain_pos + 3] = 'M';
+  packet[remain_pos + 4] = 'Q';
+  packet[remain_pos + 5] = 'T';
+  packet[remain_pos + 6] = 'T';
+  /* protocol level */
+  packet[remain_pos + 7] = 0x04;
+  /* CONNECT flag: CleanSession */
+  packet[remain_pos + 8] = 0x02;
+  /* keep-alive 0 = disabled */
+  packet[remain_pos + 9] = 0x00;
+  packet[remain_pos + 10] = 0x3c;
+  /*end of variable header*/
+  return remain_pos + 10;
+}
+
 static CURLcode mqtt_connect(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
-  const size_t client_id_offset = 14;
-  const size_t packetlen = client_id_offset + MQTT_CLIENTID_LEN;
+  int pos = 0;
+  int rc = 0;
+  /*remain length*/
+  int remain_pos = 0;
+  char remain[4] = {0};
+  size_t packetlen = 0;
+  size_t payloadlen = 0;
+  size_t start_user = 0;
+  size_t start_pwd = 0;
   char client_id[MQTT_CLIENTID_LEN + 1] = "curl";
   const size_t clen = strlen("curl");
-  char packet[32] = {
-    MQTT_MSG_CONNECT,  /* packet type */
-    0x00,              /* remaining length */
-    0x00, 0x04,        /* protocol length */
-    'M','Q','T','T',   /* protocol name */
-    0x04,              /* protocol level */
-    0x02,              /* CONNECT flag: CleanSession */
-    0x00, 0x3c,        /* keep-alive 0 = disabled */
-    0x00, 0x00         /* payload1 length */
-  };
-  packet[1] = (packetlen - 2) & 0x7f;
-  packet[client_id_offset - 1] = MQTT_CLIENTID_LEN;
+  char *packet = NULL;
+
+  /* extracting username from request */
+  const char *username = data->state.aptr.user ?
+    data->state.aptr.user : "";
+  const size_t ulen = strlen(username);
+  /* extracting password from request */
+  const char *passwd = data->state.aptr.passwd ?
+    data->state.aptr.passwd : "";
+  const size_t plen = strlen(passwd);
+
+  payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2;
+  /* The plus 2 are for the MSB and LSB describing the length of the string to
+   * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
+  if(ulen)
+    payloadlen += 2;
+  if(plen)
+    payloadlen += 2;
+
+  /* getting how much occupy the remain length */
+  remain_pos = mqtt_encode_len(remain, payloadlen + 10);
+
+  /* 10 length of variable header and 1 the first byte of the fixed header */
+  packetlen = payloadlen + 10 + remain_pos + 1;
+
+  /* allocating packet */
+  if(packetlen > 268435455)
+    return CURLE_WEIRD_SERVER_REPLY;
+  packet = malloc(packetlen);
+  if(!packet)
+    return CURLE_OUT_OF_MEMORY;
+  memset(packet, 0, packetlen);
+
+  /* set initial values for CONN pack */
+  pos = init_connpack(packet, remain, remain_pos);
 
   result = Curl_rand_hex(data, (unsigned char *)&client_id[clen],
                          MQTT_CLIENTID_LEN - clen + 1);
-  memcpy(&packet[client_id_offset], client_id, MQTT_CLIENTID_LEN);
-  infof(data, "Using client id '%s'\n", client_id);
+  /* add client id */
+  rc = add_client_id(client_id, strlen(client_id), packet, pos + 1);
+  if(rc) {
+    failf(data, "Client ID length mismatched: [%lu]", strlen(client_id));
+    result = CURLE_WEIRD_SERVER_REPLY;
+    goto end;
+  }
+  infof(data, "Using client id '%s'", client_id);
+
+  /* position where starts the user payload */
+  start_user = pos + 3 + MQTT_CLIENTID_LEN;
+  /* position where starts the password payload */
+  start_pwd = start_user + ulen;
+  /* if user name was provided, add it to the packet */
+  if(ulen) {
+    start_pwd += 2;
+
+    rc = add_user(username, ulen,
+                  (unsigned char *)packet, start_user, remain_pos);
+    if(rc) {
+      failf(data, "Username is too large: [%lu]", ulen);
+      result = CURLE_WEIRD_SERVER_REPLY;
+      goto end;
+    }
+  }
+
+  /* if passwd was provided, add it to the packet */
+  if(plen) {
+    rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
+    if(rc) {
+      failf(data, "Password is too large: [%lu]", plen);
+      result = CURLE_WEIRD_SERVER_REPLY;
+      goto end;
+    }
+  }
+
   if(!result)
     result = mqtt_send(data, packet, packetlen);
+
+end:
+  if(packet)
+    free(packet);
+  Curl_safefree(data->state.aptr.user);
+  Curl_safefree(data->state.aptr.passwd);
   return result;
 }
 
@@ -213,35 +382,12 @@
 static CURLcode mqtt_get_topic(struct Curl_easy *data,
                                char **topic, size_t *topiclen)
 {
-  CURLcode result = CURLE_OK;
   char *path = data->state.up.path;
-
-  if(strlen(path) > 1) {
-    result = Curl_urldecode(data, path + 1, 0, topic, topiclen,
-                            REJECT_NADA);
-  }
-  else {
-    failf(data, "Error: No topic specified.");
-    result = CURLE_URL_MALFORMAT;
-  }
-  return result;
-}
-
-
-static int mqtt_encode_len(char *buf, size_t len)
-{
-  unsigned char encoded;
-  int i;
-
-  for(i = 0; (len > 0) && (i<4); i++) {
-    encoded = len % 0x80;
-    len /= 0x80;
-    if(len)
-      encoded |= 0x80;
-    buf[i] = encoded;
-  }
-
-  return i;
+  if(strlen(path) > 1)
+    return Curl_urldecode(data, path + 1, 0, topic, topiclen,
+                          REJECT_NADA);
+  failf(data, "No MQTT topic found. Forgot to URL encode it?");
+  return CURLE_URL_MALFORMAT;
 }
 
 static CURLcode mqtt_subscribe(struct Curl_easy *data)
@@ -418,7 +564,7 @@
   struct connectdata *conn = data->conn;
   struct mqtt_conn *mqtt = &conn->proto.mqtt;
 #ifdef CURLDEBUG
-  infof(data, "%s (from %s) (next is %s)\n",
+  infof(data, "%s (from %s) (next is %s)",
         statenames[state],
         statenames[mqtt->state],
         (state == MQTT_FIRST)? statenames[nextstate] : "");
@@ -465,7 +611,7 @@
       goto MQTT_SUBACK_COMING;
     }
     else if(packet == MQTT_MSG_DISCONNECT) {
-      infof(data, "Got DISCONNECT\n");
+      infof(data, "Got DISCONNECT");
       *done = TRUE;
       goto end;
     }
@@ -476,7 +622,13 @@
 
     /* -- switched state -- */
     remlen = mq->remaining_length;
-    infof(data, "Remaining length: %zd bytes\n", remlen);
+    infof(data, "Remaining length: %zd bytes", remlen);
+    if(data->set.max_filesize &&
+       (curl_off_t)remlen > data->set.max_filesize) {
+      failf(data, "Maximum file size exceeded");
+      result = CURLE_FILESIZE_EXCEEDED;
+      goto end;
+    }
     Curl_pgrsSetDownloadSize(data, remlen);
     data->req.bytecount = 0;
     data->req.size = remlen;
@@ -491,12 +643,12 @@
     result = Curl_read(data, sockfd, (char *)pkt, rest, &nread);
     if(result) {
       if(CURLE_AGAIN == result) {
-        infof(data, "EEEE AAAAGAIN\n");
+        infof(data, "EEEE AAAAGAIN");
       }
       goto end;
     }
     if(!nread) {
-      infof(data, "server disconnected\n");
+      infof(data, "server disconnected");
       result = CURLE_PARTIAL_FILE;
       goto end;
     }
@@ -562,7 +714,7 @@
       return result;
   }
 
-  infof(data, "mqtt_doing: state [%d]\n", (int) mqtt->state);
+  infof(data, "mqtt_doing: state [%d]", (int) mqtt->state);
   switch(mqtt->state) {
   case MQTT_FIRST:
     /* Read the initial byte only */
@@ -582,6 +734,10 @@
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
       pkt[mq->npacket++] = byte;
     } while((byte & 0x80) && (mq->npacket < 4));
+    if(nread && (byte & 0x80))
+      /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
+         127 * 128^3 bytes. server tried to send more */
+      result = CURLE_WEIRD_SERVER_REPLY;
     if(result)
       break;
     mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL);
@@ -593,7 +749,7 @@
     mqstate(data, MQTT_FIRST, MQTT_FIRST);
 
     if(mq->firstbyte == MQTT_MSG_DISCONNECT) {
-      infof(data, "Got DISCONNECT\n");
+      infof(data, "Got DISCONNECT");
       *done = TRUE;
     }
     break;
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index 1b3e261..518ceb5 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -169,7 +169,7 @@
       connection_id = data->conn->connection_id;
 
     infof(data,
-          "STATE: %s => %s handle %p; line %d (connection #%ld)\n",
+          "STATE: %s => %s handle %p; line %d (connection #%ld)",
           statename[oldstate], statename[data->mstate],
           (void *)data, lineno, connection_id);
   }
@@ -562,7 +562,7 @@
   struct connectdata *conn = data->conn;
   unsigned int i;
 
-  DEBUGF(infof(data, "multi_done\n"));
+  DEBUGF(infof(data, "multi_done"));
 
   if(data->state.done)
     /* Stop if multi_done() has already been called */
@@ -610,7 +610,7 @@
     /* Stop if still used. */
     CONNCACHE_UNLOCK(data);
     DEBUGF(infof(data, "Connection still in use %zu, "
-                 "no more multi_done now!\n",
+                 "no more multi_done now!",
                  conn->easyq.size));
     return CURLE_OK;
   }
@@ -687,7 +687,7 @@
     if(Curl_conncache_return_conn(data, conn)) {
       /* remember the most recently used connection */
       data->state.lastconnect_id = conn->connection_id;
-      infof(data, "%s\n", buffer);
+      infof(data, "%s", buffer);
     }
     else
       data->state.lastconnect_id = -1;
@@ -709,7 +709,6 @@
     return 1;
 
   connclose(conn, "Removing connect-only easy handle");
-  conn->bits.connect_only = FALSE;
 
   return 1;
 }
@@ -1043,16 +1042,27 @@
 
   data = multi->easyp;
   while(data) {
-    int bitmap = multi_getsock(data, sockbunch);
+    int bitmap;
+#ifdef __clang_analyzer_
+    /* to prevent "The left operand of '>=' is a garbage value" warnings */
+    memset(sockbunch, 0, sizeof(sockbunch));
+#endif
+    bitmap = multi_getsock(data, sockbunch);
 
     for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
       curl_socket_t s = CURL_SOCKET_BAD;
 
-      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK(sockbunch[i])) {
+        if(!FDSET_SOCK(sockbunch[i]))
+          /* pretend it doesn't exist */
+          continue;
         FD_SET(sockbunch[i], read_fd_set);
         s = sockbunch[i];
       }
-      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK(sockbunch[i])) {
+        if(!FDSET_SOCK(sockbunch[i]))
+          /* pretend it doesn't exist */
+          continue;
         FD_SET(sockbunch[i], write_fd_set);
         s = sockbunch[i];
       }
@@ -1096,6 +1106,9 @@
   WSANETWORKEVENTS wsa_events;
   DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
 #endif
+#ifndef ENABLE_WAKEUP
+  (void)use_wakeup;
+#endif
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -1176,7 +1189,7 @@
 #ifdef USE_WINSOCK
         long mask = 0;
 #endif
-        if(bitmap & GETSOCK_READSOCK(i)) {
+        if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
           s = sockbunch[i];
 #ifdef USE_WINSOCK
           mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
@@ -1185,7 +1198,7 @@
           ufds[nfds].events = POLLIN;
           ++nfds;
         }
-        if(bitmap & GETSOCK_WRITESOCK(i)) {
+        if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
           s = sockbunch[i];
 #ifdef USE_WINSOCK
           mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
@@ -1540,6 +1553,58 @@
 }
 
 /*
+ * Check whether a timeout occurred, and handle it if it did
+ */
+static bool multi_handle_timeout(struct Curl_easy *data,
+                                 struct curltime *now,
+                                 bool *stream_error,
+                                 CURLcode *result,
+                                 bool connect_timeout)
+{
+  timediff_t timeout_ms;
+  timeout_ms = Curl_timeleft(data, now, connect_timeout);
+
+  if(timeout_ms < 0) {
+    /* Handle timed out */
+    if(data->mstate == MSTATE_RESOLVING)
+      failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
+            " milliseconds",
+            Curl_timediff(*now, data->progress.t_startsingle));
+    else if(data->mstate == MSTATE_CONNECTING)
+      failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
+            " milliseconds",
+            Curl_timediff(*now, data->progress.t_startsingle));
+    else {
+      struct SingleRequest *k = &data->req;
+      if(k->size != -1) {
+        failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+              " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
+              CURL_FORMAT_CURL_OFF_T " bytes received",
+              Curl_timediff(*now, data->progress.t_startsingle),
+              k->bytecount, k->size);
+      }
+      else {
+        failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+              " milliseconds with %" CURL_FORMAT_CURL_OFF_T
+              " bytes received",
+              Curl_timediff(*now, data->progress.t_startsingle),
+              k->bytecount);
+      }
+    }
+
+    /* Force connection closed if the connection has indeed been used */
+    if(data->mstate > MSTATE_DO) {
+      streamclose(data->conn, "Disconnected with pending data");
+      *stream_error = TRUE;
+    }
+    *result = CURLE_OPERATION_TIMEDOUT;
+    (void)multi_done(data, *result, TRUE);
+  }
+
+  return (timeout_ms < 0);
+}
+
+/*
  * We are doing protocol-specific connecting and this is being called over and
  * over from the multi interface until the connection phase is done on
  * protocol layer.
@@ -1670,7 +1735,6 @@
   bool done = FALSE;
   CURLMcode rc;
   CURLcode result = CURLE_OK;
-  timediff_t timeout_ms;
   timediff_t recv_timeout_ms;
   timediff_t send_timeout_ms;
   int control;
@@ -1685,7 +1749,7 @@
     rc = CURLM_OK;
 
     if(multi_ischanged(multi, TRUE)) {
-      DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n"));
+      DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!"));
       process_pending_handles(multi); /* multiplexed */
     }
 
@@ -1700,47 +1764,16 @@
     if(data->conn &&
        (data->mstate >= MSTATE_CONNECT) &&
        (data->mstate < MSTATE_COMPLETED)) {
+      /* Check for overall operation timeout here but defer handling the
+       * connection timeout to later, to allow for a connection to be set up
+       * in the window since we last checked timeout. This prevents us
+       * tearing down a completed connection in the case where we were slow
+       * to check the timeout (e.g. process descheduled during this loop).
+       * We set connect_timeout=FALSE to do this. */
+
       /* we need to wait for the connect state as only then is the start time
          stored, but we must not check already completed handles */
-      timeout_ms = Curl_timeleft(data, nowp,
-                                 (data->mstate <= MSTATE_DO)?
-                                 TRUE:FALSE);
-
-      if(timeout_ms < 0) {
-        /* Handle timed out */
-        if(data->mstate == MSTATE_RESOLVING)
-          failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
-                " milliseconds",
-                Curl_timediff(*nowp, data->progress.t_startsingle));
-        else if(data->mstate == MSTATE_CONNECTING)
-          failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
-                " milliseconds",
-                Curl_timediff(*nowp, data->progress.t_startsingle));
-        else {
-          struct SingleRequest *k = &data->req;
-          if(k->size != -1) {
-            failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
-                  " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
-                  CURL_FORMAT_CURL_OFF_T " bytes received",
-                  Curl_timediff(*nowp, data->progress.t_startsingle),
-                  k->bytecount, k->size);
-          }
-          else {
-            failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
-                  " milliseconds with %" CURL_FORMAT_CURL_OFF_T
-                  " bytes received",
-                  Curl_timediff(*nowp, data->progress.t_startsingle),
-                  k->bytecount);
-          }
-        }
-
-        /* Force connection closed if the connection has indeed been used */
-        if(data->mstate > MSTATE_DO) {
-          streamclose(data->conn, "Disconnected with pending data");
-          stream_error = TRUE;
-        }
-        result = CURLE_OPERATION_TIMEDOUT;
-        (void)multi_done(data, result, TRUE);
+      if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) {
         /* Skip the statemachine and go directly to error handling section. */
         goto statemachine_end;
       }
@@ -1792,7 +1825,7 @@
       }
       else if(data->state.previouslypending) {
         /* this transfer comes from the pending queue so try move another */
-        infof(data, "Transfer was pending, now try another\n");
+        infof(data, "Transfer was pending, now try another");
         process_pending_handles(data->multi);
       }
 
@@ -1847,7 +1880,7 @@
         data->state.async.done = TRUE;
 #endif
         result = CURLE_OK;
-        infof(data, "Hostname '%s' was found in DNS cache\n", hostname);
+        infof(data, "Hostname '%s' was found in DNS cache", hostname);
       }
 
       if(!dns)
@@ -2279,7 +2312,7 @@
         CURLcode ret = Curl_retry_request(data, &newurl);
 
         if(!ret) {
-          infof(data, "Downgrades to HTTP/1.1!\n");
+          infof(data, "Downgrades to HTTP/1.1!");
           streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1");
           data->state.httpwant = CURL_HTTP_VERSION_1_1;
           /* clear the error message bit too as we ignore the one we got */
@@ -2418,6 +2451,21 @@
     default:
       return CURLM_INTERNAL_ERROR;
     }
+
+    if(data->conn &&
+       data->mstate >= MSTATE_CONNECT &&
+       data->mstate < MSTATE_DO &&
+       rc != CURLM_CALL_MULTI_PERFORM &&
+       !multi_ischanged(multi, false)) {
+      /* We now handle stream timeouts if and only if this will be the last
+       * loop iteration. We only check this on the last iteration to ensure
+       * that if we know we have additional work to do immediately
+       * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
+       * declaring the connection timed out as we may almost have a completed
+       * connection. */
+      multi_handle_timeout(data, nowp, &stream_error, &result, TRUE);
+    }
+
     statemachine_end:
 
     if(data->mstate < MSTATE_COMPLETED) {
@@ -3339,7 +3387,7 @@
     rc = Curl_splayremove(multi->timetree, &data->state.timenode,
                           &multi->timetree);
     if(rc)
-      infof(data, "Internal error removing splay node = %d\n", rc);
+      infof(data, "Internal error removing splay node = %d", rc);
   }
 
   /* Indicate that we are in the splay tree and insert the new timer expiry
@@ -3386,7 +3434,7 @@
     rc = Curl_splayremove(multi->timetree, &data->state.timenode,
                           &multi->timetree);
     if(rc)
-      infof(data, "Internal error clearing splay node = %d\n", rc);
+      infof(data, "Internal error clearing splay node = %d", rc);
 
     /* flush the timeout list too */
     while(list->size > 0) {
@@ -3394,7 +3442,7 @@
     }
 
 #ifdef DEBUGBUILD
-    infof(data, "Expire cleared (transfer %p)\n", data);
+    infof(data, "Expire cleared (transfer %p)", data);
 #endif
     nowp->tv_sec = 0;
     nowp->tv_usec = 0;
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h
index 96b8474..2e4a6ff 100644
--- a/Utilities/cmcurl/lib/multihandle.h
+++ b/Utilities/cmcurl/lib/multihandle.h
@@ -153,6 +153,9 @@
   bool recheckstate; /* see Curl_multi_connchanged */
   bool in_callback;            /* true while executing a callback */
   bool ipv6_works;
+#ifdef USE_OPENSSL
+  bool ssl_seeded;
+#endif
 };
 
 #endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c
index 13610bb..0a4ae2c 100644
--- a/Utilities/cmcurl/lib/netrc.c
+++ b/Utilities/cmcurl/lib/netrc.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -42,7 +42,8 @@
 enum host_lookup_state {
   NOTHING,
   HOSTFOUND,    /* the 'machine' keyword was found */
-  HOSTVALID     /* this is "our" machine! */
+  HOSTVALID,    /* this is "our" machine! */
+  MACDEF
 };
 
 #define NETRC_FILE_MISSING 1
@@ -84,12 +85,17 @@
     int  netrcbuffsize = (int)sizeof(netrcbuffer);
 
     while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
+      if(state == MACDEF) {
+        if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
+          state = NOTHING;
+        else
+          continue;
+      }
       tok = strtok_r(netrcbuffer, " \t\n", &tok_buf);
       if(tok && *tok == '#')
         /* treat an initial hash as a comment line */
         continue;
       while(tok) {
-
         if((login && *login) && (password && *password)) {
           done = TRUE;
           break;
@@ -97,7 +103,13 @@
 
         switch(state) {
         case NOTHING:
-          if(strcasecompare("machine", tok)) {
+          if(strcasecompare("macdef", tok)) {
+            /* Define a macro. A macro is defined with the specified name; its
+               contents begin with the next .netrc line and continue until a
+               null line (consecutive new-line characters) is encountered. */
+            state = MACDEF;
+          }
+          else if(strcasecompare("machine", tok)) {
             /* the next tok is the machine name, this is in itself the
                delimiter that starts the stuff entered for this machine,
                after this we need to search for 'login' and
@@ -109,6 +121,11 @@
             retcode = NETRC_SUCCESS; /* we did find our host */
           }
           break;
+        case MACDEF:
+          if(!strlen(tok)) {
+            state = NOTHING;
+          }
+          break;
         case HOSTFOUND:
           if(strcasecompare(host, tok)) {
             /* and yes, this is our host! */
diff --git a/Utilities/cmcurl/lib/non-ascii.c b/Utilities/cmcurl/lib/non-ascii.c
index 932cf89..3b77ae9 100644
--- a/Utilities/cmcurl/lib/non-ascii.c
+++ b/Utilities/cmcurl/lib/non-ascii.c
@@ -31,6 +31,7 @@
 #include "sendf.h"
 #include "urldata.h"
 #include "multiif.h"
+#include "strerror.h"
 
 #include "curl_memory.h"
 /* The last #include file should be: */
@@ -104,6 +105,7 @@
     iconv_t *cd = &tmpcd;
     char *input_ptr, *output_ptr;
     size_t in_bytes, out_bytes, rc;
+    char ebuffer[STRERROR_LEN];
 
     /* open an iconv conversion descriptor if necessary */
     if(data)
@@ -116,7 +118,7 @@
               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
               CURL_ICONV_CODESET_OF_NETWORK,
               CURL_ICONV_CODESET_OF_HOST,
-              errno, strerror(errno));
+              errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
         return CURLE_CONV_FAILED;
       }
     }
@@ -130,7 +132,7 @@
     if((rc == ICONV_ERROR) || (in_bytes)) {
       failf(data,
             "The Curl_convert_to_network iconv call failed with errno %i: %s",
-            errno, strerror(errno));
+            errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
       return CURLE_CONV_FAILED;
     }
 #else
@@ -170,6 +172,7 @@
     iconv_t *cd = &tmpcd;
     char *input_ptr, *output_ptr;
     size_t in_bytes, out_bytes, rc;
+    char ebuffer[STRERROR_LEN];
 
     /* open an iconv conversion descriptor if necessary */
     if(data)
@@ -182,7 +185,7 @@
               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
               CURL_ICONV_CODESET_OF_HOST,
               CURL_ICONV_CODESET_OF_NETWORK,
-              errno, strerror(errno));
+              errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
         return CURLE_CONV_FAILED;
       }
     }
@@ -196,7 +199,7 @@
     if((rc == ICONV_ERROR) || (in_bytes)) {
       failf(data,
             "Curl_convert_from_network iconv call failed with errno %i: %s",
-            errno, strerror(errno));
+            errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
       return CURLE_CONV_FAILED;
     }
 #else
@@ -237,6 +240,7 @@
     char *input_ptr;
     char *output_ptr;
     size_t in_bytes, out_bytes, rc;
+    char ebuffer[STRERROR_LEN];
 
     /* open an iconv conversion descriptor if necessary */
     if(data)
@@ -249,7 +253,7 @@
               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
               CURL_ICONV_CODESET_OF_HOST,
               CURL_ICONV_CODESET_FOR_UTF8,
-              errno, strerror(errno));
+              errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
         return CURLE_CONV_FAILED;
       }
     }
@@ -263,7 +267,7 @@
     if((rc == ICONV_ERROR) || (in_bytes)) {
       failf(data,
             "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
-            errno, strerror(errno));
+            errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
       return CURLE_CONV_FAILED;
     }
     if(output_ptr < input_ptr) {
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index 0b8bc34..fb5e743 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -247,7 +247,7 @@
 #ifdef USE_SSL
   if(conn->handler->flags & PROTOPT_SSL) {
     CURLcode result;
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &li->ssldone);
     if(result)
       return result;
@@ -270,7 +270,8 @@
   if(conn->handler->flags & PROTOPT_SSL) {
     /* Is the SSL handshake complete yet? */
     if(!li->ssldone) {
-      CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
+      CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                                     FIRSTSOCKET,
                                                      &li->ssldone);
       if(result || !li->ssldone)
         return result;
@@ -399,7 +400,7 @@
 
   connkeep(conn, "OpenLDAP do");
 
-  infof(data, "LDAP local: %s\n", data->state.url);
+  infof(data, "LDAP local: %s", data->state.url);
 
   rc = ldap_url_parse(data->state.url, &ludp);
   if(rc != LDAP_URL_SUCCESS) {
@@ -509,7 +510,7 @@
       else {
         /* successful */
         if(code == LDAP_SIZELIMIT_EXCEEDED)
-          infof(data, "There are more than %d entries\n", lr->nument);
+          infof(data, "There are more than %d entries", lr->nument);
         data->req.size = data->req.bytecount;
         *err = CURLE_OK;
         ret = 0;
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index 4811739..84c7f51 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -402,7 +402,7 @@
         clipamount = gotbytes - i;
         restart = TRUE;
         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
-                     "server response left\n",
+                     "server response left",
                      (int)clipamount));
       }
       else if(keepon) {
@@ -412,7 +412,7 @@
              with it. We keep the first bytes of the line then we throw
              away the rest. */
           infof(data, "Excessive server response line length received, "
-                "%zd bytes. Stripping\n", gotbytes);
+                "%zd bytes. Stripping", gotbytes);
           restart = TRUE;
 
           /* we keep 40 bytes since all our pingpong protocols are only
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index 9b6ea64..d3f3de6 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -75,7 +75,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
@@ -308,7 +307,7 @@
   };
 
   if(pop3c->state != newstate)
-    infof(data, "POP3 %p state change from %s to %s\n",
+    infof(data, "POP3 %p state change from %s to %s",
           (void *)pop3c, names[pop3c->state], names[newstate]);
 #endif
 
@@ -370,8 +369,9 @@
 {
   /* Start the SSL connection */
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
-                                                 &pop3c->ssldone);
+  CURLcode result =
+    Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET,
+                                 &pop3c->ssldone);
 
   if(!result) {
     if(pop3c->state != POP3_UPGRADETLS)
@@ -551,7 +551,7 @@
       result = pop3_perform_user(data, conn);
     else {
       /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported!\n");
+      infof(data, "No known authentication mechanisms supported!");
       result = CURLE_LOGIN_DENIED;
     }
   }
@@ -740,28 +740,23 @@
       }
     }
   }
-  else if(pop3code == '+') {
-    if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-      /* We don't have a SSL/TLS connection yet, but SSL is requested */
-      if(pop3c->tls_supported)
-        /* Switch to TLS connection now */
-        result = pop3_perform_starttls(data, conn);
-      else if(data->set.use_ssl == CURLUSESSL_TRY)
-        /* Fallback and carry on with authentication */
-        result = pop3_perform_authentication(data, conn);
-      else {
-        failf(data, "STLS not supported.");
-        result = CURLE_USE_SSL_FAILED;
-      }
-    }
-    else
-      result = pop3_perform_authentication(data, conn);
-  }
   else {
     /* Clear text is supported when CAPA isn't recognised */
-    pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
+    if(pop3code != '+')
+      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
 
-    result = pop3_perform_authentication(data, conn);
+    if(!data->set.use_ssl || conn->ssl[FIRSTSOCKET].use)
+      result = pop3_perform_authentication(data, conn);
+    else if(pop3code == '+' && pop3c->tls_supported)
+      /* Switch to TLS connection now */
+      result = pop3_perform_starttls(data, conn);
+    else if(data->set.use_ssl <= CURLUSESSL_TRY)
+      /* Fallback and carry on with authentication */
+      result = pop3_perform_authentication(data, conn);
+    else {
+      failf(data, "STLS not supported.");
+      result = CURLE_USE_SSL_FAILED;
+    }
   }
 
   return result;
@@ -776,6 +771,10 @@
   CURLcode result = CURLE_OK;
   (void)instate; /* no use for this yet */
 
+  /* Pipelining in response is forbidden. */
+  if(data->conn->proto.pop3c.pp.cache_size)
+    return CURLE_WEIRD_SERVER_REPLY;
+
   if(pop3code != '+') {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
       failf(data, "STARTTLS denied");
@@ -1031,7 +1030,7 @@
   struct pop3_conn *pop3c = &conn->proto.pop3c;
 
   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &pop3c->ssldone);
     if(result || !pop3c->ssldone)
       return result;
@@ -1172,7 +1171,7 @@
   struct connectdata *conn = data->conn;
   struct POP3 *pop3 = data->req.p.pop3;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* Requested no body means no transfer */
@@ -1191,7 +1190,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
@@ -1274,11 +1273,11 @@
   CURLcode result = pop3_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = pop3_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index 4bcd615..f5ef6bd 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -377,7 +377,12 @@
 {
   if(us < 1)
     return size * 1000000;
-  return (curl_off_t)((long double)size/us * 1000000);
+  else if(size < CURL_OFF_T_MAX/1000000)
+    return (size * 1000000) / us;
+  else if(us >= 1000000)
+    return size / (us / 1000000);
+  else
+    return CURL_OFF_T_MAX;
 }
 
 /* returns TRUE if it's time to show the progress meter */
diff --git a/Utilities/cmcurl/lib/quic.h b/Utilities/cmcurl/lib/quic.h
index 947f13e..b030359 100644
--- a/Utilities/cmcurl/lib/quic.h
+++ b/Utilities/cmcurl/lib/quic.h
@@ -45,7 +45,7 @@
                                 struct connectdata *conn,
                                 int sockindex,
                                 bool *connected);
-int Curl_quic_ver(char *p, size_t len);
+void Curl_quic_ver(char *p, size_t len);
 CURLcode Curl_quic_done_sending(struct Curl_easy *data);
 void Curl_quic_done(struct Curl_easy *data, bool premature);
 bool Curl_quic_data_pending(const struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c
index 951fedb..8f2c1ba 100644
--- a/Utilities/cmcurl/lib/rand.c
+++ b/Utilities/cmcurl/lib/rand.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -87,7 +87,7 @@
 
   if(!seeded) {
     struct curltime now = Curl_now();
-    infof(data, "WARNING: Using weak random seed\n");
+    infof(data, "WARNING: Using weak random seed");
     randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
     randseed = randseed * 1103515245 + 12345;
     randseed = randseed * 1103515245 + 12345;
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c
index 007d5c5..30fefb9 100644
--- a/Utilities/cmcurl/lib/rtsp.c
+++ b/Utilities/cmcurl/lib/rtsp.c
@@ -231,7 +231,7 @@
     }
     if(data->set.rtspreq == RTSPREQ_RECEIVE &&
             (data->conn->proto.rtspc.rtp_channel == -1)) {
-      infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
+      infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv);
     }
   }
 
@@ -651,7 +651,7 @@
       }
       /* We have the full RTP interleaved packet
        * Write out the header including the leading '$' */
-      DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
+      DEBUGF(infof(data, "RTP write channel %d rtp_length %d",
              rtspc->rtp_channel, rtp_length));
       result = rtp_client_write(data, &rtp[0], rtp_length + 4);
       if(result) {
@@ -682,7 +682,7 @@
   }
 
   if(rtp_dataleft && rtp[0] == '$') {
-    DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft,
+    DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft,
           *readmore ? "(READMORE)" : ""));
 
     /* Store the incomplete RTP packet for a "rewind" */
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h
index 1e9cb7d..da11ade 100644
--- a/Utilities/cmcurl/lib/rtsp.h
+++ b/Utilities/cmcurl/lib/rtsp.h
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 #ifdef USE_HYPER
-#define CURL_DISABLE_RTSP
+#define CURL_DISABLE_RTSP 1
 #endif
 
 #ifndef CURL_DISABLE_RTSP
diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h
index 4db6487..59a571d 100644
--- a/Utilities/cmcurl/lib/select.h
+++ b/Utilities/cmcurl/lib/select.h
@@ -97,8 +97,10 @@
 #if defined(TPF)
 #define VALID_SOCK(x) 1
 #define VERIFY_SOCK(x) Curl_nop_stmt
+#define FDSET_SOCK(x) 1
 #elif defined(USE_WINSOCK)
 #define VALID_SOCK(s) ((s) < INVALID_SOCKET)
+#define FDSET_SOCK(x) 1
 #define VERIFY_SOCK(x) do { \
   if(!VALID_SOCK(x)) { \
     SET_SOCKERRNO(WSAEINVAL); \
@@ -106,13 +108,17 @@
   } \
 } while(0)
 #else
-#define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
-#define VERIFY_SOCK(x) do { \
-  if(!VALID_SOCK(x)) { \
-    SET_SOCKERRNO(EINVAL); \
-    return -1; \
-  } \
-} while(0)
+#define VALID_SOCK(s) ((s) >= 0)
+
+/* If the socket is small enough to get set or read from an fdset */
+#define FDSET_SOCK(s) ((s) < FD_SETSIZE)
+
+#define VERIFY_SOCK(x) do {                     \
+    if(!VALID_SOCK(x) || !FDSET_SOCK(x)) {      \
+      SET_SOCKERRNO(EINVAL);                    \
+      return -1;                                \
+    }                                           \
+  } while(0)
 #endif
 
 #endif /* HEADER_CURL_SELECT_H */
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index e41bb80..14ca84b 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -236,29 +236,21 @@
 #endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
 
 /* Curl_infof() is for info message along the way */
+#define MAXINFO 2048
 
 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 {
+  DEBUGASSERT(!strchr(fmt, '\n'));
   if(data && data->set.verbose) {
     va_list ap;
     size_t len;
-    char print_buffer[2048 + 1];
+    char buffer[MAXINFO + 2];
     va_start(ap, fmt);
-    len = mvsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
-    /*
-     * Indicate truncation of the input by replacing the last 3 characters
-     * with "...", and transfer the newline over in case the format had one.
-     */
-    if(len >= sizeof(print_buffer)) {
-      len = strlen(fmt);
-      if(fmt[--len] == '\n')
-        msnprintf(print_buffer + (sizeof(print_buffer) - 5), 5, "...\n");
-      else
-        msnprintf(print_buffer + (sizeof(print_buffer) - 4), 4, "...");
-    }
+    len = mvsnprintf(buffer, MAXINFO, fmt, ap);
     va_end(ap);
-    len = strlen(print_buffer);
-    Curl_debug(data, CURLINFO_TEXT, print_buffer, len);
+    buffer[len++] = '\n';
+    buffer[len] = '\0';
+    Curl_debug(data, CURLINFO_TEXT, buffer, len);
   }
 }
 
@@ -274,14 +266,14 @@
     size_t len;
     char error[CURL_ERROR_SIZE + 2];
     va_start(ap, fmt);
-    (void)mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
-    len = strlen(error);
+    len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
 
     if(data->set.errorbuffer && !data->state.errorbuf) {
       strcpy(data->set.errorbuffer, error);
       data->state.errorbuf = TRUE; /* wrote error string */
     }
     error[len++] = '\n';
+    error[len] = '\0';
     Curl_debug(data, CURLINFO_TEXT, error, len);
     va_end(ap);
   }
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index fb8b86d..08827d1 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -1689,7 +1689,7 @@
     break;
   case CURLOPT_SSLCERT_BLOB:
     /*
-     * Blob that holds file name of the SSL certificate to use
+     * Blob that holds file content of the SSL certificate to use
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_CERT],
                              va_arg(param, struct curl_blob *));
@@ -1704,7 +1704,7 @@
     break;
   case CURLOPT_PROXY_SSLCERT_BLOB:
     /*
-     * Blob that holds file name of the SSL certificate to use for proxy
+     * Blob that holds file content of the SSL certificate to use for proxy
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY],
                              va_arg(param, struct curl_blob *));
@@ -1735,7 +1735,7 @@
     break;
   case CURLOPT_SSLKEY_BLOB:
     /*
-     * Blob that holds file name of the SSL key to use
+     * Blob that holds file content of the SSL key to use
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_KEY],
                              va_arg(param, struct curl_blob *));
@@ -1750,7 +1750,7 @@
     break;
   case CURLOPT_PROXY_SSLKEY_BLOB:
     /*
-     * Blob that holds file name of the SSL key to use for proxy
+     * Blob that holds file content of the SSL key to use for proxy
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY],
                              va_arg(param, struct curl_blob *));
@@ -1872,7 +1872,7 @@
     break;
   case CURLOPT_DOH_SSL_VERIFYPEER:
     /*
-     * Enable peer SSL verifying for DOH.
+     * Enable peer SSL verifying for DoH.
      */
     data->set.doh_verifypeer = (0 != va_arg(param, long)) ?
       TRUE : FALSE;
@@ -1911,7 +1911,7 @@
     break;
   case CURLOPT_DOH_SSL_VERIFYHOST:
     /*
-     * Enable verification of the host name in the peer certificate for DOH
+     * Enable verification of the host name in the peer certificate for DoH
      */
     arg = va_arg(param, long);
 
@@ -1955,7 +1955,7 @@
     break;
   case CURLOPT_DOH_SSL_VERIFYSTATUS:
     /*
-     * Enable certificate status verifying for DOH.
+     * Enable certificate status verifying for DoH.
      */
     if(!Curl_ssl_cert_status_request()) {
       result = CURLE_NOT_BUILT_IN;
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c
index c34f97e..a2e7e41 100644
--- a/Utilities/cmcurl/lib/sha256.c
+++ b/Utilities/cmcurl/lib/sha256.c
@@ -42,8 +42,9 @@
 #ifdef USE_MBEDTLS
 #include <mbedtls/version.h>
 
-#if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
-  #define HAS_RESULT_CODE_BASED_FUNCTIONS
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+   (MBEDTLS_VERSION_NUMBER < 0x03000000)
+  #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
 #endif
 #endif /* USE_MBEDTLS */
 
@@ -105,8 +106,8 @@
 
 static void SHA256_Init(SHA256_CTX *ctx)
 {
-#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_sha256_starts(ctx, 0);
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+  (void) mbedtls_sha256_starts(ctx, 0);
 #else
   (void) mbedtls_sha256_starts_ret(ctx, 0);
 #endif
@@ -116,8 +117,8 @@
                           const unsigned char *data,
                           unsigned int length)
 {
-#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_sha256_update(ctx, data, length);
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+  (void) mbedtls_sha256_update(ctx, data, length);
 #else
   (void) mbedtls_sha256_update_ret(ctx, data, length);
 #endif
@@ -125,8 +126,8 @@
 
 static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
 {
-#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_sha256_finish(ctx, digest);
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+  (void) mbedtls_sha256_finish(ctx, digest);
 #else
   (void) mbedtls_sha256_finish_ret(ctx, digest);
 #endif
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index 39facb2..fd49cf6 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -204,7 +204,7 @@
   };
 
   if(smbc->state != newstate)
-    infof(data, "SMB conn %p state change from %s to %s\n",
+    infof(data, "SMB conn %p state change from %s to %s",
           (void *)smbc, names[smbc->state], names[newstate]);
 #endif
 
@@ -230,7 +230,7 @@
   };
 
   if(req->state != newstate)
-    infof(data, "SMB request %p state change from %s to %s\n",
+    infof(data, "SMB request %p state change from %s to %s",
           (void *)req, names[req->state], names[newstate]);
 #endif
 
@@ -670,7 +670,7 @@
 #ifdef USE_SSL
     if((conn->handler->flags & PROTOPT_SSL)) {
       bool ssl_done = FALSE;
-      result = Curl_ssl_connect_nonblocking(data, conn,
+      result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                             FIRSTSOCKET, &ssl_done);
       if(result && result != CURLE_AGAIN)
         return result;
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index feffc05..02ddaca 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -78,7 +78,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
@@ -308,7 +307,7 @@
   };
 
   if(smtpc->state != newstate)
-    infof(data, "SMTP %p state change from %s to %s\n",
+    infof(data, "SMTP %p state change from %s to %s",
           (void *)smtpc, names[smtpc->state], names[newstate]);
 #endif
 
@@ -397,7 +396,8 @@
   /* Start the SSL connection */
   struct connectdata *conn = data->conn;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
+  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                                 FIRSTSOCKET,
                                                  &smtpc->ssldone);
 
   if(!result) {
@@ -484,7 +484,7 @@
       state(data, SMTP_AUTH);
     else {
       /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported!\n");
+      infof(data, "No known authentication mechanisms supported!");
       result = CURLE_LOGIN_DENIED;
     }
   }
@@ -834,6 +834,10 @@
   CURLcode result = CURLE_OK;
   (void)instate; /* no use for this yet */
 
+  /* Pipelining in response is forbidden. */
+  if(data->conn->proto.smtpc.pp.cache_size)
+    return CURLE_WEIRD_SERVER_REPLY;
+
   if(smtpcode != 220) {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
       failf(data, "STARTTLS denied, code %d", smtpcode);
@@ -1258,7 +1262,7 @@
   struct smtp_conn *smtpc = &conn->proto.smtpc;
 
   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &smtpc->ssldone);
     if(result || !smtpc->ssldone)
       return result;
@@ -1455,7 +1459,7 @@
   struct connectdata *conn = data->conn;
   struct SMTP *smtp = data->req.p.smtp;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* Requested no body means no transfer */
@@ -1495,7 +1499,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
@@ -1579,11 +1583,11 @@
   CURLcode result = smtp_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = smtp_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
diff --git a/Utilities/cmcurl/lib/socketpair.c b/Utilities/cmcurl/lib/socketpair.c
index 2c580ad..409d2ad 100644
--- a/Utilities/cmcurl/lib/socketpair.c
+++ b/Utilities/cmcurl/lib/socketpair.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2019 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -48,6 +48,10 @@
 #endif /* !INADDR_LOOPBACK */
 #endif /* !WIN32 */
 
+#include "nonblock.h" /* for curlx_nonblock */
+#include "timeval.h"  /* needed before select.h */
+#include "select.h"   /* for Curl_poll */
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -59,12 +63,11 @@
   union {
     struct sockaddr_in inaddr;
     struct sockaddr addr;
-  } a;
+  } a, a2;
   curl_socket_t listener;
   curl_socklen_t addrlen = sizeof(a.inaddr);
   int reuse = 1;
-  char data[2][12];
-  ssize_t dlen;
+  struct pollfd pfd[1];
   (void)domain;
   (void)type;
   (void)protocol;
@@ -85,7 +88,8 @@
     goto error;
   if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
     goto error;
-  if(getsockname(listener, &a.addr, &addrlen) == -1)
+  if(getsockname(listener, &a.addr, &addrlen) == -1 ||
+     addrlen < (int)sizeof(a.inaddr))
     goto error;
   if(listen(listener, 1) == -1)
     goto error;
@@ -94,18 +98,30 @@
     goto error;
   if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
     goto error;
+
+  /* use non-blocking accept to make sure we don't block forever */
+  if(curlx_nonblock(listener, TRUE) < 0)
+    goto error;
+  pfd[0].fd = listener;
+  pfd[0].events = POLLIN;
+  pfd[0].revents = 0;
+  (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */
   socks[1] = accept(listener, NULL, NULL);
   if(socks[1] == CURL_SOCKET_BAD)
     goto error;
 
   /* verify that nothing else connected */
-  msnprintf(data[0], sizeof(data[0]), "%p", socks);
-  dlen = strlen(data[0]);
-  if(swrite(socks[0], data[0], dlen) != dlen)
+  addrlen = sizeof(a.inaddr);
+  if(getsockname(socks[0], &a.addr, &addrlen) == -1 ||
+     addrlen < (int)sizeof(a.inaddr))
     goto error;
-  if(sread(socks[1], data[1], sizeof(data[1])) != dlen)
+  addrlen = sizeof(a2.inaddr);
+  if(getpeername(socks[1], &a2.addr, &addrlen) == -1 ||
+     addrlen < (int)sizeof(a2.inaddr))
     goto error;
-  if(memcmp(data[0], data[1], dlen))
+  if(a.inaddr.sin_family != a2.inaddr.sin_family ||
+     a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr ||
+     a.inaddr.sin_port != a2.inaddr.sin_port)
     goto error;
 
   sclose(listener);
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index 5cde4a4..db4c808 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -99,24 +99,24 @@
 }
 #endif
 
-#ifndef DEBUGBUILD
-#define sxstate(x,y) socksstate(x,y)
-#else
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#define DEBUG_AND_VERBOSE
 #define sxstate(x,y) socksstate(x,y, __LINE__)
+#else
+#define sxstate(x,y) socksstate(x,y)
 #endif
 
-
 /* always use this function to change state, to make debugging easier */
 static void socksstate(struct Curl_easy *data,
                        enum connect_t state
-#ifdef DEBUGBUILD
+#ifdef DEBUG_AND_VERBOSE
                        , int lineno
 #endif
 )
 {
   struct connectdata *conn = data->conn;
   enum connect_t oldstate = conn->cnnct.state;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef DEBUG_AND_VERBOSE
   /* synced with the state list in urldata.h */
   static const char * const statename[] = {
     "INIT",
@@ -146,9 +146,9 @@
 
   conn->cnnct.state = state;
 
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef DEBUG_AND_VERBOSE
   infof(data,
-        "SXSTATE: %s => %s conn %p; line %d\n",
+        "SXSTATE: %s => %s conn %p; line %d",
         statename[oldstate], statename[conn->cnnct.state], conn,
         lineno);
 #endif
@@ -214,10 +214,10 @@
     /* SOCKS4 can only do IPv4, insist! */
     conn->ip_version = CURL_IPRESOLVE_V4;
     if(conn->bits.httpproxy)
-      infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
+      infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
             protocol4a ? "a" : "", hostname, remote_port);
 
-    infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
+    infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
 
     /*
      * Compose socks4 request
@@ -244,7 +244,7 @@
         return CURLPX_RESOLVE_HOST;
       else if(rc == CURLRESOLV_PENDING) {
         sxstate(data, CONNECT_RESOLVING);
-        infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname);
+        infof(data, "SOCKS4 non-blocking resolve of %s", hostname);
         return CURLPX_OK;
       }
       sxstate(data, CONNECT_RESOLVED);
@@ -264,7 +264,7 @@
       data->state.async.dns = dns;
       data->state.async.done = TRUE;
 #endif
-      infof(data, "Hostname '%s' was found\n", hostname);
+      infof(data, "Hostname '%s' was found", hostname);
       sxstate(data, CONNECT_RESOLVED);
     }
     else {
@@ -279,18 +279,21 @@
   CONNECT_RESOLVED:
   case CONNECT_RESOLVED: {
     struct Curl_addrinfo *hp = NULL;
-    char buf[64];
     /*
      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
      * returns a Curl_addrinfo pointer that may not always look the same.
      */
-    if(dns)
+    if(dns) {
       hp = dns->addr;
-    if(hp) {
-      Curl_printable_address(hp, buf, sizeof(buf));
 
-      if(hp->ai_family == AF_INET) {
+      /* scan for the first IPv4 address */
+      while(hp && (hp->ai_family != AF_INET))
+        hp = hp->ai_next;
+
+      if(hp) {
         struct sockaddr_in *saddr_in;
+        char buf[64];
+        Curl_printable_address(hp, buf, sizeof(buf));
 
         saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
         socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
@@ -298,20 +301,19 @@
         socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
         socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
 
-        infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)\n", buf);
-      }
-      else {
-        hp = NULL; /* fail! */
-        failf(data, "SOCKS4 connection to %s not supported", buf);
-      }
+        infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
 
-      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+        Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+      }
+      else
+        failf(data, "SOCKS4 connection to %s not supported", hostname);
     }
-    if(!hp) {
+    else
       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
             hostname);
+
+    if(!hp)
       return CURLPX_RESOLVE_HOST;
-    }
   }
     /* FALLTHROUGH */
   CONNECT_REQ_INIT:
@@ -435,7 +437,7 @@
   /* Result */
   switch(socksreq[1]) {
   case 90:
-    infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
+    infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
     break;
   case 91:
     failf(data,
@@ -528,19 +530,19 @@
   switch(sx->state) {
   case CONNECT_SOCKS_INIT:
     if(conn->bits.httpproxy)
-      infof(data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
+      infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
             hostname, remote_port);
 
     /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
     if(!socks5_resolve_local && hostname_len > 255) {
       infof(data, "SOCKS5: server resolving disabled for hostnames of "
-            "length > 255 [actual len=%zu]\n", hostname_len);
+            "length > 255 [actual len=%zu]", hostname_len);
       socks5_resolve_local = TRUE;
     }
 
     if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
       infof(data,
-            "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
+            "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu",
             auth);
     if(!(auth & CURLAUTH_BASIC))
       /* disable username/password auth */
@@ -778,7 +780,7 @@
       data->state.async.dns = dns;
       data->state.async.done = TRUE;
 #endif
-      infof(data, "SOCKS5: hostname '%s' found\n", hostname);
+      infof(data, "SOCKS5: hostname '%s' found", hostname);
     }
 
     if(!dns) {
@@ -820,7 +822,7 @@
         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
+      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
     }
 #ifdef ENABLE_IPV6
     else if(hp->ai_family == AF_INET6) {
@@ -834,7 +836,7 @@
           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
+      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
     }
 #endif
     else {
@@ -858,7 +860,7 @@
       socksreq[len++] = (char) hostname_len; /* one byte address length */
       memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
       len += hostname_len;
-      infof(data, "SOCKS5 connect to %s:%d (remotely resolved)\n",
+      infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
             hostname, remote_port);
     }
     /* FALLTHROUGH */
@@ -1022,7 +1024,7 @@
     }
     sxstate(data, CONNECT_DONE);
   }
-  infof(data, "SOCKS5 request granted.\n");
+  infof(data, "SOCKS5 request granted.");
 
   *done = TRUE;
   return CURLPX_OK; /* Proxy was successful! */
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c
index 10b942a..34bfa37 100644
--- a/Utilities/cmcurl/lib/socks_gssapi.c
+++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -328,7 +328,7 @@
   user[gss_send_token.length] = '\0';
   gss_release_name(&gss_status, &gss_client_name);
   gss_release_buffer(&gss_status, &gss_send_token);
-  infof(data, "SOCKS5 server authenticated user %s with GSS-API.\n",user);
+  infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user);
   free(user);
   user = NULL;
 
@@ -344,7 +344,7 @@
   else if(gss_ret_flags & GSS_C_INTEG_FLAG)
     gss_enc = 1;
 
-  infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
+  infof(data, "SOCKS5 server supports GSS-API %s data protection.",
         (gss_enc == 0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
   /* force for the moment to no data protection */
   gss_enc = 0;
@@ -518,7 +518,7 @@
 
   (void)curlx_nonblock(sock, TRUE);
 
-  infof(data, "SOCKS5 access with%s protection granted.\n",
+  infof(data, "SOCKS5 access with%s protection granted.",
         (socksreq[0] == 0)?"out GSS-API data":
         ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
 
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c
index 813c6be..cb225b9 100644
--- a/Utilities/cmcurl/lib/socks_sspi.c
+++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -327,7 +327,7 @@
     failf(data, "Failed to determine user name.");
     return CURLE_COULDNT_CONNECT;
   }
-  infof(data, "SOCKS5 server authenticated user %s with GSS-API.\n",
+  infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
         names.sUserName);
   s_pSecFn->FreeContextBuffer(names.sUserName);
 
@@ -343,7 +343,7 @@
   else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
     gss_enc = 1;
 
-  infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
+  infof(data, "SOCKS5 server supports GSS-API %s data protection.",
         (gss_enc == 0)?"no":((gss_enc == 1)?"integrity":"confidentiality") );
   /* force to no data protection, avoid encryption/decryption for now */
   gss_enc = 0;
@@ -591,7 +591,7 @@
   }
   (void)curlx_nonblock(sock, TRUE);
 
-  infof(data, "SOCKS5 access with%s protection granted.\n",
+  infof(data, "SOCKS5 access with%s protection granted.",
         (socksreq[0] == 0)?"out GSS-API data":
         ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
 
diff --git a/Utilities/cmcurl/lib/strdup.c b/Utilities/cmcurl/lib/strdup.c
index 9af47ea..85cf33b 100644
--- a/Utilities/cmcurl/lib/strdup.c
+++ b/Utilities/cmcurl/lib/strdup.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -24,6 +24,10 @@
 
 #include <curl/curl.h>
 
+#ifdef WIN32
+#include <wchar.h>
+#endif
+
 #include "strdup.h"
 #include "curl_memory.h"
 
@@ -50,6 +54,28 @@
 }
 #endif
 
+#ifdef WIN32
+/***************************************************************************
+ *
+ * Curl_wcsdup(source)
+ *
+ * Copies the 'source' wchar string to a newly allocated buffer (that is
+ * returned).
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+wchar_t *Curl_wcsdup(const wchar_t *src)
+{
+  size_t length = wcslen(src);
+
+  if(length > (SIZE_T_MAX / sizeof(wchar_t)) - 1)
+    return (wchar_t *)NULL; /* integer overflow */
+
+  return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t));
+}
+#endif
+
 /***************************************************************************
  *
  * Curl_memdup(source, length)
diff --git a/Utilities/cmcurl/lib/strdup.h b/Utilities/cmcurl/lib/strdup.h
index 0936956..8c8a6f2 100644
--- a/Utilities/cmcurl/lib/strdup.h
+++ b/Utilities/cmcurl/lib/strdup.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -26,6 +26,9 @@
 #ifndef HAVE_STRDUP
 extern char *curlx_strdup(const char *str);
 #endif
+#ifdef WIN32
+wchar_t* Curl_wcsdup(const wchar_t* src);
+#endif
 void *Curl_memdup(const void *src, size_t buffer_length);
 void *Curl_saferealloc(void *ptr, size_t size);
 
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c
index 5298a0d..8a27197 100644
--- a/Utilities/cmcurl/lib/strerror.c
+++ b/Utilities/cmcurl/lib/strerror.c
@@ -188,8 +188,8 @@
   case CURLE_UNKNOWN_OPTION:
     return "An unknown option was passed in to libcurl";
 
-  case CURLE_TELNET_OPTION_SYNTAX :
-    return "Malformed telnet option";
+  case CURLE_SETOPT_OPTION_SYNTAX :
+    return "Malformed option provided in a setopt";
 
   case CURLE_GOT_NOTHING:
     return "Server returned nothing (no headers, no data)";
@@ -735,7 +735,7 @@
 #if defined(WIN32)
   /* 'sys_nerr' is the maximum errno number, it is not widely portable */
   if(err >= 0 && err < sys_nerr)
-    strncpy(buf, strerror(err), max);
+    strncpy(buf, sys_errlist[err], max);
   else
 #endif
   {
@@ -786,6 +786,7 @@
   }
 #else
   {
+    /* !checksrc! disable STRERROR 1 */
     const char *msg = strerror(err);
     if(msg)
       strncpy(buf, msg, max);
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index fdd137f..a81bb81 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -268,9 +268,9 @@
   if(data->set.verbose) {
     if(cmd == CURL_IAC) {
       if(CURL_TELCMD_OK(option))
-        infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
+        infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
       else
-        infof(data, "%s IAC %d\n", direction, option);
+        infof(data, "%s IAC %d", direction, option);
     }
     else {
       const char *fmt = (cmd == CURL_WILL) ? "WILL" :
@@ -287,12 +287,12 @@
           opt = NULL;
 
         if(opt)
-          infof(data, "%s %s %s\n", direction, fmt, opt);
+          infof(data, "%s %s %s", direction, fmt, opt);
         else
-          infof(data, "%s %s %d\n", direction, fmt, option);
+          infof(data, "%s %s %d", direction, fmt, option);
       }
       else
-        infof(data, "%s %d %d\n", direction, cmd, option);
+        infof(data, "%s %d %d", direction, cmd, option);
     }
   }
 }
@@ -765,8 +765,6 @@
         break;
       }
     }
-    if(direction)
-      infof(data, "\n");
   }
 }
 
@@ -834,7 +832,7 @@
           tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
         else {
           failf(data, "Syntax error in telnet option: %s", head->data);
-          result = CURLE_TELNET_OPTION_SYNTAX;
+          result = CURLE_SETOPT_OPTION_SYNTAX;
           break;
         }
         continue;
@@ -855,7 +853,7 @@
       break;
     }
     failf(data, "Syntax error in telnet option: %s", head->data);
-    result = CURLE_TELNET_OPTION_SYNTAX;
+    result = CURLE_SETOPT_OPTION_SYNTAX;
     break;
   }
 
@@ -922,12 +920,17 @@
         size_t tmplen = (strlen(v->data) + 1);
         /* Add the variable only if it fits */
         if(len + tmplen < (int)sizeof(temp)-6) {
-          if(sscanf(v->data, "%127[^,],%127s", varname, varval) == 2) {
-            msnprintf((char *)&temp[len], sizeof(temp) - len,
-                      "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
-                      CURL_NEW_ENV_VALUE, varval);
-            len += tmplen;
-          }
+          int rv;
+          char sep[2] = "";
+          varval[0] = 0;
+          rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval);
+          if(rv == 1)
+            len += msnprintf((char *)&temp[len], sizeof(temp) - len,
+                             "%c%s", CURL_NEW_ENV_VAR, varname);
+          else if(rv >= 2)
+            len += msnprintf((char *)&temp[len], sizeof(temp) - len,
+                             "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
+                             CURL_NEW_ENV_VALUE, varval);
         }
       }
       msnprintf((char *)&temp[len], sizeof(temp) - len,
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index 11150af..aae997d 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -239,7 +239,7 @@
 
   infof(state->data,
         "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T
-        ", retry %d maxtry %d\n",
+        ", retry %d maxtry %d",
         (int)state->state, timeout_ms, state->retry_time, state->retry_max);
 
   /* init RX time */
@@ -325,7 +325,7 @@
       return CURLE_TFTP_ILLEGAL;
     }
 
-    infof(data, "got option=(%s) value=(%s)\n", option, value);
+    infof(data, "got option=(%s) value=(%s)", option, value);
 
     if(checkprefix(option, TFTP_OPTION_BLKSIZE)) {
       long blksize;
@@ -356,14 +356,14 @@
       }
 
       state->blksize = (int)blksize;
-      infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK",
+      infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK",
             state->blksize, "requested", state->requested_blksize);
     }
     else if(checkprefix(option, TFTP_OPTION_TSIZE)) {
       long tsize = 0;
 
       tsize = strtol(value, NULL, 10);
-      infof(data, "%s (%ld)\n", "tsize parsed from OACK", tsize);
+      infof(data, "%s (%ld)", "tsize parsed from OACK", tsize);
 
       /* tsize should be ignored on upload: Who cares about the size of the
          remote file? */
@@ -397,7 +397,7 @@
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   struct Curl_easy *data = state->data;
 
-  infof(data, "%s\n", "Connected for transmit");
+  infof(data, "%s", "Connected for transmit");
 #endif
   state->state = TFTP_STATE_TX;
   result = tftp_set_timeouts(state);
@@ -413,7 +413,7 @@
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   struct Curl_easy *data = state->data;
 
-  infof(data, "%s\n", "Connected for receive");
+  infof(data, "%s", "Connected for receive");
 #endif
   state->state = TFTP_STATE_RX;
   result = tftp_set_timeouts(state);
@@ -596,12 +596,12 @@
     else if(state->block == rblock) {
       /* This is the last recently received block again. Log it and ACK it
          again. */
-      infof(data, "Received last DATA packet block %d again.\n", rblock);
+      infof(data, "Received last DATA packet block %d again.", rblock);
     }
     else {
       /* totally unexpected, just log it */
       infof(data,
-            "Received unexpected DATA packet block %d, expecting block %d\n",
+            "Received unexpected DATA packet block %d, expecting block %d",
             rblock, NEXT_BLOCKNUM(state->block));
       break;
     }
@@ -653,7 +653,7 @@
     /* Increment the retry count and fail if over the limit */
     state->retries++;
     infof(data,
-          "Timeout waiting for block %d ACK.  Retries = %d\n",
+          "Timeout waiting for block %d ACK.  Retries = %d",
           NEXT_BLOCKNUM(state->block), state->retries);
     if(state->retries > state->retry_max) {
       state->error = TFTP_ERR_TIMEOUT;
@@ -720,11 +720,11 @@
          /* There's a bug in tftpd-hpa that causes it to send us an ack for
           * 65535 when the block number wraps to 0. So when we're expecting
           * 0, also accept 65535. See
-          * http://syslinux.zytor.com/archives/2010-September/015253.html
+          * https://www.syslinux.org/archives/2010-September/015612.html
           * */
          !(state->block == 0 && rblock == 65535)) {
         /* This isn't the expected block.  Log it and up the retry counter */
-        infof(data, "Received ACK for block %d, expecting %d\n",
+        infof(data, "Received ACK for block %d, expecting %d",
               rblock, state->block);
         state->retries++;
         /* Bail out if over the maximum */
@@ -797,7 +797,7 @@
     /* Increment the retry counter and log the timeout */
     state->retries++;
     infof(data, "Timeout waiting for block %d ACK. "
-          " Retries = %d\n", NEXT_BLOCKNUM(state->block), state->retries);
+          " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
     /* Decide if we've had enough */
     if(state->retries > state->retry_max) {
       state->error = TFTP_ERR_TIMEOUT;
@@ -906,22 +906,22 @@
 
   switch(state->state) {
   case TFTP_STATE_START:
-    DEBUGF(infof(data, "TFTP_STATE_START\n"));
+    DEBUGF(infof(data, "TFTP_STATE_START"));
     result = tftp_send_first(state, event);
     break;
   case TFTP_STATE_RX:
-    DEBUGF(infof(data, "TFTP_STATE_RX\n"));
+    DEBUGF(infof(data, "TFTP_STATE_RX"));
     result = tftp_rx(state, event);
     break;
   case TFTP_STATE_TX:
-    DEBUGF(infof(data, "TFTP_STATE_TX\n"));
+    DEBUGF(infof(data, "TFTP_STATE_TX"));
     result = tftp_tx(state, event);
     break;
   case TFTP_STATE_FIN:
-    infof(data, "%s\n", "TFTP finished");
+    infof(data, "%s", "TFTP finished");
     break;
   default:
-    DEBUGF(infof(data, "STATE: %d\n", state->state));
+    DEBUGF(infof(data, "STATE: %d", state->state));
     failf(data, "%s", "Internal state machine error");
     result = CURLE_TFTP_ILLEGAL;
     break;
@@ -1153,7 +1153,7 @@
       size_t strn = state->rbytes - 4;
       state->error = (tftp_error_t)error;
       if(tftp_strnlen(str, strn) < strn)
-        infof(data, "TFTP error: %s\n", str);
+        infof(data, "TFTP error: %s", str);
       break;
     }
     case TFTP_EVENT_ACK:
@@ -1288,7 +1288,7 @@
   result = tftp_multi_statemach(data, dophase_done);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   else if(!result) {
     /* The multi code doesn't have this logic for the DOING state so we
@@ -1325,7 +1325,7 @@
   tftp_multi_statemach(data, dophase_done);
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index bca4e54..05fec79 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -188,7 +188,7 @@
     /* at this point we already verified that the callback exists
        so we compile and store the trailers buffer, then proceed */
     infof(data,
-          "Moving trailers state machine from initialized to sending.\n");
+          "Moving trailers state machine from initialized to sending.");
     data->state.trailers_state = TRAILERS_SENDING;
     Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS);
 
@@ -211,7 +211,7 @@
       curl_slist_free_all(trailers);
       return result;
     }
-    infof(data, "Successfully compiled trailers.\r\n");
+    infof(data, "Successfully compiled trailers.");
     curl_slist_free_all(trailers);
   }
 #endif
@@ -376,7 +376,7 @@
       data->set.trailer_callback = NULL;
       /* mark the transfer as done */
       data->req.upload_done = TRUE;
-      infof(data, "Signaling end of chunked upload after trailers.\n");
+      infof(data, "Signaling end of chunked upload after trailers.");
     }
     else
 #endif
@@ -385,7 +385,7 @@
         /* mark this as done once this chunk is transferred */
         data->req.upload_done = TRUE;
         infof(data,
-              "Signaling end of chunked upload via terminating chunk.\n");
+              "Signaling end of chunked upload via terminating chunk.");
       }
 
     if(added_crlf)
@@ -463,7 +463,7 @@
       err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
                                    data->set.ioctl_client);
       Curl_set_in_callback(data, false);
-      infof(data, "the ioctl callback returned %d\n", (int)err);
+      infof(data, "the ioctl callback returned %d", (int)err);
 
       if(err) {
         failf(data, "ioctl callback returned error %d", (int)err);
@@ -530,7 +530,7 @@
   default:
     if(timeofdoc <= data->set.timevalue) {
       infof(data,
-            "The requested document is not new enough\n");
+            "The requested document is not new enough");
       data->info.timecond = TRUE;
       return FALSE;
     }
@@ -538,7 +538,7 @@
   case CURL_TIMECOND_IFUNMODSINCE:
     if(timeofdoc >= data->set.timevalue) {
       infof(data,
-            "The requested document is not old enough\n");
+            "The requested document is not old enough");
       data->info.timecond = TRUE;
       return FALSE;
     }
@@ -615,7 +615,7 @@
     else {
       /* read nothing but since we wanted nothing we consider this an OK
          situation to proceed from */
-      DEBUGF(infof(data, "readwrite_data: we're done!\n"));
+      DEBUGF(infof(data, "readwrite_data: we're done!"));
       nread = 0;
     }
 
@@ -638,10 +638,10 @@
          server closed the connection and we bail out from this! */
 #ifdef USE_NGHTTP2
       if(is_http2 && !nread)
-        DEBUGF(infof(data, "nread == 0, stream closed, bailing\n"));
+        DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
       else
 #endif
-        DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
+        DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
       k->keepon &= ~KEEP_RECV;
       break;
     }
@@ -684,7 +684,7 @@
           infof(data,
                 "Excess found:"
                 " excess = %zd"
-                " url = %s (zero-length body)\n",
+                " url = %s (zero-length body)",
                 nread, data->state.up.path);
         }
 
@@ -764,7 +764,7 @@
              written to the client. */
           if(conn->chunk.datasize) {
             infof(data, "Leftovers after chunking: % "
-                  CURL_FORMAT_CURL_OFF_T "u bytes\n",
+                  CURL_FORMAT_CURL_OFF_T "u bytes",
                   conn->chunk.datasize);
           }
         }
@@ -775,7 +775,7 @@
       /* Account for body content stored in the header buffer */
       if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) {
         size_t headlen = Curl_dyn_len(&data->state.headerb);
-        DEBUGF(infof(data, "Increasing bytecount by %zu\n", headlen));
+        DEBUGF(infof(data, "Increasing bytecount by %zu", headlen));
         k->bytecount += headlen;
       }
 
@@ -789,7 +789,7 @@
                 " excess = %zu"
                 ", size = %" CURL_FORMAT_CURL_OFF_T
                 ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
-                ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
+                ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
                 excess, k->size, k->maxdownload, k->bytecount);
           connclose(conn, "excess found in a read");
         }
@@ -898,7 +898,7 @@
     /* When we've read the entire thing and the close bit is set, the server
        may now close the connection. If there's now any kind of sending going
        on from our side, we need to stop that immediately. */
-    infof(data, "we are done reading and this is set to close, stop send\n");
+    infof(data, "we are done reading and this is set to close, stop send");
     k->keepon &= ~KEEP_SEND; /* no writing anymore either */
   }
 
@@ -923,7 +923,7 @@
   return CURLE_OK;
 }
 
-#if defined(WIN32) && !defined(USE_LWIPSOCK)
+#if defined(WIN32) && defined(USE_WINSOCK)
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #endif
@@ -1128,7 +1128,7 @@
        (k->writebytecount == data->state.infilesize)) {
       /* we have sent all data we were supposed to */
       k->upload_done = TRUE;
-      infof(data, "We are completely uploaded and fine\n");
+      infof(data, "We are completely uploaded and fine");
     }
 
     if(k->upload_present != bytes_written) {
@@ -1199,7 +1199,7 @@
 
   if(data->state.drain) {
     select_res |= CURL_CSELECT_IN;
-    DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data\n"));
+    DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data"));
   }
 
   if(!select_res) /* Call for select()/poll() only, if read/write/error
@@ -1212,8 +1212,12 @@
   }
 
 #ifdef USE_HYPER
-  if(conn->datastream)
-    return conn->datastream(data, conn, &didwhat, done, select_res);
+  if(conn->datastream) {
+    result = conn->datastream(data, conn, &didwhat, done, select_res);
+    if(result || *done)
+      return result;
+  }
+  else {
 #endif
   /* We go ahead and do a read if we have a readable socket or if
      the stream was rewound (in which case we have data in a
@@ -1232,6 +1236,9 @@
     if(result)
       return result;
   }
+#ifdef USE_HYPER
+  }
+#endif
 
   k->now = Curl_now();
   if(!didwhat) {
@@ -1256,7 +1263,7 @@
         k->exp100 = EXP100_SEND_DATA;
         k->keepon |= KEEP_SEND;
         Curl_expire_done(data, EXPIRE_100_TIMEOUT);
-        infof(data, "Done waiting for 100-continue\n");
+        infof(data, "Done waiting for 100-continue");
       }
     }
   }
@@ -1496,7 +1503,7 @@
     }
 #endif
     Curl_http2_init_state(&data->state);
-    Curl_hsts_loadcb(data, data->hsts);
+    result = Curl_hsts_loadcb(data, data->hsts);
   }
 
   /*
@@ -1632,7 +1639,8 @@
   DEBUGASSERT(data->state.uh);
   uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
                     (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
-                    ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) );
+                    ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
+                    CURLU_ALLOW_SPACE);
   if(uc) {
     if(type != FOLLOW_FAKE)
       return Curl_uc_to_curlcode(uc);
@@ -1671,7 +1679,7 @@
   data->state.url = newurl;
   data->state.url_alloc = TRUE;
 
-  infof(data, "Issue another request to this URL: '%s'\n", data->state.url);
+  infof(data, "Issue another request to this URL: '%s'", data->state.url);
 
   /*
    * We get here when the HTTP code is 300-399 (and 401). We need to perform
@@ -1714,7 +1722,7 @@
         || data->state.httpreq == HTTPREQ_POST_FORM
         || data->state.httpreq == HTTPREQ_POST_MIME)
        && !(data->set.keep_post & CURL_REDIR_POST_301)) {
-      infof(data, "Switch from POST to GET\n");
+      infof(data, "Switch from POST to GET");
       data->state.httpreq = HTTPREQ_GET;
     }
     break;
@@ -1739,7 +1747,7 @@
         || data->state.httpreq == HTTPREQ_POST_FORM
         || data->state.httpreq == HTTPREQ_POST_MIME)
        && !(data->set.keep_post & CURL_REDIR_POST_302)) {
-      infof(data, "Switch from POST to GET\n");
+      infof(data, "Switch from POST to GET");
       data->state.httpreq = HTTPREQ_GET;
     }
     break;
@@ -1757,7 +1765,7 @@
         !(data->set.keep_post & CURL_REDIR_POST_303))) {
       data->state.httpreq = HTTPREQ_GET;
       data->set.upload = false;
-      infof(data, "Switch to %s\n",
+      infof(data, "Switch to %s",
             data->set.opt_no_body?"HEAD":"GET");
     }
     break;
@@ -1818,7 +1826,7 @@
        to issue again, but the nghttp2 API can deliver the message to other
        streams as well, which is why this adds the check the data counters
        too. */
-    infof(data, "REFUSED_STREAM, retrying a fresh connect\n");
+    infof(data, "REFUSED_STREAM, retrying a fresh connect");
     data->state.refused_stream = FALSE; /* clear again */
     retry = TRUE;
   }
@@ -1830,8 +1838,8 @@
       data->state.retrycount = 0;
       return CURLE_SEND_ERROR;
     }
-    infof(data, "Connection died, retrying a fresh connect\
-(retry count: %d)\n", data->state.retrycount);
+    infof(data, "Connection died, retrying a fresh connect (retry count: %d)",
+          data->state.retrycount);
     *url = strdup(data->state.url);
     if(!*url)
       return CURLE_OUT_OF_MEMORY;
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 1ee38af..37b6c0e 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -62,6 +62,14 @@
 #ifdef USE_LIBIDN2
 #include <idn2.h>
 
+#if defined(WIN32) && defined(UNICODE)
+#define IDN2_LOOKUP(name, host, flags) \
+  idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
+#else
+#define IDN2_LOOKUP(name, host, flags) \
+  idn2_lookup_ul((const char *)name, (char **)host, flags)
+#endif
+
 #elif defined(USE_WIN32_IDN)
 /* prototype for curl_win32_idn_to_ascii() */
 bool curl_win32_idn_to_ascii(const char *in, char **out);
@@ -92,7 +100,6 @@
 #include "speedcheck.h"
 #include "warnless.h"
 #include "non-ascii.h"
-#include "inet_pton.h"
 #include "getinfo.h"
 #include "urlapi-int.h"
 #include "system_win32.h"
@@ -726,7 +733,16 @@
 {
   DEBUGASSERT(conn);
   DEBUGASSERT(data);
-  infof(data, "Closing connection %ld\n", conn->connection_id);
+  infof(data, "Closing connection %ld", conn->connection_id);
+
+#ifndef USE_HYPER
+  if(conn->connect_state && conn->connect_state->prot_save) {
+    /* If this was closed with a CONNECT in progress, cleanup this temporary
+       struct arrangement */
+    data->req.p.http = NULL;
+    Curl_safefree(conn->connect_state->prot_save);
+  }
+#endif
 
   /* possible left-overs from the async name resolvers */
   Curl_resolver_cancel(data);
@@ -824,7 +840,7 @@
    * are other users of it
    */
   if(CONN_INUSE(conn) && !dead_connection) {
-    DEBUGF(infof(data, "Curl_disconnect when inuse: %zu\n", CONN_INUSE(conn)));
+    DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
     return CURLE_OK;
   }
 
@@ -957,7 +973,7 @@
   idletime /= 1000; /* integer seconds is fine */
 
   if(idletime > data->set.maxage_conn) {
-    infof(data, "Too old connection (%ld seconds), disconnect it\n",
+    infof(data, "Too old connection (%ld seconds), disconnect it",
           idletime);
     return TRUE;
   }
@@ -1006,7 +1022,7 @@
     }
 
     if(dead) {
-      infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);
+      infof(data, "Connection %ld seems to be dead!", conn->connection_id);
       Curl_conncache_remove_conn(data, conn, FALSE);
       return TRUE;
     }
@@ -1122,7 +1138,7 @@
     /* Max pipe length is zero (unlimited) for multiplexed connections */
     struct Curl_llist_element *curr;
 
-    infof(data, "Found bundle for host %s: %p [%s]\n",
+    infof(data, "Found bundle for host %s: %p [%s]",
           hostbundle, (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ?
                                        "can multiplex" : "serially"));
 
@@ -1130,22 +1146,22 @@
     if(canmultiplex) {
       if(bundle->multiuse == BUNDLE_UNKNOWN) {
         if(data->set.pipewait) {
-          infof(data, "Server doesn't support multiplex yet, wait\n");
+          infof(data, "Server doesn't support multiplex yet, wait");
           *waitpipe = TRUE;
           CONNCACHE_UNLOCK(data);
           return FALSE; /* no re-use */
         }
 
-        infof(data, "Server doesn't support multiplex (yet)\n");
+        infof(data, "Server doesn't support multiplex (yet)");
         canmultiplex = FALSE;
       }
       if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
          !Curl_multiplex_wanted(data->multi)) {
-        infof(data, "Could multiplex, but not asked to!\n");
+        infof(data, "Could multiplex, but not asked to!");
         canmultiplex = FALSE;
       }
       if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
-        infof(data, "Can not multiplex, even if we wanted to!\n");
+        infof(data, "Can not multiplex, even if we wanted to!");
         canmultiplex = FALSE;
       }
     }
@@ -1193,7 +1209,7 @@
              completed yet and until then we don't re-use this connection */
           if(!check->primary_ip[0]) {
             infof(data,
-                  "Connection #%ld is still name resolving, can't reuse\n",
+                  "Connection #%ld is still name resolving, can't reuse",
                   check->connection_id);
             continue;
           }
@@ -1202,7 +1218,7 @@
         if(check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) {
           foundPendingCandidate = TRUE;
           /* Don't pick a connection that hasn't connected yet */
-          infof(data, "Connection #%ld isn't open enough, can't reuse\n",
+          infof(data, "Connection #%ld isn't open enough, can't reuse",
                 check->connection_id);
           continue;
         }
@@ -1357,7 +1373,7 @@
                                         &check->ssl_config)) {
               DEBUGF(infof(data,
                            "Connection #%ld has different SSL parameters, "
-                           "can't reuse\n",
+                           "can't reuse",
                            check->connection_id));
               continue;
             }
@@ -1365,7 +1381,7 @@
               foundPendingCandidate = TRUE;
               DEBUGF(infof(data,
                            "Connection #%ld has not started SSL connect, "
-                           "can't reuse\n",
+                           "can't reuse",
                            check->connection_id));
               continue;
             }
@@ -1452,14 +1468,14 @@
             /* Multiplexed connections can only be HTTP/2 for now */
             struct http_conn *httpc = &check->proto.httpc;
             if(multiplexed >= httpc->settings.max_concurrent_streams) {
-              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n",
+              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
                     multiplexed);
               continue;
             }
             else if(multiplexed >=
                     Curl_multi_max_concurrent_streams(data->multi)) {
               infof(data, "client side MAX_CONCURRENT_STREAMS reached"
-                    ", skip (%zu)\n",
+                    ", skip (%zu)",
                     multiplexed);
               continue;
             }
@@ -1467,7 +1483,7 @@
 #endif
           /* When not multiplexed, we have a match here! */
           chosen = check;
-          infof(data, "Multiplexed connection found!\n");
+          infof(data, "Multiplexed connection found!");
           break;
         }
         else {
@@ -1490,7 +1506,7 @@
 
   if(foundPendingCandidate && data->set.pipewait) {
     infof(data,
-          "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set\n");
+          "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
     *waitpipe = TRUE;
   }
 
@@ -1505,7 +1521,7 @@
                          struct connectdata *conn)
 {
   if(data->set.verbose)
-    infof(data, "Connected to %s (%s) port %ld (#%ld)\n",
+    infof(data, "Connected to %s (%s) port %u (#%ld)",
 #ifndef CURL_DISABLE_PROXY
           conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
           conn->bits.httpproxy ? conn->http_proxy.host.dispname :
@@ -1577,12 +1593,12 @@
 #else
       int flags = IDN2_NFC_INPUT;
 #endif
-      int rc = idn2_lookup_ul((const char *)host->name, &ace_hostname, flags);
+      int rc = IDN2_LOOKUP(host->name, &ace_hostname, flags);
       if(rc != IDN2_OK)
         /* fallback to TR46 Transitional mode for better IDNA2003
            compatibility */
-        rc = idn2_lookup_ul((const char *)host->name, &ace_hostname,
-                            IDN2_TRANSITIONAL);
+        rc = IDN2_LOOKUP(host->name, &ace_hostname,
+                         IDN2_TRANSITIONAL);
       if(rc == IDN2_OK) {
         host->encalloc = (char *)ace_hostname;
         /* change the name pointer to point to the encoded hostname */
@@ -1609,7 +1625,7 @@
       return CURLE_URL_MALFORMAT;
     }
 #else
-    infof(data, "IDN support not present, can't parse Unicode domains\n");
+    infof(data, "IDN support not present, can't parse Unicode domains");
 #endif
   }
   return CURLE_OK;
@@ -1876,9 +1892,13 @@
 #else
       scopeidx = if_nametoindex(zoneid);
 #endif
-      if(!scopeidx)
-        infof(data, "Invalid zoneid: %s; %s\n", zoneid,
-              strerror(errno));
+      if(!scopeidx) {
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+        char buffer[STRERROR_LEN];
+        infof(data, "Invalid zoneid: %s; %s", zoneid,
+              Curl_strerror(errno, buffer, sizeof(buffer)));
+#endif
+      }
       else
         conn->scope_id = scopeidx;
     }
@@ -1934,7 +1954,7 @@
                      CURLU_DISALLOW_USER : 0) |
                     (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
     if(uc) {
-      DEBUGF(infof(data, "curl_url_set rejected %s\n", data->state.url));
+      DEBUGF(infof(data, "curl_url_set rejected %s", data->state.url));
       return Curl_uc_to_curlcode(uc);
     }
 
@@ -1979,7 +1999,7 @@
       }
       data->state.url = url;
       data->state.url_alloc = TRUE;
-      infof(data, "Switched from HTTP to HTTPS due to HSTS => %s\n",
+      infof(data, "Switched from HTTP to HTTPS due to HSTS => %s",
             data->state.url);
     }
   }
@@ -2333,7 +2353,7 @@
     }
   }
   if(proxy)
-    infof(data, "Uses proxy env variable %s == '%s'\n", envp, proxy);
+    infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
 
   return proxy;
 }
@@ -2448,7 +2468,7 @@
     conn->bits.proxy_user_passwd = TRUE; /* enable it */
   }
 
-  curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
+  (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
 
   if(portptr) {
     port = (int)strtol(portptr, NULL, 10);
@@ -2576,7 +2596,7 @@
       no_proxy = curl_getenv(p);
     }
     if(no_proxy) {
-      infof(data, "Uses proxy env variable %s == '%s'\n", p, no_proxy);
+      infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy);
     }
   }
 
@@ -2871,11 +2891,13 @@
   char **passwdp = &conn->passwd;
   char **optionsp = &conn->options;
 
+#ifndef CURL_DISABLE_NETRC
   if(data->set.use_netrc == CURL_NETRC_REQUIRED && conn->bits.user_passwd) {
     Curl_safefree(*userp);
     Curl_safefree(*passwdp);
     conn->bits.user_passwd = FALSE; /* disable user+password */
   }
+#endif
 
   if(data->set.str[STRING_OPTIONS]) {
     free(*optionsp);
@@ -2884,6 +2906,7 @@
       return CURLE_OUT_OF_MEMORY;
   }
 
+#ifndef CURL_DISABLE_NETRC
   conn->bits.netrc = FALSE;
   if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
     bool netrc_user_changed = FALSE;
@@ -2895,7 +2918,7 @@
                           &netrc_user_changed, &netrc_passwd_changed,
                           data->set.str[STRING_NETRC_FILE]);
     if(ret > 0) {
-      infof(data, "Couldn't find host %s in the %s file; using defaults\n",
+      infof(data, "Couldn't find host %s in the %s file; using defaults",
             conn->host.name, data->set.str[STRING_NETRC_FILE]);
     }
     else if(ret < 0) {
@@ -2909,6 +2932,7 @@
       conn->bits.user_passwd = TRUE; /* enable user+password */
     }
   }
+#endif
 
   /* for updated strings, we update them in the URL */
   if(*userp) {
@@ -2996,6 +3020,7 @@
   char *host_portno;
   char *portptr;
   int port = -1;
+  CURLcode result = CURLE_OK;
 
 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
   (void) data;
@@ -3025,7 +3050,7 @@
     if(*ptr == '%') {
       /* There might be a zone identifier */
       if(strncmp("%25", ptr, 3))
-        infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
+        infof(data, "Please URL encode %% as %%25, see RFC 6874.");
       ptr++;
       /* Allow unreserved characters as defined in RFC 3986 */
       while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
@@ -3036,7 +3061,7 @@
       /* yeps, it ended nicely with a bracket as well */
       *ptr++ = '\0';
     else
-      infof(data, "Invalid IPv6 address format\n");
+      infof(data, "Invalid IPv6 address format");
     portptr = ptr;
     /* Note that if this didn't end with a bracket, we still advanced the
      * hostptr first, but I can't see anything wrong with that as no host
@@ -3044,8 +3069,8 @@
      */
 #else
     failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in!");
-    free(host_dup);
-    return CURLE_NOT_BUILT_IN;
+    result = CURLE_NOT_BUILT_IN;
+    goto error;
 #endif
   }
 
@@ -3058,10 +3083,10 @@
     if(*host_portno) {
       long portparse = strtol(host_portno, &endp, 10);
       if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
-        infof(data, "No valid port number in connect to host string (%s)\n",
+        failf(data, "No valid port number in connect to host string (%s)",
               host_portno);
-        hostptr = NULL;
-        port = -1;
+        result = CURLE_SETOPT_OPTION_SYNTAX;
+        goto error;
       }
       else
         port = (int)portparse; /* we know it will fit */
@@ -3072,15 +3097,16 @@
   if(hostptr) {
     *hostname_result = strdup(hostptr);
     if(!*hostname_result) {
-      free(host_dup);
-      return CURLE_OUT_OF_MEMORY;
+      result = CURLE_OUT_OF_MEMORY;
+      goto error;
     }
   }
 
   *port_result = port;
 
+  error:
   free(host_dup);
-  return CURLE_OK;
+  return result;
 }
 
 /*
@@ -3176,7 +3202,7 @@
       conn->conn_to_host.name = host;
       conn->bits.conn_to_host = TRUE;
 
-      infof(data, "Connecting to hostname: %s\n", host);
+      infof(data, "Connecting to hostname: %s", host);
     }
     else {
       /* no "connect to host" */
@@ -3187,7 +3213,7 @@
     if(port >= 0) {
       conn->conn_to_port = port;
       conn->bits.conn_to_port = TRUE;
-      infof(data, "Connecting to port: %d\n", port);
+      infof(data, "Connecting to port: %d", port);
     }
     else {
       /* no "connect to port" */
@@ -3248,7 +3274,7 @@
       conn->conn_to_port = as->dst.port;
       conn->bits.conn_to_port = TRUE;
       conn->bits.altused = TRUE;
-      infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d\n",
+      infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d",
             Curl_alpnid2str(srcalpnid), host, conn->remote_port,
             Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port);
       if(srcalpnid != as->dst.alpnid) {
@@ -3356,9 +3382,12 @@
       if(rc == CURLRESOLV_PENDING)
         *async = TRUE;
 
-      else if(rc == CURLRESOLV_TIMEDOUT)
+      else if(rc == CURLRESOLV_TIMEDOUT) {
+        failf(data, "Failed to resolve host '%s' with timeout after %ld ms",
+              connhost->dispname,
+              Curl_timediff(Curl_now(), data->progress.t_startsingle));
         result = CURLE_OPERATION_TIMEDOUT;
-
+      }
       else if(!hostaddr) {
         failf(data, "Could not resolve host: %s", connhost->dispname);
         result = CURLE_COULDNT_RESOLVE_HOST;
@@ -3600,7 +3629,7 @@
   if(result)
     goto out;
 
-  /* Check for overridden login details and set them accordingly so they
+  /* Check for overridden login details and set them accordingly so that
      they are known when protocol->setup_connection is called! */
   result = override_login(data, conn);
   if(result)
@@ -3736,6 +3765,8 @@
   */
   data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
   data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
+  data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
+  data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
   data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
   data->set.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
   data->set.ssl.primary.cipher_list =
@@ -3763,8 +3794,11 @@
   data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
   data->set.proxy_ssl.primary.ca_info_blob =
     data->set.blobs[BLOB_CAINFO_PROXY];
+  data->set.proxy_ssl.primary.issuercert =
+    data->set.str[STRING_SSL_ISSUERCERT_PROXY];
+  data->set.proxy_ssl.primary.issuercert_blob =
+    data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
   data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
-  data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
   data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
   data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY];
   data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
@@ -3773,7 +3807,6 @@
   data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
 #endif
   data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
-  data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
   data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE];
   data->set.ssl.key = data->set.str[STRING_KEY];
   data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE];
@@ -3787,9 +3820,7 @@
   data->set.proxy_ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
 #endif
 #endif
-
   data->set.ssl.key_blob = data->set.blobs[BLOB_KEY];
-  data->set.ssl.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
 
   if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
                                     &conn->ssl_config)) {
@@ -3841,14 +3872,14 @@
     *in_connect = conn;
 
 #ifndef CURL_DISABLE_PROXY
-    infof(data, "Re-using existing connection! (#%ld) with %s %s\n",
+    infof(data, "Re-using existing connection! (#%ld) with %s %s",
           conn->connection_id,
           conn->bits.proxy?"proxy":"host",
           conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
           conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
           conn->host.dispname);
 #else
-    infof(data, "Re-using existing connection! (#%ld) with host %s\n",
+    infof(data, "Re-using existing connection! (#%ld) with host %s",
           conn->connection_id, conn->host.dispname);
 #endif
   }
@@ -3888,7 +3919,7 @@
         if(conn_candidate)
           (void)Curl_disconnect(data, conn_candidate, FALSE);
         else {
-          infof(data, "No more connections allowed to host %s: %zu\n",
+          infof(data, "No more connections allowed to host %s: %zu",
                 bundlehost, max_host_connections);
           connections_available = FALSE;
         }
@@ -3908,13 +3939,13 @@
       if(conn_candidate)
         (void)Curl_disconnect(data, conn_candidate, FALSE);
       else {
-        infof(data, "No connections available in cache\n");
+        infof(data, "No connections available in cache");
         connections_available = FALSE;
       }
     }
 
     if(!connections_available) {
-      infof(data, "No connections available.\n");
+      infof(data, "No connections available.");
 
       conn_free(conn);
       *in_connect = NULL;
@@ -3939,14 +3970,14 @@
        connection based. */
     if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
        data->state.authhost.done) {
-      infof(data, "NTLM picked AND auth done set, clear picked!\n");
+      infof(data, "NTLM picked AND auth done set, clear picked!");
       data->state.authhost.picked = CURLAUTH_NONE;
       data->state.authhost.done = FALSE;
     }
 
     if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
        data->state.authproxy.done) {
-      infof(data, "NTLM-proxy picked AND auth done set, clear picked!\n");
+      infof(data, "NTLM-proxy picked AND auth done set, clear picked!");
       data->state.authproxy.picked = CURLAUTH_NONE;
       data->state.authproxy.done = FALSE;
     }
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index 6483208..7f03862 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -131,7 +131,7 @@
  */
 static bool urlchar_needs_escaping(int c)
 {
-    return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
+  return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
 }
 
 /*
@@ -580,7 +580,7 @@
 }
 
 /* scan for byte values < 31 or 127 */
-static CURLUcode junkscan(const char *part)
+static bool junkscan(const char *part, unsigned int flags)
 {
   if(part) {
     static const char badbytes[]={
@@ -588,17 +588,18 @@
       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-      0x7f,
-      0x00 /* null-terminate */
+      0x7f, 0x00 /* null-terminate */
     };
     size_t n = strlen(part);
     size_t nfine = strcspn(part, badbytes);
     if(nfine != n)
       /* since we don't know which part is scanned, return a generic error
          code */
-      return CURLUE_MALFORMED_INPUT;
+      return TRUE;
+    if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' '))
+      return TRUE;
   }
-  return CURLUE_OK;
+  return FALSE;
 }
 
 static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
@@ -769,8 +770,7 @@
   size_t schemelen = 0;
   size_t urllen;
 
-  if(!url)
-    return CURLUE_MALFORMED_INPUT;
+  DEBUGASSERT(url);
 
   /*************************************************************
    * Parse the URL.
@@ -884,9 +884,8 @@
          !(flags & CURLU_NON_SUPPORT_SCHEME))
         return CURLUE_UNSUPPORTED_SCHEME;
 
-      if(junkscan(schemep))
+      if(junkscan(schemep, flags))
         return CURLUE_MALFORMED_INPUT;
-
     }
     else {
       /* no scheme! */
@@ -927,7 +926,7 @@
     }
   }
 
-  if(junkscan(path))
+  if(junkscan(path, flags))
     return CURLUE_MALFORMED_INPUT;
 
   if((flags & CURLU_URLENCODE) && path[0]) {
@@ -991,7 +990,7 @@
     /*
      * Parse the login details and strip them out of the host name.
      */
-    if(junkscan(hostname))
+    if(junkscan(hostname, flags))
       return CURLUE_MALFORMED_INPUT;
 
     result = parse_hostname_login(u, &hostname, flags);
@@ -1155,7 +1154,7 @@
       const struct Curl_handler *h =
         Curl_builtin_scheme(u->scheme);
       if(h) {
-        msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+        msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
         ptr = portbuf;
       }
     }
@@ -1214,7 +1213,7 @@
         /* there's no stored port number, but asked to deliver
            a default one for the scheme */
         if(h) {
-          msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+          msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
           port = portbuf;
         }
       }
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index fb905c3..6ffd976 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -246,6 +246,7 @@
   long version_max;      /* max supported version the client wants to use*/
   char *CApath;          /* certificate dir (doesn't work on windows) */
   char *CAfile;          /* certificate to verify peer against */
+  char *issuercert;      /* optional issuer certificate filename */
   char *clientcert;
   char *random_file;     /* path to file containing "random" data */
   char *egdsocket;       /* path to file containing the EGD daemon socket */
@@ -254,6 +255,7 @@
   char *pinned_key;
   struct curl_blob *cert_blob;
   struct curl_blob *ca_info_blob;
+  struct curl_blob *issuercert_blob;
   char *curves;          /* list of curves to use */
   BIT(verifypeer);       /* set TRUE if this is desired */
   BIT(verifyhost);       /* set TRUE if CN/SAN must match hostname */
@@ -265,8 +267,6 @@
   struct ssl_primary_config primary;
   long certverifyresult; /* result from the certificate verification */
   char *CRLfile;   /* CRL to check certificate revocation */
-  char *issuercert;/* optional issuer certificate filename */
-  struct curl_blob *issuercert_blob;
   curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
   void *fsslctxp;        /* parameter for call back */
   char *cert_type; /* format for certificate (default: PEM)*/
@@ -508,7 +508,9 @@
   BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */
   BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */
 #endif
+#ifndef CURL_DISABLE_NETRC
   BIT(netrc);         /* name+password provided by netrc */
+#endif
   BIT(bound); /* set true if bind() has already been done on this socket/
                  connection */
   BIT(multiplex); /* connection is multiplexed */
@@ -702,14 +704,15 @@
 #ifndef CURL_DISABLE_DOH
   struct dohdata *doh; /* DoH specific data for this request */
 #endif
-  BIT(header);       /* incoming data has HTTP header */
+  BIT(header);        /* incoming data has HTTP header */
   BIT(content_range); /* set TRUE if Content-Range: was found */
-  BIT(upload_done);  /* set to TRUE when doing chunked transfer-encoding
-                        upload and we're uploading the last chunk */
-  BIT(ignorebody);   /* we read a response-body but we ignore it! */
+  BIT(upload_done);   /* set to TRUE when doing chunked transfer-encoding
+                         upload and we're uploading the last chunk */
+  BIT(ignorebody);    /* we read a response-body but we ignore it! */
   BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
                          204 or 304 */
-  BIT(chunk); /* if set, this is a chunked transfer-encoding */
+  BIT(chunk);         /* if set, this is a chunked transfer-encoding */
+  BIT(ignore_cl);     /* ignore content-length */
   BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
                          on upload */
   BIT(getheader);    /* TRUE if header parsing is wanted */
@@ -1411,6 +1414,7 @@
   trailers_state trailers_state; /* whether we are sending trailers
                                        and what stage are we at */
 #ifdef USE_HYPER
+  bool hconnect;  /* set if a CONNECT request */
   CURLcode hresult; /* used to pass return codes back from hyper callbacks */
 #endif
 
@@ -1726,8 +1730,10 @@
                                */
   curl_sshkeycallback ssh_keyfunc; /* key matching callback */
   void *ssh_keyfunc_userp;         /* custom pointer to callback */
+#ifndef CURL_DISABLE_NETRC
   enum CURL_NETRC_OPTION
        use_netrc;        /* defined in include/curl.h */
+#endif
   curl_usessl use_ssl;   /* if AUTH TLS is to be attempted etc, for FTP or
                             IMAP or POP3 or others! */
   long new_file_perms;    /* Permissions to use when creating remote files */
@@ -1847,9 +1853,9 @@
   BIT(disallow_username_in_url); /* disallow username in url */
   BIT(doh); /* DNS-over-HTTPS enabled */
   BIT(doh_get); /* use GET for DoH requests, instead of POST */
-  BIT(doh_verifypeer);     /* DOH certificate peer verification */
-  BIT(doh_verifyhost);     /* DOH certificate hostname verification */
-  BIT(doh_verifystatus);   /* DOH certificate status verification */
+  BIT(doh_verifypeer);     /* DoH certificate peer verification */
+  BIT(doh_verifyhost);     /* DoH certificate hostname verification */
+  BIT(doh_verifystatus);   /* DoH certificate status verification */
   BIT(http09_allowed); /* allow HTTP/0.9 responses */
   BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
                                 recipients */
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index 2602ffd..94f8f8c 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -112,7 +112,7 @@
 
   /* Ensure we have a valid challenge message */
   if(!Curl_bufref_len(chlg)) {
-    infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
+    infof(data, "DIGEST-MD5 handshake failure (empty challenge message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -197,7 +197,9 @@
      status == SEC_I_COMPLETE_AND_CONTINUE)
     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
     char buffer[STRERROR_LEN];
+#endif
 
     s_pSecFn->FreeCredentialsHandle(&credentials);
     Curl_sspi_free_identity(p_identity);
@@ -207,7 +209,7 @@
     if(status == SEC_E_INSUFFICIENT_MEMORY)
       return CURLE_OUT_OF_MEMORY;
 
-    infof(data, "schannel: InitializeSecurityContext failed: %s\n",
+    infof(data, "schannel: InitializeSecurityContext failed: %s",
           Curl_sspi_strerror(status, buffer, sizeof(buffer)));
 
     return CURLE_AUTH_ERROR;
@@ -461,7 +463,7 @@
     if(status == SEC_E_OK)
       output_token_len = chlg_buf[4].cbBuffer;
     else { /* delete the context so a new one can be made */
-      infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx\n",
+      infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx",
             (long)status);
       s_pSecFn->DeleteSecurityContext(digest->http_context);
       Curl_safefree(digest->http_context);
@@ -585,7 +587,9 @@
        status == SEC_I_COMPLETE_AND_CONTINUE)
       s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
     else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
       char buffer[STRERROR_LEN];
+#endif
 
       s_pSecFn->FreeCredentialsHandle(&credentials);
 
@@ -597,7 +601,7 @@
       if(status == SEC_E_INSUFFICIENT_MEMORY)
         return CURLE_OUT_OF_MEMORY;
 
-      infof(data, "schannel: InitializeSecurityContext failed: %s\n",
+      infof(data, "schannel: InitializeSecurityContext failed: %s",
             Curl_sspi_strerror(status, buffer, sizeof(buffer)));
 
       return CURLE_AUTH_ERROR;
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
index b43982b..67d43bd 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -123,7 +123,7 @@
 
   if(chlg) {
     if(!Curl_bufref_len(chlg)) {
-      infof(data, "GSSAPI handshake failure (empty challenge message)\n");
+      infof(data, "GSSAPI handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
     input_token.value = (void *) Curl_bufref_ptr(chlg);
@@ -170,6 +170,7 @@
  * Parameters:
  *
  * data    [in]     - The session handle.
+ * authzid [in]     - The authorization identity if some.
  * chlg    [in]     - Optional challenge message.
  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
  * out     [out]    - The result storage.
@@ -177,6 +178,7 @@
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+                                                  const char *authzid,
                                                   const struct bufref *chlg,
                                                   struct kerberos5data *krb5,
                                                   struct bufref *out)
@@ -189,39 +191,17 @@
   OM_uint32 unused_status;
   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
-  unsigned int indata = 0;
-  unsigned int outdata = 0;
+  unsigned char *indata;
   gss_qop_t qop = GSS_C_QOP_DEFAULT;
   unsigned int sec_layer = 0;
   unsigned int max_size = 0;
-  gss_name_t username = GSS_C_NO_NAME;
-  gss_buffer_desc username_token;
 
   /* Ensure we have a valid challenge message */
   if(!Curl_bufref_len(chlg)) {
-    infof(data, "GSSAPI handshake failure (empty security message)\n");
+    infof(data, "GSSAPI handshake failure (empty security message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
-  /* Get the fully qualified username back from the context */
-  major_status = gss_inquire_context(&minor_status, krb5->context,
-                                     &username, NULL, NULL, NULL, NULL,
-                                     NULL, NULL);
-  if(GSS_ERROR(major_status)) {
-    Curl_gss_log_error(data, "gss_inquire_context() failed: ",
-                       major_status, minor_status);
-    return CURLE_AUTH_ERROR;
-  }
-
-  /* Convert the username from internal format to a displayable token */
-  major_status = gss_display_name(&minor_status, username,
-                                  &username_token, NULL);
-  if(GSS_ERROR(major_status)) {
-    Curl_gss_log_error(data, "gss_display_name() failed: ",
-                       major_status, minor_status);
-    return CURLE_AUTH_ERROR;
-  }
-
   /* Setup the challenge "input" security buffer */
   input_token.value = (void *) Curl_bufref_ptr(chlg);
   input_token.length = Curl_bufref_len(chlg);
@@ -232,32 +212,32 @@
   if(GSS_ERROR(major_status)) {
     Curl_gss_log_error(data, "gss_unwrap() failed: ",
                        major_status, minor_status);
-    gss_release_buffer(&unused_status, &username_token);
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
   if(output_token.length != 4) {
-    infof(data, "GSSAPI handshake failure (invalid security data)\n");
-    gss_release_buffer(&unused_status, &username_token);
+    infof(data, "GSSAPI handshake failure (invalid security data)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
-  /* Copy the data out and free the challenge as it is not required anymore */
-  memcpy(&indata, output_token.value, 4);
+  /* Extract the security layer and the maximum message size */
+  indata = output_token.value;
+  sec_layer = indata[0];
+  max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+
+  /* Free the challenge as it is not required anymore */
   gss_release_buffer(&unused_status, &output_token);
 
-  /* Extract the security layer */
-  sec_layer = indata & 0x000000FF;
+  /* Process the security layer */
   if(!(sec_layer & GSSAUTH_P_NONE)) {
-    infof(data, "GSSAPI handshake failure (invalid security layer)\n");
+    infof(data, "GSSAPI handshake failure (invalid security layer)");
 
-    gss_release_buffer(&unused_status, &username_token);
     return CURLE_BAD_CONTENT_ENCODING;
   }
+  sec_layer &= GSSAUTH_P_NONE;  /* We do not support a security layer */
 
-  /* Extract the maximum message size the server can receive */
-  max_size = ntohl(indata & 0xFFFFFF00);
+  /* Process the maximum message size the server can receive */
   if(max_size > 0) {
     /* The server has told us it supports a maximum receive buffer, however, as
        we don't require one unless we are encrypting data, we tell the server
@@ -266,26 +246,24 @@
   }
 
   /* Allocate our message */
-  messagelen = sizeof(outdata) + username_token.length + 1;
+  messagelen = 4;
+  if(authzid)
+    messagelen += strlen(authzid);
   message = malloc(messagelen);
-  if(!message) {
-    gss_release_buffer(&unused_status, &username_token);
+  if(!message)
     return CURLE_OUT_OF_MEMORY;
-  }
 
-  /* Populate the message with the security layer, client supported receive
-     message size and authorization identity including the 0x00 based
-     terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
-     identity is not terminated with the zero-valued (%x00) octet." it seems
-     necessary to include it. */
-  outdata = htonl(max_size) | sec_layer;
-  memcpy(message, &outdata, sizeof(outdata));
-  memcpy(message + sizeof(outdata), username_token.value,
-         username_token.length);
-  message[messagelen - 1] = '\0';
+  /* Populate the message with the security layer and client supported receive
+     message size. */
+  message[0] = sec_layer & 0xFF;
+  message[1] = (max_size >> 16) & 0xFF;
+  message[2] = (max_size >> 8) & 0xFF;
+  message[3] = max_size & 0xFF;
 
-  /* Free the username token as it is not required anymore */
-  gss_release_buffer(&unused_status, &username_token);
+  /* If given, append the authorization identity. */
+
+  if(authzid && *authzid)
+    memcpy(message + 4, authzid, messagelen - 4);
 
   /* Setup the "authentication data" security buffer */
   input_token.value = message;
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
index e110644..c652fd7 100644
--- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -173,7 +173,7 @@
 
   if(chlg) {
     if(!Curl_bufref_len(chlg)) {
-      infof(data, "GSSAPI handshake failure (empty challenge message)\n");
+      infof(data, "GSSAPI handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
 
@@ -238,13 +238,15 @@
  * Parameters:
  *
  * data    [in]     - The session handle.
- * chlg     [in]     - The optional challenge message.
+ * authzid [in]     - The authorization identity if some.
+ * chlg    [in]     - The optional challenge message.
  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
  * out     [out]    - The result storage.
  *
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+                                                  const char *authzid,
                                                   const struct bufref *chlg,
                                                   struct kerberos5data *krb5,
                                                   struct bufref *out)
@@ -260,19 +262,20 @@
   SecBuffer wrap_buf[3];
   SecBufferDesc input_desc;
   SecBufferDesc wrap_desc;
-  unsigned long indata = 0;
-  unsigned long outdata = 0;
+  unsigned char *indata;
   unsigned long qop = 0;
   unsigned long sec_layer = 0;
   unsigned long max_size = 0;
   SecPkgContext_Sizes sizes;
-  SecPkgCredentials_Names names;
   SECURITY_STATUS status;
-  char *user_name;
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+  (void) data;
+#endif
 
   /* Ensure we have a valid challenge message */
   if(!Curl_bufref_len(chlg)) {
-    infof(data, "GSSAPI handshake failure (empty security message)\n");
+    infof(data, "GSSAPI handshake failure (empty security message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -287,17 +290,6 @@
   if(status != SEC_E_OK)
     return CURLE_AUTH_ERROR;
 
-  /* Get the fully qualified username back from the context */
-  status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
-                                                SECPKG_CRED_ATTR_NAMES,
-                                                &names);
-
-  if(status == SEC_E_INSUFFICIENT_MEMORY)
-    return CURLE_OUT_OF_MEMORY;
-
-  if(status != SEC_E_OK)
-    return CURLE_AUTH_ERROR;
-
   /* Setup the "input" security buffer */
   input_desc.ulVersion = SECBUFFER_VERSION;
   input_desc.cBuffers = 2;
@@ -312,29 +304,32 @@
   /* Decrypt the inbound challenge and obtain the qop */
   status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
   if(status != SEC_E_OK) {
-    infof(data, "GSSAPI handshake failure (empty security message)\n");
+    infof(data, "GSSAPI handshake failure (empty security message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
   if(input_buf[1].cbBuffer != 4) {
-    infof(data, "GSSAPI handshake failure (invalid security data)\n");
+    infof(data, "GSSAPI handshake failure (invalid security data)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
-  /* Copy the data out and free the challenge as it is not required anymore */
-  memcpy(&indata, input_buf[1].pvBuffer, 4);
+  /* Extract the security layer and the maximum message size */
+  indata = input_buf[1].pvBuffer;
+  sec_layer = indata[0];
+  max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+
+  /* Free the challenge as it is not required anymore */
   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
 
-  /* Extract the security layer */
-  sec_layer = indata & 0x000000FF;
+  /* Process the security layer */
   if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
-    infof(data, "GSSAPI handshake failure (invalid security layer)\n");
+    infof(data, "GSSAPI handshake failure (invalid security layer)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
+  sec_layer &= KERB_WRAP_NO_ENCRYPT;  /* We do not support a security layer */
 
-  /* Extract the maximum message size the server can receive */
-  max_size = ntohl(indata & 0xFFFFFF00);
+  /* Process the maximum message size the server can receive */
   if(max_size > 0) {
     /* The server has told us it supports a maximum receive buffer, however, as
        we don't require one unless we are encrypting data, we tell the server
@@ -347,33 +342,28 @@
   if(!trailer)
     return CURLE_OUT_OF_MEMORY;
 
-  /* Convert the user name to UTF8 when operating with Unicode */
-  user_name = curlx_convert_tchar_to_UTF8(names.sUserName);
-  if(!user_name) {
-    free(trailer);
-
-    return CURLE_OUT_OF_MEMORY;
-  }
-
   /* Allocate our message */
-  messagelen = sizeof(outdata) + strlen(user_name) + 1;
+  messagelen = 4;
+  if(authzid)
+    messagelen += strlen(authzid);
   message = malloc(messagelen);
   if(!message) {
     free(trailer);
-    curlx_unicodefree(user_name);
 
     return CURLE_OUT_OF_MEMORY;
   }
 
-  /* Populate the message with the security layer, client supported receive
-     message size and authorization identity including the 0x00 based
-     terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
-     identity is not terminated with the zero-valued (%x00) octet." it seems
-     necessary to include it. */
-  outdata = htonl(max_size) | sec_layer;
-  memcpy(message, &outdata, sizeof(outdata));
-  strcpy((char *) message + sizeof(outdata), user_name);
-  curlx_unicodefree(user_name);
+  /* Populate the message with the security layer and client supported receive
+     message size. */
+  message[0] = sec_layer & 0xFF;
+  message[1] = (max_size >> 16) & 0xFF;
+  message[2] = (max_size >> 8) & 0xFF;
+  message[3] = max_size & 0xFF;
+
+  /* If given, append the authorization identity. */
+
+  if(authzid && *authzid)
+    memcpy(message + 4, authzid, messagelen - 4);
 
   /* Allocate the padding */
   padding = malloc(sizes.cbBlockSize);
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index 47e5357..0aa3f1c 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -182,7 +182,7 @@
          (target_info_offset + target_info_len) > type2len ||
          target_info_offset < 48) {
         infof(data, "NTLM handshake failure (bad type-2 message). "
-              "Target Info Offset Len is set incorrect by the peer\n");
+              "Target Info Offset Len is set incorrect by the peer");
         return CURLE_BAD_CONTENT_ENCODING;
       }
 
@@ -286,7 +286,7 @@
      (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) ||
      (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) {
     /* This was not a good enough type-2 message */
-    infof(data, "NTLM handshake failure (bad type-2 message)\n");
+    infof(data, "NTLM handshake failure (bad type-2 message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -296,7 +296,7 @@
   if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) {
     result = ntlm_decode_type2_target(data, type2ref, ntlm);
     if(result) {
-      infof(data, "NTLM handshake failure (bad type-2 message)\n");
+      infof(data, "NTLM handshake failure (bad type-2 message)");
       return result;
     }
   }
@@ -533,7 +533,7 @@
   /* Get the machine's un-qualified host name as NTLM doesn't like the fully
      qualified domain name */
   if(Curl_gethostname(host, sizeof(host))) {
-    infof(data, "gethostname() failed, continuing without!\n");
+    infof(data, "gethostname() failed, continuing without!");
     hostlen = 0;
   }
   else {
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
index 1b1a176..3e39dad 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -206,7 +206,7 @@
 
   /* Ensure we have a valid type-2 message */
   if(!Curl_bufref_len(type2)) {
-    infof(data, "NTLM handshake failure (empty type-2 message)\n");
+    infof(data, "NTLM handshake failure (empty type-2 message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -253,6 +253,9 @@
   unsigned long attrs;
   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
 
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+  (void) data;
+#endif
   (void) passwdp;
   (void) userp;
 
@@ -309,7 +312,7 @@
                                                &type_3_desc,
                                                &attrs, &expiry);
   if(status != SEC_E_OK) {
-    infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
+    infof(data, "NTLM handshake failure (type-3 message): Status=%x",
           status);
 
     if(status == SEC_E_INSUFFICIENT_MEMORY)
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
index 120925f..8e8932b 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -137,8 +137,7 @@
 
     /* Ensure we have a valid challenge message */
     if(!chlg) {
-      infof(data, "SPNEGO handshake failure (empty challenge message)\n");
-
+      infof(data, "SPNEGO handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
 
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
index 4aa1ba9..68bb17d 100644
--- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -191,8 +191,7 @@
 
     /* Ensure we have a valid challenge message */
     if(!chlg) {
-      infof(data, "SPNEGO handshake failure (empty challenge message)\n");
-
+      infof(data, "SPNEGO handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
 
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h
index ec5b000..47a7c0b 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.h
+++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -194,6 +194,7 @@
 /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security
    token message */
 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+                                                  const char *authzid,
                                                   const struct bufref *chlg,
                                                   struct kerberos5data *krb5,
                                                   struct bufref *out);
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index b67b9a4..c84ef85 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -75,28 +75,26 @@
 #endif
 
 #ifdef HAVE_BROTLI
-static size_t brotli_version(char *buf, size_t bufsz)
+static void brotli_version(char *buf, size_t bufsz)
 {
   uint32_t brotli_version = BrotliDecoderVersion();
   unsigned int major = brotli_version >> 24;
   unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12;
   unsigned int patch = brotli_version & 0x00000FFF;
-
-  return msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+  (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
 }
 #endif
 
 #ifdef HAVE_ZSTD
-static size_t zstd_version(char *buf, size_t bufsz)
+static void zstd_version(char *buf, size_t bufsz)
 {
   unsigned long zstd_version = (unsigned long)ZSTD_versionNumber();
   unsigned int major = (unsigned int)(zstd_version / (100 * 100));
   unsigned int minor = (unsigned int)((zstd_version -
-                         (major * 100 * 100)) / 100);
+                                       (major * 100 * 100)) / 100);
   unsigned int patch = (unsigned int)(zstd_version -
-                         (major * 100 * 100) - (minor * 100));
-
-  return msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+                                      (major * 100 * 100) - (minor * 100));
+  (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
 }
 #endif
 
diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.c b/Utilities/cmcurl/lib/vquic/ngtcp2.c
index 7f07675..9fcfe81 100644
--- a/Utilities/cmcurl/lib/vquic/ngtcp2.c
+++ b/Utilities/cmcurl/lib/vquic/ngtcp2.c
@@ -28,6 +28,9 @@
 #include <nghttp3/nghttp3.h>
 #ifdef USE_OPENSSL
 #include <openssl/err.h>
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#elif defined(USE_GNUTLS)
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
 #endif
 #include "urldata.h"
 #include "sendf.h"
@@ -86,7 +89,8 @@
 #define QUIC_PRIORITY \
   "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
   "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
-  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1"
+  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+  "%DISABLE_TLS13_COMPAT_MODE"
 #endif
 
 static CURLcode ng_process_ingress(struct Curl_easy *data,
@@ -116,42 +120,6 @@
 }
 #endif
 
-#ifdef USE_OPENSSL
-static ngtcp2_crypto_level
-quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)
-{
-  switch(ossl_level) {
-  case ssl_encryption_initial:
-    return NGTCP2_CRYPTO_LEVEL_INITIAL;
-  case ssl_encryption_early_data:
-    return NGTCP2_CRYPTO_LEVEL_EARLY;
-  case ssl_encryption_handshake:
-    return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
-  case ssl_encryption_application:
-    return NGTCP2_CRYPTO_LEVEL_APPLICATION;
-  default:
-    assert(0);
-  }
-}
-#elif defined(USE_GNUTLS)
-static ngtcp2_crypto_level
-quic_from_gtls_level(gnutls_record_encryption_level_t gtls_level)
-{
-  switch(gtls_level) {
-  case GNUTLS_ENCRYPTION_LEVEL_INITIAL:
-    return NGTCP2_CRYPTO_LEVEL_INITIAL;
-  case GNUTLS_ENCRYPTION_LEVEL_EARLY:
-    return NGTCP2_CRYPTO_LEVEL_EARLY;
-  case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE:
-    return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
-  case GNUTLS_ENCRYPTION_LEVEL_APPLICATION:
-    return NGTCP2_CRYPTO_LEVEL_APPLICATION;
-  default:
-    assert(0);
-  }
-}
-#endif
-
 static void qlog_callback(void *user_data, uint32_t flags,
                           const void *data, size_t datalen)
 {
@@ -222,27 +190,9 @@
                                   ngtcp2_crypto_level level,
                                   const uint8_t *data, size_t len)
 {
-  struct quic_handshake *crypto_data;
   int rv;
 
-  crypto_data = &qs->crypto_data[level];
-  if(!crypto_data->buf) {
-    crypto_data->buf = malloc(4096);
-    if(!crypto_data->buf)
-      return 0;
-    crypto_data->alloclen = 4096;
-  }
-
-  /* TODO Just pretend that handshake does not grow more than 4KiB for
-     now */
-  assert(crypto_data->len + len <= crypto_data->alloclen);
-
-  memcpy(&crypto_data->buf[crypto_data->len], data, len);
-  crypto_data->len += len;
-
-  rv = ngtcp2_conn_submit_crypto_data(
-    qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len),
-    len);
+  rv = ngtcp2_conn_submit_crypto_data(qs->qconn, level, data, len);
   if(rv) {
     H3BUGF(fprintf(stderr, "write_client_handshake failed\n"));
   }
@@ -259,7 +209,7 @@
                                        size_t secretlen)
 {
   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  int level = quic_from_ossl_level(ossl_level);
+  int level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
 
   if(ngtcp2_crypto_derive_and_install_rx_key(
        qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0)
@@ -281,7 +231,8 @@
                                    const uint8_t *data, size_t len)
 {
   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level);
+  ngtcp2_crypto_level level =
+      ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
 
   return write_client_handshake(qs, level, data, len);
 }
@@ -369,7 +320,8 @@
                        const void *tx_secret, size_t secretlen)
 {
   struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  int level = quic_from_gtls_level(gtls_level);
+  int level =
+      ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
 
   if(level != NGTCP2_CRYPTO_LEVEL_EARLY &&
      ngtcp2_crypto_derive_and_install_rx_key(
@@ -394,7 +346,8 @@
                      size_t len)
 {
   struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  ngtcp2_crypto_level level = quic_from_gtls_level(gtls_level);
+  ngtcp2_crypto_level level =
+      ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
   int rv;
 
   if(htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
@@ -542,22 +495,6 @@
 }
 #endif
 
-static int
-cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level,
-                    uint64_t offset,
-                    const uint8_t *data, size_t datalen,
-                    void *user_data)
-{
-  (void)offset;
-  (void)user_data;
-
-  if(ngtcp2_crypto_read_write_crypto_data(tconn, crypto_level, data,
-                                          datalen) != 0)
-    return NGTCP2_ERR_CRYPTO;
-
-  return 0;
-}
-
 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
 {
   (void)user_data;
@@ -622,8 +559,8 @@
   return 0;
 }
 
-static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
-                           uint64_t app_error_code,
+static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
+                           int64_t stream_id, uint64_t app_error_code,
                            void *user_data, void *stream_user_data)
 {
   struct quicsocket *qs = (struct quicsocket *)user_data;
@@ -632,6 +569,10 @@
   (void)stream_user_data;
   /* stream is closed... */
 
+  if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
+    app_error_code = NGHTTP3_H3_NO_ERROR;
+  }
+
   rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
                                  app_error_code);
   if(rv) {
@@ -652,7 +593,25 @@
   (void)app_error_code;
   (void)stream_user_data;
 
-  rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id);
+  rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
+  if(rv) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+
+static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
+                                  uint64_t app_error_code, void *user_data,
+                                  void *stream_user_data)
+{
+  struct quicsocket *qs = (struct quicsocket *)user_data;
+  int rv;
+  (void)tconn;
+  (void)app_error_code;
+  (void)stream_user_data;
+
+  rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
   if(rv) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
@@ -712,14 +671,13 @@
 static ngtcp2_callbacks ng_callbacks = {
   ngtcp2_crypto_client_initial_cb,
   NULL, /* recv_client_initial */
-  cb_recv_crypto_data,
+  ngtcp2_crypto_recv_crypto_data_cb,
   cb_handshake_completed,
   NULL, /* recv_version_negotiation */
   ngtcp2_crypto_encrypt_cb,
   ngtcp2_crypto_decrypt_cb,
   ngtcp2_crypto_hp_mask_cb,
   cb_recv_stream_data,
-  NULL, /* acked_crypto_offset */
   cb_acked_stream_data_offset,
   NULL, /* stream_open */
   cb_stream_close,
@@ -744,7 +702,9 @@
   ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
   NULL, /* recv_datagram */
   NULL, /* ack_datagram */
-  NULL  /* lost_datagram */
+  NULL, /* lost_datagram */
+  NULL, /* get_path_challenge_data */
+  cb_stream_stop_sending
 };
 
 /*
@@ -778,7 +738,7 @@
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
-  infof(data, "Connect socket %d over QUIC to %s:%d\n",
+  infof(data, "Connect socket %d over QUIC to %s:%d",
         sockfd, ipbuf, port);
 
   qs->version = NGTCP2_PROTO_VER_MAX;
@@ -827,15 +787,14 @@
 }
 
 /*
- * Store ngtp2 version info in this buffer, Prefix with a space.  Return total
- * length written.
+ * Store ngtcp2 version info in this buffer.
  */
-int Curl_quic_ver(char *p, size_t len)
+void Curl_quic_ver(char *p, size_t len)
 {
   const ngtcp2_info *ng2 = ngtcp2_version(0);
   nghttp3_info *ht3 = nghttp3_version(0);
-  return msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
-                   ng2->version_str, ht3->version_str);
+  (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
+                  ng2->version_str, ht3->version_str);
 }
 
 static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
@@ -859,7 +818,6 @@
 
 static void qs_disconnect(struct quicsocket *qs)
 {
-  int i;
   if(!qs->conn) /* already closed */
     return;
   qs->conn = NULL;
@@ -880,8 +838,6 @@
     qs->cred = NULL;
   }
 #endif
-  for(i = 0; i < 3; i++)
-    Curl_safefree(qs->crypto_data[i].buf);
   nghttp3_conn_del(qs->h3conn);
   ngtcp2_conn_del(qs->qconn);
 #ifdef USE_OPENSSL
@@ -951,7 +907,7 @@
   (void)stream_id;
   (void)app_error_code;
   (void)user_data;
-  H3BUGF(infof(data, "cb_h3_stream_close CALLED\n"));
+  H3BUGF(infof(data, "cb_h3_stream_close CALLED"));
 
   stream->closed = TRUE;
   Curl_expire(data, 0, EXPIRE_QUIC);
@@ -1143,14 +1099,10 @@
   NULL, /* begin_trailers */
   cb_h3_recv_header,
   NULL, /* end_trailers */
-  NULL, /* http_begin_push_promise */
-  NULL, /* http_recv_push_promise */
-  NULL, /* http_end_push_promise */
-  NULL, /* http_cancel_push */
   cb_h3_send_stop_sending,
-  NULL, /* push_stream */
   NULL, /* end_stream */
   NULL, /* reset_stream */
+  NULL /* shutdown */
 };
 
 static int init_ngh3_conn(struct quicsocket *qs)
@@ -1287,7 +1239,7 @@
     return 0;
   }
 
-  infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n");
+  infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN");
   *curlcode = CURLE_AGAIN;
   return -1;
 }
@@ -1304,7 +1256,7 @@
   if(!data->set.postfields) {
     stream->h3out->used -= datalen;
     H3BUGF(infof(data,
-                 "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n",
+                 "cb_h3_acked_stream_data, %zd bytes, %zd left unacked",
                  datalen, stream->h3out->used));
     DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
 
@@ -1366,13 +1318,13 @@
       if(!stream->upload_left)
         *pflags = NGHTTP3_DATA_FLAG_EOF;
     }
-    H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n",
+    H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
                  nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
                  out->used));
   }
   if(stream->upload_done && !stream->upload_len &&
      (stream->upload_left <= 0)) {
-    H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n"));
+    H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF"));
     *pflags = NGHTTP3_DATA_FLAG_EOF;
     return nread ? 1 : 0;
   }
@@ -1565,7 +1517,7 @@
     if(acc > MAX_ACC) {
       infof(data, "http_request: Warning: The cumulative length of all "
             "headers exceeds %d bytes and that could cause the "
-            "stream to be rejected.\n", MAX_ACC);
+            "stream to be rejected.", MAX_ACC);
     }
   }
 
@@ -1611,7 +1563,7 @@
 
   Curl_safefree(nva);
 
-  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
+  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
         stream3_id, (void *)data);
 
   return CURLE_OK;
@@ -1641,7 +1593,7 @@
     sent = len;
   }
   else {
-    H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes\n",
+    H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes",
                  len));
     if(!stream->upload_len) {
       stream->upload_mem = mem;
@@ -1660,6 +1612,12 @@
     return -1;
   }
 
+  /* Reset post upload buffer after resumed. */
+  if(stream->upload_mem) {
+    stream->upload_mem = NULL;
+    stream->upload_len = 0;
+  }
+
   *curlcode = CURLE_OK;
   return sent;
 }
@@ -1758,8 +1716,7 @@
   int rv;
   ssize_t sent;
   ssize_t outlen;
-  uint8_t out[NGTCP2_MAX_PKTLEN_IPV4];
-  size_t pktlen;
+  uint8_t out[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
   ngtcp2_path_storage ps;
   ngtcp2_tstamp ts = timestamp();
   struct sockaddr_storage remote_addr;
@@ -1772,19 +1729,6 @@
   ssize_t ndatalen;
   uint32_t flags;
 
-  switch(qs->local_addr.ss_family) {
-  case AF_INET:
-    pktlen = NGTCP2_MAX_PKTLEN_IPV4;
-    break;
-#ifdef ENABLE_IPV6
-  case AF_INET6:
-    pktlen = NGTCP2_MAX_PKTLEN_IPV6;
-    break;
-#endif
-  default:
-    assert(0);
-  }
-
   rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
   if(rv) {
     failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
@@ -1811,15 +1755,16 @@
 
     flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
             (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
-    outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, out, pktlen,
+    outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, out,
+                                       sizeof(out),
                                        &ndatalen, flags, stream_id,
                                        (const ngtcp2_vec *)vec, veccnt, ts);
     if(outlen == 0) {
       break;
     }
     if(outlen < 0) {
-      if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED ||
-         outlen == NGTCP2_ERR_STREAM_SHUT_WR) {
+      switch(outlen) {
+      case NGTCP2_ERR_STREAM_DATA_BLOCKED:
         assert(ndatalen == -1);
         rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
         if(rv) {
@@ -1828,8 +1773,17 @@
           return CURLE_SEND_ERROR;
         }
         continue;
-      }
-      else if(outlen == NGTCP2_ERR_WRITE_MORE) {
+      case NGTCP2_ERR_STREAM_SHUT_WR:
+        assert(ndatalen == -1);
+        rv = nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
+        if(rv) {
+          failf(data,
+                "nghttp3_conn_shutdown_stream_write returned error: %s\n",
+                nghttp3_strerror(rv));
+          return CURLE_SEND_ERROR;
+        }
+        continue;
+      case NGTCP2_ERR_WRITE_MORE:
         assert(ndatalen >= 0);
         rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
         if(rv) {
@@ -1838,8 +1792,7 @@
           return CURLE_SEND_ERROR;
         }
         continue;
-      }
-      else {
+      default:
         assert(ndatalen == -1);
         failf(data, "ngtcp2_conn_writev_stream returned error: %s",
               ngtcp2_strerror((int)outlen));
diff --git a/Utilities/cmcurl/lib/vquic/ngtcp2.h b/Utilities/cmcurl/lib/vquic/ngtcp2.h
index cbede45..5014530 100644
--- a/Utilities/cmcurl/lib/vquic/ngtcp2.h
+++ b/Utilities/cmcurl/lib/vquic/ngtcp2.h
@@ -34,13 +34,6 @@
 #include <gnutls/gnutls.h>
 #endif
 
-struct quic_handshake {
-  char *buf;       /* pointer to the buffer */
-  size_t alloclen; /* size of allocation */
-  size_t len;      /* size of content in buffer */
-  size_t nread;    /* how many bytes have been read */
-};
-
 struct quicsocket {
   struct connectdata *conn; /* point back to the connection */
   ngtcp2_conn *qconn;
@@ -56,7 +49,6 @@
   gnutls_certificate_credentials_t cred;
   gnutls_session_t ssl;
 #endif
-  struct quic_handshake crypto_data[3];
   /* the last TLS alert description generated by the local endpoint */
   uint8_t tls_alert;
   struct sockaddr_storage local_addr;
diff --git a/Utilities/cmcurl/lib/vquic/quiche.c b/Utilities/cmcurl/lib/vquic/quiche.c
index b62d884..f757760 100644
--- a/Utilities/cmcurl/lib/vquic/quiche.c
+++ b/Utilities/cmcurl/lib/vquic/quiche.c
@@ -258,7 +258,7 @@
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
-  infof(data, "Connect socket %d over QUIC to %s:%ld\n",
+  infof(data, "Connect socket %d over QUIC to %s:%ld",
         sockfd, ipbuf, port);
 
   Curl_persistconninfo(data, conn, NULL, -1);
@@ -277,7 +277,7 @@
       offset += 1 + alpn_len;
     }
 
-    infof(data, "Sent QUIC client Initial, ALPN: %s\n",
+    infof(data, "Sent QUIC client Initial, ALPN: %s",
           alpn_protocols + 1);
   }
 
@@ -345,7 +345,7 @@
   if(quiche_conn_is_established(qs->conn)) {
     *done = TRUE;
     result = quiche_has_connected(conn, 0, sockindex);
-    DEBUGF(infof(data, "quiche established connection!\n"));
+    DEBUGF(infof(data, "quiche established connection!"));
   }
 
   return result;
@@ -422,10 +422,9 @@
       return CURLE_SEND_ERROR;
     }
 
-    sent = sendto(sockfd, out, sent, 0,
-                  (struct sockaddr *)&send_info.to, send_info.to_len);
+    sent = send(sockfd, out, sent, 0);
     if(sent < 0) {
-      failf(data, "sendto() returned %zd", sent);
+      failf(data, "send() returned %zd", sent);
       return CURLE_SEND_ERROR;
     }
   } while(1);
@@ -492,7 +491,7 @@
   headers.nlen = 0;
 
   if(process_ingress(data, sockfd, qs)) {
-    infof(data, "h3_stream_recv returns on ingress\n");
+    infof(data, "h3_stream_recv returns on ingress");
     *curlcode = CURLE_RECV_ERROR;
     return -1;
   }
@@ -505,7 +504,7 @@
 
     if(s != stream->stream3_id) {
       /* another transfer, ignore for now */
-      infof(data, "Got h3 for stream %u, expects %u\n",
+      infof(data, "Got h3 for stream %u, expects %u",
             s, stream->stream3_id);
       continue;
     }
@@ -586,7 +585,7 @@
     sent = len;
   }
   else {
-    H3BUGF(infof(data, "Pass on %zd body bytes to quiche\n", len));
+    H3BUGF(infof(data, "Pass on %zd body bytes to quiche", len));
     sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
                                (uint8_t *)mem, len, FALSE);
     if(sent < 0) {
@@ -605,12 +604,11 @@
 }
 
 /*
- * Store quiche version info in this buffer, Prefix with a space.  Return total
- * length written.
+ * Store quiche version info in this buffer.
  */
-int Curl_quic_ver(char *p, size_t len)
+void Curl_quic_ver(char *p, size_t len)
 {
-  return msnprintf(p, len, "quiche/%s", quiche_version());
+  (void)msnprintf(p, len, "quiche/%s", quiche_version());
 }
 
 /* Index where :authority header field will appear in request header
@@ -780,7 +778,7 @@
     for(i = 0; i < nheader; ++i) {
       acc += nva[i].name_len + nva[i].value_len;
 
-      H3BUGF(infof(data, "h3 [%.*s: %.*s]\n",
+      H3BUGF(infof(data, "h3 [%.*s: %.*s]",
                    nva[i].name_len, nva[i].name,
                    nva[i].value_len, nva[i].value));
     }
@@ -788,7 +786,7 @@
     if(acc > MAX_ACC) {
       infof(data, "http_request: Warning: The cumulative length of all "
             "headers exceeds %d bytes and that could cause the "
-            "stream to be rejected.\n", MAX_ACC);
+            "stream to be rejected.", MAX_ACC);
     }
   }
 
@@ -825,13 +823,13 @@
   Curl_safefree(nva);
 
   if(stream3_id < 0) {
-    H3BUGF(infof(data, "quiche_h3_send_request returned %d\n",
+    H3BUGF(infof(data, "quiche_h3_send_request returned %d",
                  stream3_id));
     result = CURLE_SEND_ERROR;
     goto fail;
   }
 
-  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
+  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
         stream3_id, (void *)data);
   stream->stream3_id = stream3_id;
 
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index d146d15..3e317e8 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -74,7 +74,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "inet_ntop.h"
 #include "parsedate.h"          /* for the week day and month names */
 #include "sockaddr.h"           /* required for Curl_sockaddr_storage */
@@ -303,7 +302,7 @@
 
 
   if(sshc->state != nowstate) {
-    infof(data, "SSH %p state change from %s to %s (line %d)\n",
+    infof(data, "SSH %p state change from %s to %s (line %d)",
           (void *) sshc, names[sshc->state], names[nowstate],
           lineno);
   }
@@ -368,7 +367,7 @@
     for(i = 0; i < 16; i++)
       msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
 
-    infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
+    infof(data, "SSH MD5 fingerprint: %s", md5buffer);
 
     if(!strcasecompare(md5buffer, pubkey_md5)) {
       failf(data,
@@ -732,7 +731,7 @@
 
         if(rc == SSH_AUTH_SUCCESS) {
           sshc->authed = TRUE;
-          infof(data, "Authenticated with none\n");
+          infof(data, "Authenticated with none");
           state(data, SSH_AUTH_DONE);
           break;
         }
@@ -744,7 +743,7 @@
         sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL);
         if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
           state(data, SSH_AUTH_PKEY_INIT);
-          infof(data, "Authentication using SSH public key file\n");
+          infof(data, "Authentication using SSH public key file");
         }
         else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
           state(data, SSH_AUTH_GSSAPI);
@@ -810,7 +809,7 @@
         if(rc == SSH_AUTH_SUCCESS) {
           rc = SSH_OK;
           sshc->authed = TRUE;
-          infof(data, "Completed public key authentication\n");
+          infof(data, "Completed public key authentication");
           state(data, SSH_AUTH_DONE);
           break;
         }
@@ -827,12 +826,12 @@
 
       if(rc == SSH_AUTH_SUCCESS) {
         sshc->authed = TRUE;
-        infof(data, "Completed public key authentication\n");
+        infof(data, "Completed public key authentication");
         state(data, SSH_AUTH_DONE);
         break;
       }
       else {
-        infof(data, "Failed public key authentication (rc: %d)\n", rc);
+        infof(data, "Failed public key authentication (rc: %d)", rc);
         MOVE_TO_SECONDARY_AUTH;
       }
       break;
@@ -852,7 +851,7 @@
       if(rc == SSH_AUTH_SUCCESS) {
         rc = SSH_OK;
         sshc->authed = TRUE;
-        infof(data, "Completed gssapi authentication\n");
+        infof(data, "Completed gssapi authentication");
         state(data, SSH_AUTH_DONE);
         break;
       }
@@ -878,7 +877,7 @@
       }
       if(rc == SSH_OK) {
         sshc->authed = TRUE;
-        infof(data, "completed keyboard interactive authentication\n");
+        infof(data, "completed keyboard interactive authentication");
       }
       state(data, SSH_AUTH_DONE);
       break;
@@ -901,7 +900,7 @@
 
       if(rc == SSH_AUTH_SUCCESS) {
         sshc->authed = TRUE;
-        infof(data, "Completed password authentication\n");
+        infof(data, "Completed password authentication");
         state(data, SSH_AUTH_DONE);
       }
       else {
@@ -919,7 +918,7 @@
       /*
        * At this point we have an authenticated ssh session.
        */
-      infof(data, "Authentication complete\n");
+      infof(data, "Authentication complete");
 
       Curl_pgrsTime(data, TIMER_APPCONNECT);      /* SSH is connected */
 
@@ -930,7 +929,7 @@
         state(data, SSH_SFTP_INIT);
         break;
       }
-      infof(data, "SSH CONNECT phase done\n");
+      infof(data, "SSH CONNECT phase done");
       state(data, SSH_STOP);
       break;
 
@@ -970,7 +969,7 @@
          we get the homedir here, we get the "workingpath" in the DO action
          since the homedir will remain the same between request but the
          working path will not. */
-      DEBUGF(infof(data, "SSH CONNECT phase done\n"));
+      DEBUGF(infof(data, "SSH CONNECT phase done"));
       state(data, SSH_STOP);
       break;
 
@@ -983,7 +982,7 @@
       }
 
       if(data->set.quote) {
-        infof(data, "Sending quote commands\n");
+        infof(data, "Sending quote commands");
         sshc->quote_item = data->set.quote;
         state(data, SSH_SFTP_QUOTE);
       }
@@ -994,7 +993,7 @@
 
     case SSH_SFTP_POSTQUOTE_INIT:
       if(data->set.postquote) {
-        infof(data, "Sending quote commands\n");
+        infof(data, "Sending quote commands");
         sshc->quote_item = data->set.postquote;
         state(data, SSH_SFTP_QUOTE);
       }
@@ -1367,7 +1366,7 @@
       if(sshc->slash_pos) {
         *sshc->slash_pos = 0;
 
-        infof(data, "Creating directory '%s'\n", protop->path);
+        infof(data, "Creating directory '%s'", protop->path);
         state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
         break;
       }
@@ -1731,7 +1730,7 @@
     if(data->req.size == 0) {
       /* no data to transfer */
       Curl_setup_transfer(data, -1, -1, FALSE, -1);
-      infof(data, "File already completely downloaded\n");
+      infof(data, "File already completely downloaded");
       state(data, SSH_STOP);
       break;
     }
@@ -1764,7 +1763,7 @@
       }
       Curl_safefree(protop->path);
 
-      DEBUGF(infof(data, "SFTP DONE done\n"));
+      DEBUGF(infof(data, "SFTP DONE done"));
 
       /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
          After nextstate is executed, the control should come back to
@@ -1933,7 +1932,7 @@
           break;
         }
         if(rc != SSH_OK) {
-          infof(data, "Failed to close libssh scp channel: %s\n",
+          infof(data, "Failed to close libssh scp channel: %s",
                 ssh_get_error(sshc->ssh_session));
         }
       }
@@ -1946,7 +1945,7 @@
         ssh_scp_free(sshc->scp_session);
         sshc->scp_session = NULL;
       }
-      DEBUGF(infof(data, "SCP DONE phase complete\n"));
+      DEBUGF(infof(data, "SCP DONE phase complete"));
 
       ssh_set_blocking(sshc->ssh_session, 0);
 
@@ -2213,7 +2212,7 @@
   }
 
   if(conn->user && conn->user[0] != '\0') {
-    infof(data, "User: %s\n", conn->user);
+    infof(data, "User: %s", conn->user);
     rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
     if(rc != SSH_OK) {
       failf(data, "Could not set user");
@@ -2222,7 +2221,7 @@
   }
 
   if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
-    infof(data, "Known hosts: %s\n", data->set.str[STRING_SSH_KNOWNHOSTS]);
+    infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
     rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
                          data->set.str[STRING_SSH_KNOWNHOSTS]);
     if(rc != SSH_OK) {
@@ -2279,7 +2278,7 @@
   result = myssh_multi_statemach(data, dophase_done);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   return result;
 }
@@ -2300,7 +2299,7 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE;        /* not done yet */
 
@@ -2312,7 +2311,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -2481,7 +2480,7 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
 
@@ -2494,7 +2493,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -2506,7 +2505,7 @@
 {
   CURLcode result = myssh_multi_statemach(data, dophase_done);
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   return result;
 }
@@ -2521,7 +2520,7 @@
   CURLcode result = CURLE_OK;
   (void) dead_connection;
 
-  DEBUGF(infof(data, "SSH DISCONNECT starts now\n"));
+  DEBUGF(infof(data, "SSH DISCONNECT starts now"));
 
   if(conn->proto.sshc.ssh_session) {
     /* only if there's a session still around to use! */
@@ -2529,7 +2528,7 @@
     result = myssh_block_statemach(data, TRUE);
   }
 
-  DEBUGF(infof(data, "SSH DISCONNECT is done\n"));
+  DEBUGF(infof(data, "SSH DISCONNECT is done"));
 
   return result;
 
@@ -2940,9 +2939,9 @@
   (void)ssh_finalize();
 }
 
-size_t Curl_ssh_version(char *buffer, size_t buflen)
+void Curl_ssh_version(char *buffer, size_t buflen)
 {
-  return msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION);
+  (void)msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION);
 }
 
 #endif                          /* USE_LIBSSH */
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index 8a6345b..a772f1f 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -73,7 +73,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "inet_ntop.h"
 #include "parsedate.h" /* for the week day and month names */
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
@@ -378,7 +377,7 @@
   DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
 
   if(sshc->state != nowstate) {
-    infof(data, "SFTP %p state change from %s to %s\n",
+    infof(data, "SFTP %p state change from %s to %s",
           (void *)sshc, names[sshc->state], names[nowstate]);
   }
 #endif
@@ -491,7 +490,7 @@
         break;
 #endif
       default:
-        infof(data, "unsupported key type, can't check knownhosts!\n");
+        infof(data, "unsupported key type, can't check knownhosts!");
         keybit = 0;
         break;
       }
@@ -519,7 +518,7 @@
                                            &host);
 #endif
 
-        infof(data, "SSH host check: %d, key: %s\n", keycheck,
+        infof(data, "SSH host check: %d, key: %s", keycheck,
               (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
               host->key:"<none>");
 
@@ -586,7 +585,7 @@
                                           LIBSSH2_KNOWNHOST_KEYENC_RAW|
                                           keybit, NULL);
         if(addrc)
-          infof(data, "Warning adding the known host %s failed!\n",
+          infof(data, "Warning adding the known host %s failed!",
                 conn->host.name);
         else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE ||
                 rc == CURLKHSTAT_FINE_REPLACE) {
@@ -597,7 +596,7 @@
                                         data->set.str[STRING_SSH_KNOWNHOSTS],
                                         LIBSSH2_KNOWNHOST_FILE_OPENSSH);
           if(wrc) {
-            infof(data, "Warning, writing %s failed!\n",
+            infof(data, "Warning, writing %s failed!",
                   data->set.str[STRING_SSH_KNOWNHOSTS]);
           }
         }
@@ -626,7 +625,7 @@
     int i;
     for(i = 0; i < 16; i++)
       msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
-    infof(data, "SSH MD5 fingerprint: %s\n", md5buffer);
+    infof(data, "SSH MD5 fingerprint: %s", md5buffer);
   }
 
   /* Before we authenticate we check the hostkey's MD5 fingerprint
@@ -645,7 +644,7 @@
       sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
       return sshc->actualcode;
     }
-    infof(data, "MD5 checksum match!\n");
+    infof(data, "MD5 checksum match!");
     /* as we already matched, we skip the check for known hosts */
     return CURLE_OK;
   }
@@ -702,7 +701,7 @@
           if(store->name[0] == '[') {
             kh_name_end = strstr(store->name, "]:");
             if(!kh_name_end) {
-              infof(data, "Invalid host pattern %s in %s\n",
+              infof(data, "Invalid host pattern %s in %s",
                     store->name, data->set.str[STRING_SSH_KNOWNHOSTS]);
               continue;
             }
@@ -729,7 +728,7 @@
     }
 
     if(found) {
-      infof(data, "Found host %s in %s\n",
+      infof(data, "Found host %s in %s",
             conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
 
       switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) {
@@ -768,13 +767,13 @@
         return CURLE_SSH;
       }
 
-      infof(data, "Set \"%s\" as SSH hostkey type\n", hostkey_method);
+      infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method);
       result = libssh2_session_error_to_CURLE(
         libssh2_session_method_pref(
           sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method));
     }
     else {
-      infof(data, "Did not find host %s in %s\n",
+      infof(data, "Did not find host %s in %s",
             conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
     }
   }
@@ -874,7 +873,7 @@
       if(!sshc->authlist) {
         if(libssh2_userauth_authenticated(sshc->ssh_session)) {
           sshc->authed = TRUE;
-          infof(data, "SSH user accepted with no authentication\n");
+          infof(data, "SSH user accepted with no authentication");
           state(data, SSH_AUTH_DONE);
           break;
         }
@@ -887,7 +886,7 @@
         }
         break;
       }
-      infof(data, "SSH authentication methods available: %s\n",
+      infof(data, "SSH authentication methods available: %s",
             sshc->authlist);
 
       state(data, SSH_AUTH_PKEY_INIT);
@@ -972,8 +971,8 @@
           sshc->passphrase = "";
 
         if(sshc->rsa_pub)
-          infof(data, "Using SSH public key file '%s'\n", sshc->rsa_pub);
-        infof(data, "Using SSH private key file '%s'\n", sshc->rsa);
+          infof(data, "Using SSH public key file '%s'", sshc->rsa_pub);
+        infof(data, "Using SSH private key file '%s'", sshc->rsa);
 
         state(data, SSH_AUTH_PKEY);
       }
@@ -1000,14 +999,14 @@
 
       if(rc == 0) {
         sshc->authed = TRUE;
-        infof(data, "Initialized SSH public key authentication\n");
+        infof(data, "Initialized SSH public key authentication");
         state(data, SSH_AUTH_DONE);
       }
       else {
         char *err_msg = NULL;
         (void)libssh2_session_last_error(sshc->ssh_session,
                                          &err_msg, NULL, 0);
-        infof(data, "SSH public key authentication failed: %s\n", err_msg);
+        infof(data, "SSH public key authentication failed: %s", err_msg);
         state(data, SSH_AUTH_PASS_INIT);
         rc = 0; /* clear rc and continue */
       }
@@ -1035,7 +1034,7 @@
       }
       if(rc == 0) {
         sshc->authed = TRUE;
-        infof(data, "Initialized password authentication\n");
+        infof(data, "Initialized password authentication");
         state(data, SSH_AUTH_DONE);
       }
       else {
@@ -1069,7 +1068,7 @@
         if(!sshc->ssh_agent) {
           sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
           if(!sshc->ssh_agent) {
-            infof(data, "Could not create agent object\n");
+            infof(data, "Could not create agent object");
 
             state(data, SSH_AUTH_KEY_INIT);
             break;
@@ -1080,7 +1079,7 @@
         if(rc == LIBSSH2_ERROR_EAGAIN)
           break;
         if(rc < 0) {
-          infof(data, "Failure connecting to agent\n");
+          infof(data, "Failure connecting to agent");
           state(data, SSH_AUTH_KEY_INIT);
           rc = 0; /* clear rc and continue */
         }
@@ -1100,7 +1099,7 @@
       if(rc == LIBSSH2_ERROR_EAGAIN)
         break;
       if(rc < 0) {
-        infof(data, "Failure requesting identities to agent\n");
+        infof(data, "Failure requesting identities to agent");
         state(data, SSH_AUTH_KEY_INIT);
         rc = 0; /* clear rc and continue */
       }
@@ -1136,13 +1135,13 @@
       }
 
       if(rc < 0)
-        infof(data, "Failure requesting identities to agent\n");
+        infof(data, "Failure requesting identities to agent");
       else if(rc == 1)
-        infof(data, "No identity would match\n");
+        infof(data, "No identity would match");
 
       if(rc == LIBSSH2_ERROR_NONE) {
         sshc->authed = TRUE;
-        infof(data, "Agent based authentication successful\n");
+        infof(data, "Agent based authentication successful");
         state(data, SSH_AUTH_DONE);
       }
       else {
@@ -1174,7 +1173,7 @@
       }
       if(rc == 0) {
         sshc->authed = TRUE;
-        infof(data, "Initialized keyboard interactive authentication\n");
+        infof(data, "Initialized keyboard interactive authentication");
       }
       state(data, SSH_AUTH_DONE);
       break;
@@ -1190,7 +1189,7 @@
       /*
        * At this point we have an authenticated ssh session.
        */
-      infof(data, "Authentication complete\n");
+      infof(data, "Authentication complete");
 
       Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
 
@@ -1201,7 +1200,7 @@
         state(data, SSH_SFTP_INIT);
         break;
       }
-      infof(data, "SSH CONNECT phase done\n");
+      infof(data, "SSH CONNECT phase done");
       state(data, SSH_STOP);
       break;
 
@@ -1261,7 +1260,7 @@
              a time-out or similar */
           result = CURLE_SSH;
         sshc->actualcode = result;
-        DEBUGF(infof(data, "error = %lu makes libcurl = %d\n",
+        DEBUGF(infof(data, "error = %lu makes libcurl = %d",
                      sftperr, (int)result));
         state(data, SSH_STOP);
         break;
@@ -1271,7 +1270,7 @@
        we get the homedir here, we get the "workingpath" in the DO action
        since the homedir will remain the same between request but the
        working path will not. */
-    DEBUGF(infof(data, "SSH CONNECT phase done\n"));
+    DEBUGF(infof(data, "SSH CONNECT phase done"));
     state(data, SSH_STOP);
     break;
 
@@ -1285,7 +1284,7 @@
       }
 
       if(data->set.quote) {
-        infof(data, "Sending quote commands\n");
+        infof(data, "Sending quote commands");
         sshc->quote_item = data->set.quote;
         state(data, SSH_SFTP_QUOTE);
       }
@@ -1296,7 +1295,7 @@
 
     case SSH_SFTP_POSTQUOTE_INIT:
       if(data->set.postquote) {
-        infof(data, "Sending quote commands\n");
+        infof(data, "Sending quote commands");
         sshc->quote_item = data->set.postquote;
         state(data, SSH_SFTP_QUOTE);
       }
@@ -2048,7 +2047,7 @@
       if(sshc->slash_pos) {
         *sshc->slash_pos = 0;
 
-        infof(data, "Creating directory '%s'\n", sshp->path);
+        infof(data, "Creating directory '%s'", sshp->path);
         state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
         break;
       }
@@ -2411,7 +2410,7 @@
     if(data->req.size == 0) {
       /* no data to transfer */
       Curl_setup_transfer(data, -1, -1, FALSE, -1);
-      infof(data, "File already completely downloaded\n");
+      infof(data, "File already completely downloaded");
       state(data, SSH_STOP);
       break;
     }
@@ -2446,14 +2445,14 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to close libssh2 file: %d %s\n", rc, err_msg);
+          infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
         }
         sshc->sftp_handle = NULL;
       }
 
       Curl_safefree(sshp->path);
 
-      DEBUGF(infof(data, "SFTP DONE done\n"));
+      DEBUGF(infof(data, "SFTP DONE done"));
 
       /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
          After nextstate is executed, the control should come back to
@@ -2483,7 +2482,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
                                            NULL, 0);
-          infof(data, "Failed to close libssh2 file: %d %s\n", rc, err_msg);
+          infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
         }
         sshc->sftp_handle = NULL;
       }
@@ -2493,7 +2492,7 @@
           break;
         }
         if(rc < 0) {
-          infof(data, "Failed to stop libssh2 sftp subsystem\n");
+          infof(data, "Failed to stop libssh2 sftp subsystem");
         }
         sshc->sftp_session = NULL;
       }
@@ -2668,7 +2667,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to send libssh2 channel EOF: %d %s\n",
+          infof(data, "Failed to send libssh2 channel EOF: %d %s",
                 rc, err_msg);
         }
       }
@@ -2685,7 +2684,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to get channel EOF: %d %s\n", rc, err_msg);
+          infof(data, "Failed to get channel EOF: %d %s", rc, err_msg);
         }
       }
       state(data, SSH_SCP_WAIT_CLOSE);
@@ -2701,7 +2700,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Channel failed to close: %d %s\n", rc, err_msg);
+          infof(data, "Channel failed to close: %d %s", rc, err_msg);
         }
       }
       state(data, SSH_SCP_CHANNEL_FREE);
@@ -2717,12 +2716,12 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to free libssh2 scp subsystem: %d %s\n",
+          infof(data, "Failed to free libssh2 scp subsystem: %d %s",
                 rc, err_msg);
         }
         sshc->ssh_channel = NULL;
       }
-      DEBUGF(infof(data, "SCP DONE phase complete\n"));
+      DEBUGF(infof(data, "SCP DONE phase complete"));
 #if 0 /* PREV */
       state(data, SSH_SESSION_DISCONNECT);
 #endif
@@ -2743,7 +2742,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to free libssh2 scp subsystem: %d %s\n",
+          infof(data, "Failed to free libssh2 scp subsystem: %d %s",
                 rc, err_msg);
         }
         sshc->ssh_channel = NULL;
@@ -2758,7 +2757,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to disconnect libssh2 session: %d %s\n",
+          infof(data, "Failed to disconnect libssh2 session: %d %s",
                 rc, err_msg);
         }
       }
@@ -2787,7 +2786,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to disconnect from libssh2 agent: %d %s\n",
+          infof(data, "Failed to disconnect from libssh2 agent: %d %s",
                 rc, err_msg);
         }
         libssh2_agent_free(sshc->ssh_agent);
@@ -2809,7 +2808,7 @@
           char *err_msg = NULL;
           (void)libssh2_session_last_error(sshc->ssh_session,
                                            &err_msg, NULL, 0);
-          infof(data, "Failed to free libssh2 session: %d %s\n", rc, err_msg);
+          infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
         }
         sshc->ssh_session = NULL;
       }
@@ -2938,6 +2937,7 @@
 {
   struct ssh_conn *sshc = &conn->proto.sshc;
   CURLcode result = CURLE_OK;
+  struct curltime dis = Curl_now();
 
   while((sshc->state != SSH_STOP) && !result) {
     bool block;
@@ -2962,6 +2962,12 @@
         return CURLE_OPERATION_TIMEDOUT;
       }
     }
+    else if(Curl_timediff(now, dis) > 1000) {
+      /* disconnect timeout */
+      failf(data, "Disconnect timed out");
+      result = CURLE_OK;
+      break;
+    }
 
     if(block) {
       int dir = libssh2_session_block_directions(sshc->ssh_session);
@@ -3078,10 +3084,10 @@
 
 #ifdef CURL_LIBSSH2_DEBUG
   if(conn->user) {
-    infof(data, "User: %s\n", conn->user);
+    infof(data, "User: %s", conn->user);
   }
   if(conn->passwd) {
-    infof(data, "Password: %s\n", conn->passwd);
+    infof(data, "Password: %s", conn->passwd);
   }
   sock = conn->sock[FIRSTSOCKET];
 #endif /* CURL_LIBSSH2_DEBUG */
@@ -3115,7 +3121,7 @@
     sshrecv.recvptr = ssh_tls_recv;
     sshsend.sendptr = ssh_tls_send;
 
-    infof(data, "Uses HTTPS proxy!\n");
+    infof(data, "Uses HTTPS proxy!");
     /*
       Setup libssh2 callbacks to make it read/write TLS from the socket.
 
@@ -3153,7 +3159,7 @@
 #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\n");
+      infof(data, "Failed to enable compression for ssh session");
   }
 
 #ifdef HAVE_LIBSSH2_KNOWNHOST_API
@@ -3171,14 +3177,14 @@
                                     data->set.str[STRING_SSH_KNOWNHOSTS],
                                     LIBSSH2_KNOWNHOST_FILE_OPENSSH);
     if(rc < 0)
-      infof(data, "Failed to read known hosts from %s\n",
+      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);
-  infof(data, "SSH socket: %d\n", (int)sock);
+  infof(data, "SSH socket: %d", (int)sock);
 #endif /* CURL_LIBSSH2_DEBUG */
 
   state(data, SSH_INIT);
@@ -3205,7 +3211,7 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
 
@@ -3218,7 +3224,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -3232,7 +3238,7 @@
   result = ssh_multi_statemach(data, dophase_done);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   return result;
 }
@@ -3394,7 +3400,7 @@
 {
   CURLcode result = CURLE_OK;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
 
@@ -3407,7 +3413,7 @@
   *connected = data->conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -3420,7 +3426,7 @@
   CURLcode result = ssh_multi_statemach(data, dophase_done);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   return result;
 }
@@ -3435,7 +3441,7 @@
   struct ssh_conn *sshc = &conn->proto.sshc;
   (void) dead_connection;
 
-  DEBUGF(infof(data, "SSH DISCONNECT starts now\n"));
+  DEBUGF(infof(data, "SSH DISCONNECT starts now"));
 
   if(sshc->ssh_session) {
     /* only if there's a session still around to use! */
@@ -3443,7 +3449,7 @@
     result = ssh_block_statemach(data, conn, TRUE);
   }
 
-  DEBUGF(infof(data, "SSH DISCONNECT is done\n"));
+  DEBUGF(infof(data, "SSH DISCONNECT is done"));
 
   return result;
 
@@ -3602,9 +3608,9 @@
 #endif
 }
 
-size_t Curl_ssh_version(char *buffer, size_t buflen)
+void Curl_ssh_version(char *buffer, size_t buflen)
 {
-  return msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION);
+  (void)msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION);
 }
 
 /* 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 505b078..7972081 100644
--- a/Utilities/cmcurl/lib/vssh/ssh.h
+++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -262,7 +262,7 @@
 /* generic SSH backend functions */
 CURLcode Curl_ssh_init(void);
 void Curl_ssh_cleanup(void);
-size_t Curl_ssh_version(char *buffer, size_t buflen);
+void Curl_ssh_version(char *buffer, size_t buflen);
 void Curl_ssh_attach(struct Curl_easy *data,
                      struct connectdata *conn);
 #else
diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c
index 9f3266a..4b1e2ec 100644
--- a/Utilities/cmcurl/lib/vssh/wolfssh.c
+++ b/Utilities/cmcurl/lib/vssh/wolfssh.c
@@ -205,7 +205,7 @@
   DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
 
   if(sshc->state != nowstate) {
-    infof(data, "wolfssh %p state change from %s to %s\n",
+    infof(data, "wolfssh %p state change from %s to %s",
           (void *)sshc, names[sshc->state], names[nowstate]);
   }
 #endif
@@ -274,7 +274,7 @@
     return -1;
   }
   DEBUGASSERT(rc == (int)len);
-  infof(data, "sent %zd bytes SFTP from offset %zd\n",
+  infof(data, "sent %zd bytes SFTP from offset %zd",
         len, sshc->offset);
   sshc->offset += len;
   return (ssize_t)rc;
@@ -348,7 +348,7 @@
                     void *ctx)
 {
   struct Curl_easy *data = ctx;
-  DEBUGF(infof(data, "wolfssh callback: type %s\n",
+  DEBUGF(infof(data, "wolfssh callback: type %s",
                authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
                "PUBLICCKEY"));
   if(authtype == WOLFSSH_USERAUTH_PASSWORD) {
@@ -468,7 +468,7 @@
         state(data, SSH_STOP);
         return CURLE_SSH;
       }
-      infof(data, "wolfssh connected!\n");
+      infof(data, "wolfssh connected!");
       state(data, SSH_STOP);
       break;
     case SSH_STOP:
@@ -489,7 +489,7 @@
         return CURLE_OK;
       }
       else if(rc == WS_SUCCESS) {
-        infof(data, "wolfssh SFTP connected!\n");
+        infof(data, "wolfssh SFTP connected!");
         state(data, SSH_SFTP_REALPATH);
       }
       else {
@@ -518,7 +518,7 @@
         else {
           memcpy(sshc->homedir, name->fName, name->fSz);
           sshc->homedir[name->fSz] = 0;
-          infof(data, "wolfssh SFTP realpath succeeded!\n");
+          infof(data, "wolfssh SFTP realpath succeeded!");
         }
         wolfSSH_SFTPNAME_list_free(name);
         state(data, SSH_STOP);
@@ -536,7 +536,7 @@
       }
 
       if(data->set.quote) {
-        infof(data, "Sending quote commands\n");
+        infof(data, "Sending quote commands");
         sshc->quote_item = data->set.quote;
         state(data, SSH_SFTP_QUOTE);
       }
@@ -616,7 +616,7 @@
         return CURLE_OK;
       }
       else if(rc == WS_SUCCESS) {
-        infof(data, "wolfssh SFTP open succeeded!\n");
+        infof(data, "wolfssh SFTP open succeeded!");
       }
       else {
         failf(data, "wolfssh SFTP upload open failed: %d", rc);
@@ -727,7 +727,7 @@
         return CURLE_OK;
       }
       else if(rc == WS_SUCCESS) {
-        infof(data, "wolfssh SFTP open succeeded!\n");
+        infof(data, "wolfssh SFTP open succeeded!");
         state(data, SSH_SFTP_DOWNLOAD_STAT);
         return CURLE_OK;
       }
@@ -753,7 +753,7 @@
         return CURLE_OK;
       }
       else if(rc == WS_SUCCESS) {
-        infof(data, "wolfssh STAT succeeded!\n");
+        infof(data, "wolfssh STAT succeeded!");
       }
       else {
         failf(data, "wolfssh SFTP open failed: %d", rc);
@@ -769,12 +769,12 @@
       data->req.maxdownload = size;
       Curl_pgrsSetDownloadSize(data, size);
 
-      infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes\n", size);
+      infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
 
       /* We cannot seek with wolfSSH so resuming and range requests are not
          possible */
       if(data->state.use_range || data->state.resume_from) {
-        infof(data, "wolfSSH cannot do range/seek on SFTP\n");
+        infof(data, "wolfSSH cannot do range/seek on SFTP");
         return CURLE_BAD_DOWNLOAD_RESUME;
       }
 
@@ -782,7 +782,7 @@
       if(data->req.size == 0) {
         /* no data to transfer */
         Curl_setup_transfer(data, -1, -1, FALSE, -1);
-        infof(data, "File already completely downloaded\n");
+        infof(data, "File already completely downloaded");
         state(data, SSH_STOP);
         break;
       }
@@ -911,7 +911,7 @@
     /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
        try again */
     if(*done) {
-      DEBUGF(infof(data, "wssh_statemach_act says DONE\n"));
+      DEBUGF(infof(data, "wssh_statemach_act says DONE"));
     }
   } while(!result && !*done && !block);
 
@@ -937,7 +937,7 @@
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
 
@@ -950,7 +950,7 @@
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -1107,7 +1107,7 @@
   CURLcode result = wssh_multi_statemach(data, dophase_done);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   return result;
 }
@@ -1119,7 +1119,7 @@
   CURLcode result = CURLE_OK;
   (void)dead;
 
-  DEBUGF(infof(data, "SSH DISCONNECT starts now\n"));
+  DEBUGF(infof(data, "SSH DISCONNECT starts now"));
 
   if(conn->proto.sshc.ssh_session) {
     /* only if there's a session still around to use! */
@@ -1127,7 +1127,7 @@
     result = wssh_block_statemach(data, TRUE);
   }
 
-  DEBUGF(infof(data, "SSH DISCONNECT is done\n"));
+  DEBUGF(infof(data, "SSH DISCONNECT is done"));
   return result;
 }
 
@@ -1148,9 +1148,9 @@
   return bitmap;
 }
 
-size_t Curl_ssh_version(char *buffer, size_t buflen)
+void Curl_ssh_version(char *buffer, size_t buflen)
 {
-  return msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
+  (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
 }
 
 CURLcode Curl_ssh_init(void)
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c
index 7f72971..e87649e 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.c
+++ b/Utilities/cmcurl/lib/vtls/bearssl.c
@@ -68,6 +68,14 @@
   size_t dn_len;
 };
 
+#define CAFILE_SOURCE_PATH 1
+#define CAFILE_SOURCE_BLOB 2
+struct cafile_source {
+  const int type;
+  const char * const data;
+  const size_t len;
+};
+
 static void append_dn(void *ctx, const void *buf, size_t len)
 {
   struct cafile_parser *ca = ctx;
@@ -90,7 +98,8 @@
     br_x509_decoder_push(&ca->xc, buf, len);
 }
 
-static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors,
+static CURLcode load_cafile(struct cafile_source *source,
+                            br_x509_trust_anchor **anchors,
                             size_t *anchors_len)
 {
   struct cafile_parser ca;
@@ -100,13 +109,22 @@
   br_x509_trust_anchor *new_anchors;
   size_t new_anchors_len;
   br_x509_pkey *pkey;
-  FILE *fp;
-  unsigned char buf[BUFSIZ], *p;
+  FILE *fp = 0;
+  unsigned char buf[BUFSIZ];
+  const unsigned char *p;
   const char *name;
   size_t n, i, pushed;
 
-  fp = fopen(path, "rb");
-  if(!fp)
+  DEBUGASSERT(source->type == CAFILE_SOURCE_PATH
+              || source->type == CAFILE_SOURCE_BLOB);
+
+  if(source->type == CAFILE_SOURCE_PATH) {
+    fp = fopen(source->data, "rb");
+    if(!fp)
+      return CURLE_SSL_CACERT_BADFILE;
+  }
+
+  if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX)
     return CURLE_SSL_CACERT_BADFILE;
 
   ca.err = CURLE_OK;
@@ -115,11 +133,17 @@
   ca.anchors_len = 0;
   br_pem_decoder_init(&pc);
   br_pem_decoder_setdest(&pc, x509_push, &ca);
-  for(;;) {
-    n = fread(buf, 1, sizeof(buf), fp);
-    if(n == 0)
-      break;
-    p = buf;
+  do {
+    if(source->type == CAFILE_SOURCE_PATH) {
+      n = fread(buf, 1, sizeof(buf), fp);
+      if(n == 0)
+        break;
+      p = buf;
+    }
+    else if(source->type == CAFILE_SOURCE_BLOB) {
+      n = source->len;
+      p = (unsigned char *) source->data;
+    }
     while(n) {
       pushed = br_pem_decoder_push(&pc, p, n);
       if(ca.err)
@@ -211,12 +235,13 @@
         goto fail;
       }
     }
-  }
-  if(ferror(fp))
+  } while(source->type != CAFILE_SOURCE_BLOB);
+  if(fp && ferror(fp))
     ca.err = CURLE_READ_ERROR;
 
 fail:
-  fclose(fp);
+  if(fp)
+    fclose(fp);
   if(ca.err == CURLE_OK) {
     *anchors = ca.anchors;
     *anchors_len = ca.anchors_len;
@@ -299,8 +324,11 @@
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
-  const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
-  const char * const hostname = SSL_HOST_NAME();
+  const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob);
+  const char * const ssl_cafile =
+    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+    (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile));
+  const char *hostname = SSL_HOST_NAME();
   const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
   const bool verifyhost = SSL_CONN_CONFIG(verifyhost);
   CURLcode ret;
@@ -340,8 +368,30 @@
     return CURLE_SSL_CONNECT_ERROR;
   }
 
+  if(ca_info_blob) {
+    struct cafile_source source = {
+      CAFILE_SOURCE_BLOB,
+      ca_info_blob->data,
+      ca_info_blob->len,
+    };
+    ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+    if(ret != CURLE_OK) {
+      if(verifypeer) {
+        failf(data, "error importing CA certificate blob");
+        return ret;
+      }
+      /* Only warn if no certificate verification is required. */
+      infof(data, "error importing CA certificate blob, continuing anyway");
+    }
+  }
+
   if(ssl_cafile) {
-    ret = load_cafile(ssl_cafile, &backend->anchors, &backend->anchors_len);
+    struct cafile_source source = {
+      CAFILE_SOURCE_PATH,
+      ssl_cafile,
+      0,
+    };
+    ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
     if(ret != CURLE_OK) {
       if(verifypeer) {
         failf(data, "error setting certificate verify locations."
@@ -349,7 +399,7 @@
         return ret;
       }
       infof(data, "error setting certificate verify locations,"
-            " continuing anyway:\n");
+            " continuing anyway:");
     }
   }
 
@@ -373,7 +423,7 @@
     if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
                               &session, NULL, sockindex)) {
       br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
-      infof(data, "BearSSL: re-using session ID\n");
+      infof(data, "BearSSL: re-using session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -392,12 +442,12 @@
 #endif
       ) {
       backend->protocols[cur++] = ALPN_H2;
-      infof(data, "ALPN, offering %s\n", ALPN_H2);
+      infof(data, "ALPN, offering %s", ALPN_H2);
     }
 #endif
 
     backend->protocols[cur++] = ALPN_HTTP_1_1;
-    infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+    infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
 
     br_ssl_engine_set_protocol_names(&backend->ctx.eng,
                                      backend->protocols, cur);
@@ -538,7 +588,7 @@
 
     protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
     if(protocol) {
-      infof(data, "ALPN, server accepted to use %s\n", protocol);
+      infof(data, "ALPN, server accepted to use %s", protocol);
 
 #ifdef USE_HTTP2
       if(!strcmp(protocol, ALPN_H2))
@@ -548,12 +598,12 @@
       if(!strcmp(protocol, ALPN_HTTP_1_1))
         conn->negnpn = CURL_HTTP_VERSION_1_1;
       else
-        infof(data, "ALPN, unrecognized protocol %s\n", protocol);
+        infof(data, "ALPN, unrecognized protocol %s", protocol);
       Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                           BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
     else
-      infof(data, "ALPN, server did not agree to a protocol\n");
+      infof(data, "ALPN, server did not agree to a protocol");
   }
 
   if(SSL_SET_OPTION(primary.sessionid)) {
@@ -840,30 +890,32 @@
 }
 
 const struct Curl_ssl Curl_ssl_bearssl = {
-  { CURLSSLBACKEND_BEARSSL, "bearssl" },
-  0,
+  { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
+  SSLSUPP_CAINFO_BLOB,
   sizeof(struct ssl_backend_data),
 
-  Curl_none_init,
-  Curl_none_cleanup,
-  bearssl_version,
-  Curl_none_check_cxn,
-  Curl_none_shutdown,
-  bearssl_data_pending,
-  bearssl_random,
-  Curl_none_cert_status_request,
-  bearssl_connect,
-  bearssl_connect_nonblocking,
-  Curl_ssl_getsock,
-  bearssl_get_internals,
-  bearssl_close,
-  Curl_none_close_all,
-  bearssl_session_free,
-  Curl_none_set_engine,
-  Curl_none_set_engine_default,
-  Curl_none_engines_list,
-  Curl_none_false_start,
-  bearssl_sha256sum
+  Curl_none_init,                  /* init */
+  Curl_none_cleanup,               /* cleanup */
+  bearssl_version,                 /* version */
+  Curl_none_check_cxn,             /* check_cxn */
+  Curl_none_shutdown,              /* shutdown */
+  bearssl_data_pending,            /* data_pending */
+  bearssl_random,                  /* random */
+  Curl_none_cert_status_request,   /* cert_status_request */
+  bearssl_connect,                 /* connect */
+  bearssl_connect_nonblocking,     /* connect_nonblocking */
+  Curl_ssl_getsock,                /* getsock */
+  bearssl_get_internals,           /* get_internals */
+  bearssl_close,                   /* close_one */
+  Curl_none_close_all,             /* close_all */
+  bearssl_session_free,            /* session_free */
+  Curl_none_set_engine,            /* set_engine */
+  Curl_none_set_engine_default,    /* set_engine_default */
+  Curl_none_engines_list,          /* engines_list */
+  Curl_none_false_start,           /* false_start */
+  bearssl_sha256sum,               /* sha256sum */
+  NULL,                            /* associate_connection */
+  NULL                             /* disassociate_connection */
 };
 
 #endif /* USE_BEARSSL */
diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c
index ca95376..e451f6a 100644
--- a/Utilities/cmcurl/lib/vtls/gskit.c
+++ b/Utilities/cmcurl/lib/vtls/gskit.c
@@ -180,6 +180,7 @@
 static CURLcode gskit_status(struct Curl_easy *data, int rc,
                              const char *procname, CURLcode defcode)
 {
+  char buffer[STRERROR_LEN];
   /* Process GSKit status and map it to a CURLcode. */
   switch(rc) {
   case GSK_OK:
@@ -208,7 +209,8 @@
     case ENOMEM:
       return CURLE_OUT_OF_MEMORY;
     default:
-      failf(data, "%s I/O error: %s", procname, strerror(errno));
+      failf(data, "%s I/O error: %s", procname,
+            Curl_strerror(errno, buffer, sizeof(buffer)));
       break;
     }
     break;
@@ -223,13 +225,15 @@
 static CURLcode set_enum(struct Curl_easy *data, gsk_handle h,
                 GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok)
 {
+  char buffer[STRERROR_LEN];
   int rc = gsk_attribute_set_enum(h, id, value);
 
   switch(rc) {
   case GSK_OK:
     return CURLE_OK;
   case GSK_ERROR_IO:
-    failf(data, "gsk_attribute_set_enum() I/O error: %s", strerror(errno));
+    failf(data, "gsk_attribute_set_enum() I/O error: %s",
+          Curl_strerror(errno, buffer, sizeof(buffer)));
     break;
   case GSK_ATTRIBUTE_INVALID_ID:
     if(unsupported_ok)
@@ -245,13 +249,15 @@
 static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h,
                         GSK_BUF_ID id, const char *buffer, bool unsupported_ok)
 {
+  char buffer[STRERROR_LEN];
   int rc = gsk_attribute_set_buffer(h, id, buffer, 0);
 
   switch(rc) {
   case GSK_OK:
     return CURLE_OK;
   case GSK_ERROR_IO:
-    failf(data, "gsk_attribute_set_buffer() I/O error: %s", strerror(errno));
+    failf(data, "gsk_attribute_set_buffer() I/O error: %s",
+          Curl_strerror(errno, buffer, sizeof(buffer)));
     break;
   case GSK_ATTRIBUTE_INVALID_ID:
     if(unsupported_ok)
@@ -267,6 +273,7 @@
 static CURLcode set_numeric(struct Curl_easy *data,
                             gsk_handle h, GSK_NUM_ID id, int value)
 {
+  char buffer[STRERROR_LEN];
   int rc = gsk_attribute_set_numeric_value(h, id, value);
 
   switch(rc) {
@@ -274,7 +281,7 @@
     return CURLE_OK;
   case GSK_ERROR_IO:
     failf(data, "gsk_attribute_set_numeric_value() I/O error: %s",
-          strerror(errno));
+          Curl_strerror(errno, buffer, sizeof(buffer)));
     break;
   default:
     failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc));
@@ -287,13 +294,15 @@
 static CURLcode set_callback(struct Curl_easy *data,
                              gsk_handle h, GSK_CALLBACK_ID id, void *info)
 {
+  char buffer[STRERROR_LEN];
   int rc = gsk_attribute_set_callback(h, id, info);
 
   switch(rc) {
   case GSK_OK:
     return CURLE_OK;
   case GSK_ERROR_IO:
-    failf(data, "gsk_attribute_set_callback() I/O error: %s", strerror(errno));
+    failf(data, "gsk_attribute_set_callback() I/O error: %s",
+          Curl_strerror(errno, buffer, sizeof(buffer)));
     break;
   default:
     failf(data, "gsk_attribute_set_callback(): %s", gsk_strerror(rc));
@@ -966,7 +975,9 @@
         continue;       /* Retry. */
       }
       if(errno != ETIME) {
-        failf(data, "QsoWaitForIOCompletion() I/O error: %s", strerror(errno));
+        char buffer[STRERROR_LEN];
+        failf(data, "QsoWaitForIOCompletion() I/O error: %s",
+              Curl_strerror(errno, buffer, sizeof(buffer)));
         cancel_async_handshake(conn, sockindex);
         close_async_handshake(connssl);
         return CURLE_SSL_CONNECT_ERROR;
@@ -1011,7 +1022,7 @@
      CURLE_OK) {
     int i;
 
-    infof(data, "Server certificate:\n");
+    infof(data, "Server certificate:");
     p = cdev;
     for(i = 0; i++ < cdec; p++)
       switch(p->cert_data_id) {
@@ -1020,16 +1031,16 @@
         certend = cert + cdev->cert_data_l;
         break;
       case CERT_DN_PRINTABLE:
-        infof(data, "\t subject: %.*s\n", p->cert_data_l, p->cert_data_p);
+        infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p);
         break;
       case CERT_ISSUER_DN_PRINTABLE:
-        infof(data, "\t issuer: %.*s\n", p->cert_data_l, p->cert_data_p);
+        infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p);
         break;
       case CERT_VALID_FROM:
-        infof(data, "\t start date: %.*s\n", p->cert_data_l, p->cert_data_p);
+        infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p);
         break;
       case CERT_VALID_TO:
-        infof(data, "\t expire date: %.*s\n", p->cert_data_l, p->cert_data_p);
+        infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p);
         break;
     }
   }
@@ -1192,6 +1203,7 @@
   int what;
   int rc;
   char buf[120];
+  int loop = 10; /* don't get stuck */
 
   if(!BACKEND->handle)
     return 0;
@@ -1206,7 +1218,7 @@
   what = SOCKET_READABLE(conn->sock[sockindex],
                          SSL_SHUTDOWN_TIMEOUT);
 
-  for(;;) {
+  while(loop--) {
     ssize_t nread;
 
     if(what < 0) {
@@ -1228,7 +1240,8 @@
     nread = read(conn->sock[sockindex], buf, sizeof(buf));
 
     if(nread < 0) {
-      failf(data, "read: %s", strerror(errno));
+      char buffer[STRERROR_LEN];
+      failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer)));
       rc = -1;
     }
 
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index ecde5c4..1b145d8 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -147,7 +147,7 @@
 
   msnprintf(str,
             sizeof(str),
-            "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT",
+            "  %s: %s, %02d %s %4d %02d:%02d:%02d GMT",
             text,
             Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
             tm->tm_mday,
@@ -156,7 +156,7 @@
             tm->tm_hour,
             tm->tm_min,
             tm->tm_sec);
-  infof(data, "%s\n", str);
+  infof(data, "%s", str);
 }
 #endif
 
@@ -266,7 +266,7 @@
       if(!strerr)
         strerr = gnutls_strerror(rc);
 
-      infof(data, "gnutls_handshake() warning: %s\n", strerr);
+      infof(data, "gnutls_handshake() warning: %s", strerr);
       continue;
     }
     else if(rc < 0) {
@@ -330,6 +330,9 @@
       ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2;
     }
   }
+  else if(ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT) {
+    ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3;
+  }
 
   switch(ssl_version | ssl_version_max) {
   case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0:
@@ -338,11 +341,11 @@
     return CURLE_OK;
   case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.0:+VERS-TLS1.1";
+      "+VERS-TLS1.1:+VERS-TLS1.0";
     return CURLE_OK;
   case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2";
+      "+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0";
     return CURLE_OK;
   case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
@@ -350,7 +353,7 @@
     return CURLE_OK;
   case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.1:+VERS-TLS1.2";
+      "+VERS-TLS1.2:+VERS-TLS1.1";
     return CURLE_OK;
   case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
@@ -360,25 +363,16 @@
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
       "+VERS-TLS1.3";
     return CURLE_OK;
-  case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_DEFAULT:
-    *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2"
-      ":+VERS-TLS1.3";
+  case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_3:
+    *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0";
     return CURLE_OK;
-  case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_DEFAULT:
+  case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_3:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.1:+VERS-TLS1.2"
-      ":+VERS-TLS1.3";
+      "+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1";
     return CURLE_OK;
-  case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT:
+  case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_3:
     *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.2"
-      ":+VERS-TLS1.3";
-    return CURLE_OK;
-  case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_DEFAULT:
-    *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
-      "+VERS-TLS1.2"
-      ":+VERS-TLS1.3";
+      "+VERS-TLS1.3:+VERS-TLS1.2";
     return CURLE_OK;
   }
 
@@ -438,7 +432,7 @@
 
 #ifdef HAVE_GNUTLS_SRP
   if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) {
-    infof(data, "Using TLS-SRP username: %s\n", SSL_SET_OPTION(username));
+    infof(data, "Using TLS-SRP username: %s", SSL_SET_OPTION(username));
 
     rc = gnutls_srp_allocate_client_credentials(
            &backend->srp_client_cred);
@@ -468,7 +462,7 @@
                                                 SSL_CONN_CONFIG(CAfile),
                                                 GNUTLS_X509_FMT_PEM);
     if(rc < 0) {
-      infof(data, "error reading ca cert file %s (%s)\n",
+      infof(data, "error reading ca cert file %s (%s)",
             SSL_CONN_CONFIG(CAfile), gnutls_strerror(rc));
       if(SSL_CONN_CONFIG(verifypeer)) {
         *certverifyresult = rc;
@@ -476,7 +470,7 @@
       }
     }
     else
-      infof(data, "found %d certificates in %s\n", rc,
+      infof(data, "found %d certificates in %s", rc,
             SSL_CONN_CONFIG(CAfile));
   }
 
@@ -486,7 +480,7 @@
                                                SSL_CONN_CONFIG(CApath),
                                                GNUTLS_X509_FMT_PEM);
     if(rc < 0) {
-      infof(data, "error reading ca cert file %s (%s)\n",
+      infof(data, "error reading ca cert file %s (%s)",
             SSL_CONN_CONFIG(CApath), gnutls_strerror(rc));
       if(SSL_CONN_CONFIG(verifypeer)) {
         *certverifyresult = rc;
@@ -494,7 +488,7 @@
       }
     }
     else
-      infof(data, "found %d certificates in %s\n",
+      infof(data, "found %d certificates in %s",
             rc, SSL_CONN_CONFIG(CApath));
   }
 
@@ -517,7 +511,7 @@
       return CURLE_SSL_CRL_BADFILE;
     }
     else
-      infof(data, "found %d CRL in %s\n",
+      infof(data, "found %d CRL in %s",
             rc, SSL_SET_OPTION(CRLfile));
   }
 
@@ -550,7 +544,7 @@
      (gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname,
                              strlen(hostname)) < 0))
     infof(data, "WARNING: failed to configure server name indication (SNI) "
-          "TLS extension\n");
+          "TLS extension");
 
   /* Use default priorities */
   rc = gnutls_set_default_priority(session);
@@ -603,11 +597,12 @@
     free(prioritysrp);
 
     if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
-      infof(data, "This GnuTLS does not support SRP\n");
+      infof(data, "This GnuTLS does not support SRP");
     }
   }
   else {
 #endif
+    infof(data, "GnuTLS ciphers: %s", prioritylist);
     rc = gnutls_priority_set_direct(session, prioritylist, &err);
 #ifdef HAVE_GNUTLS_SRP
   }
@@ -632,14 +627,14 @@
       protocols[cur].data = (unsigned char *)ALPN_H2;
       protocols[cur].size = ALPN_H2_LENGTH;
       cur++;
-      infof(data, "ALPN, offering %.*s\n", ALPN_H2_LENGTH, ALPN_H2);
+      infof(data, "ALPN, offering %.*s", ALPN_H2_LENGTH, ALPN_H2);
     }
 #endif
 
     protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
     protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
     cur++;
-    infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+    infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
 
     gnutls_alpn_set_protocols(session, protocols, cur, 0);
   }
@@ -745,7 +740,7 @@
       gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
 
       /* Informational message */
-      infof(data, "SSL re-using session ID\n");
+      infof(data, "SSL re-using session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -848,7 +843,7 @@
                                      gnutls_cipher_get(session),
                                      gnutls_mac_get(session));
 
-  infof(data, "SSL connection using %s / %s\n",
+  infof(data, "SSL connection using %s / %s",
         gnutls_protocol_get_name(version), ptr);
 
   /* This function will return the peer's raw certificate (chain) as sent by
@@ -861,7 +856,7 @@
   if(!chainp) {
     if(SSL_CONN_CONFIG(verifypeer) ||
        SSL_CONN_CONFIG(verifyhost) ||
-       SSL_SET_OPTION(issuercert)) {
+       SSL_CONN_CONFIG(issuercert)) {
 #ifdef HAVE_GNUTLS_SRP
       if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP
          && SSL_SET_OPTION(username) != NULL
@@ -879,7 +874,7 @@
       }
 #endif
     }
-    infof(data, "\t common name: WARNING couldn't obtain\n");
+    infof(data, " common name: WARNING couldn't obtain");
   }
 
   if(data->set.ssl.certinfo && chainp) {
@@ -926,13 +921,13 @@
         return CURLE_PEER_FAILED_VERIFICATION;
       }
       else
-        infof(data, "\t server certificate verification FAILED\n");
+        infof(data, "  server certificate verification FAILED");
     }
     else
-      infof(data, "\t server certificate verification OK\n");
+      infof(data, "  server certificate verification OK");
   }
   else
-    infof(data, "\t server certificate verification SKIPPED\n");
+    infof(data, "  server certificate verification SKIPPED");
 
   if(SSL_CONN_CONFIG(verifystatus)) {
     if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
@@ -944,7 +939,7 @@
 
       rc = gnutls_ocsp_status_request_get(session, &status_request);
 
-      infof(data, "\t server certificate status verification FAILED\n");
+      infof(data, " server certificate status verification FAILED");
 
       if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
         failf(data, "No OCSP response received");
@@ -1032,10 +1027,10 @@
       return CURLE_SSL_INVALIDCERTSTATUS;
     }
     else
-      infof(data, "\t server certificate status verification OK\n");
+      infof(data, "  server certificate status verification OK");
   }
   else
-    infof(data, "\t server certificate status verification SKIPPED\n");
+    infof(data, "  server certificate status verification SKIPPED");
 
   /* initialize an X.509 certificate structure. */
   gnutls_x509_crt_init(&x509_cert);
@@ -1045,21 +1040,21 @@
        gnutls_x509_crt_t format */
     gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
 
-  if(SSL_SET_OPTION(issuercert)) {
+  if(SSL_CONN_CONFIG(issuercert)) {
     gnutls_x509_crt_init(&x509_issuer);
-    issuerp = load_file(SSL_SET_OPTION(issuercert));
+    issuerp = load_file(SSL_CONN_CONFIG(issuercert));
     gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
     rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer);
     gnutls_x509_crt_deinit(x509_issuer);
     unload_file(issuerp);
     if(rc <= 0) {
       failf(data, "server certificate issuer check failed (IssuerCert: %s)",
-            SSL_SET_OPTION(issuercert)?SSL_SET_OPTION(issuercert):"none");
+            SSL_CONN_CONFIG(issuercert)?SSL_CONN_CONFIG(issuercert):"none");
       gnutls_x509_crt_deinit(x509_cert);
       return CURLE_SSL_ISSUER_ERROR;
     }
-    infof(data, "\t server certificate issuer check OK (Issuer Cert: %s)\n",
-          SSL_SET_OPTION(issuercert)?SSL_SET_OPTION(issuercert):"none");
+    infof(data, "  server certificate issuer check OK (Issuer Cert: %s)",
+          SSL_CONN_CONFIG(issuercert)?SSL_CONN_CONFIG(issuercert):"none");
   }
 
   size = sizeof(certname);
@@ -1069,7 +1064,7 @@
                                      certname,
                                      &size);
   if(rc) {
-    infof(data, "error fetching CN from cert:%s\n",
+    infof(data, "error fetching CN from cert:%s",
           gnutls_strerror(rc));
   }
 
@@ -1129,11 +1124,11 @@
       return CURLE_PEER_FAILED_VERIFICATION;
     }
     else
-      infof(data, "\t common name: %s (does not match '%s')\n",
+      infof(data, "  common name: %s (does not match '%s')",
             certname, SSL_HOST_DISPNAME());
   }
   else
-    infof(data, "\t common name: %s (matched)\n", certname);
+    infof(data, "  common name: %s (matched)", certname);
 
   /* Check for time-based validity */
   certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
@@ -1146,7 +1141,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     else
-      infof(data, "\t server certificate expiration date verify FAILED\n");
+      infof(data, "  server certificate expiration date verify FAILED");
   }
   else {
     if(certclock < time(NULL)) {
@@ -1157,10 +1152,10 @@
         return CURLE_PEER_FAILED_VERIFICATION;
       }
       else
-        infof(data, "\t server certificate expiration date FAILED\n");
+        infof(data, "  server certificate expiration date FAILED");
     }
     else
-      infof(data, "\t server certificate expiration date OK\n");
+      infof(data, "  server certificate expiration date OK");
   }
 
   certclock = gnutls_x509_crt_get_activation_time(x509_cert);
@@ -1173,7 +1168,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     else
-      infof(data, "\t server certificate activation date verify FAILED\n");
+      infof(data, "  server certificate activation date verify FAILED");
   }
   else {
     if(certclock > time(NULL)) {
@@ -1184,10 +1179,10 @@
         return CURLE_PEER_FAILED_VERIFICATION;
       }
       else
-        infof(data, "\t server certificate activation date FAILED\n");
+        infof(data, "  server certificate activation date FAILED");
     }
     else
-      infof(data, "\t server certificate activation date OK\n");
+      infof(data, "  server certificate activation date OK");
   }
 
   ptr = SSL_PINNED_PUB_KEY();
@@ -1213,19 +1208,19 @@
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   /* public key algorithm's parameters */
   algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits);
-  infof(data, "\t certificate public key: %s\n",
+  infof(data, "  certificate public key: %s",
         gnutls_pk_algorithm_get_name(algo));
 
   /* version of the X.509 certificate. */
-  infof(data, "\t certificate version: #%d\n",
+  infof(data, "  certificate version: #%d",
         gnutls_x509_crt_get_version(x509_cert));
 
 
   rc = gnutls_x509_crt_get_dn2(x509_cert, &certfields);
   if(rc)
-    infof(data, "Failed to get certificate name\n");
+    infof(data, "Failed to get certificate name");
   else {
-    infof(data, "\t subject: %s\n", certfields.data);
+    infof(data, "  subject: %s", certfields.data);
 
     certclock = gnutls_x509_crt_get_activation_time(x509_cert);
     showtime(data, "start date", certclock);
@@ -1238,9 +1233,9 @@
 
   rc = gnutls_x509_crt_get_issuer_dn2(x509_cert, &certfields);
   if(rc)
-    infof(data, "Failed to get certificate issuer\n");
+    infof(data, "Failed to get certificate issuer");
   else {
-    infof(data, "\t issuer: %s\n", certfields.data);
+    infof(data, "  issuer: %s", certfields.data);
 
     gnutls_free(certfields.data);
   }
@@ -1251,7 +1246,7 @@
   if(conn->bits.tls_enable_alpn) {
     rc = gnutls_alpn_get_selected_protocol(session, &proto);
     if(rc == 0) {
-      infof(data, "ALPN, server accepted to use %.*s\n", proto.size,
+      infof(data, "ALPN, server accepted to use %.*s", proto.size,
           proto.data);
 
 #ifdef USE_HTTP2
@@ -1268,7 +1263,7 @@
       }
     }
     else
-      infof(data, "ALPN, server did not agree to a protocol\n");
+      infof(data, "ALPN, server did not agree to a protocol");
 
     Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                         BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
@@ -1438,6 +1433,10 @@
 {
   struct ssl_backend_data *backend = connssl->backend;
   if(backend->session) {
+    char buf[32];
+    /* Maybe the server has already sent a close notify alert.
+       Read it to avoid an RST on the TCP connection. */
+    (void)gnutls_record_recv(backend->session, buf, sizeof(buf));
     gnutls_bye(backend->session, GNUTLS_SHUT_WR);
     gnutls_deinit(backend->session);
     backend->session = NULL;
@@ -1506,7 +1505,7 @@
           break;
         case GNUTLS_E_AGAIN:
         case GNUTLS_E_INTERRUPTED:
-          infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED\n");
+          infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED");
           break;
         default:
           retval = -1;
@@ -1620,7 +1619,7 @@
 }
 
 static void *gtls_get_internals(struct ssl_connect_data *connssl,
-                                     CURLINFO info UNUSED_PARAM)
+                                CURLINFO info UNUSED_PARAM)
 {
   struct ssl_backend_data *backend = connssl->backend;
   (void)info;
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index 3a0be0f..780d13e 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -41,7 +41,9 @@
 #include <mbedtls/net.h>
 #endif
 #include <mbedtls/ssl.h>
+#if MBEDTLS_VERSION_NUMBER < 0x03000000
 #include <mbedtls/certs.h>
+#endif
 #include <mbedtls/x509.h>
 
 #include <mbedtls/error.h>
@@ -89,6 +91,10 @@
 #define THREADING_SUPPORT
 #endif
 
+#ifndef MBEDTLS_ERROR_C
+#define mbedtls_strerror(a,b,c) b[0] = 0
+#endif
+
 #if defined(THREADING_SUPPORT)
 static mbedtls_entropy_context ts_entropy;
 
@@ -179,6 +185,17 @@
 
 static CURLcode mbedtls_version_from_curl(int *mbedver, long version)
 {
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+  switch(version) {
+    case CURL_SSLVERSION_TLSv1_0:
+    case CURL_SSLVERSION_TLSv1_1:
+    case CURL_SSLVERSION_TLSv1_2:
+      *mbedver = MBEDTLS_SSL_MINOR_VERSION_3;
+      return CURLE_OK;
+    case CURL_SSLVERSION_TLSv1_3:
+      break;
+  }
+#else
   switch(version) {
     case CURL_SSLVERSION_TLSv1_0:
       *mbedver = MBEDTLS_SSL_MINOR_VERSION_1;
@@ -192,6 +209,8 @@
     case CURL_SSLVERSION_TLSv1_3:
       break;
   }
+#endif
+
   return CURLE_SSL_CONNECT_ERROR;
 }
 
@@ -201,8 +220,13 @@
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+  int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3;
+  int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3;
+#else
   int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1;
   int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1;
+#endif
   long ssl_version = SSL_CONN_CONFIG(version);
   long ssl_version_max = SSL_CONN_CONFIG(version_max);
   CURLcode result = CURLE_OK;
@@ -250,12 +274,14 @@
   const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
   const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
   char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
+  const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
   const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
   const char * const hostname = SSL_HOST_NAME();
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
   const long int port = SSL_HOST_PORT();
+#endif
   int ret = -1;
   char errorbuf[128];
-  errorbuf[0] = 0;
 
   if((SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) ||
      (SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3)) {
@@ -270,9 +296,7 @@
   ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex,
                               &ts_entropy, NULL, 0);
   if(ret) {
-#ifdef MBEDTLS_ERROR_C
     mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
     failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s",
           -ret, errorbuf);
   }
@@ -283,9 +307,7 @@
   ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func,
                               &backend->entropy, NULL, 0);
   if(ret) {
-#ifdef MBEDTLS_ERROR_C
     mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
     failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s",
           -ret, errorbuf);
   }
@@ -298,9 +320,7 @@
     ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile);
 
     if(ret<0) {
-#ifdef MBEDTLS_ERROR_C
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
       failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s",
             ssl_cafile, -ret, errorbuf);
 
@@ -313,9 +333,7 @@
     ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath);
 
     if(ret<0) {
-#ifdef MBEDTLS_ERROR_C
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
       failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s",
             ssl_capath, -ret, errorbuf);
 
@@ -331,9 +349,7 @@
     ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert);
 
     if(ret) {
-#ifdef MBEDTLS_ERROR_C
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
       failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s",
             ssl_cert, -ret, errorbuf);
 
@@ -341,25 +357,70 @@
     }
   }
 
+  if(ssl_cert_blob) {
+    const unsigned char *blob_data =
+      (const unsigned char *)ssl_cert_blob->data;
+    ret = mbedtls_x509_crt_parse(&backend->clicert, blob_data,
+                                 ssl_cert_blob->len);
+
+    if(ret) {
+      mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+      failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
+            SSL_SET_OPTION(key), -ret, errorbuf);
+      return CURLE_SSL_CERTPROBLEM;
+    }
+  }
+
   /* Load the client private key */
   mbedtls_pk_init(&backend->pk);
 
-  if(SSL_SET_OPTION(key)) {
-    ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key),
-                                   SSL_SET_OPTION(key_passwd));
+  if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) {
+    if(SSL_SET_OPTION(key)) {
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+      ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key),
+                                     SSL_SET_OPTION(key_passwd),
+                                     mbedtls_ctr_drbg_random,
+                                     &backend->ctr_drbg);
+#else
+      ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key),
+                                     SSL_SET_OPTION(key_passwd));
+#endif
+
+      if(ret) {
+        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+        failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
+              SSL_SET_OPTION(key), -ret, errorbuf);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+    }
+    else {
+      const struct curl_blob *ssl_key_blob = SSL_SET_OPTION(key_blob);
+      const unsigned char *key_data =
+        (const unsigned char *)ssl_key_blob->data;
+      const char *passwd = SSL_SET_OPTION(key_passwd);
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+      ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len,
+                                 (const unsigned char *)passwd,
+                                 passwd ? strlen(passwd) : 0,
+                                 mbedtls_ctr_drbg_random,
+                                 &backend->ctr_drbg);
+#else
+      ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len,
+                                 (const unsigned char *)passwd,
+                                 passwd ? strlen(passwd) : 0);
+#endif
+
+      if(ret) {
+        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+        failf(data, "Error parsing private key - mbedTLS: (-0x%04X) %s",
+              -ret, errorbuf);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+    }
+
     if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
                      mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
       ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
-
-    if(ret) {
-#ifdef MBEDTLS_ERROR_C
-      mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
-      failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
-            SSL_SET_OPTION(key), -ret, errorbuf);
-
-      return CURLE_SSL_CERTPROBLEM;
-    }
   }
 
   /* Load the CRL */
@@ -369,9 +430,7 @@
     ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile);
 
     if(ret) {
-#ifdef MBEDTLS_ERROR_C
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
       failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s",
             ssl_crlfile, -ret, errorbuf);
 
@@ -379,7 +438,7 @@
     }
   }
 
-  infof(data, "mbedTLS: Connecting to %s:%ld\n", hostname, port);
+  infof(data, "mbedTLS: Connecting to %s:%ld", hostname, port);
 
   mbedtls_ssl_config_init(&backend->config);
 
@@ -404,10 +463,12 @@
   switch(SSL_CONN_CONFIG(version)) {
   case CURL_SSLVERSION_DEFAULT:
   case CURL_SSLVERSION_TLSv1:
+#if MBEDTLS_VERSION_NUMBER < 0x03000000
     mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
                                  MBEDTLS_SSL_MINOR_VERSION_1);
-    infof(data, "mbedTLS: Set min SSL version to TLS 1.0\n");
+    infof(data, "mbedTLS: Set min SSL version to TLS 1.0");
     break;
+#endif
   case CURL_SSLVERSION_TLSv1_0:
   case CURL_SSLVERSION_TLSv1_1:
   case CURL_SSLVERSION_TLSv1_2:
@@ -459,7 +520,7 @@
         failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret);
         return CURLE_SSL_CONNECT_ERROR;
       }
-      infof(data, "mbedTLS re-using session\n");
+      infof(data, "mbedTLS re-using session");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -468,7 +529,7 @@
                             &backend->cacert,
                             &backend->crl);
 
-  if(SSL_SET_OPTION(key)) {
+  if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) {
     mbedtls_ssl_conf_own_cert(&backend->config,
                               &backend->clicert, &backend->pk);
   }
@@ -497,7 +558,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     for(p = &backend->protocols[0]; *p; ++p)
-      infof(data, "ALPN, offering %s\n", *p);
+      infof(data, "ALPN, offering %s", *p);
   }
 #endif
 
@@ -553,18 +614,14 @@
   }
   else if(ret) {
     char errorbuf[128];
-    errorbuf[0] = 0;
-#ifdef MBEDTLS_ERROR_C
     mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
     failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s",
           -ret, errorbuf);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  infof(data, "mbedTLS: Handshake complete, cipher is %s\n",
-        mbedtls_ssl_get_ciphersuite(&backend->ssl)
-    );
+  infof(data, "mbedTLS: Handshake complete, cipher is %s",
+        mbedtls_ssl_get_ciphersuite(&backend->ssl));
 
   ret = mbedtls_ssl_get_verify_result(&backend->ssl);
 
@@ -601,9 +658,9 @@
       return CURLE_OUT_OF_MEMORY;
 
     if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0)
-      infof(data, "Dumping cert info:\n%s\n", buffer);
+      infof(data, "Dumping cert info: %s", buffer);
     else
-      infof(data, "Unable to dump certificate information.\n");
+      infof(data, "Unable to dump certificate information");
 
     free(buffer);
   }
@@ -611,10 +668,15 @@
   if(pinnedpubkey) {
     int size;
     CURLcode result;
-    mbedtls_x509_crt *p;
-    unsigned char pubkey[PUB_DER_MAX_BYTES];
+    mbedtls_x509_crt *p = NULL;
+    unsigned char *pubkey = NULL;
 
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+    if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) ||
+       !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) {
+#else
     if(!peercert || !peercert->raw.p || !peercert->raw.len) {
+#endif
       failf(data, "Failed due to missing peer certificate");
       return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
     }
@@ -624,39 +686,54 @@
     if(!p)
       return CURLE_OUT_OF_MEMORY;
 
+    pubkey = malloc(PUB_DER_MAX_BYTES);
+
+    if(!pubkey) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto pinnedpubkey_error;
+    }
+
     mbedtls_x509_crt_init(p);
 
     /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der
        needs a non-const key, for now.
        https://github.com/ARMmbed/mbedtls/issues/396 */
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+    if(mbedtls_x509_crt_parse_der(p,
+                        peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
+                        peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) {
+#else
     if(mbedtls_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) {
+#endif
       failf(data, "Failed copying peer certificate");
-      mbedtls_x509_crt_free(p);
-      free(p);
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+      result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+      goto pinnedpubkey_error;
     }
 
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+    size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey,
+                                       PUB_DER_MAX_BYTES);
+#else
     size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES);
+#endif
 
     if(size <= 0) {
       failf(data, "Failed copying public key from peer certificate");
-      mbedtls_x509_crt_free(p);
-      free(p);
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+      result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+      goto pinnedpubkey_error;
     }
 
     /* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */
     result = Curl_pin_peer_pubkey(data,
                                   pinnedpubkey,
                                   &pubkey[PUB_DER_MAX_BYTES - size], size);
-    if(result) {
-      mbedtls_x509_crt_free(p);
-      free(p);
-      return result;
-    }
-
+    pinnedpubkey_error:
     mbedtls_x509_crt_free(p);
     free(p);
+    free(pubkey);
+    if(result) {
+      return result;
+    }
   }
 
 #ifdef HAS_ALPN
@@ -664,7 +741,7 @@
     const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
 
     if(next_protocol) {
-      infof(data, "ALPN, server accepted to use %s\n", next_protocol);
+      infof(data, "ALPN, server accepted to use %s", next_protocol);
 #ifdef USE_NGHTTP2
       if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
                   NGHTTP2_PROTO_VERSION_ID_LEN) &&
@@ -679,7 +756,7 @@
         }
     }
     else {
-      infof(data, "ALPN, server did not agree to a protocol\n");
+      infof(data, "ALPN, server did not agree to a protocol");
     }
     Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                         BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
@@ -687,7 +764,7 @@
 #endif
 
   connssl->connecting_state = ssl_connect_3;
-  infof(data, "SSL connected\n");
+  infof(data, "SSL connected");
 
   return CURLE_OK;
 }
@@ -775,8 +852,13 @@
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
-
+  char buf[32];
   (void) data;
+
+  /* Maybe the server has already sent a close notify alert.
+     Read it to avoid an RST on the TCP connection. */
+  (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf));
+
   mbedtls_pk_free(&backend->pk);
   mbedtls_x509_crt_free(&backend->clicert);
   mbedtls_x509_crt_free(&backend->cacert);
@@ -844,15 +926,12 @@
   mbedtls_ctr_drbg_context ctr_drbg;
   mbedtls_entropy_init(&ctr_entropy);
   mbedtls_ctr_drbg_init(&ctr_drbg);
-  errorbuf[0] = 0;
 
   ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
                               &ctr_entropy, NULL, 0);
 
   if(ret) {
-#ifdef MBEDTLS_ERROR_C
     mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
     failf(data, "Failed - mbedTLS: ctr_drbg_seed returned (-0x%04X) %s",
           -ret, errorbuf);
   }
@@ -860,9 +939,7 @@
     ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length);
 
     if(ret) {
-#ifdef MBEDTLS_ERROR_C
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
-#endif /* MBEDTLS_ERROR_C */
       failf(data, "mbedTLS: ctr_drbg_init returned (-0x%04X) %s",
             -ret, errorbuf);
     }
@@ -1046,12 +1123,17 @@
                                   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);
 #else
   /* returns 0 on success, otherwise failure */
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+  if(mbedtls_sha256(input, inputlen, sha256sum, 0) != 0)
+#else
   if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0)
+#endif
     return CURLE_BAD_FUNCTION_ARGUMENT;
 #endif
   return CURLE_OK;
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
index 473f517..751755c 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls_threadlock.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2013 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2013 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
  *
  * This software is licensed as described in the file COPYING, which
@@ -55,10 +55,8 @@
     return 0;     /* error, no number of threads defined */
 
   for(i = 0;  i < NUMT;  i++) {
-    int ret;
 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-    ret = pthread_mutex_init(&mutex_buf[i], NULL);
-    if(ret)
+    if(pthread_mutex_init(&mutex_buf[i], NULL))
       return 0; /* pthread_mutex_init failed */
 #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
     mutex_buf[i] = CreateMutex(0, FALSE, 0);
@@ -78,14 +76,11 @@
     return 0; /* error, no threads locks defined */
 
   for(i = 0; i < NUMT; i++) {
-    int ret;
 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-    ret = pthread_mutex_destroy(&mutex_buf[i]);
-    if(ret)
+    if(pthread_mutex_destroy(&mutex_buf[i]))
       return 0; /* pthread_mutex_destroy failed */
 #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
-    ret = CloseHandle(mutex_buf[i]);
-    if(!ret)
+    if(!CloseHandle(mutex_buf[i]))
       return 0; /* CloseHandle failed */
 #endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
   }
@@ -98,17 +93,14 @@
 int Curl_mbedtlsthreadlock_lock_function(int n)
 {
   if(n < NUMT) {
-    int ret;
 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-    ret = pthread_mutex_lock(&mutex_buf[n]);
-    if(ret) {
+    if(pthread_mutex_lock(&mutex_buf[n])) {
       DEBUGF(fprintf(stderr,
                      "Error: mbedtlsthreadlock_lock_function failed\n"));
       return 0; /* pthread_mutex_lock failed */
     }
 #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
-    ret = (WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED?1:0);
-    if(ret) {
+    if(WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED) {
       DEBUGF(fprintf(stderr,
                      "Error: mbedtlsthreadlock_lock_function failed\n"));
       return 0; /* pthread_mutex_lock failed */
@@ -121,17 +113,14 @@
 int Curl_mbedtlsthreadlock_unlock_function(int n)
 {
   if(n < NUMT) {
-    int ret;
 #if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-    ret = pthread_mutex_unlock(&mutex_buf[n]);
-    if(ret) {
+    if(pthread_mutex_unlock(&mutex_buf[n])) {
       DEBUGF(fprintf(stderr,
                      "Error: mbedtlsthreadlock_unlock_function failed\n"));
       return 0; /* pthread_mutex_unlock failed */
     }
 #elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
-    ret = ReleaseMutex(mutex_buf[n]);
-    if(!ret) {
+    if(!ReleaseMutex(mutex_buf[n])) {
       DEBUGF(fprintf(stderr,
                      "Error: mbedtlsthreadlock_unlock_function failed\n"));
       return 0; /* pthread_mutex_lock failed */
diff --git a/Utilities/cmcurl/lib/vtls/mesalink.c b/Utilities/cmcurl/lib/vtls/mesalink.c
index bf8600d..3db9184 100644
--- a/Utilities/cmcurl/lib/vtls/mesalink.c
+++ b/Utilities/cmcurl/lib/vtls/mesalink.c
@@ -167,14 +167,14 @@
       }
       infof(data,
           "error setting certificate verify locations,"
-          " continuing anyway:\n");
+          " continuing anyway:");
     }
     else {
-      infof(data, "successfully set certificate verify locations:\n");
+      infof(data, "successfully set certificate verify locations:");
     }
-    infof(data, " CAfile: %s\n",
+    infof(data, " CAfile: %s",
           SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none");
-    infof(data, " CApath: %s\n",
+    infof(data, " CApath: %s",
           SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none");
   }
 
@@ -196,7 +196,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     infof(data,
-          "client cert: %s\n",
+          "client cert: %s",
           SSL_CONN_CONFIG(clientcert)?
           SSL_CONN_CONFIG(clientcert): "none");
   }
@@ -209,7 +209,7 @@
       return CURLE_SSL_CIPHER;
     }
 #endif
-    infof(data, "Cipher selection: %s\n", ciphers);
+    infof(data, "Cipher selection: %s", ciphers);
   }
 
   if(BACKEND->handle)
@@ -273,7 +273,7 @@
         return CURLE_SSL_CONNECT_ERROR;
       }
       /* Informational message */
-      infof(data, "SSL re-using session ID\n");
+      infof(data, "SSL re-using session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -326,7 +326,7 @@
 
   connssl->connecting_state = ssl_connect_3;
   infof(data,
-        "SSL connection using %s / %s\n",
+        "SSL connection using %s / %s",
         SSL_get_version(BACKEND->handle),
         SSL_get_cipher_name(BACKEND->handle));
 
@@ -356,7 +356,7 @@
                               sockindex));
     if(incache) {
       if(old_ssl_sessionid != our_ssl_sessionid) {
-        infof(data, "old SSL session ID is stale, removing\n");
+        infof(data, "old SSL session ID is stale, removing");
         Curl_ssl_delsessionid(data, old_ssl_sessionid);
         incache = FALSE;
       }
diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c
index 1582b1e..cf65789 100644
--- a/Utilities/cmcurl/lib/vtls/nss.c
+++ b/Utilities/cmcurl/lib/vtls/nss.c
@@ -433,7 +433,7 @@
   n = strchr(str, '/');
   if(!n) {
     infof(data, "warning: certificate file name \"%s\" handled as nickname; "
-          "please use \"./%s\" to force file name\n", str, str);
+          "please use \"./%s\" to force file name", str, str);
     return strdup(str);
   }
 
@@ -824,7 +824,7 @@
 #endif
 
   if(!SSL_CONN_CONFIG(verifypeer)) {
-    infof(data, "skipping SSL peer certificate verification\n");
+    infof(data, "skipping SSL peer certificate verification");
     return SECSuccess;
   }
 
@@ -857,15 +857,15 @@
 #endif
     case SSL_NEXT_PROTO_NO_SUPPORT:
     case SSL_NEXT_PROTO_NO_OVERLAP:
-      infof(data, "ALPN/NPN, server did not agree to a protocol\n");
+      infof(data, "ALPN/NPN, server did not agree to a protocol");
       return;
 #ifdef SSL_ENABLE_ALPN
     case SSL_NEXT_PROTO_SELECTED:
-      infof(data, "ALPN, server accepted to use %.*s\n", buflen, buf);
+      infof(data, "ALPN, server accepted to use %.*s", buflen, buf);
       break;
 #endif
     case SSL_NEXT_PROTO_NEGOTIATED:
-      infof(data, "NPN, server accepted to use %.*s\n", buflen, buf);
+      infof(data, "NPN, server accepted to use %.*s", buflen, buf);
       break;
     }
 
@@ -937,7 +937,7 @@
 
   *canFalseStart = PR_TRUE;
 
-  infof(data, "Trying TLS False Start\n");
+  infof(data, "Trying TLS False Start");
 
 end:
   return SECSuccess;
@@ -955,17 +955,17 @@
   subject = CERT_NameToAscii(&cert->subject);
   issuer = CERT_NameToAscii(&cert->issuer);
   common_name = CERT_GetCommonName(&cert->subject);
-  infof(data, "\tsubject: %s\n", subject);
+  infof(data, "subject: %s\n", subject);
 
   CERT_GetCertTimes(cert, &notBefore, &notAfter);
   PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
   PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
-  infof(data, "\tstart date: %s\n", timeString);
+  infof(data, " start date: %s", timeString);
   PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
   PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
-  infof(data, "\texpire date: %s\n", timeString);
-  infof(data, "\tcommon name: %s\n", common_name);
-  infof(data, "\tissuer: %s\n", issuer);
+  infof(data, " expire date: %s", timeString);
+  infof(data, " common name: %s", common_name);
+  infof(data, " issuer: %s", issuer);
 
   PR_Free(subject);
   PR_Free(issuer);
@@ -987,13 +987,13 @@
      channel.cipherSuite) {
     if(SSL_GetCipherSuiteInfo(channel.cipherSuite,
                               &suite, sizeof(suite)) == SECSuccess) {
-      infof(data, "SSL connection using %s\n", suite.cipherSuiteName);
+      infof(data, "SSL connection using %s", suite.cipherSuiteName);
     }
   }
 
   cert = SSL_PeerCertificate(sock);
   if(cert) {
-    infof(data, "Server certificate:\n");
+    infof(data, "Server certificate:");
 
     if(!data->set.ssl.certinfo) {
       display_cert_info(data, cert);
@@ -1058,7 +1058,7 @@
   /* print only info about the cert, the error is printed off the callback */
   cert = SSL_PeerCertificate(sock);
   if(cert) {
-    infof(data, "Server certificate:\n");
+    infof(data, "Server certificate:");
     display_cert_info(data, cert);
     CERT_DestroyCertificate(cert);
   }
@@ -1132,7 +1132,7 @@
   /* report the resulting status */
   switch(result) {
   case CURLE_OK:
-    infof(data, "pinned public key verified successfully!\n");
+    infof(data, "pinned public key verified successfully!");
     break;
   case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
     failf(data, "failed to verify pinned public key");
@@ -1196,7 +1196,7 @@
       return SECFailure;
     }
 
-    infof(data, "NSS: client certificate from file\n");
+    infof(data, "NSS: client certificate from file");
     display_cert_info(data, cert);
 
     *pRetCert = cert;
@@ -1234,7 +1234,7 @@
     return SECFailure;
   }
 
-  infof(data, "NSS: using client certificate: %s\n", nickname);
+  infof(data, "NSS: using client certificate: %s", nickname);
   display_cert_info(data, *pRetCert);
   return SECSuccess;
 }
@@ -1355,7 +1355,7 @@
     if(!certpath)
       return CURLE_OUT_OF_MEMORY;
 
-    infof(data, "Initializing NSS with certpath: %s\n", certpath);
+    infof(data, "Initializing NSS with certpath: %s", certpath);
     nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
                                   NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
     free(certpath);
@@ -1365,10 +1365,10 @@
 
     err = PR_GetError();
     err_name = nss_error_to_name(err);
-    infof(data, "Unable to initialize NSS database: %d (%s)\n", err, err_name);
+    infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name);
   }
 
-  infof(data, "Initializing NSS with certpath: none\n");
+  infof(data, "Initializing NSS with certpath: none");
   nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
          | NSS_INIT_NOCERTDB   | NSS_INIT_NOMODDB       | NSS_INIT_FORCEOPEN
          | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
@@ -1546,6 +1546,14 @@
   const bool client_cert = (backend->client_nickname != NULL)
     || (backend->obj_clicert != NULL);
 
+  if(backend->handle) {
+    char buf[32];
+    /* Maybe the server has already sent a close notify alert.
+       Read it to avoid an RST on the TCP connection. */
+    (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0,
+                  PR_INTERVAL_NO_WAIT);
+  }
+
   free(backend->client_nickname);
   backend->client_nickname = NULL;
 
@@ -1650,8 +1658,8 @@
   if(capath && !capath[0])
     capath = NULL;
 
-  infof(data, " CAfile: %s\n", cafile ? cafile : "none");
-  infof(data, " CApath: %s\n", capath ? capath : "none");
+  infof(data, " CAfile: %s", cafile ? cafile : "none");
+  infof(data, " CApath: %s", capath ? capath : "none");
 
   /* load libnssckbi.so if no other trust roots were specified */
   use_trust_module = !cafile && !capath;
@@ -1660,7 +1668,7 @@
   if(use_trust_module && !trust_module) {
     /* libnssckbi.so needed but not yet loaded --> load it! */
     result = nss_load_module(&trust_module, trust_library, "trust");
-    infof(data, "%s %s\n", (result) ? "failed to load" : "loaded",
+    infof(data, "%s %s", (result) ? "failed to load" : "loaded",
           trust_library);
     if(result == CURLE_FAILED_INIT)
       /* If libnssckbi.so is not available (or fails to load), one can still
@@ -1669,7 +1677,7 @@
   }
   else if(!use_trust_module && trust_module) {
     /* libnssckbi.so not needed but already loaded --> unload it! */
-    infof(data, "unloading %s\n", trust_library);
+    infof(data, "unloading %s", trust_library);
     nss_unload_module(&trust_module);
   }
   PR_Unlock(nss_trustload_lock);
@@ -1702,7 +1710,7 @@
         if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
           /* This is purposefully tolerant of errors so non-PEM files can
            * be in the same directory */
-          infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath);
+          infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath);
 
         free(fullpath);
       }
@@ -1710,7 +1718,7 @@
       PR_CloseDir(dir);
     }
     else
-      infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath);
+      infof(data, "warning: CURLOPT_CAPATH not a directory (%s)", capath);
   }
 
   return CURLE_OK;
@@ -1813,7 +1821,7 @@
       curlerr = CURLE_SSL_CERTPROBLEM;
 
     /* print the error number and error string */
-    infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err));
+    infof(data, "NSS error %d (%s)", err, nss_error_to_name(err));
 
     /* print a human-readable message describing the error if available */
     nss_print_error_message(data, err);
@@ -1887,7 +1895,7 @@
   PR_Unlock(nss_initlock);
   if(result == CURLE_FAILED_INIT)
     infof(data, "WARNING: failed to load NSS PEM library %s. Using "
-                "OpenSSL PEM certificates will not work.\n", pem_library);
+                "OpenSSL PEM certificates will not work.", pem_library);
   else if(result)
     goto error;
 
@@ -1922,8 +1930,8 @@
     sslver_req_str = nss_sslver_to_name(sslver.max);
     sslver_supp_str = nss_sslver_to_name(sslver_supported.max);
     if(sslver_req_str && sslver_supp_str)
-      infof(data, "Falling back from %s to max supported SSL version (%s)\n",
-                  sslver_req_str, sslver_supp_str);
+      infof(data, "Falling back from %s to max supported SSL version (%s)",
+            sslver_req_str, sslver_supp_str);
     free(sslver_req_str);
     free(sslver_supp_str);
     sslver.max = sslver_supported.max;
@@ -1936,11 +1944,11 @@
   /* unless the user explicitly asks to allow the protocol vulnerability, we
      use the work-around */
   if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
-    infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n",
+    infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d",
           ssl_cbc_random_iv);
 #else
   if(ssl_cbc_random_iv)
-    infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
+    infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in");
 #endif
 
   if(SSL_CONN_CONFIG(cipher_list)) {
@@ -1951,7 +1959,7 @@
   }
 
   if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost))
-    infof(data, "warning: ignoring value of ssl.verifyhost\n");
+    infof(data, "warning: ignoring value of ssl.verifyhost");
 
   /* bypass the default SSL_AuthCertificate() hook in case we do not want to
    * verify peer */
@@ -1971,7 +1979,7 @@
     const CURLcode rv = nss_load_ca_certificates(data, conn, sockindex);
     if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer))
       /* not a fatal error because we are not going to verify the peer */
-      infof(data, "warning: CA certificates failed to load\n");
+      infof(data, "warning: CA certificates failed to load");
     else if(rv) {
       result = rv;
       goto error;
@@ -1984,7 +1992,7 @@
       result = rv;
       goto error;
     }
-    infof(data, "  CRLfile: %s\n", SSL_SET_OPTION(CRLfile));
+    infof(data, "  CRLfile: %s", SSL_SET_OPTION(CRLfile));
   }
 
   if(SSL_SET_OPTION(primary.clientcert)) {
@@ -2179,9 +2187,9 @@
   if(result)
     goto error;
 
-  if(SSL_SET_OPTION(issuercert)) {
+  if(SSL_CONN_CONFIG(issuercert)) {
     SECStatus ret = SECFailure;
-    char *nickname = dup_nickname(data, SSL_SET_OPTION(issuercert));
+    char *nickname = dup_nickname(data, SSL_CONN_CONFIG(issuercert));
     if(nickname) {
       /* we support only nicknames in case of issuercert for now */
       ret = check_issuer_cert(backend->handle, nickname);
@@ -2189,12 +2197,12 @@
     }
 
     if(SECFailure == ret) {
-      infof(data, "SSL certificate issuer check failed\n");
+      infof(data, "SSL certificate issuer check failed");
       result = CURLE_SSL_ISSUER_ERROR;
       goto error;
     }
     else {
-      infof(data, "SSL certificate issuer check ok\n");
+      infof(data, "SSL certificate issuer check ok");
     }
   }
 
@@ -2306,7 +2314,7 @@
     else {
       /* print the error number and error string */
       const char *err_name = nss_error_to_name(err);
-      infof(data, "SSL write: error %d (%s)\n", err, err_name);
+      infof(data, "SSL write: error %d (%s)", err, err_name);
 
       /* print a human-readable message describing the error if available */
       nss_print_error_message(data, err);
@@ -2348,7 +2356,7 @@
     else {
       /* print the error number and error string */
       const char *err_name = nss_error_to_name(err);
-      infof(data, "SSL read: errno %d (%s)\n", err, err_name);
+      infof(data, "SSL read: errno %d (%s)", err, err_name);
 
       /* print a human-readable message describing the error if available */
       nss_print_error_message(data, err);
@@ -2427,7 +2435,7 @@
 }
 
 static void *nss_get_internals(struct ssl_connect_data *connssl,
-                                    CURLINFO info UNUSED_PARAM)
+                               CURLINFO info UNUSED_PARAM)
 {
   struct ssl_backend_data *backend = connssl->backend;
   (void)info;
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index ebd7abc..87f4b02 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -194,7 +194,7 @@
      !defined(OPENSSL_IS_BORINGSSL))
 #define HAVE_SSL_CTX_SET_CIPHERSUITES
 #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
-/* SET_EC_CURVES available under the same preconditions: see
+/* SET_EC_CURVES is available under the same preconditions: see
  * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
  */
 #define HAVE_SSL_CTX_SET_EC_CURVES
@@ -209,8 +209,8 @@
 #endif
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-/* up2date versions of OpenSSL maintain the default reasonably secure without
- * breaking compatibility, so it is better not to override the default by curl
+/* up2date versions of OpenSSL maintain reasonably secure defaults without
+ * breaking compatibility, so it is better not to override the defaults in curl
  */
 #define DEFAULT_CIPHER_SELECTION NULL
 #else
@@ -435,17 +435,16 @@
 
 static CURLcode ossl_seed(struct Curl_easy *data)
 {
-  /* we have the "SSL is seeded" boolean static to prevent multiple
-     time-consuming seedings in vain */
-  static bool ssl_seeded = FALSE;
   char fname[256];
 
-  if(ssl_seeded)
+  /* This might get called before it has been added to a multi handle */
+  if(data->multi && data->multi->ssl_seeded)
     return CURLE_OK;
 
   if(rand_enough()) {
     /* OpenSSL 1.1.0+ will return here */
-    ssl_seeded = TRUE;
+    if(data->multi)
+      data->multi->ssl_seeded = TRUE;
     return CURLE_OK;
   }
 
@@ -518,7 +517,7 @@
       return CURLE_OK;
   }
 
-  infof(data, "libcurl is now using a weak random seed!\n");
+  infof(data, "libcurl is now using a weak random seed!");
   return (rand_enough() ? CURLE_OK :
     CURLE_SSL_CONNECT_ERROR /* confusing error code */);
 }
@@ -1193,7 +1192,7 @@
                          CONF_MFLAGS_IGNORE_MISSING_FILE);
 #endif
 
-  /* Lets get nice error messages */
+  /* Let's get nice error messages */
   SSL_load_error_strings();
 
   /* Init the global ciphers and digests */
@@ -1354,7 +1353,7 @@
 #ifdef USE_OPENSSL_ENGINE
   if(data->state.engine) {
     if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
-      infof(data, "set default crypto engine '%s'\n",
+      infof(data, "set default crypto engine '%s'",
             ENGINE_get_id(data->state.engine));
     }
     else {
@@ -1400,7 +1399,13 @@
 {
   struct ssl_backend_data *backend = connssl->backend;
   if(backend->handle) {
+    char buf[32];
     set_logger(conn, data);
+
+    /* Maybe the server has already sent a close notify alert.
+       Read it to avoid an RST on the TCP connection. */
+    (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
+
     (void)SSL_shutdown(backend->handle);
     SSL_set_connect_state(backend->handle);
 
@@ -1442,6 +1447,7 @@
   int err;
   bool done = FALSE;
   struct ssl_backend_data *backend = connssl->backend;
+  int loop = 10;
 
 #ifndef CURL_DISABLE_FTP
   /* This has only been tested on the proftpd server, and the mod_tls code
@@ -1455,7 +1461,7 @@
 
   if(backend->handle) {
     buffsize = (int)sizeof(buf);
-    while(!done) {
+    while(!done && loop--) {
       int what = SOCKET_READABLE(conn->sock[sockindex],
                                  SSL_SHUTDOWN_TIMEOUT);
       if(what > 0) {
@@ -1475,11 +1481,11 @@
           break;
         case SSL_ERROR_WANT_READ:
           /* there's data pending, re-invoke SSL_read() */
-          infof(data, "SSL_ERROR_WANT_READ\n");
+          infof(data, "SSL_ERROR_WANT_READ");
           break;
         case SSL_ERROR_WANT_WRITE:
           /* SSL wants a write. Really odd. Let's bail out. */
-          infof(data, "SSL_ERROR_WANT_WRITE\n");
+          infof(data, "SSL_ERROR_WANT_WRITE");
           done = TRUE;
           break;
         default:
@@ -1511,14 +1517,14 @@
 #ifdef HAVE_SSL_GET_SHUTDOWN
       switch(SSL_get_shutdown(backend->handle)) {
       case SSL_SENT_SHUTDOWN:
-        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
+        infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN");
         break;
       case SSL_RECEIVED_SHUTDOWN:
-        infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
+        infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN");
         break;
       case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
         infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
-              "SSL_RECEIVED__SHUTDOWN\n");
+              "SSL_RECEIVED__SHUTDOWN");
         break;
       }
 #endif
@@ -1585,7 +1591,7 @@
       if(Curl_cert_hostcheck(match_pattern2, hostname)) {
         res = TRUE;
         infof(data,
-                " subjectAltName: host \"%s\" matched cert's \"%s\"\n",
+                " subjectAltName: host \"%s\" matched cert's \"%s\"",
                 dispname, match_pattern2);
       }
     }
@@ -1604,7 +1610,7 @@
   (void)data;
 #endif
   if(Curl_cert_hostcheck(match_pattern, hostname)) {
-    infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"\n",
+    infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"",
                   dispname, match_pattern);
     return TRUE;
   }
@@ -1724,7 +1730,7 @@
           if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) {
             ipmatched = TRUE;
             infof(data,
-                  " subjectAltName: host \"%s\" matched cert's IP address!\n",
+                  " subjectAltName: host \"%s\" matched cert's IP address!",
                   dispname);
           }
           break;
@@ -1741,7 +1747,7 @@
     /* an alternative name matched */
     ;
   else if(dNSName || iPAddress) {
-    infof(data, " subjectAltName does not match %s\n", dispname);
+    infof(data, " subjectAltName does not match %s", dispname);
     failf(data, "SSL: no alternative certificate subject name matches "
           "target host name '%s'", dispname);
     result = CURLE_PEER_FAILED_VERIFICATION;
@@ -1763,7 +1769,7 @@
 
     /* we have the name entry and we will now convert this to a string
        that we can use for comparison. Doing this we support BMPstring,
-       UTF8 etc. */
+       UTF8, etc. */
 
     if(i >= 0) {
       ASN1_STRING *tmp =
@@ -1823,7 +1829,7 @@
       result = CURLE_PEER_FAILED_VERIFICATION;
     }
     else {
-      infof(data, " common name: %s (matched)\n", peer_CN);
+      infof(data, " common name: %s (matched)", peer_CN);
     }
     if(peer_CN)
       OPENSSL_free(peer_CN);
@@ -1959,7 +1965,7 @@
     goto end;
   }
 
-  infof(data, "SSL certificate status: %s (%d)\n",
+  infof(data, "SSL certificate status: %s (%d)",
         OCSP_cert_status_str(cert_status), cert_status);
 
   switch(cert_status) {
@@ -2054,6 +2060,10 @@
       case SSL3_MT_ENCRYPTED_EXTENSIONS:
         return "Encrypted Extensions";
 #endif
+#ifdef SSL3_MT_SUPPLEMENTAL_DATA
+      case SSL3_MT_SUPPLEMENTAL_DATA:
+        return "Supplemental data";
+#endif
 #ifdef SSL3_MT_END_OF_EARLY_DATA
       case SSL3_MT_END_OF_EARLY_DATA:
         return "End of early data";
@@ -2152,7 +2162,7 @@
 
   /* Log progress for interesting records only (like Handshake or Alert), skip
    * all raw record headers (content_type == SSL3_RT_HEADER or ssl_ver == 0).
-   * For TLS 1.3, skip notification of the decrypted inner Content Type.
+   * For TLS 1.3, skip notification of the decrypted inner Content-Type.
    */
   if(ssl_ver
 #ifdef SSL3_RT_INNER_CONTENT_TYPE
@@ -2263,7 +2273,7 @@
 #ifdef USE_HTTP2
   if(data->state.httpwant >= CURL_HTTP_VERSION_2 &&
      !select_next_protocol(out, outlen, in, inlen, ALPN_H2, ALPN_H2_LENGTH)) {
-    infof(data, "NPN, negotiated HTTP2 (%s)\n", ALPN_H2);
+    infof(data, "NPN, negotiated HTTP2 (%s)", ALPN_H2);
     conn->negnpn = CURL_HTTP_VERSION_2;
     return SSL_TLSEXT_ERR_OK;
   }
@@ -2271,12 +2281,12 @@
 
   if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1,
                            ALPN_HTTP_1_1_LENGTH)) {
-    infof(data, "NPN, negotiated HTTP1.1\n");
+    infof(data, "NPN, negotiated HTTP1.1");
     conn->negnpn = CURL_HTTP_VERSION_1_1;
     return SSL_TLSEXT_ERR_OK;
   }
 
-  infof(data, "NPN, no overlap, use HTTP1.1\n");
+  infof(data, "NPN, no overlap, use HTTP1.1");
   *out = (unsigned char *)ALPN_HTTP_1_1;
   *outlen = ALPN_HTTP_1_1_LENGTH;
   conn->negnpn = CURL_HTTP_VERSION_1_1;
@@ -2293,7 +2303,7 @@
   long curl_ssl_version_min = SSL_CONN_CONFIG(version);
   long curl_ssl_version_max;
 
-  /* convert cURL min SSL version option to OpenSSL constant */
+  /* convert curl min SSL version option to OpenSSL constant */
 #if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
   uint16_t ossl_ssl_version_min = 0;
   uint16_t ossl_ssl_version_max = 0;
@@ -2323,7 +2333,7 @@
      We don't want to pass 0 to SSL_CTX_set_min_proto_version as
      it would enable all versions down to the lowest supported by
      the library.
-     So we skip this, and stay with the OS default
+     So we skip this, and stay with the library default
   */
   if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) {
     if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) {
@@ -2334,7 +2344,7 @@
   /* ... then, TLS max version */
   curl_ssl_version_max = SSL_CONN_CONFIG(version_max);
 
-  /* convert cURL max SSL version option to OpenSSL constant */
+  /* convert curl max SSL version option to OpenSSL constant */
   switch(curl_ssl_version_max) {
     case CURL_SSLVERSION_MAX_TLSv1_0:
       ossl_ssl_version_max = TLS1_VERSION;
@@ -2493,7 +2503,7 @@
                                         &old_ssl_sessionid, NULL, sockindex));
     if(incache) {
       if(old_ssl_sessionid != ssl_sessionid) {
-        infof(data, "old SSL session ID is stale, removing\n");
+        infof(data, "old SSL session ID is stale, removing");
         Curl_ssl_delsessionid(data, old_ssl_sessionid);
         incache = FALSE;
       }
@@ -2517,7 +2527,7 @@
 static CURLcode load_cacert_from_memory(SSL_CTX *ctx,
                                         const struct curl_blob *ca_info_blob)
 {
-  /* these need freed at the end */
+  /* these need to be freed at the end */
   BIO *cbio = NULL;
   STACK_OF(X509_INFO) *inf = NULL;
 
@@ -2652,8 +2662,7 @@
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if(backend->ctx)
-    SSL_CTX_free(backend->ctx);
+  DEBUGASSERT(!backend->ctx);
   backend->ctx = SSL_CTX_new(req_method);
 
   if(!backend->ctx) {
@@ -2675,23 +2684,23 @@
   }
 #endif
 
-  /* OpenSSL contains code to work-around lots of bugs and flaws in various
+  /* OpenSSL contains code to work around lots of bugs and flaws in various
      SSL-implementations. SSL_CTX_set_options() is used to enabled those
      work-arounds. The man page for this option states that SSL_OP_ALL enables
      all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
      enable the bug workaround options if compatibility with somewhat broken
      implementations is desired."
 
-     The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
+     The "-no_ticket" option was introduced in OpenSSL 0.9.8j. It's a flag to
      disable "rfc4507bis session ticket support".  rfc4507bis was later turned
      into the proper RFC5077 it seems: https://tools.ietf.org/html/rfc5077
 
      The enabled extension concerns the session management. I wonder how often
-     libcurl stops a connection and then resumes a TLS session. also, sending
-     the session data is some overhead. .I suggest that you just use your
+     libcurl stops a connection and then resumes a TLS session. Also, sending
+     the session data is some overhead. I suggest that you just use your
      proposed patch (which explicitly disables TICKET).
 
-     If someone writes an application with libcurl and openssl who wants to
+     If someone writes an application with libcurl and OpenSSL who wants to
      enable the feature, one can do this in the SSL callback.
 
      SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
@@ -2727,7 +2736,7 @@
 #endif
 
 #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
-  /* unless the user explicitly ask to allow the protocol vulnerability we
+  /* unless the user explicitly asks to allow the protocol vulnerability we
      use the work-around */
   if(!SSL_SET_OPTION(enable_beast))
     ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
@@ -2787,14 +2796,14 @@
 
       memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH);
       cur += ALPN_H2_LENGTH;
-      infof(data, "ALPN, offering %s\n", ALPN_H2);
+      infof(data, "ALPN, offering %s", ALPN_H2);
     }
 #endif
 
     protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
     memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
     cur += ALPN_HTTP_1_1_LENGTH;
-    infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+    infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
 
     /* expects length prefixed preference ordered list of protocols in wire
      * format
@@ -2826,7 +2835,7 @@
       failf(data, "failed setting cipher list: %s", ciphers);
       return CURLE_SSL_CIPHER;
     }
-    infof(data, "Cipher selection: %s\n", ciphers);
+    infof(data, "Cipher selection: %s", ciphers);
   }
 
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
@@ -2837,7 +2846,7 @@
         failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
         return CURLE_SSL_CIPHER;
       }
-      infof(data, "TLS 1.3 cipher selection: %s\n", ciphers13);
+      infof(data, "TLS 1.3 cipher selection: %s", ciphers13);
     }
   }
 #endif
@@ -2863,7 +2872,7 @@
   if(ssl_authtype == CURL_TLSAUTH_SRP) {
     char * const ssl_username = SSL_SET_OPTION(username);
 
-    infof(data, "Using TLS-SRP username: %s\n", ssl_username);
+    infof(data, "Using TLS-SRP username: %s", ssl_username);
 
     if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
       failf(data, "Unable to set SRP user name");
@@ -2874,7 +2883,7 @@
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
     if(!SSL_CONN_CONFIG(cipher_list)) {
-      infof(data, "Setting cipher list SRP\n");
+      infof(data, "Setting cipher list SRP");
 
       if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
         failf(data, "failed setting SRP cipher list");
@@ -2927,7 +2936,7 @@
                                NULL, cert_name, sizeof(cert_name))) {
           strcpy(cert_name, "Unknown");
         }
-        infof(data, "SSL: Checking cert \"%s\"\n", cert_name);
+        infof(data, "SSL: Checking cert %s\"\n", cert_name);
 #endif
 
         encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
@@ -3009,7 +3018,7 @@
            OpenSSL. */
         if(X509_STORE_add_cert(store, x509) == 1) {
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-          infof(data, "SSL: Imported cert \"%s\"\n", cert_name);
+          infof(data, "SSL: Imported cert \"%s\"", cert_name);
 #endif
           imported_native_ca = true;
         }
@@ -3024,9 +3033,9 @@
         return result;
     }
     if(imported_native_ca)
-      infof(data, "successfully imported windows ca store\n");
+      infof(data, "successfully imported Windows CA store");
     else
-      infof(data, "error importing windows ca store, continuing anyway\n");
+      infof(data, "error importing Windows CA store, continuing anyway");
   }
 #endif
 
@@ -3038,8 +3047,8 @@
         failf(data, "error importing CA certificate blob");
         return result;
       }
-      /* Only warning if no certificate verification is required. */
-      infof(data, "error importing CA certificate blob, continuing anyway\n");
+      /* Only warn if no certificate verification is required. */
+      infof(data, "error importing CA certificate blob, continuing anyway");
     }
   }
 
@@ -3053,10 +3062,10 @@
           failf(data, "error setting certificate file: %s", ssl_cafile);
           return CURLE_SSL_CACERT_BADFILE;
         }
-        /* Continue with a warning if no certificate verif is required. */
-        infof(data, "error setting certificate file, continuing anyway\n");
+        /* Continue with warning if certificate verification isn't required. */
+        infof(data, "error setting certificate file, continuing anyway");
       }
-      infof(data, " CAfile: %s\n", ssl_cafile);
+      infof(data, " CAfile: %s", ssl_cafile);
     }
     if(ssl_capath) {
       if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
@@ -3065,16 +3074,16 @@
           failf(data, "error setting certificate path: %s", ssl_capath);
           return CURLE_SSL_CACERT_BADFILE;
         }
-        /* Continue with a warning if no certificate verif is required. */
-        infof(data, "error setting certificate path, continuing anyway\n");
+        /* Continue with warning if certificate verification isn't required. */
+        infof(data, "error setting certificate path, continuing anyway");
       }
-      infof(data, " CApath: %s\n", ssl_capath);
+      infof(data, " CApath: %s", ssl_capath);
     }
   }
 #else
   if(ssl_cafile || ssl_capath) {
     /* tell SSL where to find CA certificates that are used to verify
-       the servers certificate. */
+       the server's certificate. */
     if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) {
       if(verifypeer && !imported_native_ca) {
         /* Fail if we insist on successfully verifying the server. */
@@ -3087,14 +3096,14 @@
       /* Just continue with a warning if no strict certificate verification
          is required. */
       infof(data, "error setting certificate verify locations,"
-            " continuing anyway:\n");
+            " continuing anyway:");
     }
     else {
       /* Everything is fine. */
-      infof(data, "successfully set certificate verify locations:\n");
+      infof(data, "successfully set certificate verify locations:");
     }
-    infof(data, " CAfile: %s\n", ssl_cafile ? ssl_cafile : "none");
-    infof(data, " CApath: %s\n", ssl_capath ? ssl_capath : "none");
+    infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+    infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
   }
 #endif
 
@@ -3102,13 +3111,13 @@
   if(verifypeer &&
      !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) {
     /* verifying the peer without any CA certificates won't
-       work so use openssl's built in default as fallback */
+       work so use openssl's built-in default as fallback */
     SSL_CTX_set_default_verify_paths(backend->ctx);
   }
 #endif
 
   if(ssl_crlfile) {
-    /* tell SSL where to find CRL file that is used to check certificate
+    /* tell OpenSSL where to find CRL file that is used to check certificate
      * revocation */
     lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx),
                                  X509_LOOKUP_file());
@@ -3118,11 +3127,11 @@
       return CURLE_SSL_CRL_BADFILE;
     }
     /* Everything is fine. */
-    infof(data, "successfully load CRL file:\n");
+    infof(data, "successfully loaded CRL file:");
     X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
                          X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
 
-    infof(data, "  CRLfile: %s\n", ssl_crlfile);
+    infof(data, "  CRLfile: %s", ssl_crlfile);
   }
 
   if(verifypeer) {
@@ -3144,7 +3153,7 @@
          only, instead of needing the whole chain.
 
          Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we
-         cannot do partial chains with CRL check.
+         cannot do partial chains with a CRL check.
       */
       X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
                            X509_V_FLAG_PARTIAL_CHAIN);
@@ -3152,7 +3161,7 @@
 #endif
   }
 
-  /* SSL always tries to verify the peer, this only says whether it should
+  /* OpenSSL always tries to verify the peer, this only says whether it should
    * fail to connect if the verification fails, or if it should continue
    * anyway. In the latter case the result of the verification is checked with
    * SSL_get_verify_result() below. */
@@ -3167,7 +3176,7 @@
 #endif
 
   /* Enable the session cache because it's a prerequisite for the "new session"
-   * callback. Use the "external storage" mode to avoid that OpenSSL creates
+   * callback. Use the "external storage" mode to prevent OpenSSL from creating
    * an internal session cache.
    */
   SSL_CTX_set_session_cache_mode(backend->ctx,
@@ -3186,7 +3195,7 @@
     }
   }
 
-  /* Lets make an SSL structure */
+  /* Let's make an SSL structure */
   if(backend->handle)
     SSL_free(backend->handle);
   backend->handle = SSL_new(backend->ctx);
@@ -3226,7 +3235,7 @@
     data->state.buffer[nlen] = 0;
     if(!SSL_set_tlsext_host_name(backend->handle, data->state.buffer))
       infof(data, "WARNING: failed to configure server name indication (SNI) "
-            "TLS extension\n");
+            "TLS extension");
   }
 #endif
 
@@ -3244,7 +3253,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     /* Informational message */
-    infof(data, "SSL re-using session ID\n");
+    infof(data, "SSL re-using session ID");
   }
   Curl_ssl_sessionid_unlock(data);
 
@@ -3326,7 +3335,7 @@
       /* the connection failed, we're not waiting for anything else. */
       connssl->connecting_state = ssl_connect_2;
 
-      /* Get the earliest error code from the thread's error queue and removes
+      /* Get the earliest error code from the thread's error queue and remove
          the entry. */
       errdetail = ERR_get_error();
 
@@ -3355,7 +3364,7 @@
     !defined(LIBRESSL_VERSION_NUMBER) && \
     !defined(OPENSSL_IS_BORINGSSL))
       /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
-         OpenSSL version above v1.1.1, not Libre SSL nor BoringSSL */
+         OpenSSL version above v1.1.1, not LibreSSL nor BoringSSL */
       else if((lib == ERR_LIB_SSL) &&
               (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
           /* If client certificate is required, communicate the
@@ -3372,7 +3381,7 @@
       /* detail is already set to the SSL error above */
 
       /* If we e.g. use SSLv2 request-method and the server doesn't like us
-       * (RST connection etc.), OpenSSL gives no explanation whatsoever and
+       * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
        * the SO_ERROR is also lost.
        */
       if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
@@ -3395,11 +3404,11 @@
     }
   }
   else {
-    /* we have been connected fine, we're not waiting for anything else. */
+    /* we connected fine, we're not waiting for anything else. */
     connssl->connecting_state = ssl_connect_3;
 
     /* Informational message */
-    infof(data, "SSL connection using %s / %s\n",
+    infof(data, "SSL connection using %s / %s",
           SSL_get_version(backend->handle),
           SSL_get_cipher(backend->handle));
 
@@ -3412,7 +3421,7 @@
       unsigned int len;
       SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
       if(len) {
-        infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol);
+        infof(data, "ALPN, server accepted to use %.*s", len, neg_protocol);
 
 #ifdef USE_HTTP2
         if(len == ALPN_H2_LENGTH &&
@@ -3427,7 +3436,7 @@
         }
       }
       else
-        infof(data, "ALPN, server did not agree to a protocol\n");
+        infof(data, "ALPN, server did not agree to a protocol");
 
       Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                           BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
@@ -3637,7 +3646,7 @@
 
     pubkey = X509_get_pubkey(x);
     if(!pubkey)
-      infof(data, "   Unable to load public key\n");
+      infof(data, "   Unable to load public key");
     else {
       int pktype;
 #ifdef HAVE_OPAQUE_EVP_PKEY
@@ -3814,7 +3823,7 @@
 }
 
 /*
- * Get the server cert, verify it and show it etc, only call failf() if the
+ * Get the server cert, verify it and show it, etc., only call failf() if the
  * 'strict' argument is TRUE as otherwise all this is for informational
  * purposes only!
  *
@@ -3851,23 +3860,23 @@
     return CURLE_PEER_FAILED_VERIFICATION;
   }
 
-  infof(data, "%s certificate:\n", SSL_IS_PROXY() ? "Proxy" : "Server");
+  infof(data, "%s certificate:", SSL_IS_PROXY() ? "Proxy" : "Server");
 
   rc = x509_name_oneline(X509_get_subject_name(backend->server_cert),
                          buffer, sizeof(buffer));
-  infof(data, " subject: %s\n", rc?"[NONE]":buffer);
+  infof(data, " subject: %s", rc?"[NONE]":buffer);
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   {
     long len;
     ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert));
     len = BIO_get_mem_data(mem, (char **) &ptr);
-    infof(data, " start date: %.*s\n", len, ptr);
+    infof(data, " start date: %.*s", (int)len, ptr);
     (void)BIO_reset(mem);
 
     ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert));
     len = BIO_get_mem_data(mem, (char **) &ptr);
-    infof(data, " expire date: %.*s\n", len, ptr);
+    infof(data, " expire date: %.*s", (int)len, ptr);
     (void)BIO_reset(mem);
   }
 #endif
@@ -3891,16 +3900,16 @@
     result = CURLE_PEER_FAILED_VERIFICATION;
   }
   else {
-    infof(data, " issuer: %s\n", buffer);
+    infof(data, " issuer: %s", buffer);
 
     /* We could do all sorts of certificate verification stuff here before
        deallocating the certificate. */
 
     /* e.g. match issuer name with provided issuer certificate */
-    if(SSL_SET_OPTION(issuercert) || SSL_SET_OPTION(issuercert_blob)) {
-      if(SSL_SET_OPTION(issuercert_blob))
-        fp = BIO_new_mem_buf(SSL_SET_OPTION(issuercert_blob)->data,
-                             (int)SSL_SET_OPTION(issuercert_blob)->len);
+    if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) {
+      if(SSL_CONN_CONFIG(issuercert_blob))
+        fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data,
+                             (int)SSL_CONN_CONFIG(issuercert_blob)->len);
       else {
         fp = BIO_new(BIO_s_file());
         if(!fp) {
@@ -3914,10 +3923,10 @@
           return CURLE_OUT_OF_MEMORY;
         }
 
-        if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) {
+        if(BIO_read_filename(fp, SSL_CONN_CONFIG(issuercert)) <= 0) {
           if(strict)
             failf(data, "SSL: Unable to open issuer cert (%s)",
-                  SSL_SET_OPTION(issuercert));
+                  SSL_CONN_CONFIG(issuercert));
           BIO_free(fp);
           X509_free(backend->server_cert);
           backend->server_cert = NULL;
@@ -3929,7 +3938,7 @@
       if(!issuer) {
         if(strict)
           failf(data, "SSL: Unable to read issuer cert (%s)",
-                SSL_SET_OPTION(issuercert));
+                SSL_CONN_CONFIG(issuercert));
         BIO_free(fp);
         X509_free(issuer);
         X509_free(backend->server_cert);
@@ -3940,7 +3949,7 @@
       if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) {
         if(strict)
           failf(data, "SSL: Certificate issuer check failed (%s)",
-                SSL_SET_OPTION(issuercert));
+                SSL_CONN_CONFIG(issuercert));
         BIO_free(fp);
         X509_free(issuer);
         X509_free(backend->server_cert);
@@ -3948,8 +3957,8 @@
         return CURLE_SSL_ISSUER_ERROR;
       }
 
-      infof(data, " SSL certificate issuer check ok (%s)\n",
-            SSL_SET_OPTION(issuercert));
+      infof(data, " SSL certificate issuer check ok (%s)",
+            SSL_CONN_CONFIG(issuercert));
       BIO_free(fp);
       X509_free(issuer);
     }
@@ -3967,11 +3976,11 @@
       }
       else
         infof(data, " SSL certificate verify result: %s (%ld),"
-              " continuing anyway.\n",
+              " continuing anyway.",
               X509_verify_cert_error_string(lerr), lerr);
     }
     else
-      infof(data, " SSL certificate verify ok.\n");
+      infof(data, " SSL certificate verify ok.");
   }
 
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
@@ -4015,7 +4024,7 @@
   /*
    * We check certificates to authenticate the server; otherwise we risk
    * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
-   * verify the peer ignore faults and failures from the server cert
+   * verify the peer, ignore faults and failures from the server cert
    * operations.
    */
 
@@ -4053,7 +4062,7 @@
     const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
 
     if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
+      /* no need to continue if time is already up */
       failf(data, "SSL connection timeout");
       return CURLE_OPERATION_TIMEDOUT;
     }
@@ -4244,7 +4253,7 @@
 #endif
         ) {
         char ver[120];
-        ossl_version(ver, 120);
+        (void)ossl_version(ver, sizeof(ver));
         failf(data, "Error: %s does not support double SSL tunneling.", ver);
       }
       else
@@ -4534,9 +4543,6 @@
     return;
 
   if(SSL_SET_OPTION(primary.sessionid)) {
-    bool isproxy = FALSE;
-    bool incache;
-    void *old_ssl_sessionid = NULL;
     int data_idx = ossl_get_ssl_data_index();
     int connectdata_idx = ossl_get_ssl_conn_index();
     int sockindex_idx = ossl_get_ssl_sockindex_index();
@@ -4544,9 +4550,6 @@
 
     if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
        proxy_idx >= 0) {
-      /* Invalidate the session cache entry, if any */
-      isproxy = SSL_get_ex_data(backend->handle, proxy_idx) ? TRUE : FALSE;
-
       /* Disable references to data in "new session" callback to avoid
        * accessing a stale pointer. */
       SSL_set_ex_data(backend->handle, data_idx, NULL);
@@ -4554,13 +4557,6 @@
       SSL_set_ex_data(backend->handle, sockindex_idx, NULL);
       SSL_set_ex_data(backend->handle, proxy_idx, NULL);
     }
-
-    Curl_ssl_sessionid_lock(data);
-    incache = !(Curl_ssl_getsessionid(data, conn, isproxy,
-                                      &old_ssl_sessionid, NULL, sockindex));
-    if(incache)
-      Curl_ssl_delsessionid(data, old_ssl_sessionid);
-    Curl_ssl_sessionid_unlock(data);
   }
 }
 
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c
index d5247f9..2ac97ce 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.c
+++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -34,7 +34,7 @@
 #include "sendf.h"
 #include "vtls.h"
 #include "select.h"
-
+#include "strerror.h"
 #include "multiif.h"
 
 struct ssl_backend_data
@@ -73,7 +73,7 @@
                     struct connectdata *conn UNUSED_PARAM,
                     int sockindex UNUSED_PARAM)
 {
-  infof(data, "rustls_connect: unimplemented\n");
+  infof(data, "rustls_connect: unimplemented");
   return CURLE_SSL_CONNECT_ERROR;
 }
 
@@ -129,10 +129,12 @@
   io_error = rustls_connection_read_tls(rconn, read_cb,
     &conn->sock[sockindex], &tls_bytes_read);
   if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
-    infof(data, "sread: EAGAIN or EWOULDBLOCK\n");
+    infof(data, "sread: EAGAIN or EWOULDBLOCK");
   }
   else if(io_error) {
-    failf(data, "reading from socket: %s", strerror(io_error));
+    char buffer[STRERROR_LEN];
+    failf(data, "reading from socket: %s",
+          Curl_strerror(io_error, buffer, sizeof(buffer)));
     *err = CURLE_READ_ERROR;
     return -1;
   }
@@ -142,7 +144,7 @@
     return -1;
   }
 
-  infof(data, "cr_recv read %ld bytes from the network\n", tls_bytes_read);
+  infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read);
 
   rresult = rustls_connection_process_new_packets(rconn);
   if(rresult != RUSTLS_RESULT_OK) {
@@ -173,12 +175,12 @@
         available data has been read." If we bring in more ciphertext with
         read_tls, more plaintext will become available. So don't tell curl
         this is an EOF. Instead, say "come back later." */
-      infof(data, "cr_recv got 0 bytes of plaintext\n");
+      infof(data, "cr_recv got 0 bytes of plaintext");
       backend->data_pending = FALSE;
       break;
     }
     else {
-      infof(data, "cr_recv copied out %ld bytes of plaintext\n", n);
+      infof(data, "cr_recv copied out %ld bytes of plaintext", n);
       plain_bytes_copied += n;
     }
   }
@@ -218,7 +220,7 @@
   rustls_result rresult;
   rustls_io_result io_error;
 
-  infof(data, "cr_send %ld bytes of plaintext\n", plainlen);
+  infof(data, "cr_send %ld bytes of plaintext", plainlen);
 
   if(plainlen > 0) {
     rresult = rustls_connection_write(rconn, plainbuf, plainlen,
@@ -239,12 +241,14 @@
     io_error = rustls_connection_write_tls(rconn, write_cb,
       &conn->sock[sockindex], &tlswritten);
     if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
-      infof(data, "swrite: EAGAIN after %ld bytes\n", tlswritten_total);
+      infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total);
       *err = CURLE_AGAIN;
       return -1;
     }
     else if(io_error) {
-      failf(data, "writing to socket: %s", strerror(io_error));
+      char buffer[STRERROR_LEN];
+      failf(data, "writing to socket: %s",
+            Curl_strerror(io_error, buffer, sizeof(buffer)));
       *err = CURLE_WRITE_ERROR;
       return -1;
     }
@@ -253,7 +257,7 @@
       *err = CURLE_WRITE_ERROR;
       return -1;
     }
-    infof(data, "cr_send wrote %ld bytes to network\n", tlswritten);
+    infof(data, "cr_send wrote %ld bytes to network", tlswritten);
     tlswritten_total += tlswritten;
   }
 
@@ -304,10 +308,10 @@
 
   config_builder = rustls_client_config_builder_new();
 #ifdef USE_HTTP2
-  infof(data, "offering ALPN for HTTP/1.1 and HTTP/2\n");
+  infof(data, "offering ALPN for HTTP/1.1 and HTTP/2");
   rustls_client_config_builder_set_protocols(config_builder, alpn, 2);
 #else
-  infof(data, "offering ALPN for HTTP/1.1 only\n");
+  infof(data, "offering ALPN for HTTP/1.1 only");
   rustls_client_config_builder_set_protocols(config_builder, alpn, 1);
 #endif
   if(!verifypeer) {
@@ -332,15 +336,6 @@
       return CURLE_SSL_CACERT_BADFILE;
     }
   }
-  else {
-    result = rustls_client_config_builder_load_native_roots(config_builder);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data, "failed to load trusted certificates");
-      rustls_client_config_free(
-        rustls_client_config_builder_build(config_builder));
-      return CURLE_SSL_CACERT_BADFILE;
-    }
-  }
 
   backend->config = rustls_client_config_builder_build(config_builder);
   DEBUGASSERT(rconn == NULL);
@@ -364,24 +359,24 @@
 
   rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
   if(NULL == protocol) {
-    infof(data, "ALPN, server did not agree to a protocol\n");
+    infof(data, "ALPN, server did not agree to a protocol");
     return;
   }
 
 #ifdef USE_HTTP2
   if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
-    infof(data, "ALPN, negotiated h2\n");
+    infof(data, "ALPN, negotiated h2");
     conn->negnpn = CURL_HTTP_VERSION_2;
   }
   else
 #endif
   if(len == ALPN_HTTP_1_1_LENGTH &&
       0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
-    infof(data, "ALPN, negotiated http/1.1\n");
+    infof(data, "ALPN, negotiated http/1.1");
     conn->negnpn = CURL_HTTP_VERSION_1_1;
   }
   else {
-    infof(data, "ALPN, negotiated an unrecognized protocol\n");
+    infof(data, "ALPN, negotiated an unrecognized protocol");
   }
 
   Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
@@ -424,7 +419,7 @@
     * once the handshake is done.
     */
     if(!rustls_connection_is_handshaking(rconn)) {
-      infof(data, "Done handshaking\n");
+      infof(data, "Done handshaking");
       /* Done with the handshake. Set up callbacks to send/receive data. */
       connssl->state = ssl_connection_complete;
 
@@ -449,22 +444,19 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     if(0 == what) {
-      infof(data, "Curl_socket_check: %s would block\n",
-          wants_read&&wants_write ?
-            "writing and reading" :
-            wants_write ?
-              "writing" :
-              "reading");
+      infof(data, "Curl_socket_check: %s would block",
+            wants_read&&wants_write ? "writing and reading" :
+            wants_write ? "writing" : "reading");
       *done = FALSE;
       return CURLE_OK;
     }
     /* socket is readable or writable */
 
     if(wants_write) {
-      infof(data, "rustls_connection wants us to write_tls.\n");
+      infof(data, "rustls_connection wants us to write_tls.");
       cr_send(data, sockindex, NULL, 0, &tmperr);
       if(tmperr == CURLE_AGAIN) {
-        infof(data, "writing would block\n");
+        infof(data, "writing would block");
         /* fall through */
       }
       else if(tmperr != CURLE_OK) {
@@ -473,11 +465,11 @@
     }
 
     if(wants_read) {
-      infof(data, "rustls_connection wants us to read_tls.\n");
+      infof(data, "rustls_connection wants us to read_tls.");
 
       cr_recv(data, sockindex, NULL, 0, &tmperr);
       if(tmperr == CURLE_AGAIN) {
-        infof(data, "reading would block\n");
+        infof(data, "reading would block");
         /* fall through */
       }
       else if(tmperr != CURLE_OK) {
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 3286a9e..722a937 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -141,6 +141,12 @@
 #  define CALG_SHA_256 0x0000800c
 #endif
 
+/* Work around typo in classic MinGW's w32api up to version 5.0,
+   see https://osdn.net/projects/mingw/ticket/38391 */
+#if !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH)
+#define ALG_CLASS_DHASH ALG_CLASS_HASH
+#endif
+
 #define BACKEND connssl->backend
 
 static Curl_recv schannel_recv;
@@ -279,13 +285,7 @@
 #ifdef CALG_HMAC
   CIPHEROPTION(CALG_HMAC);
 #endif
-#if !defined(__W32API_MAJOR_VERSION) ||                                 \
-  !defined(__W32API_MINOR_VERSION) ||                                   \
-  defined(__MINGW64_VERSION_MAJOR) ||                                   \
-  (__W32API_MAJOR_VERSION > 5)     ||                                   \
-  ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0))
-  /* CALG_TLS1PRF has a syntax error in MinGW's w32api up to version 5.0,
-     see https://osdn.net/projects/mingw/ticket/38391 */
+#ifdef CALG_TLS1PRF
   CIPHEROPTION(CALG_TLS1PRF);
 #endif
 #ifdef CALG_HASH_REPLACE_OWF
@@ -372,23 +372,23 @@
 
   store_name_len = sep - path;
 
-  if(_tcsnccmp(path, TEXT("CurrentUser"), store_name_len) == 0)
+  if(_tcsncmp(path, TEXT("CurrentUser"), store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_CURRENT_USER;
-  else if(_tcsnccmp(path, TEXT("LocalMachine"), store_name_len) == 0)
+  else if(_tcsncmp(path, TEXT("LocalMachine"), store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE;
-  else if(_tcsnccmp(path, TEXT("CurrentService"), store_name_len) == 0)
+  else if(_tcsncmp(path, TEXT("CurrentService"), store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE;
-  else if(_tcsnccmp(path, TEXT("Services"), store_name_len) == 0)
+  else if(_tcsncmp(path, TEXT("Services"), store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_SERVICES;
-  else if(_tcsnccmp(path, TEXT("Users"), store_name_len) == 0)
+  else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_USERS;
-  else if(_tcsnccmp(path, TEXT("CurrentUserGroupPolicy"),
+  else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"),
                     store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY;
-  else if(_tcsnccmp(path, TEXT("LocalMachineGroupPolicy"),
+  else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"),
                     store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY;
-  else if(_tcsnccmp(path, TEXT("LocalMachineEnterprise"),
+  else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"),
                     store_name_len) == 0)
     *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE;
   else
@@ -413,6 +413,341 @@
   return CURLE_OK;
 }
 #endif
+static CURLcode
+schannel_acquire_credential_handle(struct Curl_easy *data,
+                                   struct connectdata *conn,
+                                   int sockindex)
+{
+  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  SCHANNEL_CRED schannel_cred;
+  PCCERT_CONTEXT client_certs[1] = { NULL };
+  SECURITY_STATUS sspi_status = SEC_E_OK;
+  CURLcode result;
+
+  /* setup Schannel API options */
+  memset(&schannel_cred, 0, sizeof(schannel_cred));
+  schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+
+  if(conn->ssl_config.verifypeer) {
+#ifdef HAS_MANUAL_VERIFY_API
+    if(BACKEND->use_manual_cred_validation)
+      schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
+    else
+#endif
+      schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
+
+    if(SSL_SET_OPTION(no_revoke)) {
+      schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+        SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+
+      DEBUGF(infof(data, "schannel: disabled server certificate revocation "
+                   "checks"));
+    }
+    else if(SSL_SET_OPTION(revoke_best_effort)) {
+      schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+        SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN;
+
+      DEBUGF(infof(data, "schannel: ignore revocation offline errors"));
+    }
+    else {
+      schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
+
+      DEBUGF(infof(data,
+                   "schannel: checking server certificate revocation"));
+    }
+  }
+  else {
+    schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
+      SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+      SCH_CRED_IGNORE_REVOCATION_OFFLINE;
+    DEBUGF(infof(data,
+                 "schannel: disabled server cert revocation checks"));
+  }
+
+  if(!conn->ssl_config.verifyhost) {
+    schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
+    DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
+                 "comparing the supplied target name with the subject "
+                 "names in server certificates."));
+  }
+
+  if(!SSL_SET_OPTION(auto_client_cert)) {
+    schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS;
+    schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+    infof(data, "schannel: disabled automatic use of client certificate");
+  }
+  else
+    infof(data, "schannel: enabled automatic use of client certificate");
+
+  switch(conn->ssl_config.version) {
+  case CURL_SSLVERSION_DEFAULT:
+  case CURL_SSLVERSION_TLSv1:
+  case CURL_SSLVERSION_TLSv1_0:
+  case CURL_SSLVERSION_TLSv1_1:
+  case CURL_SSLVERSION_TLSv1_2:
+  case CURL_SSLVERSION_TLSv1_3:
+  {
+    result = set_ssl_version_min_max(&schannel_cred, data, conn);
+    if(result != CURLE_OK)
+      return result;
+    break;
+  }
+  case CURL_SSLVERSION_SSLv3:
+  case CURL_SSLVERSION_SSLv2:
+    failf(data, "SSL versions not supported");
+    return CURLE_NOT_BUILT_IN;
+  default:
+    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  if(SSL_CONN_CONFIG(cipher_list)) {
+    result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list),
+                             BACKEND->algIds);
+    if(CURLE_OK != result) {
+      failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
+      return result;
+    }
+  }
+
+
+#ifdef HAS_CLIENT_CERT_PATH
+  /* client certificate */
+  if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
+    DWORD cert_store_name = 0;
+    TCHAR *cert_store_path = NULL;
+    TCHAR *cert_thumbprint_str = NULL;
+    CRYPT_HASH_BLOB cert_thumbprint;
+    BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
+    HCERTSTORE cert_store = NULL;
+    FILE *fInCert = NULL;
+    void *certdata = NULL;
+    size_t certsize = 0;
+    bool blob = data->set.ssl.primary.cert_blob != NULL;
+    TCHAR *cert_path = NULL;
+    if(blob) {
+      certdata = data->set.ssl.primary.cert_blob->data;
+      certsize = data->set.ssl.primary.cert_blob->len;
+    }
+    else {
+      cert_path = curlx_convert_UTF8_to_tchar(
+        data->set.ssl.primary.clientcert);
+      if(!cert_path)
+        return CURLE_OUT_OF_MEMORY;
+
+      result = get_cert_location(cert_path, &cert_store_name,
+        &cert_store_path, &cert_thumbprint_str);
+
+      if(result && (data->set.ssl.primary.clientcert[0]!='\0'))
+        fInCert = fopen(data->set.ssl.primary.clientcert, "rb");
+
+      if(result && !fInCert) {
+        failf(data, "schannel: Failed to get certificate location"
+              " or file for %s",
+              data->set.ssl.primary.clientcert);
+        curlx_unicodefree(cert_path);
+        return result;
+      }
+    }
+
+    if((fInCert || blob) && (data->set.ssl.cert_type) &&
+        (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
+      failf(data, "schannel: certificate format compatibility error "
+              " for %s",
+              blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
+      curlx_unicodefree(cert_path);
+      return CURLE_SSL_CERTPROBLEM;
+    }
+
+    if(fInCert || blob) {
+      /* Reading a .P12 or .pfx file, like the example at bottom of
+           https://social.msdn.microsoft.com/Forums/windowsdesktop/
+                          en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
+      */
+      CRYPT_DATA_BLOB datablob;
+      WCHAR* pszPassword;
+      size_t pwd_len = 0;
+      int str_w_len = 0;
+      const char *cert_showfilename_error = blob ?
+        "(memory blob)" : data->set.ssl.primary.clientcert;
+      curlx_unicodefree(cert_path);
+      if(fInCert) {
+        long cert_tell = 0;
+        bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
+        if(continue_reading)
+          cert_tell = ftell(fInCert);
+        if(cert_tell < 0)
+          continue_reading = FALSE;
+        else
+          certsize = (size_t)cert_tell;
+        if(continue_reading)
+          continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
+        if(continue_reading)
+          certdata = malloc(certsize + 1);
+        if((!certdata) ||
+           ((int) fread(certdata, certsize, 1, fInCert) != 1))
+          continue_reading = FALSE;
+        fclose(fInCert);
+        if(!continue_reading) {
+          failf(data, "schannel: Failed to read cert file %s",
+              data->set.ssl.primary.clientcert);
+          free(certdata);
+          return CURLE_SSL_CERTPROBLEM;
+        }
+      }
+
+      /* Convert key-pair data to the in-memory certificate store */
+      datablob.pbData = (BYTE*)certdata;
+      datablob.cbData = (DWORD)certsize;
+
+      if(data->set.ssl.key_passwd != NULL)
+        pwd_len = strlen(data->set.ssl.key_passwd);
+      pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
+      if(pszPassword) {
+        if(pwd_len > 0)
+          str_w_len = MultiByteToWideChar(CP_UTF8,
+             MB_ERR_INVALID_CHARS,
+             data->set.ssl.key_passwd, (int)pwd_len,
+             pszPassword, (int)(pwd_len + 1));
+
+        if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
+          pszPassword[str_w_len] = 0;
+        else
+          pszPassword[0] = 0;
+
+        cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
+        free(pszPassword);
+      }
+      if(!blob)
+        free(certdata);
+      if(!cert_store) {
+        DWORD errorcode = GetLastError();
+        if(errorcode == ERROR_INVALID_PASSWORD)
+          failf(data, "schannel: Failed to import cert file %s, "
+                "password is bad",
+                cert_showfilename_error);
+        else
+          failf(data, "schannel: Failed to import cert file %s, "
+                "last error is 0x%x",
+                cert_showfilename_error, errorcode);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+
+      client_certs[0] = CertFindCertificateInStore(
+        cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
+        CERT_FIND_ANY, NULL, NULL);
+
+      if(!client_certs[0]) {
+        failf(data, "schannel: Failed to get certificate from file %s"
+              ", last error is 0x%x",
+              cert_showfilename_error, GetLastError());
+        CertCloseStore(cert_store, 0);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+
+      schannel_cred.cCreds = 1;
+      schannel_cred.paCred = client_certs;
+    }
+    else {
+      cert_store =
+        CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0,
+                      (HCRYPTPROV)NULL,
+                      CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name,
+                      cert_store_path);
+      if(!cert_store) {
+        failf(data, "schannel: Failed to open cert store %x %s, "
+              "last error is 0x%x",
+              cert_store_name, cert_store_path, GetLastError());
+        free(cert_store_path);
+        curlx_unicodefree(cert_path);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+      free(cert_store_path);
+
+      cert_thumbprint.pbData = cert_thumbprint_data;
+      cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN;
+
+      if(!CryptStringToBinary(cert_thumbprint_str,
+                              CERT_THUMBPRINT_STR_LEN,
+                              CRYPT_STRING_HEX,
+                              cert_thumbprint_data,
+                              &cert_thumbprint.cbData,
+                              NULL, NULL)) {
+        curlx_unicodefree(cert_path);
+        CertCloseStore(cert_store, 0);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+
+      client_certs[0] = CertFindCertificateInStore(
+        cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
+        CERT_FIND_HASH, &cert_thumbprint, NULL);
+
+      curlx_unicodefree(cert_path);
+
+      if(client_certs[0]) {
+        schannel_cred.cCreds = 1;
+        schannel_cred.paCred = client_certs;
+      }
+      else {
+        /* CRYPT_E_NOT_FOUND / E_INVALIDARG */
+        CertCloseStore(cert_store, 0);
+        return CURLE_SSL_CERTPROBLEM;
+      }
+    }
+    CertCloseStore(cert_store, 0);
+  }
+#else
+  if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
+    failf(data, "schannel: client cert support not built in");
+    return CURLE_NOT_BUILT_IN;
+  }
+#endif
+
+  /* allocate memory for the re-usable credential handle */
+  BACKEND->cred = (struct Curl_schannel_cred *)
+    calloc(1, sizeof(struct Curl_schannel_cred));
+  if(!BACKEND->cred) {
+    failf(data, "schannel: unable to allocate memory");
+
+    if(client_certs[0])
+      CertFreeCertificateContext(client_certs[0]);
+
+    return CURLE_OUT_OF_MEMORY;
+  }
+  BACKEND->cred->refcount = 1;
+
+  /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
+   */
+  sspi_status =
+    s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
+                                       SECPKG_CRED_OUTBOUND, NULL,
+                                       &schannel_cred, NULL, NULL,
+                                       &BACKEND->cred->cred_handle,
+                                       &BACKEND->cred->time_stamp);
+
+  if(client_certs[0])
+    CertFreeCertificateContext(client_certs[0]);
+
+  if(sspi_status != SEC_E_OK) {
+    char buffer[STRERROR_LEN];
+    failf(data, "schannel: AcquireCredentialsHandle failed: %s",
+          Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+    Curl_safefree(BACKEND->cred);
+    switch(sspi_status) {
+    case SEC_E_INSUFFICIENT_MEMORY:
+      return CURLE_OUT_OF_MEMORY;
+    case SEC_E_NO_CREDENTIALS:
+    case SEC_E_SECPKG_NOT_FOUND:
+    case SEC_E_NOT_OWNER:
+    case SEC_E_UNKNOWN_CREDENTIALS:
+    case SEC_E_INTERNAL_ERROR:
+    default:
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  return CURLE_OK;
+}
 
 static CURLcode
 schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
@@ -427,8 +762,6 @@
 #ifdef HAS_ALPN
   unsigned char alpn_buffer[128];
 #endif
-  SCHANNEL_CRED schannel_cred;
-  PCCERT_CONTEXT client_certs[1] = { NULL };
   SECURITY_STATUS sspi_status = SEC_E_OK;
   struct Curl_schannel_cred *old_cred = NULL;
   struct in_addr addr;
@@ -440,7 +773,7 @@
   char * const hostname = SSL_HOST_NAME();
 
   DEBUGF(infof(data,
-               "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
+               "schannel: SSL/TLS connection with %s port %hu (step 1/3)",
                hostname, conn->remote_port));
 
   if(curlx_verify_windows_version(5, 1, PLATFORM_WINNT,
@@ -448,7 +781,7 @@
     /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and
        algorithms that may not be supported by all servers. */
     infof(data, "schannel: Windows version is old and may not be able to "
-          "connect to some servers due to lack of SNI, algorithms, etc.\n");
+          "connect to some servers due to lack of SNI, algorithms, etc.");
   }
 
 #ifdef HAS_ALPN
@@ -503,338 +836,21 @@
                               SSL_IS_PROXY() ? TRUE : FALSE,
                               (void **)&old_cred, NULL, sockindex)) {
       BACKEND->cred = old_cred;
-      DEBUGF(infof(data, "schannel: re-using existing credential handle\n"));
+      DEBUGF(infof(data, "schannel: re-using existing credential handle"));
 
       /* increment the reference counter of the credential/session handle */
       BACKEND->cred->refcount++;
       DEBUGF(infof(data,
-                   "schannel: incremented credential handle refcount = %d\n",
+                   "schannel: incremented credential handle refcount = %d",
                    BACKEND->cred->refcount));
     }
     Curl_ssl_sessionid_unlock(data);
   }
 
   if(!BACKEND->cred) {
-    /* setup Schannel API options */
-    memset(&schannel_cred, 0, sizeof(schannel_cred));
-    schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
-
-    if(conn->ssl_config.verifypeer) {
-#ifdef HAS_MANUAL_VERIFY_API
-      if(BACKEND->use_manual_cred_validation)
-        schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
-      else
-#endif
-        schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
-
-      if(SSL_SET_OPTION(no_revoke)) {
-        schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-          SCH_CRED_IGNORE_REVOCATION_OFFLINE;
-
-        DEBUGF(infof(data, "schannel: disabled server certificate revocation "
-                     "checks\n"));
-      }
-      else if(SSL_SET_OPTION(revoke_best_effort)) {
-        schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-          SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN;
-
-        DEBUGF(infof(data, "schannel: ignore revocation offline errors"));
-      }
-      else {
-        schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
-
-        DEBUGF(infof(data,
-                     "schannel: checking server certificate revocation\n"));
-      }
-    }
-    else {
-      schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
-        SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
-        SCH_CRED_IGNORE_REVOCATION_OFFLINE;
-      DEBUGF(infof(data,
-                   "schannel: disabled server cert revocation checks\n"));
-    }
-
-    if(!conn->ssl_config.verifyhost) {
-      schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
-      DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from "
-                   "comparing the supplied target name with the subject "
-                   "names in server certificates.\n"));
-    }
-
-    if(!SSL_SET_OPTION(auto_client_cert)) {
-      schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS;
-      schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
-      infof(data, "schannel: disabled automatic use of client certificate\n");
-    }
-    else
-      infof(data, "schannel: enabled automatic use of client certificate\n");
-
-    switch(conn->ssl_config.version) {
-    case CURL_SSLVERSION_DEFAULT:
-    case CURL_SSLVERSION_TLSv1:
-    case CURL_SSLVERSION_TLSv1_0:
-    case CURL_SSLVERSION_TLSv1_1:
-    case CURL_SSLVERSION_TLSv1_2:
-    case CURL_SSLVERSION_TLSv1_3:
-    {
-      result = set_ssl_version_min_max(&schannel_cred, data, conn);
-      if(result != CURLE_OK)
-        return result;
-      break;
-    }
-    case CURL_SSLVERSION_SSLv3:
-    case CURL_SSLVERSION_SSLv2:
-      failf(data, "SSL versions not supported");
-      return CURLE_NOT_BUILT_IN;
-    default:
-      failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-
-    if(SSL_CONN_CONFIG(cipher_list)) {
-      result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list),
-                               BACKEND->algIds);
-      if(CURLE_OK != result) {
-        failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
-        return result;
-      }
-    }
-
-
-#ifdef HAS_CLIENT_CERT_PATH
-    /* client certificate */
-    if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
-      DWORD cert_store_name = 0;
-      TCHAR *cert_store_path = NULL;
-      TCHAR *cert_thumbprint_str = NULL;
-      CRYPT_HASH_BLOB cert_thumbprint;
-      BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN];
-      HCERTSTORE cert_store = NULL;
-      FILE *fInCert = NULL;
-      void *certdata = NULL;
-      size_t certsize = 0;
-      bool blob = data->set.ssl.primary.cert_blob != NULL;
-      TCHAR *cert_path = NULL;
-      if(blob) {
-        certdata = data->set.ssl.primary.cert_blob->data;
-        certsize = data->set.ssl.primary.cert_blob->len;
-      }
-      else {
-        cert_path = curlx_convert_UTF8_to_tchar(
-          data->set.ssl.primary.clientcert);
-        if(!cert_path)
-          return CURLE_OUT_OF_MEMORY;
-
-        result = get_cert_location(cert_path, &cert_store_name,
-          &cert_store_path, &cert_thumbprint_str);
-
-        if(result && (data->set.ssl.primary.clientcert[0]!='\0'))
-          fInCert = fopen(data->set.ssl.primary.clientcert, "rb");
-
-        if(result && !fInCert) {
-          failf(data, "schannel: Failed to get certificate location"
-                " or file for %s",
-                data->set.ssl.primary.clientcert);
-          curlx_unicodefree(cert_path);
-          return result;
-        }
-      }
-
-      if((fInCert || blob) && (data->set.ssl.cert_type) &&
-          (!strcasecompare(data->set.ssl.cert_type, "P12"))) {
-        failf(data, "schannel: certificate format compatibility error "
-                " for %s",
-                blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
-        curlx_unicodefree(cert_path);
-        return CURLE_SSL_CERTPROBLEM;
-      }
-
-      if(fInCert || blob) {
-        /* Reading a .P12 or .pfx file, like the example at bottom of
-             https://social.msdn.microsoft.com/Forums/windowsdesktop/
-                            en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5
-        */
-        CRYPT_DATA_BLOB datablob;
-        WCHAR* pszPassword;
-        size_t pwd_len = 0;
-        int str_w_len = 0;
-        const char *cert_showfilename_error = blob ?
-          "(memory blob)" : data->set.ssl.primary.clientcert;
-        curlx_unicodefree(cert_path);
-        if(fInCert) {
-          long cert_tell = 0;
-          bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0;
-          if(continue_reading)
-            cert_tell = ftell(fInCert);
-          if(cert_tell < 0)
-            continue_reading = FALSE;
-          else
-            certsize = (size_t)cert_tell;
-          if(continue_reading)
-            continue_reading = fseek(fInCert, 0, SEEK_SET) == 0;
-          if(continue_reading)
-            certdata = malloc(certsize + 1);
-          if((!certdata) ||
-             ((int) fread(certdata, certsize, 1, fInCert) != 1))
-            continue_reading = FALSE;
-          fclose(fInCert);
-          if(!continue_reading) {
-            failf(data, "schannel: Failed to read cert file %s",
-                data->set.ssl.primary.clientcert);
-            free(certdata);
-            return CURLE_SSL_CERTPROBLEM;
-          }
-        }
-
-        /* Convert key-pair data to the in-memory certificate store */
-        datablob.pbData = (BYTE*)certdata;
-        datablob.cbData = (DWORD)certsize;
-
-        if(data->set.ssl.key_passwd != NULL)
-          pwd_len = strlen(data->set.ssl.key_passwd);
-        pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
-        if(pszPassword) {
-          if(pwd_len > 0)
-            str_w_len = MultiByteToWideChar(CP_UTF8,
-               MB_ERR_INVALID_CHARS,
-               data->set.ssl.key_passwd, (int)pwd_len,
-               pszPassword, (int)(pwd_len + 1));
-
-          if((str_w_len >= 0) && (str_w_len <= (int)pwd_len))
-            pszPassword[str_w_len] = 0;
-          else
-            pszPassword[0] = 0;
-
-          cert_store = PFXImportCertStore(&datablob, pszPassword, 0);
-          free(pszPassword);
-        }
-        if(!blob)
-          free(certdata);
-        if(!cert_store) {
-          DWORD errorcode = GetLastError();
-          if(errorcode == ERROR_INVALID_PASSWORD)
-            failf(data, "schannel: Failed to import cert file %s, "
-                  "password is bad",
-                  cert_showfilename_error);
-          else
-            failf(data, "schannel: Failed to import cert file %s, "
-                  "last error is 0x%x",
-                  cert_showfilename_error, errorcode);
-          return CURLE_SSL_CERTPROBLEM;
-        }
-
-        client_certs[0] = CertFindCertificateInStore(
-          cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
-          CERT_FIND_ANY, NULL, NULL);
-
-        if(!client_certs[0]) {
-          failf(data, "schannel: Failed to get certificate from file %s"
-                ", last error is 0x%x",
-                cert_showfilename_error, GetLastError());
-          CertCloseStore(cert_store, 0);
-          return CURLE_SSL_CERTPROBLEM;
-        }
-
-        schannel_cred.cCreds = 1;
-        schannel_cred.paCred = client_certs;
-      }
-      else {
-        cert_store =
-          CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0,
-                        (HCRYPTPROV)NULL,
-                        CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name,
-                        cert_store_path);
-        if(!cert_store) {
-          failf(data, "schannel: Failed to open cert store %x %s, "
-                "last error is 0x%x",
-                cert_store_name, cert_store_path, GetLastError());
-          free(cert_store_path);
-          curlx_unicodefree(cert_path);
-          return CURLE_SSL_CERTPROBLEM;
-        }
-        free(cert_store_path);
-
-        cert_thumbprint.pbData = cert_thumbprint_data;
-        cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN;
-
-        if(!CryptStringToBinary(cert_thumbprint_str,
-                                CERT_THUMBPRINT_STR_LEN,
-                                CRYPT_STRING_HEX,
-                                cert_thumbprint_data,
-                                &cert_thumbprint.cbData,
-                                NULL, NULL)) {
-          curlx_unicodefree(cert_path);
-          CertCloseStore(cert_store, 0);
-          return CURLE_SSL_CERTPROBLEM;
-        }
-
-        client_certs[0] = CertFindCertificateInStore(
-          cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
-          CERT_FIND_HASH, &cert_thumbprint, NULL);
-
-        curlx_unicodefree(cert_path);
-
-        if(client_certs[0]) {
-          schannel_cred.cCreds = 1;
-          schannel_cred.paCred = client_certs;
-        }
-        else {
-          /* CRYPT_E_NOT_FOUND / E_INVALIDARG */
-          CertCloseStore(cert_store, 0);
-          return CURLE_SSL_CERTPROBLEM;
-        }
-      }
-      CertCloseStore(cert_store, 0);
-    }
-#else
-    if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) {
-      failf(data, "schannel: client cert support not built in");
-      return CURLE_NOT_BUILT_IN;
-    }
-#endif
-
-    /* allocate memory for the re-usable credential handle */
-    BACKEND->cred = (struct Curl_schannel_cred *)
-      calloc(1, sizeof(struct Curl_schannel_cred));
-    if(!BACKEND->cred) {
-      failf(data, "schannel: unable to allocate memory");
-
-      if(client_certs[0])
-        CertFreeCertificateContext(client_certs[0]);
-
-      return CURLE_OUT_OF_MEMORY;
-    }
-    BACKEND->cred->refcount = 1;
-
-    /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
-     */
-    sspi_status =
-      s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
-                                         SECPKG_CRED_OUTBOUND, NULL,
-                                         &schannel_cred, NULL, NULL,
-                                         &BACKEND->cred->cred_handle,
-                                         &BACKEND->cred->time_stamp);
-
-    if(client_certs[0])
-      CertFreeCertificateContext(client_certs[0]);
-
-    if(sspi_status != SEC_E_OK) {
-      char buffer[STRERROR_LEN];
-      failf(data, "schannel: AcquireCredentialsHandle failed: %s",
-            Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
-      Curl_safefree(BACKEND->cred);
-      switch(sspi_status) {
-      case SEC_E_INSUFFICIENT_MEMORY:
-        return CURLE_OUT_OF_MEMORY;
-      case SEC_E_NO_CREDENTIALS:
-      case SEC_E_SECPKG_NOT_FOUND:
-      case SEC_E_NOT_OWNER:
-      case SEC_E_UNKNOWN_CREDENTIALS:
-      case SEC_E_INTERNAL_ERROR:
-      default:
-        return CURLE_SSL_CONNECT_ERROR;
-      }
+    result = schannel_acquire_credential_handle(data, conn, sockindex);
+    if(result != CURLE_OK) {
+      return result;
     }
   }
 
@@ -844,7 +860,7 @@
      || Curl_inet_pton(AF_INET6, hostname, &addr6)
 #endif
     ) {
-    infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
+    infof(data, "schannel: using IP address, SNI is not supported by OS.");
   }
 
 #ifdef HAS_ALPN
@@ -877,14 +893,14 @@
       alpn_buffer[cur++] = ALPN_H2_LENGTH;
       memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH);
       cur += ALPN_H2_LENGTH;
-      infof(data, "schannel: ALPN, offering %s\n", ALPN_H2);
+      infof(data, "schannel: ALPN, offering %s", ALPN_H2);
     }
 #endif
 
     alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
     memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
     cur += ALPN_HTTP_1_1_LENGTH;
-    infof(data, "schannel: ALPN, offering %s\n", ALPN_HTTP_1_1);
+    infof(data, "schannel: ALPN, offering %s", ALPN_HTTP_1_1);
 
     *list_len = curlx_uitous(cur - list_start_index);
     *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
@@ -972,7 +988,7 @@
   }
 
   DEBUGF(infof(data, "schannel: sending initial handshake data: "
-               "sending %lu bytes...\n", outbuf.cbBuffer));
+               "sending %lu bytes.", outbuf.cbBuffer));
 
   /* send initial handshake data which is now stored in output buffer */
   result = Curl_write_plain(data, conn->sock[sockindex], outbuf.pvBuffer,
@@ -985,7 +1001,7 @@
   }
 
   DEBUGF(infof(data, "schannel: sent initial handshake data: "
-               "sent %zd bytes\n", written));
+               "sent %zd bytes", written));
 
   BACKEND->recv_unrecoverable_err = CURLE_OK;
   BACKEND->recv_sspi_close_notify = false;
@@ -1019,7 +1035,7 @@
   doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
 
   DEBUGF(infof(data,
-               "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
+               "schannel: SSL/TLS connection with %s port %hu (step 2/3)",
                hostname, conn->remote_port));
 
   if(!BACKEND->cred || !BACKEND->ctxt)
@@ -1081,7 +1097,7 @@
         if(connssl->connecting_state != ssl_connect_2_writing)
           connssl->connecting_state = ssl_connect_2_reading;
         DEBUGF(infof(data, "schannel: failed to receive handshake, "
-                     "need more data\n"));
+                     "need more data"));
         return CURLE_OK;
       }
       else if((result != CURLE_OK) || (nread == 0)) {
@@ -1093,11 +1109,11 @@
       /* increase encrypted data buffer offset */
       BACKEND->encdata_offset += nread;
       BACKEND->encdata_is_incomplete = false;
-      DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread));
+      DEBUGF(infof(data, "schannel: encrypted data got %zd", nread));
     }
 
     DEBUGF(infof(data,
-                 "schannel: encrypted data buffer: offset %zu length %zu\n",
+                 "schannel: encrypted data buffer: offset %zu length %zu",
                  BACKEND->encdata_offset, BACKEND->encdata_length));
 
     /* setup input buffers */
@@ -1142,7 +1158,7 @@
       BACKEND->encdata_is_incomplete = true;
       connssl->connecting_state = ssl_connect_2_reading;
       DEBUGF(infof(data,
-                   "schannel: received incomplete message, need more data\n"));
+                   "schannel: received incomplete message, need more data"));
       return CURLE_OK;
     }
 
@@ -1154,7 +1170,7 @@
       BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
       connssl->connecting_state = ssl_connect_2_writing;
       DEBUGF(infof(data,
-                   "schannel: a client certificate has been requested\n"));
+                   "schannel: a client certificate has been requested"));
       return CURLE_OK;
     }
 
@@ -1164,7 +1180,7 @@
         /* search for handshake tokens that need to be send */
         if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
           DEBUGF(infof(data, "schannel: sending next handshake data: "
-                       "sending %lu bytes...\n", outbuf[i].cbBuffer));
+                       "sending %lu bytes.", outbuf[i].cbBuffer));
 
           /* send handshake token to server */
           result = Curl_write_plain(data, conn->sock[sockindex],
@@ -1219,7 +1235,7 @@
 
     /* check if there was additional remaining encrypted data */
     if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
-      DEBUGF(infof(data, "schannel: encrypted data length: %lu\n",
+      DEBUGF(infof(data, "schannel: encrypted data length: %lu",
                    inbuf[1].cbBuffer));
       /*
         There are two cases where we could be getting extra data here:
@@ -1259,7 +1275,7 @@
   /* check if the handshake is complete */
   if(sspi_status == SEC_E_OK) {
     connssl->connecting_state = ssl_connect_3;
-    DEBUGF(infof(data, "schannel: SSL/TLS handshake complete\n"));
+    DEBUGF(infof(data, "schannel: SSL/TLS handshake complete"));
   }
 
   pubkey_ptr = SSL_PINNED_PUB_KEY();
@@ -1348,7 +1364,7 @@
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CERT_CONTEXT *ccert_context = NULL;
   bool isproxy = SSL_IS_PROXY();
-#ifdef DEBUGBUILD
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   const char * const hostname = SSL_HOST_NAME();
 #endif
 #ifdef HAS_ALPN
@@ -1358,7 +1374,7 @@
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
   DEBUGF(infof(data,
-               "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
+               "schannel: SSL/TLS connection with %s port %hu (step 3/3)",
                hostname, conn->remote_port));
 
   if(!BACKEND->cred)
@@ -1394,7 +1410,7 @@
     if(alpn_result.ProtoNegoStatus ==
        SecApplicationProtocolNegotiationStatus_Success) {
 
-      infof(data, "schannel: ALPN, server accepted to use %.*s\n",
+      infof(data, "schannel: ALPN, server accepted to use %.*s",
             alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
 
 #ifdef USE_HTTP2
@@ -1411,7 +1427,7 @@
         }
     }
     else
-      infof(data, "ALPN, server did not agree to a protocol\n");
+      infof(data, "ALPN, server did not agree to a protocol");
     Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                         BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
@@ -1428,7 +1444,7 @@
     if(incache) {
       if(old_cred != BACKEND->cred) {
         DEBUGF(infof(data,
-                     "schannel: old credential handle is stale, removing\n"));
+                     "schannel: old credential handle is stale, removing"));
         /* we're not taking old_cred ownership here, no refcount++ is needed */
         Curl_ssl_delsessionid(data, (void *)old_cred);
         incache = FALSE;
@@ -1447,7 +1463,7 @@
         /* this cred session is now also referenced by sessionid cache */
         BACKEND->cred->refcount++;
         DEBUGF(infof(data,
-                     "schannel: stored credential handle in session cache\n"));
+                     "schannel: stored credential handle in session cache"));
       }
     }
     Curl_ssl_sessionid_unlock(data);
@@ -1778,21 +1794,21 @@
    * handled in the cleanup.
    */
 
-  DEBUGF(infof(data, "schannel: client wants to read %zu bytes\n", len));
+  DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len));
   *err = CURLE_OK;
 
   if(len && len <= BACKEND->decdata_offset) {
-    infof(data, "schannel: enough decrypted data is already available\n");
+    infof(data, "schannel: enough decrypted data is already available");
     goto cleanup;
   }
   else if(BACKEND->recv_unrecoverable_err) {
     *err = BACKEND->recv_unrecoverable_err;
-    infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
+    infof(data, "schannel: an unrecoverable error occurred in a prior call");
     goto cleanup;
   }
   else if(BACKEND->recv_sspi_close_notify) {
     /* once a server has indicated shutdown there is no more encrypted data */
-    infof(data, "schannel: server indicated shutdown in a prior call\n");
+    infof(data, "schannel: server indicated shutdown in a prior call");
     goto cleanup;
   }
 
@@ -1821,12 +1837,12 @@
       BACKEND->encdata_buffer = reallocated_buffer;
       BACKEND->encdata_length = reallocated_length;
       size = BACKEND->encdata_length - BACKEND->encdata_offset;
-      DEBUGF(infof(data, "schannel: encdata_buffer resized %zu\n",
+      DEBUGF(infof(data, "schannel: encdata_buffer resized %zu",
                    BACKEND->encdata_length));
     }
 
     DEBUGF(infof(data,
-                 "schannel: encrypted data buffer: offset %zu length %zu\n",
+                 "schannel: encrypted data buffer: offset %zu length %zu",
                  BACKEND->encdata_offset, BACKEND->encdata_length));
 
     /* read encrypted data from socket */
@@ -1838,25 +1854,25 @@
       nread = -1;
       if(*err == CURLE_AGAIN)
         DEBUGF(infof(data,
-                     "schannel: Curl_read_plain returned CURLE_AGAIN\n"));
+                     "schannel: Curl_read_plain returned CURLE_AGAIN"));
       else if(*err == CURLE_RECV_ERROR)
-        infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
+        infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR");
       else
-        infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
+        infof(data, "schannel: Curl_read_plain returned error %d", *err);
     }
     else if(nread == 0) {
       BACKEND->recv_connection_closed = true;
-      DEBUGF(infof(data, "schannel: server closed the connection\n"));
+      DEBUGF(infof(data, "schannel: server closed the connection"));
     }
     else if(nread > 0) {
       BACKEND->encdata_offset += (size_t)nread;
       BACKEND->encdata_is_incomplete = false;
-      DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread));
+      DEBUGF(infof(data, "schannel: encrypted data got %zd", nread));
     }
   }
 
   DEBUGF(infof(data,
-               "schannel: encrypted data buffer: offset %zu length %zu\n",
+               "schannel: encrypted data buffer: offset %zu length %zu",
                BACKEND->encdata_offset, BACKEND->encdata_length));
 
   /* decrypt loop */
@@ -1885,7 +1901,7 @@
       /* check for successfully decrypted data, even before actual
          renegotiation or shutdown of the connection context */
       if(inbuf[1].BufferType == SECBUFFER_DATA) {
-        DEBUGF(infof(data, "schannel: decrypted data length: %lu\n",
+        DEBUGF(infof(data, "schannel: decrypted data length: %lu",
                      inbuf[1].cbBuffer));
 
         /* increase buffer in order to fit the received amount of data */
@@ -1918,15 +1934,15 @@
           BACKEND->decdata_offset += size;
         }
 
-        DEBUGF(infof(data, "schannel: decrypted data added: %zu\n", size));
+        DEBUGF(infof(data, "schannel: decrypted data added: %zu", size));
         DEBUGF(infof(data,
-                     "schannel: decrypted cached: offset %zu length %zu\n",
+                     "schannel: decrypted cached: offset %zu length %zu",
                      BACKEND->decdata_offset, BACKEND->decdata_length));
       }
 
       /* check for remaining encrypted data */
       if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
-        DEBUGF(infof(data, "schannel: encrypted data length: %lu\n",
+        DEBUGF(infof(data, "schannel: encrypted data length: %lu",
                      inbuf[3].cbBuffer));
 
         /* check if the remaining data is less than the total amount
@@ -1942,7 +1958,7 @@
         }
 
         DEBUGF(infof(data,
-                     "schannel: encrypted cached: offset %zu length %zu\n",
+                     "schannel: encrypted cached: offset %zu length %zu",
                      BACKEND->encdata_offset, BACKEND->encdata_length));
       }
       else {
@@ -1952,29 +1968,29 @@
 
       /* check if server wants to renegotiate the connection context */
       if(sspi_status == SEC_I_RENEGOTIATE) {
-        infof(data, "schannel: remote party requests renegotiation\n");
+        infof(data, "schannel: remote party requests renegotiation");
         if(*err && *err != CURLE_AGAIN) {
-          infof(data, "schannel: can't renogotiate, an error is pending\n");
+          infof(data, "schannel: can't renogotiate, an error is pending");
           goto cleanup;
         }
         if(BACKEND->encdata_offset) {
           *err = CURLE_RECV_ERROR;
           infof(data, "schannel: can't renogotiate, "
-                "encrypted data available\n");
+                "encrypted data available");
           goto cleanup;
         }
         /* begin renegotiation */
-        infof(data, "schannel: renegotiating SSL/TLS connection\n");
+        infof(data, "schannel: renegotiating SSL/TLS connection");
         connssl->state = ssl_connection_negotiating;
         connssl->connecting_state = ssl_connect_2_writing;
         *err = schannel_connect_common(data, conn, sockindex, FALSE, &done);
         if(*err) {
-          infof(data, "schannel: renegotiation failed\n");
+          infof(data, "schannel: renegotiation failed");
           goto cleanup;
         }
         /* now retry receiving data */
         sspi_status = SEC_E_OK;
-        infof(data, "schannel: SSL/TLS connection renegotiated\n");
+        infof(data, "schannel: SSL/TLS connection renegotiated");
         continue;
       }
       /* check if the server closed the connection */
@@ -1984,7 +2000,7 @@
         BACKEND->recv_sspi_close_notify = true;
         if(!BACKEND->recv_connection_closed) {
           BACKEND->recv_connection_closed = true;
-          infof(data, "schannel: server closed the connection\n");
+          infof(data, "schannel: server closed the connection");
         }
         goto cleanup;
       }
@@ -1993,7 +2009,7 @@
       BACKEND->encdata_is_incomplete = true;
       if(!*err)
         *err = CURLE_AGAIN;
-      infof(data, "schannel: failed to decrypt data, need more data\n");
+      infof(data, "schannel: failed to decrypt data, need more data");
       goto cleanup;
     }
     else {
@@ -2001,23 +2017,23 @@
       char buffer[STRERROR_LEN];
 #endif
       *err = CURLE_RECV_ERROR;
-      infof(data, "schannel: failed to read data from server: %s\n",
+      infof(data, "schannel: failed to read data from server: %s",
             Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
       goto cleanup;
     }
   }
 
   DEBUGF(infof(data,
-               "schannel: encrypted data buffer: offset %zu length %zu\n",
+               "schannel: encrypted data buffer: offset %zu length %zu",
                BACKEND->encdata_offset, BACKEND->encdata_length));
 
   DEBUGF(infof(data,
-               "schannel: decrypted data buffer: offset %zu length %zu\n",
+               "schannel: decrypted data buffer: offset %zu length %zu",
                BACKEND->decdata_offset, BACKEND->decdata_length));
 
   cleanup:
   /* Warning- there is no guarantee the encdata state is valid at this point */
-  DEBUGF(infof(data, "schannel: schannel_recv cleanup\n"));
+  DEBUGF(infof(data, "schannel: schannel_recv cleanup"));
 
   /* Error if the connection has closed without a close_notify.
 
@@ -2039,7 +2055,7 @@
       BACKEND->recv_sspi_close_notify = true;
     else {
       *err = CURLE_RECV_ERROR;
-      infof(data, "schannel: server closed abruptly (missing close_notify)\n");
+      infof(data, "schannel: server closed abruptly (missing close_notify)");
     }
   }
 
@@ -2053,9 +2069,9 @@
     memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size,
             BACKEND->decdata_offset - size);
     BACKEND->decdata_offset -= size;
-    DEBUGF(infof(data, "schannel: decrypted data returned %zu\n", size));
+    DEBUGF(infof(data, "schannel: decrypted data returned %zu", size));
     DEBUGF(infof(data,
-                 "schannel: decrypted data buffer: offset %zu length %zu\n",
+                 "schannel: decrypted data buffer: offset %zu length %zu",
                  BACKEND->decdata_offset, BACKEND->decdata_length));
     *err = CURLE_OK;
     return (ssize_t)size;
@@ -2139,7 +2155,7 @@
 
   DEBUGASSERT(data);
 
-  infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
+  infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu",
         hostname, conn->remote_port);
 
   if(BACKEND->cred && BACKEND->ctxt) {
@@ -2197,14 +2213,14 @@
       s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
       if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
         infof(data, "schannel: failed to send close msg: %s"
-              " (bytes written: %zd)\n", curl_easy_strerror(result), written);
+              " (bytes written: %zd)", curl_easy_strerror(result), written);
       }
     }
   }
 
   /* free SSPI Schannel API security context handle */
   if(BACKEND->ctxt) {
-    DEBUGF(infof(data, "schannel: clear security context handle\n"));
+    DEBUGF(infof(data, "schannel: clear security context handle"));
     s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle);
     Curl_safefree(BACKEND->ctxt);
   }
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index 25d47b8..1b283d0 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -80,7 +80,7 @@
 /* Search the substring needle,needlelen into string haystack,haystacklen
  * Strings don't need to be terminated by a '\0'.
  * Similar of OSX/Linux memmem (not available on Visual Studio).
- * Return position of beginning of first occurence or NULL if not found
+ * Return position of beginning of first occurrence or NULL if not found
  */
 static const char *c_memmem(const void *haystack, size_t haystacklen,
                             const void *needle, size_t needlelen)
@@ -204,12 +204,12 @@
   if(result == CURLE_OK) {
     if(!num_certs) {
       infof(data,
-            "schannel: did not add any certificates from CA file '%s'\n",
+            "schannel: did not add any certificates from CA file '%s'",
             ca_file_text);
     }
     else {
       infof(data,
-            "schannel: added %d certificate(s) from CA file '%s'\n",
+            "schannel: added %d certificate(s) from CA file '%s'",
             num_certs, ca_file_text);
     }
   }
@@ -526,7 +526,7 @@
       if(match_result == CURL_HOST_MATCH) {
         infof(data,
               "schannel: connection hostname (%s) validated "
-              "against certificate name (%s)\n",
+              "against certificate name (%s)",
               conn_hostname, cert_hostname);
         result = CURLE_OK;
       }
@@ -535,7 +535,7 @@
 
         infof(data,
               "schannel: connection hostname (%s) did not match "
-              "against certificate name (%s)\n",
+              "against certificate name (%s)",
               conn_hostname, cert_hostname);
 
         cert_hostname_len =
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c
index edd375e..1e6ed5f 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.c
+++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -32,6 +32,9 @@
 #include "curl_base64.h"
 #include "strtok.h"
 #include "multiif.h"
+#include "strcase.h"
+#include "x509asn1.h"
+#include "strerror.h"
 
 #ifdef USE_SECTRANSP
 
@@ -1644,7 +1647,7 @@
     }
   }
   /* All cipher suites in the list are found. Report to logs as-is */
-  infof(data, "SSL: Setting cipher suites list \"%s\"\n", ciphers);
+  infof(data, "SSL: Setting cipher suites list \"%s\"", ciphers);
 
   err = SSLSetEnabledCiphers(ssl_ctx, selected_ciphers, ciphers_count);
   if(err != noErr) {
@@ -1840,19 +1843,19 @@
 #endif
         ) {
         CFArrayAppendValue(alpnArr, CFSTR(ALPN_H2));
-        infof(data, "ALPN, offering %s\n", ALPN_H2);
+        infof(data, "ALPN, offering %s", ALPN_H2);
       }
 #endif
 
       CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1));
-      infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+      infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
 
       /* expects length prefixed preference ordered list of protocols in wire
        * format
        */
       err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr);
       if(err != noErr)
-        infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d\n",
+        infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d",
               err);
       CFRelease(alpnArr);
     }
@@ -1861,7 +1864,7 @@
 
   if(SSL_SET_OPTION(key)) {
     infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
-          "Transport. The private key must be in the Keychain.\n");
+          "Transport. The private key must be in the Keychain.");
   }
 
   if(ssl_cert || ssl_cert_blob) {
@@ -1869,24 +1872,28 @@
     bool is_cert_file = (!is_cert_data) && is_file(ssl_cert);
     SecIdentityRef cert_and_key = NULL;
 
-    /* User wants to authenticate with a client cert. Look for it:
-       If we detect that this is a file on disk, then let's load it.
-       Otherwise, assume that the user wants to use an identity loaded
-       from the Keychain. */
-    if(is_cert_file || is_cert_data) {
+    /* User wants to authenticate with a client cert. Look for it. Assume that
+       the user wants to use an identity loaded from the Keychain. If not, try
+       it as a file on disk */
+
+    if(!is_cert_data)
+      err = CopyIdentityWithLabel(ssl_cert, &cert_and_key);
+    else
+      err = !noErr;
+    if((err != noErr) && (is_cert_file || is_cert_data)) {
       if(!SSL_SET_OPTION(cert_type))
-        infof(data, "WARNING: SSL: Certificate type not set, assuming "
-                    "PKCS#12 format.\n");
-      else if(strncmp(SSL_SET_OPTION(cert_type), "P12",
-        strlen(SSL_SET_OPTION(cert_type))) != 0)
-        infof(data, "WARNING: SSL: The Security framework only supports "
-                    "loading identities that are in PKCS#12 format.\n");
+        infof(data, "SSL: Certificate type not set, assuming "
+              "PKCS#12 format.");
+      else if(!strcasecompare(SSL_SET_OPTION(cert_type), "P12")) {
+        failf(data, "SSL: The Security framework only supports "
+              "loading identities that are in PKCS#12 format.");
+        return CURLE_SSL_CERTPROBLEM;
+      }
 
       err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob,
-        SSL_SET_OPTION(key_passwd), &cert_and_key);
+                                       SSL_SET_OPTION(key_passwd),
+                                       &cert_and_key);
     }
-    else
-      err = CopyIdentityWithLabel(ssl_cert, &cert_and_key);
 
     if(err == noErr && cert_and_key) {
       SecCertificateRef cert = NULL;
@@ -1899,7 +1906,7 @@
         char *certp;
         CURLcode result = CopyCertSubject(data, cert, &certp);
         if(!result) {
-          infof(data, "Client certificate: %s\n", certp);
+          infof(data, "Client certificate: %s", certp);
           free(certp);
         }
 
@@ -2025,7 +2032,7 @@
     strlen(hostname));
 
     if(err != noErr) {
-      infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d\n",
+      infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d",
             err);
     }
 
@@ -2035,11 +2042,11 @@
   #endif
        ) {
       infof(data, "WARNING: using IP address, SNI is being disabled by "
-            "the OS.\n");
+            "the OS.");
     }
   }
   else {
-    infof(data, "WARNING: disabling hostname validation also disables SNI.\n");
+    infof(data, "WARNING: disabling hostname validation also disables SNI.");
   }
 
   ciphers = SSL_CONN_CONFIG(cipher_list);
@@ -2082,7 +2089,7 @@
         return CURLE_SSL_CONNECT_ERROR;
       }
       /* Informational message */
-      infof(data, "SSL re-using session ID\n");
+      infof(data, "SSL re-using session ID");
     }
     /* If there isn't one, then let's make one up! This has to be done prior
        to starting the handshake. */
@@ -2487,7 +2494,7 @@
         spkiHeaderLength = 23;
         break;
       default:
-        infof(data, "SSL: unhandled public key length: %d\n", pubkeylen);
+        infof(data, "SSL: unhandled public key length: %d", pubkeylen);
 #elif SECTRANSP_PINNEDPUBKEY_V2
       default:
         /* ecDSA secp256r1 pubkeylen == 91 header already included?
@@ -2778,35 +2785,35 @@
     (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol);
     switch(protocol) {
       case kSSLProtocol2:
-        infof(data, "SSL 2.0 connection using %s\n",
+        infof(data, "SSL 2.0 connection using %s",
               TLSCipherNameForNumber(cipher));
         break;
       case kSSLProtocol3:
-        infof(data, "SSL 3.0 connection using %s\n",
+        infof(data, "SSL 3.0 connection using %s",
               TLSCipherNameForNumber(cipher));
         break;
       case kTLSProtocol1:
-        infof(data, "TLS 1.0 connection using %s\n",
+        infof(data, "TLS 1.0 connection using %s",
               TLSCipherNameForNumber(cipher));
         break;
 #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
       case kTLSProtocol11:
-        infof(data, "TLS 1.1 connection using %s\n",
+        infof(data, "TLS 1.1 connection using %s",
               TLSCipherNameForNumber(cipher));
         break;
       case kTLSProtocol12:
-        infof(data, "TLS 1.2 connection using %s\n",
+        infof(data, "TLS 1.2 connection using %s",
               TLSCipherNameForNumber(cipher));
         break;
 #endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
 #if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
       case kTLSProtocol13:
-        infof(data, "TLS 1.3 connection using %s\n",
+        infof(data, "TLS 1.3 connection using %s",
               TLSCipherNameForNumber(cipher));
         break;
 #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
       default:
-        infof(data, "Unknown protocol connection\n");
+        infof(data, "Unknown protocol connection");
         break;
     }
 
@@ -2832,7 +2839,7 @@
           conn->negnpn = CURL_HTTP_VERSION_1_1;
         }
         else
-          infof(data, "ALPN, server did not agree to a protocol\n");
+          infof(data, "ALPN, server did not agree to a protocol");
 
         Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                             BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
@@ -2849,13 +2856,60 @@
   }
 }
 
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-/* This should be called during step3 of the connection at the earliest */
-static void
-show_verbose_server_cert(struct Curl_easy *data,
-                         struct connectdata *conn,
-                         int sockindex)
+static CURLcode
+add_cert_to_certinfo(struct Curl_easy *data,
+                     SecCertificateRef server_cert,
+                     int idx)
 {
+  CURLcode result = CURLE_OK;
+  const char *beg;
+  const char *end;
+  CFDataRef cert_data = SecCertificateCopyData(server_cert);
+
+  if(!cert_data)
+    return CURLE_PEER_FAILED_VERIFICATION;
+
+  beg = (const char *)CFDataGetBytePtr(cert_data);
+  end = beg + CFDataGetLength(cert_data);
+  result = Curl_extract_certinfo(data, idx, beg, end);
+  CFRelease(cert_data);
+  return result;
+}
+
+static CURLcode
+collect_server_cert_single(struct Curl_easy *data,
+                           SecCertificateRef server_cert,
+                           CFIndex idx)
+{
+  CURLcode result = CURLE_OK;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  if(data->set.verbose) {
+    char *certp;
+    result = CopyCertSubject(data, server_cert, &certp);
+    if(!result) {
+      infof(data, "Server certificate: %s", certp);
+      free(certp);
+    }
+  }
+#endif
+  if(data->set.ssl.certinfo)
+    result = add_cert_to_certinfo(data, server_cert, (int)idx);
+  return result;
+}
+
+/* This should be called during step3 of the connection at the earliest */
+static CURLcode
+collect_server_cert(struct Curl_easy *data,
+                    struct connectdata *conn,
+                    int sockindex)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  const bool show_verbose_server_cert = data->set.verbose;
+#else
+  const bool show_verbose_server_cert = false;
+#endif
+  CURLcode result = data->set.ssl.certinfo ?
+    CURLE_PEER_FAILED_VERIFICATION : CURLE_OK;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
   CFArrayRef server_certs = NULL;
@@ -2864,8 +2918,11 @@
   CFIndex i, count;
   SecTrustRef trust = NULL;
 
+  if(!show_verbose_server_cert && !data->set.ssl.certinfo)
+    return CURLE_OK;
+
   if(!backend->ssl_ctx)
-    return;
+    return result;
 
 #if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
 #if CURL_BUILD_IOS
@@ -2875,15 +2932,11 @@
      a null trust, so be on guard for that: */
   if(err == noErr && trust) {
     count = SecTrustGetCertificateCount(trust);
-    for(i = 0L ; i < count ; i++) {
-      CURLcode result;
-      char *certp;
+    if(data->set.ssl.certinfo)
+      result = Curl_ssl_init_certinfo(data, (int)count);
+    for(i = 0L ; !result && (i < count) ; i++) {
       server_cert = SecTrustGetCertificateAtIndex(trust, i);
-      result = CopyCertSubject(data, server_cert, &certp);
-      if(!result) {
-        infof(data, "Server certificate: %s\n", certp);
-        free(certp);
-      }
+      result = collect_server_cert_single(data, server_cert, i);
     }
     CFRelease(trust);
   }
@@ -2901,15 +2954,11 @@
        a null trust, so be on guard for that: */
     if(err == noErr && trust) {
       count = SecTrustGetCertificateCount(trust);
-      for(i = 0L ; i < count ; i++) {
-        char *certp;
-        CURLcode result;
+      if(data->set.ssl.certinfo)
+        result = Curl_ssl_init_certinfo(data, (int)count);
+      for(i = 0L ; !result && (i < count) ; i++) {
         server_cert = SecTrustGetCertificateAtIndex(trust, i);
-        result = CopyCertSubject(data, server_cert, &certp);
-        if(!result) {
-          infof(data, "Server certificate: %s\n", certp);
-          free(certp);
-        }
+        result = collect_server_cert_single(data, server_cert, i);
       }
       CFRelease(trust);
     }
@@ -2920,16 +2969,12 @@
     /* Just in case SSLCopyPeerCertificates() returns null too... */
     if(err == noErr && server_certs) {
       count = CFArrayGetCount(server_certs);
-      for(i = 0L ; i < count ; i++) {
-        char *certp;
-        CURLcode result;
+      if(data->set.ssl.certinfo)
+        result = Curl_ssl_init_certinfo(data, (int)count);
+      for(i = 0L ; !result && (i < count) ; i++) {
         server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs,
                                                                 i);
-        result = CopyCertSubject(data, server_cert, &certp);
-        if(!result) {
-          infof(data, "Server certificate: %s\n", certp);
-          free(certp);
-        }
+        result = collect_server_cert_single(data, server_cert, i);
       }
       CFRelease(server_certs);
     }
@@ -2941,21 +2986,17 @@
   err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs);
   if(err == noErr) {
     count = CFArrayGetCount(server_certs);
-    for(i = 0L ; i < count ; i++) {
-      CURLcode result;
-      char *certp;
+    if(data->set.ssl.certinfo)
+      result = Curl_ssl_init_certinfo(data, (int)count);
+    for(i = 0L ; !result && (i < count) ; i++) {
       server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
-      result = CopyCertSubject(data, server_cert, &certp);
-      if(!result) {
-        infof(data, "Server certificate: %s\n", certp);
-        free(certp);
-      }
+      result = collect_server_cert_single(data, server_cert, i);
     }
     CFRelease(server_certs);
   }
 #endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+  return result;
 }
-#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
 
 static CURLcode
 sectransp_connect_step3(struct Curl_easy *data, struct connectdata *conn,
@@ -2964,12 +3005,11 @@
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
 
   /* There is no step 3!
-   * Well, okay, if verbose mode is on, let's print the details of the
-   * server certificates. */
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  if(data->set.verbose)
-    show_verbose_server_cert(data, conn, sockindex);
-#endif
+   * Well, okay, let's collect server certificates, and if verbose mode is on,
+   * let's print the details of the server certificates. */
+  const CURLcode result = collect_server_cert(data, conn, sockindex);
+  if(result)
+    return result;
 
   connssl->connecting_state = ssl_connect_done;
   return CURLE_OK;
@@ -3148,6 +3188,7 @@
   int what;
   int rc;
   char buf[120];
+  int loop = 10; /* avoid getting stuck */
 
   if(!backend->ssl_ctx)
     return 0;
@@ -3163,7 +3204,7 @@
 
   what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT);
 
-  for(;;) {
+  while(loop--) {
     if(what < 0) {
       /* anything that gets here is fatally bad */
       failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -3182,7 +3223,9 @@
     nread = read(conn->sock[sockindex], buf, sizeof(buf));
 
     if(nread < 0) {
-      failf(data, "read: %s", strerror(errno));
+      char buffer[STRERROR_LEN];
+      failf(data, "read: %s",
+            Curl_strerror(errno, buffer, sizeof(buffer)));
       rc = -1;
     }
 
@@ -3427,6 +3470,7 @@
   { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */
 
   SSLSUPP_CAINFO_BLOB |
+  SSLSUPP_CERTINFO |
 #ifdef SECTRANSP_PINNEDPUBKEY
   SSLSUPP_PINNEDPUBKEY,
 #else
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index 65f4f77..e5bbe1f 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -125,6 +125,16 @@
   return !memcmp(first->data, second->data, first->len); /* same data */
 }
 
+static bool safecmp(char *a, char *b)
+{
+  if(a && b)
+    return !strcmp(a, b);
+  else if(!a && !b)
+    return TRUE; /* match */
+  return FALSE; /* no match */
+}
+
+
 bool
 Curl_ssl_config_matches(struct ssl_primary_config *data,
                         struct ssl_primary_config *needle)
@@ -136,11 +146,13 @@
      (data->verifystatus == needle->verifystatus) &&
      blobcmp(data->cert_blob, needle->cert_blob) &&
      blobcmp(data->ca_info_blob, needle->ca_info_blob) &&
-     Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
-     Curl_safe_strcasecompare(data->CAfile, needle->CAfile) &&
-     Curl_safe_strcasecompare(data->clientcert, needle->clientcert) &&
-     Curl_safe_strcasecompare(data->random_file, needle->random_file) &&
-     Curl_safe_strcasecompare(data->egdsocket, needle->egdsocket) &&
+     blobcmp(data->issuercert_blob, needle->issuercert_blob) &&
+     safecmp(data->CApath, needle->CApath) &&
+     safecmp(data->CAfile, needle->CAfile) &&
+     safecmp(data->issuercert, needle->issuercert) &&
+     safecmp(data->clientcert, needle->clientcert) &&
+     safecmp(data->random_file, needle->random_file) &&
+     safecmp(data->egdsocket, needle->egdsocket) &&
      Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) &&
      Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) &&
      Curl_safe_strcasecompare(data->curves, needle->curves) &&
@@ -163,8 +175,10 @@
 
   CLONE_BLOB(cert_blob);
   CLONE_BLOB(ca_info_blob);
+  CLONE_BLOB(issuercert_blob);
   CLONE_STRING(CApath);
   CLONE_STRING(CAfile);
+  CLONE_STRING(issuercert);
   CLONE_STRING(clientcert);
   CLONE_STRING(random_file);
   CLONE_STRING(egdsocket);
@@ -180,6 +194,7 @@
 {
   Curl_safefree(sslc->CApath);
   Curl_safefree(sslc->CAfile);
+  Curl_safefree(sslc->issuercert);
   Curl_safefree(sslc->clientcert);
   Curl_safefree(sslc->random_file);
   Curl_safefree(sslc->egdsocket);
@@ -188,6 +203,7 @@
   Curl_safefree(sslc->pinned_key);
   Curl_safefree(sslc->cert_blob);
   Curl_safefree(sslc->ca_info_blob);
+  Curl_safefree(sslc->issuercert_blob);
   Curl_safefree(sslc->curves);
 }
 
@@ -326,7 +342,7 @@
 
 CURLcode
 Curl_ssl_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
-                             int sockindex, bool *done)
+                             bool isproxy, int sockindex, bool *done)
 {
   CURLcode result;
 
@@ -345,7 +361,7 @@
   result = Curl_ssl->connect_nonblocking(data, conn, sockindex, done);
   if(result)
     conn->ssl[sockindex].use = FALSE;
-  else if(*done)
+  else if(*done && !isproxy)
     Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */
   return result;
 }
@@ -407,8 +423,9 @@
 
   DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));
 
-  if(!SSL_SET_OPTION(primary.sessionid))
-    /* session ID re-use is disabled */
+  if(!SSL_SET_OPTION(primary.sessionid) || !data->state.session)
+    /* session ID re-use is disabled or the session cache has not been
+       setup */
     return TRUE;
 
   /* Lock if shared */
@@ -443,6 +460,10 @@
     }
   }
 
+  DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
+               no_match? "Didn't find": "Found",
+               isProxy ? "proxy" : "host",
+               conn->handler->scheme, name, port));
   return no_match;
 }
 
@@ -492,14 +513,14 @@
  */
 CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
                                struct connectdata *conn,
-                               bool isProxy,
+                               const bool isProxy,
                                void *ssl_sessionid,
                                size_t idsize,
                                int sockindex)
 {
   size_t i;
-  struct Curl_ssl_session *store = &data->state.session[0];
-  long oldest_age = data->state.session[0].age; /* zero if unused */
+  struct Curl_ssl_session *store;
+  long oldest_age;
   char *clone_host;
   char *clone_conn_to_host;
   int conn_to_port;
@@ -515,6 +536,11 @@
   const char *hostname = conn->host.name;
 #endif
   (void)sockindex;
+  if(!data->state.session)
+    return CURLE_OK;
+
+  store = &data->state.session[0];
+  oldest_age = data->state.session[0].age; /* zero if unused */
   DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));
 
   clone_host = strdup(hostname);
@@ -583,6 +609,9 @@
     return CURLE_OUT_OF_MEMORY;
   }
 
+  DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]",
+               store->scheme, store->name, store->remote_port,
+               isProxy ? "PROXY" : "server"));
   return CURLE_OK;
 }
 
@@ -708,12 +737,12 @@
 
 static size_t multissl_version(char *buffer, size_t size);
 
-size_t Curl_ssl_version(char *buffer, size_t size)
+void Curl_ssl_version(char *buffer, size_t size)
 {
 #ifdef CURL_WITH_MULTI_SSL
-  return multissl_version(buffer, size);
+  (void)multissl_version(buffer, size);
 #else
-  return Curl_ssl->version(buffer, size);
+  (void)Curl_ssl->version(buffer, size);
 #endif
 }
 
@@ -940,7 +969,7 @@
     if(encode)
       return encode;
 
-    infof(data, "\t public key hash: sha256//%s\n", encoded);
+    infof(data, " public key hash: sha256//%s", encoded);
 
     /* it starts with sha256//, copy so we can modify it */
     pinkeylen = strlen(pinnedpubkey) + 1;
@@ -1374,7 +1403,7 @@
     for(i = 0; available_backends[i]; i++) {
       if(strcasecompare(env, available_backends[i]->info.name)) {
         Curl_ssl = available_backends[i];
-        curl_free(env_tmp);
+        free(env_tmp);
         return 0;
       }
     }
@@ -1382,7 +1411,7 @@
 
   /* Fall back to first available backend */
   Curl_ssl = available_backends[0];
-  curl_free(env_tmp);
+  free(env_tmp);
   return 0;
 }
 
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index 7f93e7a..beaa83d 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -193,6 +193,7 @@
                           int sockindex);
 CURLcode Curl_ssl_connect_nonblocking(struct Curl_easy *data,
                                       struct connectdata *conn,
+                                      bool isproxy,
                                       int sockindex,
                                       bool *done);
 /* tell the SSL stuff to close down all open information regarding
@@ -209,7 +210,7 @@
 
 /* init the SSL session ID cache */
 CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
-size_t Curl_ssl_version(char *buffer, size_t size);
+void Curl_ssl_version(char *buffer, size_t size);
 bool Curl_ssl_data_pending(const struct connectdata *conn,
                            int connindex);
 int Curl_ssl_check_cxn(struct connectdata *conn);
@@ -246,7 +247,7 @@
  */
 bool Curl_ssl_getsessionid(struct Curl_easy *data,
                            struct connectdata *conn,
-                           const bool isproxy,
+                           const bool isProxy,
                            void **ssl_sessionid,
                            size_t *idsize, /* set 0 if unknown */
                            int sockindex);
@@ -313,7 +314,7 @@
 #define Curl_ssl_data_pending(x,y) 0
 #define Curl_ssl_check_cxn(x) 0
 #define Curl_ssl_free_certinfo(x) Curl_nop_stmt
-#define Curl_ssl_connect_nonblocking(x,y,z,w) CURLE_NOT_BUILT_IN
+#define Curl_ssl_connect_nonblocking(x,y,z,w,a) CURLE_NOT_BUILT_IN
 #define Curl_ssl_kill_session(x) Curl_nop_stmt
 #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
 #define Curl_ssl_cert_status_request() FALSE
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index 60e27e3..16fbb89 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -239,7 +239,7 @@
     req_method = SSLv23_client_method();
 #else
     infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
-          "TLS 1.0 is used exclusively\n");
+          "TLS 1.0 is used exclusively");
     req_method = TLSv1_client_method();
 #endif
     use_sni(TRUE);
@@ -324,7 +324,7 @@
       failf(data, "failed setting cipher list: %s", ciphers);
       return CURLE_SSL_CIPHER;
     }
-    infof(data, "Cipher selection: %s\n", ciphers);
+    infof(data, "Cipher selection: %s", ciphers);
   }
 
 #ifndef NO_FILESYSTEM
@@ -347,16 +347,16 @@
         /* Just continue with a warning if no strict certificate
            verification is required. */
         infof(data, "error setting certificate verify locations,"
-              " continuing anyway:\n");
+              " continuing anyway:");
       }
     }
     else {
       /* Everything is fine. */
-      infof(data, "successfully set certificate verify locations:\n");
+      infof(data, "successfully set certificate verify locations:");
     }
-    infof(data, " CAfile: %s\n",
+    infof(data, " CAfile: %s",
           SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile) : "none");
-    infof(data, " CApath: %s\n",
+    infof(data, " CApath: %s",
           SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath) : "none");
   }
 
@@ -406,7 +406,7 @@
        (wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, hostname,
                           (unsigned short)hostname_len) != 1)) {
       infof(data, "WARNING: failed to configure server name indication (SNI) "
-            "TLS extension\n");
+            "TLS extension");
     }
   }
 #endif
@@ -450,12 +450,12 @@
 #ifdef USE_HTTP2
     if(data->state.httpwant >= CURL_HTTP_VERSION_2) {
       strcpy(protocols + strlen(protocols), ALPN_H2 ",");
-      infof(data, "ALPN, offering %s\n", ALPN_H2);
+      infof(data, "ALPN, offering %s", ALPN_H2);
     }
 #endif
 
     strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
-    infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+    infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
 
     if(wolfSSL_UseALPN(backend->handle, protocols,
                        (unsigned)strlen(protocols),
@@ -494,15 +494,11 @@
                               &ssl_sessionid, NULL, sockindex)) {
       /* we got a session id, use it! */
       if(!SSL_set_session(backend->handle, ssl_sessionid)) {
-        char error_buffer[WOLFSSL_MAX_ERROR_SZ];
-        Curl_ssl_sessionid_unlock(data);
-        failf(data, "SSL: SSL_set_session failed: %s",
-              ERR_error_string(SSL_get_error(backend->handle, 0),
-                               error_buffer));
-        return CURLE_SSL_CONNECT_ERROR;
+        Curl_ssl_delsessionid(data, ssl_sessionid);
+        infof(data, "Can't use session ID, going on without\n");
       }
-      /* Informational message */
-      infof(data, "SSL re-using session ID\n");
+      else
+        infof(data, "SSL re-using session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -529,6 +525,8 @@
   const char * const dispname = SSL_HOST_DISPNAME();
   const char * const pinnedpubkey = SSL_PINNED_PUB_KEY();
 
+  ERR_clear_error();
+
   conn->recv[sockindex] = wolfssl_recv;
   conn->send[sockindex] = wolfssl_send;
 
@@ -582,7 +580,7 @@
      * as also mismatching CN fields */
     else if(DOMAIN_NAME_MISMATCH == detail) {
 #if 1
-      failf(data, "\tsubject alt name(s) or common name do not match \"%s\"",
+      failf(data, " subject alt name(s) or common name do not match \"%s\"",
             dispname);
       return CURLE_PEER_FAILED_VERIFICATION;
 #else
@@ -594,13 +592,13 @@
        * 'conn->ssl_config.verifyhost' value. */
       if(SSL_CONN_CONFIG(verifyhost)) {
         failf(data,
-              "\tsubject alt name(s) or common name do not match \"%s\"\n",
+              " subject alt name(s) or common name do not match \"%s\"\n",
               dispname);
         return CURLE_PEER_FAILED_VERIFICATION;
       }
       else {
         infof(data,
-              "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
+              " subject alt name(s) and/or common name do not match \"%s\"",
               dispname);
         return CURLE_OK;
       }
@@ -609,14 +607,14 @@
 #if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
     else if(ASN_NO_SIGNER_E == detail) {
       if(SSL_CONN_CONFIG(verifypeer)) {
-        failf(data, "\tCA signer not available for verification");
+        failf(data, " CA signer not available for verification");
         return CURLE_SSL_CACERT_BADFILE;
       }
       else {
         /* Just continue with a warning if no strict certificate
            verification is required. */
         infof(data, "CA signer not available for verification, "
-                    "continuing anyway\n");
+                    "continuing anyway");
       }
     }
 #endif
@@ -681,7 +679,7 @@
     rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
 
     if(rc == SSL_SUCCESS) {
-      infof(data, "ALPN, server accepted to use %.*s\n", protocol_len,
+      infof(data, "ALPN, server accepted to use %.*s", protocol_len,
             protocol);
 
       if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
@@ -694,13 +692,13 @@
         conn->negnpn = CURL_HTTP_VERSION_2;
 #endif
       else
-        infof(data, "ALPN, unrecognized protocol %.*s\n", protocol_len,
+        infof(data, "ALPN, unrecognized protocol %.*s", protocol_len,
               protocol);
       Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ?
                           BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
     }
     else if(rc == SSL_ALPN_NOT_FOUND)
-      infof(data, "ALPN, server did not agree to a protocol\n");
+      infof(data, "ALPN, server did not agree to a protocol");
     else {
       failf(data, "ALPN, failure getting protocol, error %d", rc);
       return CURLE_SSL_CONNECT_ERROR;
@@ -710,11 +708,11 @@
 
   connssl->connecting_state = ssl_connect_3;
 #if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
-  infof(data, "SSL connection using %s / %s\n",
+  infof(data, "SSL connection using %s / %s",
         wolfSSL_get_version(backend->handle),
         wolfSSL_get_cipher_name(backend->handle));
 #else
-  infof(data, "SSL connected\n");
+  infof(data, "SSL connected");
 #endif
 
   return CURLE_OK;
@@ -743,7 +741,7 @@
                                         &old_ssl_sessionid, NULL, sockindex));
       if(incache) {
         if(old_ssl_sessionid != our_ssl_sessionid) {
-          infof(data, "old SSL session ID is stale, removing\n");
+          infof(data, "old SSL session ID is stale, removing");
           Curl_ssl_delsessionid(data, old_ssl_sessionid);
           incache = FALSE;
         }
@@ -779,7 +777,11 @@
   struct ssl_backend_data *backend = connssl->backend;
   char error_buffer[WOLFSSL_MAX_ERROR_SZ];
   int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
-  int rc = SSL_write(backend->handle, mem, memlen);
+  int rc;
+
+  ERR_clear_error();
+
+  rc = SSL_write(backend->handle, mem, memlen);
 
   if(rc <= 0) {
     int err = SSL_get_error(backend->handle, rc);
@@ -810,6 +812,10 @@
   (void) data;
 
   if(backend->handle) {
+    char buf[32];
+    /* Maybe the server has already sent a close notify alert.
+       Read it to avoid an RST on the TCP connection. */
+    (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
     (void)SSL_shutdown(backend->handle);
     SSL_free(backend->handle);
     backend->handle = NULL;
@@ -831,7 +837,11 @@
   struct ssl_backend_data *backend = connssl->backend;
   char error_buffer[WOLFSSL_MAX_ERROR_SZ];
   int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
-  int nread = SSL_read(backend->handle, buf, buffsize);
+  int nread;
+
+  ERR_clear_error();
+
+  nread = SSL_read(backend->handle, buf, buffsize);
 
   if(nread < 0) {
     int err = SSL_get_error(backend->handle, nread);
@@ -916,6 +926,7 @@
   (void) data;
 
   if(backend->handle) {
+    ERR_clear_error();
     SSL_free(backend->handle);
     backend->handle = NULL;
   }
@@ -1089,7 +1100,7 @@
 }
 
 static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
-                                        CURLINFO info UNUSED_PARAM)
+                                   CURLINFO info UNUSED_PARAM)
 {
   struct ssl_backend_data *backend = connssl->backend;
   (void)info;
diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c
index c0764c4..15c8156 100644
--- a/Utilities/cmcurl/lib/warnless.c
+++ b/Utilities/cmcurl/lib/warnless.c
@@ -37,85 +37,23 @@
 
 #include "warnless.h"
 
-#define CURL_MASK_SCHAR  0x7F
-#define CURL_MASK_UCHAR  0xFF
+#define CURL_MASK_UCHAR   ((unsigned char)~0)
+#define CURL_MASK_SCHAR   (CURL_MASK_UCHAR >> 1)
 
-#if (SIZEOF_SHORT == 2)
-#  define CURL_MASK_SSHORT  0x7FFF
-#  define CURL_MASK_USHORT  0xFFFF
-#elif (SIZEOF_SHORT == 4)
-#  define CURL_MASK_SSHORT  0x7FFFFFFF
-#  define CURL_MASK_USHORT  0xFFFFFFFF
-#elif (SIZEOF_SHORT == 8)
-#  define CURL_MASK_SSHORT  0x7FFFFFFFFFFFFFFF
-#  define CURL_MASK_USHORT  0xFFFFFFFFFFFFFFFF
-#else
-#  error "SIZEOF_SHORT not defined"
-#endif
+#define CURL_MASK_USHORT  ((unsigned short)~0)
+#define CURL_MASK_SSHORT  (CURL_MASK_USHORT >> 1)
 
-#if (SIZEOF_INT == 2)
-#  define CURL_MASK_SINT  0x7FFF
-#  define CURL_MASK_UINT  0xFFFF
-#elif (SIZEOF_INT == 4)
-#  define CURL_MASK_SINT  0x7FFFFFFF
-#  define CURL_MASK_UINT  0xFFFFFFFF
-#elif (SIZEOF_INT == 8)
-#  define CURL_MASK_SINT  0x7FFFFFFFFFFFFFFF
-#  define CURL_MASK_UINT  0xFFFFFFFFFFFFFFFF
-#elif (SIZEOF_INT == 16)
-#  define CURL_MASK_SINT  0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-#  define CURL_MASK_UINT  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
-#else
-#  error "SIZEOF_INT not defined"
-#endif
+#define CURL_MASK_UINT    ((unsigned int)~0)
+#define CURL_MASK_SINT    (CURL_MASK_UINT >> 1)
 
-#if (SIZEOF_LONG == 2)
-#  define CURL_MASK_SLONG  0x7FFFL
-#  define CURL_MASK_ULONG  0xFFFFUL
-#elif (SIZEOF_LONG == 4)
-#  define CURL_MASK_SLONG  0x7FFFFFFFL
-#  define CURL_MASK_ULONG  0xFFFFFFFFUL
-#elif (SIZEOF_LONG == 8)
-#  define CURL_MASK_SLONG  0x7FFFFFFFFFFFFFFFL
-#  define CURL_MASK_ULONG  0xFFFFFFFFFFFFFFFFUL
-#elif (SIZEOF_LONG == 16)
-#  define CURL_MASK_SLONG  0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL
-#  define CURL_MASK_ULONG  0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL
-#else
-#  error "SIZEOF_LONG not defined"
-#endif
+#define CURL_MASK_ULONG   ((unsigned long)~0)
+#define CURL_MASK_SLONG   (CURL_MASK_ULONG >> 1)
 
-#if (SIZEOF_CURL_OFF_T == 2)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFF)
-#elif (SIZEOF_CURL_OFF_T == 4)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFF)
-#elif (SIZEOF_CURL_OFF_T == 8)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFFFFFFFFFF)
-#elif (SIZEOF_CURL_OFF_T == 16)
-#  define CURL_MASK_SCOFFT  CURL_OFF_T_C(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
-#  define CURL_MASK_UCOFFT  CURL_OFF_TU_C(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
-#else
-#  error "SIZEOF_CURL_OFF_T not defined"
-#endif
+#define CURL_MASK_UCOFFT  ((unsigned CURL_TYPEOF_CURL_OFF_T)~0)
+#define CURL_MASK_SCOFFT  (CURL_MASK_UCOFFT >> 1)
 
-#if (SIZEOF_SIZE_T == SIZEOF_SHORT)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SSHORT
-#  define CURL_MASK_USIZE_T  CURL_MASK_USHORT
-#elif (SIZEOF_SIZE_T == SIZEOF_INT)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SINT
-#  define CURL_MASK_USIZE_T  CURL_MASK_UINT
-#elif (SIZEOF_SIZE_T == SIZEOF_LONG)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SLONG
-#  define CURL_MASK_USIZE_T  CURL_MASK_ULONG
-#elif (SIZEOF_SIZE_T == SIZEOF_CURL_OFF_T)
-#  define CURL_MASK_SSIZE_T  CURL_MASK_SCOFFT
-#  define CURL_MASK_USIZE_T  CURL_MASK_UCOFFT
-#else
-#  error "SIZEOF_SIZE_T not defined"
-#endif
+#define CURL_MASK_USIZE_T ((size_t)~0)
+#define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1)
 
 /*
 ** unsigned long to unsigned short
@@ -207,7 +145,7 @@
 # pragma warning(disable:810) /* conversion may lose significant bits */
 #endif
 
-#if (SIZEOF_LONG < SIZEOF_SIZE_T)
+#if ULONG_MAX < SIZE_T_MAX
   DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG);
 #endif
   return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
@@ -228,7 +166,7 @@
 # pragma warning(disable:810) /* conversion may lose significant bits */
 #endif
 
-#if (SIZEOF_INT < SIZEOF_SIZE_T)
+#if UINT_MAX < SIZE_T_MAX
   DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT);
 #endif
   return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
@@ -250,7 +188,7 @@
 #endif
 
   DEBUGASSERT(slnum >= 0);
-#if (SIZEOF_INT < SIZEOF_LONG)
+#if INT_MAX < LONG_MAX
   DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_SINT);
 #endif
   return (int)(slnum & (long) CURL_MASK_SINT);
@@ -272,7 +210,7 @@
 #endif
 
   DEBUGASSERT(slnum >= 0);
-#if (SIZEOF_INT < SIZEOF_LONG)
+#if UINT_MAX < LONG_MAX
   DEBUGASSERT((unsigned long) slnum <= (unsigned long) CURL_MASK_UINT);
 #endif
   return (unsigned int)(slnum & (long) CURL_MASK_UINT);
@@ -352,7 +290,7 @@
 #endif
 
   DEBUGASSERT(sznum >= 0);
-#if (SIZEOF_INT < SIZEOF_SIZE_T)
+#if INT_MAX < SSIZE_T_MAX
   DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT);
 #endif
   return (int)(sznum & (ssize_t) CURL_MASK_SINT);
diff --git a/Utilities/cmcurl/lib/x509asn1.c b/Utilities/cmcurl/lib/x509asn1.c
index 281c972..1bdaead 100644
--- a/Utilities/cmcurl/lib/x509asn1.c
+++ b/Utilities/cmcurl/lib/x509asn1.c
@@ -23,7 +23,7 @@
 #include "curl_setup.h"
 
 #if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
-    defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+    defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
 
 #include <curl/curl.h>
 #include "urldata.h"
@@ -34,6 +34,7 @@
 #include "inet_pton.h"
 #include "curl_base64.h"
 #include "x509asn1.h"
+#include "dynbuf.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -205,16 +206,16 @@
  */
 static const char *octet2str(const char *beg, const char *end)
 {
-  size_t n = end - beg;
-  char *buf = NULL;
+  struct dynbuf buf;
+  CURLcode result;
 
-  if(n <= (SIZE_T_MAX - 1) / 3) {
-    buf = malloc(3 * n + 1);
-    if(buf)
-      for(n = 0; beg < end; n += 3)
-        msnprintf(buf + n, 4, "%02x:", *(const unsigned char *) beg++);
-  }
-  return buf;
+  Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1);
+  result = Curl_dyn_addn(&buf, "", 0);
+
+  while(!result && beg < end)
+    result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++);
+
+  return Curl_dyn_ptr(&buf);
 }
 
 static const char *bit2str(const char *beg, const char *end)
@@ -517,8 +518,8 @@
   return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
                        beg, beg + 4, beg + 6,
                        beg + 8, beg + 10, sec1, sec2,
-                       fracl? ".": "", fracl, fracp,
-                       sep, tzl, tzp);
+                       fracl? ".": "", (int)fracl, fracp,
+                       sep, (int)tzl, tzp);
 }
 
 /*
@@ -558,7 +559,7 @@
   return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
                        20 - (*beg >= '5'), beg, beg + 2, beg + 4,
                        beg + 6, beg + 8, sec,
-                       tzl, tzp);
+                       (int)tzl, tzp);
 }
 
 /*
@@ -866,7 +867,7 @@
     if(data->set.ssl.certinfo)
       Curl_ssl_push_certinfo(data, certnum, label, output);
     if(!certnum)
-      infof(data, "   %s: %s\n", label, output);
+      infof(data, "   %s: %s", label, output);
     free((char *) output);
   }
 }
@@ -905,7 +906,7 @@
     if(len > 32)
       elem.beg = q;     /* Strip leading zero bytes. */
     if(!certnum)
-      infof(data, "   RSA Public Key (%lu bits)\n", len);
+      infof(data, "   RSA Public Key (%lu bits)", len);
     if(data->set.ssl.certinfo) {
       q = curl_maprintf("%lu", len);
       if(q) {
@@ -978,7 +979,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
   if(!certnum)
-    infof(data, "%2d Subject: %s\n", certnum, ccp);
+    infof(data, "%2d Subject: %s", certnum, ccp);
   free((char *) ccp);
 
   /* Issuer. */
@@ -988,7 +989,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
   if(!certnum)
-    infof(data, "   Issuer: %s\n", ccp);
+    infof(data, "   Issuer: %s", ccp);
   free((char *) ccp);
 
   /* Version (always fits in less than 32 bits). */
@@ -1003,7 +1004,7 @@
     free((char *) ccp);
   }
   if(!certnum)
-    infof(data, "   Version: %lu (0x%lx)\n", version + 1, version);
+    infof(data, "   Version: %lu (0x%lx)", version + 1, version);
 
   /* Serial number. */
   ccp = ASN1tostr(&cert.serialNumber, 0);
@@ -1012,7 +1013,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
   if(!certnum)
-    infof(data, "   Serial Number: %s\n", ccp);
+    infof(data, "   Serial Number: %s", ccp);
   free((char *) ccp);
 
   /* Signature algorithm .*/
@@ -1023,7 +1024,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
   if(!certnum)
-    infof(data, "   Signature Algorithm: %s\n", ccp);
+    infof(data, "   Signature Algorithm: %s", ccp);
   free((char *) ccp);
 
   /* Start Date. */
@@ -1033,7 +1034,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
   if(!certnum)
-    infof(data, "   Start Date: %s\n", ccp);
+    infof(data, "   Start Date: %s", ccp);
   free((char *) ccp);
 
   /* Expire Date. */
@@ -1043,7 +1044,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
   if(!certnum)
-    infof(data, "   Expire Date: %s\n", ccp);
+    infof(data, "   Expire Date: %s", ccp);
   free((char *) ccp);
 
   /* Public Key Algorithm. */
@@ -1054,7 +1055,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp);
   if(!certnum)
-    infof(data, "   Public Key Algorithm: %s\n", ccp);
+    infof(data, "   Public Key Algorithm: %s", ccp);
   do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
   free((char *) ccp);
 
@@ -1065,7 +1066,7 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
   if(!certnum)
-    infof(data, "   Signature: %s\n", ccp);
+    infof(data, "   Signature: %s", ccp);
   free((char *) ccp);
 
   /* Generate PEM certificate. */
@@ -1098,12 +1099,13 @@
   if(data->set.ssl.certinfo)
     Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
   if(!certnum)
-    infof(data, "%s\n", cp2);
+    infof(data, "%s", cp2);
   free(cp2);
   return CURLE_OK;
 }
 
-#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL */
+#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
+        * or USE_SECTRANSP */
 
 #if defined(USE_GSKIT)
 
@@ -1220,12 +1222,12 @@
   switch(matched) {
   case 1:
     /* an alternative name matched the server hostname */
-    infof(data, "\t subjectAltName: %s matched\n", dispname);
+    infof(data, "  subjectAltName: %s matched", dispname);
     return CURLE_OK;
   case 0:
     /* an alternative name field existed, but didn't match and then
        we MUST fail */
-    infof(data, "\t subjectAltName does not match %s\n", dispname);
+    infof(data, "  subjectAltName does not match %s", dispname);
     return CURLE_PEER_FAILED_VERIFICATION;
   }
 
@@ -1262,7 +1264,7 @@
     if(strlen(dnsname) != (size_t) len)         /* Nul byte in string ? */
       failf(data, "SSL: illegal cert name field");
     else if(Curl_cert_hostcheck((const char *) dnsname, hostname)) {
-      infof(data, "\t common name: %s (matched)\n", dnsname);
+      infof(data, "  common name: %s (matched)", dnsname);
       free(dnsname);
       return CURLE_OK;
     }
diff --git a/Utilities/cmcurl/lib/x509asn1.h b/Utilities/cmcurl/lib/x509asn1.h
index 326e32d..3b51eee 100644
--- a/Utilities/cmcurl/lib/x509asn1.h
+++ b/Utilities/cmcurl/lib/x509asn1.h
@@ -26,7 +26,7 @@
 #include "curl_setup.h"
 
 #if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
-    defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+    defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
 
 #include "urldata.h"
 
@@ -129,5 +129,6 @@
                                const char *beg, const char *end);
 CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
                          const char *beg, const char *end);
-#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL */
+#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
+        * or USE_SECTRANSP */
 #endif /* HEADER_CURL_X509ASN1_H */
diff --git a/Utilities/cmelf/.gitattributes b/Utilities/cmelf/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/Utilities/cmelf/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/Utilities/cmelf/elf32.h b/Utilities/cmelf/elf32.h
new file mode 100644
index 0000000..20d2ec2
--- /dev/null
+++ b/Utilities/cmelf/elf32.h
@@ -0,0 +1,265 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_ELF32_H_
+#define _SYS_ELF32_H_ 1
+
+#include "elf_common.h"
+
+/*
+ * ELF definitions common to all 32-bit architectures.
+ */
+
+typedef uint32_t	Elf32_Addr;
+typedef uint16_t	Elf32_Half;
+typedef uint32_t	Elf32_Off;
+typedef int32_t		Elf32_Sword;
+typedef uint32_t	Elf32_Word;
+typedef uint64_t	Elf32_Lword;
+
+typedef Elf32_Word	Elf32_Hashelt;
+
+/* Non-standard class-dependent datatype used for abstraction. */
+typedef Elf32_Word	Elf32_Size;
+typedef Elf32_Sword	Elf32_Ssize;
+
+/*
+ * ELF header.
+ */
+
+typedef struct {
+	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
+	Elf32_Half	e_type;		/* File type. */
+	Elf32_Half	e_machine;	/* Machine architecture. */
+	Elf32_Word	e_version;	/* ELF format version. */
+	Elf32_Addr	e_entry;	/* Entry point. */
+	Elf32_Off	e_phoff;	/* Program header file offset. */
+	Elf32_Off	e_shoff;	/* Section header file offset. */
+	Elf32_Word	e_flags;	/* Architecture-specific flags. */
+	Elf32_Half	e_ehsize;	/* Size of ELF header in bytes. */
+	Elf32_Half	e_phentsize;	/* Size of program header entry. */
+	Elf32_Half	e_phnum;	/* Number of program header entries. */
+	Elf32_Half	e_shentsize;	/* Size of section header entry. */
+	Elf32_Half	e_shnum;	/* Number of section header entries. */
+	Elf32_Half	e_shstrndx;	/* Section name strings section. */
+} Elf32_Ehdr;
+
+/*
+ * Shared object information, found in SHT_MIPS_LIBLIST.
+ */
+
+typedef struct {
+	Elf32_Word l_name;		/* The name of a shared object. */
+	Elf32_Word l_time_stamp;	/* 32-bit timestamp. */
+	Elf32_Word l_checksum;		/* Checksum of visible symbols, sizes. */
+	Elf32_Word l_version;		/* Interface version string index. */
+	Elf32_Word l_flags;		/* Flags (LL_*). */
+} Elf32_Lib;
+
+/*
+ * Section header.
+ */
+
+typedef struct {
+	Elf32_Word	sh_name;	/* Section name (index into the
+					   section header string table). */
+	Elf32_Word	sh_type;	/* Section type. */
+	Elf32_Word	sh_flags;	/* Section flags. */
+	Elf32_Addr	sh_addr;	/* Address in memory image. */
+	Elf32_Off	sh_offset;	/* Offset in file. */
+	Elf32_Word	sh_size;	/* Size in bytes. */
+	Elf32_Word	sh_link;	/* Index of a related section. */
+	Elf32_Word	sh_info;	/* Depends on section type. */
+	Elf32_Word	sh_addralign;	/* Alignment in bytes. */
+	Elf32_Word	sh_entsize;	/* Size of each entry in section. */
+} Elf32_Shdr;
+
+/*
+ * Program header.
+ */
+
+typedef struct {
+	Elf32_Word	p_type;		/* Entry type. */
+	Elf32_Off	p_offset;	/* File offset of contents. */
+	Elf32_Addr	p_vaddr;	/* Virtual address in memory image. */
+	Elf32_Addr	p_paddr;	/* Physical address (not used). */
+	Elf32_Word	p_filesz;	/* Size of contents in file. */
+	Elf32_Word	p_memsz;	/* Size of contents in memory. */
+	Elf32_Word	p_flags;	/* Access permission flags. */
+	Elf32_Word	p_align;	/* Alignment in memory and file. */
+} Elf32_Phdr;
+
+/*
+ * Dynamic structure.  The ".dynamic" section contains an array of them.
+ */
+
+typedef struct {
+	Elf32_Sword	d_tag;		/* Entry type. */
+	union {
+		Elf32_Word	d_val;	/* Integer value. */
+		Elf32_Addr	d_ptr;	/* Address value. */
+	} d_un;
+} Elf32_Dyn;
+
+/*
+ * Relocation entries.
+ */
+
+/* Relocations that don't need an addend field. */
+typedef struct {
+	Elf32_Addr	r_offset;	/* Location to be relocated. */
+	Elf32_Word	r_info;		/* Relocation type and symbol index. */
+} Elf32_Rel;
+
+/* Relocations that need an addend field. */
+typedef struct {
+	Elf32_Addr	r_offset;	/* Location to be relocated. */
+	Elf32_Word	r_info;		/* Relocation type and symbol index. */
+	Elf32_Sword	r_addend;	/* Addend. */
+} Elf32_Rela;
+
+/* Macros for accessing the fields of r_info. */
+#define ELF32_R_SYM(info)	((info) >> 8)
+#define ELF32_R_TYPE(info)	((unsigned char)(info))
+
+/* Macro for constructing r_info from field values. */
+#define ELF32_R_INFO(sym, type)	(((sym) << 8) + (unsigned char)(type))
+
+/*
+ *	Note entry header
+ */
+typedef Elf_Note Elf32_Nhdr;
+
+/*
+ *	Move entry
+ */
+typedef struct {
+	Elf32_Lword	m_value;	/* symbol value */
+	Elf32_Word 	m_info;		/* size + index */
+	Elf32_Word	m_poffset;	/* symbol offset */
+	Elf32_Half	m_repeat;	/* repeat count */
+	Elf32_Half	m_stride;	/* stride info */
+} Elf32_Move;
+
+/*
+ *	The macros compose and decompose values for Move.r_info
+ *
+ *	sym = ELF32_M_SYM(M.m_info)
+ *	size = ELF32_M_SIZE(M.m_info)
+ *	M.m_info = ELF32_M_INFO(sym, size)
+ */
+#define	ELF32_M_SYM(info)	((info)>>8)
+#define	ELF32_M_SIZE(info)	((unsigned char)(info))
+#define	ELF32_M_INFO(sym, size)	(((sym)<<8)+(unsigned char)(size))
+
+/*
+ *	Hardware/Software capabilities entry
+ */
+typedef struct {
+	Elf32_Word	c_tag;		/* how to interpret value */
+	union {
+		Elf32_Word	c_val;
+		Elf32_Addr	c_ptr;
+	} c_un;
+} Elf32_Cap;
+
+/*
+ * Symbol table entries.
+ */
+
+typedef struct {
+	Elf32_Word	st_name;	/* String table index of name. */
+	Elf32_Addr	st_value;	/* Symbol value. */
+	Elf32_Word	st_size;	/* Size of associated object. */
+	unsigned char	st_info;	/* Type and binding information. */
+	unsigned char	st_other;	/* Reserved (not used). */
+	Elf32_Half	st_shndx;	/* Section index of symbol. */
+} Elf32_Sym;
+
+/* Macros for accessing the fields of st_info. */
+#define ELF32_ST_BIND(info)		((info) >> 4)
+#define ELF32_ST_TYPE(info)		((info) & 0xf)
+
+/* Macro for constructing st_info from field values. */
+#define ELF32_ST_INFO(bind, type)	(((bind) << 4) + ((type) & 0xf))
+
+/* Macro for accessing the fields of st_other. */
+#define ELF32_ST_VISIBILITY(oth)	((oth) & 0x3)
+
+/* Structures used by Sun & GNU symbol versioning. */
+typedef struct
+{
+	Elf32_Half	vd_version;
+	Elf32_Half	vd_flags;
+	Elf32_Half	vd_ndx;
+	Elf32_Half	vd_cnt;
+	Elf32_Word	vd_hash;
+	Elf32_Word	vd_aux;
+	Elf32_Word	vd_next;
+} Elf32_Verdef;
+
+typedef struct
+{
+	Elf32_Word	vda_name;
+	Elf32_Word	vda_next;
+} Elf32_Verdaux;
+
+typedef struct
+{
+	Elf32_Half	vn_version;
+	Elf32_Half	vn_cnt;
+	Elf32_Word	vn_file;
+	Elf32_Word	vn_aux;
+	Elf32_Word	vn_next;
+} Elf32_Verneed;
+
+typedef struct
+{
+	Elf32_Word	vna_hash;
+	Elf32_Half	vna_flags;
+	Elf32_Half	vna_other;
+	Elf32_Word	vna_name;
+	Elf32_Word	vna_next;
+} Elf32_Vernaux;
+
+typedef Elf32_Half Elf32_Versym;
+
+typedef struct {
+	Elf32_Half	si_boundto;	/* direct bindings - symbol bound to */
+	Elf32_Half	si_flags;	/* per symbol flags */
+} Elf32_Syminfo;
+
+typedef struct {
+	Elf32_Word	ch_type;
+	Elf32_Word	ch_size;
+	Elf32_Word	ch_addralign;
+} Elf32_Chdr;
+
+#endif /* !_SYS_ELF32_H_ */
diff --git a/Utilities/cmelf/elf64.h b/Utilities/cmelf/elf64.h
new file mode 100644
index 0000000..6efe147
--- /dev/null
+++ b/Utilities/cmelf/elf64.h
@@ -0,0 +1,269 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_ELF64_H_
+#define _SYS_ELF64_H_ 1
+
+#include "elf_common.h"
+
+/*
+ * ELF definitions common to all 64-bit architectures.
+ */
+
+typedef uint64_t	Elf64_Addr;
+typedef uint16_t	Elf64_Half;
+typedef uint64_t	Elf64_Off;
+typedef int32_t		Elf64_Sword;
+typedef int64_t		Elf64_Sxword;
+typedef uint32_t	Elf64_Word;
+typedef uint64_t	Elf64_Lword;
+typedef uint64_t	Elf64_Xword;
+
+/*
+ * Types of dynamic symbol hash table bucket and chain elements.
+ *
+ * This is inconsistent among 64 bit architectures, so a machine dependent
+ * typedef is required.
+ */
+
+typedef Elf64_Word	Elf64_Hashelt;
+
+/* Non-standard class-dependent datatype used for abstraction. */
+typedef Elf64_Xword	Elf64_Size;
+typedef Elf64_Sxword	Elf64_Ssize;
+
+/*
+ * ELF header.
+ */
+
+typedef struct {
+	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
+	Elf64_Half	e_type;		/* File type. */
+	Elf64_Half	e_machine;	/* Machine architecture. */
+	Elf64_Word	e_version;	/* ELF format version. */
+	Elf64_Addr	e_entry;	/* Entry point. */
+	Elf64_Off	e_phoff;	/* Program header file offset. */
+	Elf64_Off	e_shoff;	/* Section header file offset. */
+	Elf64_Word	e_flags;	/* Architecture-specific flags. */
+	Elf64_Half	e_ehsize;	/* Size of ELF header in bytes. */
+	Elf64_Half	e_phentsize;	/* Size of program header entry. */
+	Elf64_Half	e_phnum;	/* Number of program header entries. */
+	Elf64_Half	e_shentsize;	/* Size of section header entry. */
+	Elf64_Half	e_shnum;	/* Number of section header entries. */
+	Elf64_Half	e_shstrndx;	/* Section name strings section. */
+} Elf64_Ehdr;
+
+/*
+ * Shared object information, found in SHT_MIPS_LIBLIST.
+ */
+
+typedef struct {
+	Elf64_Word l_name;		/* The name of a shared object. */
+	Elf64_Word l_time_stamp;	/* 64-bit timestamp. */
+	Elf64_Word l_checksum;		/* Checksum of visible symbols, sizes. */
+	Elf64_Word l_version;		/* Interface version string index. */
+	Elf64_Word l_flags;		/* Flags (LL_*). */
+} Elf64_Lib;
+
+/*
+ * Section header.
+ */
+
+typedef struct {
+	Elf64_Word	sh_name;	/* Section name (index into the
+					   section header string table). */
+	Elf64_Word	sh_type;	/* Section type. */
+	Elf64_Xword	sh_flags;	/* Section flags. */
+	Elf64_Addr	sh_addr;	/* Address in memory image. */
+	Elf64_Off	sh_offset;	/* Offset in file. */
+	Elf64_Xword	sh_size;	/* Size in bytes. */
+	Elf64_Word	sh_link;	/* Index of a related section. */
+	Elf64_Word	sh_info;	/* Depends on section type. */
+	Elf64_Xword	sh_addralign;	/* Alignment in bytes. */
+	Elf64_Xword	sh_entsize;	/* Size of each entry in section. */
+} Elf64_Shdr;
+
+/*
+ * Program header.
+ */
+
+typedef struct {
+	Elf64_Word	p_type;		/* Entry type. */
+	Elf64_Word	p_flags;	/* Access permission flags. */
+	Elf64_Off	p_offset;	/* File offset of contents. */
+	Elf64_Addr	p_vaddr;	/* Virtual address in memory image. */
+	Elf64_Addr	p_paddr;	/* Physical address (not used). */
+	Elf64_Xword	p_filesz;	/* Size of contents in file. */
+	Elf64_Xword	p_memsz;	/* Size of contents in memory. */
+	Elf64_Xword	p_align;	/* Alignment in memory and file. */
+} Elf64_Phdr;
+
+/*
+ * Dynamic structure.  The ".dynamic" section contains an array of them.
+ */
+
+typedef struct {
+	Elf64_Sxword	d_tag;		/* Entry type. */
+	union {
+		Elf64_Xword	d_val;	/* Integer value. */
+		Elf64_Addr	d_ptr;	/* Address value. */
+	} d_un;
+} Elf64_Dyn;
+
+/*
+ * Relocation entries.
+ */
+
+/* Relocations that don't need an addend field. */
+typedef struct {
+	Elf64_Addr	r_offset;	/* Location to be relocated. */
+	Elf64_Xword	r_info;		/* Relocation type and symbol index. */
+} Elf64_Rel;
+
+/* Relocations that need an addend field. */
+typedef struct {
+	Elf64_Addr	r_offset;	/* Location to be relocated. */
+	Elf64_Xword	r_info;		/* Relocation type and symbol index. */
+	Elf64_Sxword	r_addend;	/* Addend. */
+} Elf64_Rela;
+
+/* Macros for accessing the fields of r_info. */
+#define	ELF64_R_SYM(info)	((info) >> 32)
+#define	ELF64_R_TYPE(info)	((info) & 0xffffffffL)
+
+/* Macro for constructing r_info from field values. */
+#define	ELF64_R_INFO(sym, type)	(((sym) << 32) + ((type) & 0xffffffffL))
+
+#define	ELF64_R_TYPE_DATA(info)	(((Elf64_Xword)(info)<<32)>>40)
+#define	ELF64_R_TYPE_ID(info)	(((Elf64_Xword)(info)<<56)>>56)
+#define	ELF64_R_TYPE_INFO(data, type)	\
+				(((Elf64_Xword)(data)<<8)+(Elf64_Xword)(type))
+
+/*
+ *	Note entry header
+ */
+typedef Elf_Note Elf64_Nhdr;
+
+/*
+ *	Move entry
+ */
+typedef struct {
+	Elf64_Lword	m_value;	/* symbol value */
+	Elf64_Xword 	m_info;		/* size + index */
+	Elf64_Xword	m_poffset;	/* symbol offset */
+	Elf64_Half	m_repeat;	/* repeat count */
+	Elf64_Half	m_stride;	/* stride info */
+} Elf64_Move;
+
+#define	ELF64_M_SYM(info)	((info)>>8)
+#define	ELF64_M_SIZE(info)	((unsigned char)(info))
+#define	ELF64_M_INFO(sym, size)	(((sym)<<8)+(unsigned char)(size))
+
+/*
+ *	Hardware/Software capabilities entry
+ */
+typedef struct {
+	Elf64_Xword	c_tag;		/* how to interpret value */
+	union {
+		Elf64_Xword	c_val;
+		Elf64_Addr	c_ptr;
+	} c_un;
+} Elf64_Cap;
+
+/*
+ * Symbol table entries.
+ */
+
+typedef struct {
+	Elf64_Word	st_name;	/* String table index of name. */
+	unsigned char	st_info;	/* Type and binding information. */
+	unsigned char	st_other;	/* Reserved (not used). */
+	Elf64_Half	st_shndx;	/* Section index of symbol. */
+	Elf64_Addr	st_value;	/* Symbol value. */
+	Elf64_Xword	st_size;	/* Size of associated object. */
+} Elf64_Sym;
+
+/* Macros for accessing the fields of st_info. */
+#define	ELF64_ST_BIND(info)		((info) >> 4)
+#define	ELF64_ST_TYPE(info)		((info) & 0xf)
+
+/* Macro for constructing st_info from field values. */
+#define	ELF64_ST_INFO(bind, type)	(((bind) << 4) + ((type) & 0xf))
+
+/* Macro for accessing the fields of st_other. */
+#define	ELF64_ST_VISIBILITY(oth)	((oth) & 0x3)
+
+/* Structures used by Sun & GNU-style symbol versioning. */
+typedef struct {
+	Elf64_Half	vd_version;
+	Elf64_Half	vd_flags;
+	Elf64_Half	vd_ndx;
+	Elf64_Half	vd_cnt;
+	Elf64_Word	vd_hash;
+	Elf64_Word	vd_aux;
+	Elf64_Word	vd_next;
+} Elf64_Verdef;
+
+typedef struct {
+	Elf64_Word	vda_name;
+	Elf64_Word	vda_next;
+} Elf64_Verdaux;
+
+typedef struct {
+	Elf64_Half	vn_version;
+	Elf64_Half	vn_cnt;
+	Elf64_Word	vn_file;
+	Elf64_Word	vn_aux;
+	Elf64_Word	vn_next;
+} Elf64_Verneed;
+
+typedef struct {
+	Elf64_Word	vna_hash;
+	Elf64_Half	vna_flags;
+	Elf64_Half	vna_other;
+	Elf64_Word	vna_name;
+	Elf64_Word	vna_next;
+} Elf64_Vernaux;
+
+typedef Elf64_Half Elf64_Versym;
+
+typedef struct {
+	Elf64_Half	si_boundto;	/* direct bindings - symbol bound to */
+	Elf64_Half	si_flags;	/* per symbol flags */
+} Elf64_Syminfo;
+
+typedef struct {
+	Elf64_Word	ch_type;
+	Elf64_Word	ch_reserved;
+	Elf64_Xword	ch_size;
+	Elf64_Xword	ch_addralign;
+} Elf64_Chdr;
+
+#endif /* !_SYS_ELF64_H_ */
diff --git a/Utilities/cmelf/elf_common.h b/Utilities/cmelf/elf_common.h
new file mode 100644
index 0000000..395c45b
--- /dev/null
+++ b/Utilities/cmelf/elf_common.h
@@ -0,0 +1,1492 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2017, 2018 Dell EMC
+ * Copyright (c) 2000, 2001, 2008, 2011, David E. O'Brien
+ * Copyright (c) 1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_ELF_COMMON_H_
+#define	_SYS_ELF_COMMON_H_ 1
+
+/*
+ * ELF definitions that are independent of architecture or word size.
+ */
+
+/*
+ * Note header.  The ".note" section contains an array of notes.  Each
+ * begins with this header, aligned to a word boundary.  Immediately
+ * following the note header is n_namesz bytes of name, padded to the
+ * next word boundary.  Then comes n_descsz bytes of descriptor, again
+ * padded to a word boundary.  The values of n_namesz and n_descsz do
+ * not include the padding.
+ */
+
+#ifndef LOCORE
+typedef struct {
+	uint32_t	n_namesz;	/* Length of name. */
+	uint32_t	n_descsz;	/* Length of descriptor. */
+	uint32_t	n_type;		/* Type of this note. */
+} Elf_Note;
+typedef Elf_Note Elf_Nhdr;
+#endif
+
+/*
+ * Option kinds.
+ */
+#define	ODK_NULL	0	/* undefined */
+#define	ODK_REGINFO	1	/* register usage info */
+#define	ODK_EXCEPTIONS	2	/* exception processing info */
+#define	ODK_PAD		3	/* section padding */
+#define	ODK_HWPATCH	4	/* hardware patch applied */
+#define	ODK_FILL	5	/* fill value used by the linker */
+#define	ODK_TAGS	6	/* reserved space for tools */
+#define	ODK_HWAND	7	/* hardware AND patch applied */
+#define	ODK_HWOR	8	/* hardware OR patch applied */
+#define	ODK_GP_GROUP	9	/* GP group for text/data sections */
+#define	ODK_IDENT	10	/* ID information */
+#define	ODK_PAGESIZE	11	/* page size information */
+
+/*
+ * ODK_EXCEPTIONS info field masks.
+ */
+#define	OEX_FPU_MIN	0x0000001f	/* min FPU exception required */
+#define	OEX_FPU_MAX	0x00001f00	/* max FPU exception allowed */
+#define	OEX_PAGE0	0x00010000	/* page zero must be mapped */
+#define	OEX_SMM		0x00020000	/* run in sequential memory mode */
+#define	OEX_PRECISEFP	0x00040000	/* run in precise FP exception mode */
+#define	OEX_DISMISS	0x00080000	/* dismiss invalid address traps */
+
+/*
+ * ODK_PAD info field masks.
+ */
+#define	OPAD_PREFIX	0x0001
+#define	OPAD_POSTFIX	0x0002
+#define	OPAD_SYMBOL	0x0004
+
+/*
+ * ODK_HWPATCH info field masks.
+ */
+#define	OHW_R4KEOP	0x00000001	/* patch for R4000 branch at end-of-page bug */
+#define	OHW_R8KPFETCH	0x00000002	/* R8000 prefetch bug may occur */
+#define	OHW_R5KEOP	0x00000004	/* patch for R5000 branch at end-of-page bug */
+#define	OHW_R5KCVTL	0x00000008	/* R5000 cvt.[ds].l bug: clean == 1 */
+#define	OHW_R10KLDL	0x00000010UL	/* need patch for R10000 misaligned load */
+
+/*
+ * ODK_HWAND/ODK_HWOR info field and hwp_flags[12] masks.
+ */
+#define	OHWA0_R4KEOP_CHECKED	0x00000001	/* object checked for R4000 end-of-page bug */
+#define	OHWA0_R4KEOP_CLEAN	0x00000002	/* object verified clean for R4000 end-of-page bug */
+#define	OHWO0_FIXADE		0x00000001	/* object requires call to fixade */
+
+/*
+ * ODK_IDENT/ODK_GP_GROUP info field masks.
+ */
+#define	OGP_GROUP	0x0000ffff	/* GP group number */
+#define	OGP_SELF	0x00010000	/* GP group is self-contained */
+
+/*
+ * The header for GNU-style hash sections.
+ */
+
+#ifndef LOCORE
+typedef struct {
+	uint32_t	gh_nbuckets;	/* Number of hash buckets. */
+	uint32_t	gh_symndx;	/* First visible symbol in .dynsym. */
+	uint32_t	gh_maskwords;	/* #maskwords used in bloom filter. */
+	uint32_t	gh_shift2;	/* Bloom filter shift count. */
+} Elf_GNU_Hash_Header;
+#endif
+
+/* Indexes into the e_ident array.  Keep synced with
+   http://www.sco.com/developers/gabi/latest/ch4.eheader.html */
+#define	EI_MAG0		0	/* Magic number, byte 0. */
+#define	EI_MAG1		1	/* Magic number, byte 1. */
+#define	EI_MAG2		2	/* Magic number, byte 2. */
+#define	EI_MAG3		3	/* Magic number, byte 3. */
+#define	EI_CLASS	4	/* Class of machine. */
+#define	EI_DATA		5	/* Data format. */
+#define	EI_VERSION	6	/* ELF format version. */
+#define	EI_OSABI	7	/* Operating system / ABI identification */
+#define	EI_ABIVERSION	8	/* ABI version */
+#define	OLD_EI_BRAND	8	/* Start of architecture identification. */
+#define	EI_PAD		9	/* Start of padding (per SVR4 ABI). */
+#define	EI_NIDENT	16	/* Size of e_ident array. */
+
+/* Values for the magic number bytes. */
+#define	ELFMAG0		0x7f
+#define	ELFMAG1		'E'
+#define	ELFMAG2		'L'
+#define	ELFMAG3		'F'
+#define	ELFMAG		"\177ELF"	/* magic string */
+#define	SELFMAG		4		/* magic string size */
+
+/* Values for e_ident[EI_VERSION] and e_version. */
+#define	EV_NONE		0
+#define	EV_CURRENT	1
+
+/* Values for e_ident[EI_CLASS]. */
+#define	ELFCLASSNONE	0	/* Unknown class. */
+#define	ELFCLASS32	1	/* 32-bit architecture. */
+#define	ELFCLASS64	2	/* 64-bit architecture. */
+
+/* Values for e_ident[EI_DATA]. */
+#define	ELFDATANONE	0	/* Unknown data format. */
+#define	ELFDATA2LSB	1	/* 2's complement little-endian. */
+#define	ELFDATA2MSB	2	/* 2's complement big-endian. */
+
+/* Values for e_ident[EI_OSABI]. */
+#define	ELFOSABI_NONE		0	/* UNIX System V ABI */
+#define	ELFOSABI_HPUX		1	/* HP-UX operating system */
+#define	ELFOSABI_NETBSD		2	/* NetBSD */
+#define	ELFOSABI_LINUX		3	/* GNU/Linux */
+#define	ELFOSABI_HURD		4	/* GNU/Hurd */
+#define	ELFOSABI_86OPEN		5	/* 86Open common IA32 ABI */
+#define	ELFOSABI_SOLARIS	6	/* Solaris */
+#define	ELFOSABI_AIX		7	/* AIX */
+#define	ELFOSABI_IRIX		8	/* IRIX */
+#define	ELFOSABI_FREEBSD	9	/* FreeBSD */
+#define	ELFOSABI_TRU64		10	/* TRU64 UNIX */
+#define	ELFOSABI_MODESTO	11	/* Novell Modesto */
+#define	ELFOSABI_OPENBSD	12	/* OpenBSD */
+#define	ELFOSABI_OPENVMS	13	/* Open VMS */
+#define	ELFOSABI_NSK		14	/* HP Non-Stop Kernel */
+#define	ELFOSABI_AROS		15	/* Amiga Research OS */
+#define	ELFOSABI_FENIXOS	16	/* FenixOS */
+#define	ELFOSABI_CLOUDABI	17	/* Nuxi CloudABI */
+#define	ELFOSABI_OPENVOS	18	/* Stratus Technologies OpenVOS */
+#define	ELFOSABI_ARM_AEABI	64	/* ARM EABI */
+#define	ELFOSABI_ARM		97	/* ARM */
+#define	ELFOSABI_STANDALONE	255	/* Standalone (embedded) application */
+
+#define	ELFOSABI_SYSV		ELFOSABI_NONE	/* symbol used in old spec */
+#define	ELFOSABI_MONTEREY	ELFOSABI_AIX	/* Monterey */
+#define	ELFOSABI_GNU		ELFOSABI_LINUX
+
+/* e_ident */
+#define	IS_ELF(ehdr)	((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
+			 (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
+			 (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
+			 (ehdr).e_ident[EI_MAG3] == ELFMAG3)
+
+/* Values for e_type. */
+#define	ET_NONE		0	/* Unknown type. */
+#define	ET_REL		1	/* Relocatable. */
+#define	ET_EXEC		2	/* Executable. */
+#define	ET_DYN		3	/* Shared object. */
+#define	ET_CORE		4	/* Core file. */
+#define	ET_LOOS		0xfe00	/* First operating system specific. */
+#define	ET_HIOS		0xfeff	/* Last operating system-specific. */
+#define	ET_LOPROC	0xff00	/* First processor-specific. */
+#define	ET_HIPROC	0xffff	/* Last processor-specific. */
+
+/* Values for e_machine. */
+#define	EM_NONE		0	/* Unknown machine. */
+#define	EM_M32		1	/* AT&T WE32100. */
+#define	EM_SPARC	2	/* Sun SPARC. */
+#define	EM_386		3	/* Intel i386. */
+#define	EM_68K		4	/* Motorola 68000. */
+#define	EM_88K		5	/* Motorola 88000. */
+#define	EM_IAMCU	6	/* Intel MCU. */
+#define	EM_860		7	/* Intel i860. */
+#define	EM_MIPS		8	/* MIPS R3000 Big-Endian only. */
+#define	EM_S370		9	/* IBM System/370. */
+#define	EM_MIPS_RS3_LE	10	/* MIPS R3000 Little-Endian. */
+#define	EM_PARISC	15	/* HP PA-RISC. */
+#define	EM_VPP500	17	/* Fujitsu VPP500. */
+#define	EM_SPARC32PLUS	18	/* SPARC v8plus. */
+#define	EM_960		19	/* Intel 80960. */
+#define	EM_PPC		20	/* PowerPC 32-bit. */
+#define	EM_PPC64	21	/* PowerPC 64-bit. */
+#define	EM_S390		22	/* IBM System/390. */
+#define	EM_V800		36	/* NEC V800. */
+#define	EM_FR20		37	/* Fujitsu FR20. */
+#define	EM_RH32		38	/* TRW RH-32. */
+#define	EM_RCE		39	/* Motorola RCE. */
+#define	EM_ARM		40	/* ARM. */
+#define	EM_SH		42	/* Hitachi SH. */
+#define	EM_SPARCV9	43	/* SPARC v9 64-bit. */
+#define	EM_TRICORE	44	/* Siemens TriCore embedded processor. */
+#define	EM_ARC		45	/* Argonaut RISC Core. */
+#define	EM_H8_300	46	/* Hitachi H8/300. */
+#define	EM_H8_300H	47	/* Hitachi H8/300H. */
+#define	EM_H8S		48	/* Hitachi H8S. */
+#define	EM_H8_500	49	/* Hitachi H8/500. */
+#define	EM_IA_64	50	/* Intel IA-64 Processor. */
+#define	EM_MIPS_X	51	/* Stanford MIPS-X. */
+#define	EM_COLDFIRE	52	/* Motorola ColdFire. */
+#define	EM_68HC12	53	/* Motorola M68HC12. */
+#define	EM_MMA		54	/* Fujitsu MMA. */
+#define	EM_PCP		55	/* Siemens PCP. */
+#define	EM_NCPU		56	/* Sony nCPU. */
+#define	EM_NDR1		57	/* Denso NDR1 microprocessor. */
+#define	EM_STARCORE	58	/* Motorola Star*Core processor. */
+#define	EM_ME16		59	/* Toyota ME16 processor. */
+#define	EM_ST100	60	/* STMicroelectronics ST100 processor. */
+#define	EM_TINYJ	61	/* Advanced Logic Corp. TinyJ processor. */
+#define	EM_X86_64	62	/* Advanced Micro Devices x86-64 */
+#define	EM_AMD64	EM_X86_64	/* Advanced Micro Devices x86-64 (compat) */
+#define	EM_PDSP		63	/* Sony DSP Processor. */
+#define	EM_FX66		66	/* Siemens FX66 microcontroller. */
+#define	EM_ST9PLUS	67	/* STMicroelectronics ST9+ 8/16
+				   microcontroller. */
+#define	EM_ST7		68	/* STmicroelectronics ST7 8-bit
+				   microcontroller. */
+#define	EM_68HC16	69	/* Motorola MC68HC16 microcontroller. */
+#define	EM_68HC11	70	/* Motorola MC68HC11 microcontroller. */
+#define	EM_68HC08	71	/* Motorola MC68HC08 microcontroller. */
+#define	EM_68HC05	72	/* Motorola MC68HC05 microcontroller. */
+#define	EM_SVX		73	/* Silicon Graphics SVx. */
+#define	EM_ST19		74	/* STMicroelectronics ST19 8-bit mc. */
+#define	EM_VAX		75	/* Digital VAX. */
+#define	EM_CRIS		76	/* Axis Communications 32-bit embedded
+				   processor. */
+#define	EM_JAVELIN	77	/* Infineon Technologies 32-bit embedded
+				   processor. */
+#define	EM_FIREPATH	78	/* Element 14 64-bit DSP Processor. */
+#define	EM_ZSP		79	/* LSI Logic 16-bit DSP Processor. */
+#define	EM_MMIX		80	/* Donald Knuth's educational 64-bit proc. */
+#define	EM_HUANY	81	/* Harvard University machine-independent
+				   object files. */
+#define	EM_PRISM	82	/* SiTera Prism. */
+#define	EM_AVR		83	/* Atmel AVR 8-bit microcontroller. */
+#define	EM_FR30		84	/* Fujitsu FR30. */
+#define	EM_D10V		85	/* Mitsubishi D10V. */
+#define	EM_D30V		86	/* Mitsubishi D30V. */
+#define	EM_V850		87	/* NEC v850. */
+#define	EM_M32R		88	/* Mitsubishi M32R. */
+#define	EM_MN10300	89	/* Matsushita MN10300. */
+#define	EM_MN10200	90	/* Matsushita MN10200. */
+#define	EM_PJ		91	/* picoJava. */
+#define	EM_OPENRISC	92	/* OpenRISC 32-bit embedded processor. */
+#define	EM_ARC_A5	93	/* ARC Cores Tangent-A5. */
+#define	EM_XTENSA	94	/* Tensilica Xtensa Architecture. */
+#define	EM_VIDEOCORE	95	/* Alphamosaic VideoCore processor. */
+#define	EM_TMM_GPP	96	/* Thompson Multimedia General Purpose
+				   Processor. */
+#define	EM_NS32K	97	/* National Semiconductor 32000 series. */
+#define	EM_TPC		98	/* Tenor Network TPC processor. */
+#define	EM_SNP1K	99	/* Trebia SNP 1000 processor. */
+#define	EM_ST200	100	/* STMicroelectronics ST200 microcontroller. */
+#define	EM_IP2K		101	/* Ubicom IP2xxx microcontroller family. */
+#define	EM_MAX		102	/* MAX Processor. */
+#define	EM_CR		103	/* National Semiconductor CompactRISC
+				   microprocessor. */
+#define	EM_F2MC16	104	/* Fujitsu F2MC16. */
+#define	EM_MSP430	105	/* Texas Instruments embedded microcontroller
+				   msp430. */
+#define	EM_BLACKFIN	106	/* Analog Devices Blackfin (DSP) processor. */
+#define	EM_SE_C33	107	/* S1C33 Family of Seiko Epson processors. */
+#define	EM_SEP		108	/* Sharp embedded microprocessor. */
+#define	EM_ARCA		109	/* Arca RISC Microprocessor. */
+#define	EM_UNICORE	110	/* Microprocessor series from PKU-Unity Ltd.
+				   and MPRC of Peking University */
+#define	EM_AARCH64	183	/* AArch64 (64-bit ARM) */
+#define	EM_RISCV	243	/* RISC-V */
+
+/* Non-standard or deprecated. */
+#define	EM_486		6	/* Intel i486. */
+#define	EM_MIPS_RS4_BE	10	/* MIPS R4000 Big-Endian */
+#define	EM_ALPHA_STD	41	/* Digital Alpha (standard value). */
+#define	EM_ALPHA	0x9026	/* Alpha (written in the absence of an ABI) */
+
+/**
+ * e_flags
+ */
+#define	EF_ARM_RELEXEC	0x1
+#define	EF_ARM_HASENTRY	0x2
+#define	EF_ARM_SYMSARESORTED	0x4
+#define	EF_ARM_DYNSYMSUSESEGIDX	0x8
+#define	EF_ARM_MAPSYMSFIRST	0x10
+#define	EF_ARM_LE8		0x00400000
+#define	EF_ARM_BE8		0x00800000
+#define	EF_ARM_EABIMASK		0xFF000000
+#define	EF_ARM_EABI_UNKNOWN	0x00000000
+#define	EF_ARM_EABI_VER1	0x01000000
+#define	EF_ARM_EABI_VER2	0x02000000
+#define	EF_ARM_EABI_VER3	0x03000000
+#define	EF_ARM_EABI_VER4	0x04000000
+#define	EF_ARM_EABI_VER5	0x05000000
+#define	EF_ARM_INTERWORK	0x00000004
+#define	EF_ARM_APCS_26		0x00000008
+#define	EF_ARM_APCS_FLOAT	0x00000010
+#define	EF_ARM_PIC		0x00000020
+#define	EF_ARM_ALIGN8		0x00000040
+#define	EF_ARM_NEW_ABI		0x00000080
+#define	EF_ARM_OLD_ABI		0x00000100
+#define	EF_ARM_ABI_FLOAT_SOFT	0x00000200
+#define	EF_ARM_SOFT_FLOAT	EF_ARM_ABI_FLOAT_SOFT /* Pre-V5 ABI name */
+#define	EF_ARM_ABI_FLOAT_HARD	0x00000400
+#define	EF_ARM_VFP_FLOAT	EF_ARM_ABI_FLOAT_HARD /* Pre-V5 ABI name */
+#define	EF_ARM_MAVERICK_FLOAT	0x00000800
+
+#define	EF_MIPS_NOREORDER	0x00000001
+#define	EF_MIPS_PIC		0x00000002	/* Contains PIC code */
+#define	EF_MIPS_CPIC		0x00000004	/* STD PIC calling sequence */
+#define	EF_MIPS_UCODE		0x00000010
+#define	EF_MIPS_ABI2		0x00000020	/* N32 */
+#define	EF_MIPS_OPTIONS_FIRST	0x00000080
+#define	EF_MIPS_ABI		0x0000F000
+#define	EF_MIPS_ABI_O32		0x00001000
+#define	EF_MIPS_ABI_O64		0x00002000
+#define	EF_MIPS_ABI_EABI32	0x00003000
+#define	EF_MIPS_ABI_EABI64	0x00004000
+#define	EF_MIPS_ARCH_ASE	0x0F000000	/* Architectural extensions */
+#define	EF_MIPS_ARCH_ASE_MDMX	0x08000000	/* MDMX multimedia extension */
+#define	EF_MIPS_ARCH_ASE_M16	0x04000000	/* MIPS-16 ISA extensions */
+#define	EF_MIPS_ARCH		0xF0000000	/* Architecture field */
+#define	EF_MIPS_ARCH_1		0x00000000	/* -mips1 code */
+#define	EF_MIPS_ARCH_2		0x10000000	/* -mips2 code */
+#define	EF_MIPS_ARCH_3		0x20000000	/* -mips3 code */
+#define	EF_MIPS_ARCH_4		0x30000000	/* -mips4 code */
+#define	EF_MIPS_ARCH_5		0x40000000	/* -mips5 code */
+#define	EF_MIPS_ARCH_32		0x50000000	/* -mips32 code */
+#define	EF_MIPS_ARCH_64		0x60000000	/* -mips64 code */
+#define	EF_MIPS_ARCH_32R2	0x70000000	/* -mips32r2 code */
+#define	EF_MIPS_ARCH_64R2	0x80000000	/* -mips64r2 code */
+
+#define	EF_PPC_EMB		0x80000000
+#define	EF_PPC_RELOCATABLE	0x00010000
+#define	EF_PPC_RELOCATABLE_LIB	0x00008000
+
+#define	EF_RISCV_RVC		0x00000001
+#define	EF_RISCV_FLOAT_ABI_MASK	0x00000006
+#define	EF_RISCV_FLOAT_ABI_SOFT	0x00000000
+#define	EF_RISCV_FLOAT_ABI_SINGLE 0x000002
+#define	EF_RISCV_FLOAT_ABI_DOUBLE 0x000004
+#define	EF_RISCV_FLOAT_ABI_QUAD	0x00000006
+#define	EF_RISCV_RVE		0x00000008
+#define	EF_RISCV_TSO		0x00000010
+
+#define	EF_SPARC_EXT_MASK	0x00ffff00
+#define	EF_SPARC_32PLUS		0x00000100
+#define	EF_SPARC_SUN_US1	0x00000200
+#define	EF_SPARC_HAL_R1		0x00000200
+#define	EF_SPARC_SUN_US3	0x00000800
+
+#define	EF_SPARCV9_MM		0x00000003
+#define	EF_SPARCV9_TSO		0x00000000
+#define	EF_SPARCV9_PSO		0x00000001
+#define	EF_SPARCV9_RMO		0x00000002
+
+/* Special section indexes. */
+#define	SHN_UNDEF	     0		/* Undefined, missing, irrelevant. */
+#define	SHN_LORESERVE	0xff00		/* First of reserved range. */
+#define	SHN_LOPROC	0xff00		/* First processor-specific. */
+#define	SHN_HIPROC	0xff1f		/* Last processor-specific. */
+#define	SHN_LOOS	0xff20		/* First operating system-specific. */
+#define	SHN_FBSD_CACHED	SHN_LOOS	/* Transient, for sys/kern/link_elf_obj
+					   linker only: Cached global in local
+					   symtab. */
+#define	SHN_HIOS	0xff3f		/* Last operating system-specific. */
+#define	SHN_ABS		0xfff1		/* Absolute values. */
+#define	SHN_COMMON	0xfff2		/* Common data. */
+#define	SHN_XINDEX	0xffff		/* Escape -- index stored elsewhere. */
+#define	SHN_HIRESERVE	0xffff		/* Last of reserved range. */
+
+/* sh_type */
+#define	SHT_NULL		0	/* inactive */
+#define	SHT_PROGBITS		1	/* program defined information */
+#define	SHT_SYMTAB		2	/* symbol table section */
+#define	SHT_STRTAB		3	/* string table section */
+#define	SHT_RELA		4	/* relocation section with addends */
+#define	SHT_HASH		5	/* symbol hash table section */
+#define	SHT_DYNAMIC		6	/* dynamic section */
+#define	SHT_NOTE		7	/* note section */
+#define	SHT_NOBITS		8	/* no space section */
+#define	SHT_REL			9	/* relocation section - no addends */
+#define	SHT_SHLIB		10	/* reserved - purpose unknown */
+#define	SHT_DYNSYM		11	/* dynamic symbol table section */
+#define	SHT_INIT_ARRAY		14	/* Initialization function pointers. */
+#define	SHT_FINI_ARRAY		15	/* Termination function pointers. */
+#define	SHT_PREINIT_ARRAY	16	/* Pre-initialization function ptrs. */
+#define	SHT_GROUP		17	/* Section group. */
+#define	SHT_SYMTAB_SHNDX	18	/* Section indexes (see SHN_XINDEX). */
+#define	SHT_LOOS		0x60000000	/* First of OS specific semantics */
+#define	SHT_LOSUNW		0x6ffffff4
+#define	SHT_SUNW_dof		0x6ffffff4
+#define	SHT_SUNW_cap		0x6ffffff5
+#define	SHT_GNU_ATTRIBUTES	0x6ffffff5
+#define	SHT_SUNW_SIGNATURE	0x6ffffff6
+#define	SHT_GNU_HASH		0x6ffffff6
+#define	SHT_GNU_LIBLIST		0x6ffffff7
+#define	SHT_SUNW_ANNOTATE	0x6ffffff7
+#define	SHT_SUNW_DEBUGSTR	0x6ffffff8
+#define	SHT_SUNW_DEBUG		0x6ffffff9
+#define	SHT_SUNW_move		0x6ffffffa
+#define	SHT_SUNW_COMDAT		0x6ffffffb
+#define	SHT_SUNW_syminfo	0x6ffffffc
+#define	SHT_SUNW_verdef		0x6ffffffd
+#define	SHT_GNU_verdef		0x6ffffffd	/* Symbol versions provided */
+#define	SHT_SUNW_verneed	0x6ffffffe
+#define	SHT_GNU_verneed		0x6ffffffe	/* Symbol versions required */
+#define	SHT_SUNW_versym		0x6fffffff
+#define	SHT_GNU_versym		0x6fffffff	/* Symbol version table */
+#define	SHT_HISUNW		0x6fffffff
+#define	SHT_HIOS		0x6fffffff	/* Last of OS specific semantics */
+#define	SHT_LOPROC		0x70000000	/* reserved range for processor */
+#define	SHT_X86_64_UNWIND	0x70000001	/* unwind information */
+#define	SHT_AMD64_UNWIND	SHT_X86_64_UNWIND 
+
+#define	SHT_ARM_EXIDX		0x70000001	/* Exception index table. */
+#define	SHT_ARM_PREEMPTMAP	0x70000002	/* BPABI DLL dynamic linking 
+						   pre-emption map. */
+#define	SHT_ARM_ATTRIBUTES	0x70000003	/* Object file compatibility 
+						   attributes. */
+#define	SHT_ARM_DEBUGOVERLAY	0x70000004	/* See DBGOVL for details. */
+#define	SHT_ARM_OVERLAYSECTION	0x70000005	/* See DBGOVL for details. */
+#define	SHT_MIPS_LIBLIST	0x70000000
+#define	SHT_MIPS_MSYM		0x70000001
+#define	SHT_MIPS_CONFLICT	0x70000002
+#define	SHT_MIPS_GPTAB		0x70000003
+#define	SHT_MIPS_UCODE		0x70000004
+#define	SHT_MIPS_DEBUG		0x70000005
+#define	SHT_MIPS_REGINFO	0x70000006
+#define	SHT_MIPS_PACKAGE	0x70000007
+#define	SHT_MIPS_PACKSYM	0x70000008
+#define	SHT_MIPS_RELD		0x70000009
+#define	SHT_MIPS_IFACE		0x7000000b
+#define	SHT_MIPS_CONTENT	0x7000000c
+#define	SHT_MIPS_OPTIONS	0x7000000d
+#define	SHT_MIPS_DELTASYM	0x7000001b
+#define	SHT_MIPS_DELTAINST	0x7000001c
+#define	SHT_MIPS_DELTACLASS	0x7000001d
+#define	SHT_MIPS_DWARF		0x7000001e	/* MIPS gcc uses MIPS_DWARF */
+#define	SHT_MIPS_DELTADECL	0x7000001f
+#define	SHT_MIPS_SYMBOL_LIB	0x70000020
+#define	SHT_MIPS_EVENTS		0x70000021
+#define	SHT_MIPS_TRANSLATE	0x70000022
+#define	SHT_MIPS_PIXIE		0x70000023
+#define	SHT_MIPS_XLATE		0x70000024
+#define	SHT_MIPS_XLATE_DEBUG	0x70000025
+#define	SHT_MIPS_WHIRL		0x70000026
+#define	SHT_MIPS_EH_REGION	0x70000027
+#define	SHT_MIPS_XLATE_OLD	0x70000028
+#define	SHT_MIPS_PDR_EXCEPTION	0x70000029
+#define	SHT_MIPS_ABIFLAGS	0x7000002a
+
+#define	SHT_SPARC_GOTDATA	0x70000000
+
+#define	SHTORDERED
+#define	SHT_HIPROC		0x7fffffff	/* specific section header types */
+#define	SHT_LOUSER		0x80000000	/* reserved range for application */
+#define	SHT_HIUSER		0xffffffff	/* specific indexes */
+
+/* Flags for sh_flags. */
+#define	SHF_WRITE		0x1	/* Section contains writable data. */
+#define	SHF_ALLOC		0x2	/* Section occupies memory. */
+#define	SHF_EXECINSTR		0x4	/* Section contains instructions. */
+#define	SHF_MERGE		0x10	/* Section may be merged. */
+#define	SHF_STRINGS		0x20	/* Section contains strings. */
+#define	SHF_INFO_LINK		0x40	/* sh_info holds section index. */
+#define	SHF_LINK_ORDER		0x80	/* Special ordering requirements. */
+#define	SHF_OS_NONCONFORMING	0x100	/* OS-specific processing required. */
+#define	SHF_GROUP		0x200	/* Member of section group. */
+#define	SHF_TLS			0x400	/* Section contains TLS data. */
+#define	SHF_COMPRESSED		0x800	/* Section contains compressed data. */
+#define	SHF_MASKOS	0x0ff00000	/* OS-specific semantics. */
+#define	SHF_MASKPROC	0xf0000000	/* Processor-specific semantics. */
+
+/* Flags for section groups. */
+#define	GRP_COMDAT	0x1	/* COMDAT semantics. */
+
+/*
+ * Flags / mask for .gnu.versym sections.
+ */
+#define	VERSYM_VERSION	0x7fff
+#define	VERSYM_HIDDEN	0x8000
+
+/* Values for p_type. */
+#define	PT_NULL		0	/* Unused entry. */
+#define	PT_LOAD		1	/* Loadable segment. */
+#define	PT_DYNAMIC	2	/* Dynamic linking information segment. */
+#define	PT_INTERP	3	/* Pathname of interpreter. */
+#define	PT_NOTE		4	/* Auxiliary information. */
+#define	PT_SHLIB	5	/* Reserved (not used). */
+#define	PT_PHDR		6	/* Location of program header itself. */
+#define	PT_TLS		7	/* Thread local storage segment */
+#define	PT_LOOS		0x60000000	/* First OS-specific. */
+#define	PT_SUNW_UNWIND	0x6464e550	/* amd64 UNWIND program header */
+#define	PT_GNU_EH_FRAME	0x6474e550
+#define	PT_GNU_STACK	0x6474e551
+#define	PT_GNU_RELRO	0x6474e552
+#define	PT_DUMP_DELTA	0x6fb5d000	/* va->pa map for kernel dumps
+					   (currently arm). */
+#define	PT_LOSUNW	0x6ffffffa
+#define	PT_SUNWBSS	0x6ffffffa	/* Sun Specific segment */
+#define	PT_SUNWSTACK	0x6ffffffb	/* describes the stack segment */
+#define	PT_SUNWDTRACE	0x6ffffffc	/* private */
+#define	PT_SUNWCAP	0x6ffffffd	/* hard/soft capabilities segment */
+#define	PT_HISUNW	0x6fffffff
+#define	PT_HIOS		0x6fffffff	/* Last OS-specific. */
+#define	PT_LOPROC	0x70000000	/* First processor-specific type. */
+#define	PT_ARM_ARCHEXT	0x70000000	/* ARM arch compat information. */
+#define	PT_ARM_EXIDX	0x70000001	/* ARM exception unwind tables. */
+#define	PT_MIPS_REGINFO		0x70000000	/* MIPS register usage info */
+#define	PT_MIPS_RTPROC		0x70000001	/* MIPS runtime procedure tbl */
+#define	PT_MIPS_OPTIONS		0x70000002	/* MIPS e_flags value*/
+#define	PT_MIPS_ABIFLAGS	0x70000003	/* MIPS fp mode */
+#define	PT_HIPROC	0x7fffffff	/* Last processor-specific type. */
+
+#define	PT_OPENBSD_RANDOMIZE	0x65A3DBE6	/* OpenBSD random data segment */
+#define	PT_OPENBSD_WXNEEDED	0x65A3DBE7	/* OpenBSD EXEC/WRITE pages needed */
+#define	PT_OPENBSD_BOOTDATA	0x65A41BE6	/* OpenBSD section for boot args */
+
+/* Values for p_flags. */
+#define	PF_X		0x1		/* Executable. */
+#define	PF_W		0x2		/* Writable. */
+#define	PF_R		0x4		/* Readable. */
+#define	PF_MASKOS	0x0ff00000	/* Operating system-specific. */
+#define	PF_MASKPROC	0xf0000000	/* Processor-specific. */
+
+/* Extended program header index. */
+#define	PN_XNUM		0xffff
+
+/* Values for d_tag. */
+#define	DT_NULL		0	/* Terminating entry. */
+#define	DT_NEEDED	1	/* String table offset of a needed shared
+				   library. */
+#define	DT_PLTRELSZ	2	/* Total size in bytes of PLT relocations. */
+#define	DT_PLTGOT	3	/* Processor-dependent address. */
+#define	DT_HASH		4	/* Address of symbol hash table. */
+#define	DT_STRTAB	5	/* Address of string table. */
+#define	DT_SYMTAB	6	/* Address of symbol table. */
+#define	DT_RELA		7	/* Address of ElfNN_Rela relocations. */
+#define	DT_RELASZ	8	/* Total size of ElfNN_Rela relocations. */
+#define	DT_RELAENT	9	/* Size of each ElfNN_Rela relocation entry. */
+#define	DT_STRSZ	10	/* Size of string table. */
+#define	DT_SYMENT	11	/* Size of each symbol table entry. */
+#define	DT_INIT		12	/* Address of initialization function. */
+#define	DT_FINI		13	/* Address of finalization function. */
+#define	DT_SONAME	14	/* String table offset of shared object
+				   name. */
+#define	DT_RPATH	15	/* String table offset of library path. [sup] */
+#define	DT_SYMBOLIC	16	/* Indicates "symbolic" linking. [sup] */
+#define	DT_REL		17	/* Address of ElfNN_Rel relocations. */
+#define	DT_RELSZ	18	/* Total size of ElfNN_Rel relocations. */
+#define	DT_RELENT	19	/* Size of each ElfNN_Rel relocation. */
+#define	DT_PLTREL	20	/* Type of relocation used for PLT. */
+#define	DT_DEBUG	21	/* Reserved (not used). */
+#define	DT_TEXTREL	22	/* Indicates there may be relocations in
+				   non-writable segments. [sup] */
+#define	DT_JMPREL	23	/* Address of PLT relocations. */
+#define	DT_BIND_NOW	24	/* [sup] */
+#define	DT_INIT_ARRAY	25	/* Address of the array of pointers to
+				   initialization functions */
+#define	DT_FINI_ARRAY	26	/* Address of the array of pointers to
+				   termination functions */
+#define	DT_INIT_ARRAYSZ	27	/* Size in bytes of the array of
+				   initialization functions. */
+#define	DT_FINI_ARRAYSZ	28	/* Size in bytes of the array of
+				   termination functions. */
+#define	DT_RUNPATH	29	/* String table offset of a null-terminated
+				   library search path string. */
+#define	DT_FLAGS	30	/* Object specific flag values. */
+#define	DT_ENCODING	32	/* Values greater than or equal to DT_ENCODING
+				   and less than DT_LOOS follow the rules for
+				   the interpretation of the d_un union
+				   as follows: even == 'd_ptr', odd == 'd_val'
+				   or none */
+#define	DT_PREINIT_ARRAY 32	/* Address of the array of pointers to
+				   pre-initialization functions. */
+#define	DT_PREINIT_ARRAYSZ 33	/* Size in bytes of the array of
+				   pre-initialization functions. */
+#define	DT_MAXPOSTAGS	34	/* number of positive tags */
+#define	DT_LOOS		0x6000000d	/* First OS-specific */
+#define	DT_SUNW_AUXILIARY	0x6000000d	/* symbol auxiliary name */
+#define	DT_SUNW_RTLDINF		0x6000000e	/* ld.so.1 info (private) */
+#define	DT_SUNW_FILTER		0x6000000f	/* symbol filter name */
+#define	DT_SUNW_CAP		0x60000010	/* hardware/software */
+#define	DT_SUNW_ASLR		0x60000023	/* ASLR control */
+#define	DT_HIOS		0x6ffff000	/* Last OS-specific */
+
+/*
+ * DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
+ * Dyn.d_un.d_val field of the Elf*_Dyn structure.
+ */
+#define	DT_VALRNGLO	0x6ffffd00
+#define	DT_GNU_PRELINKED	0x6ffffdf5 /* prelinking timestamp */
+#define	DT_GNU_CONFLICTSZ	0x6ffffdf6 /* size of conflict section */
+#define	DT_GNU_LIBLISTSZ	0x6ffffdf7 /* size of library list */
+#define	DT_CHECKSUM	0x6ffffdf8	/* elf checksum */
+#define	DT_PLTPADSZ	0x6ffffdf9	/* pltpadding size */
+#define	DT_MOVEENT	0x6ffffdfa	/* move table entry size */
+#define	DT_MOVESZ	0x6ffffdfb	/* move table size */
+#define	DT_FEATURE	0x6ffffdfc	/* feature holder */
+#define	DT_FEATURE_1	DT_FEATURE
+#define	DT_POSFLAG_1	0x6ffffdfd	/* flags for DT_* entries, effecting */
+					/*	the following DT_* entry. */
+					/*	See DF_P1_* definitions */
+#define	DT_SYMINSZ	0x6ffffdfe	/* syminfo table size (in bytes) */
+#define	DT_SYMINENT	0x6ffffdff	/* syminfo entry size (in bytes) */
+#define	DT_VALRNGHI	0x6ffffdff
+
+/*
+ * DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+ * Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+ *
+ * If any adjustment is made to the ELF object after it has been
+ * built, these entries will need to be adjusted.
+ */
+#define	DT_ADDRRNGLO	0x6ffffe00
+#define	DT_GNU_HASH	0x6ffffef5	/* GNU-style hash table */
+#define	DT_TLSDESC_PLT	0x6ffffef6	/* loc. of PLT for tlsdesc resolver */
+#define	DT_TLSDESC_GOT	0x6ffffef7	/* loc. of GOT for tlsdesc resolver */
+#define	DT_GNU_CONFLICT	0x6ffffef8	/* address of conflict section */
+#define	DT_GNU_LIBLIST	0x6ffffef9	/* address of library list */
+#define	DT_CONFIG	0x6ffffefa	/* configuration information */
+#define	DT_DEPAUDIT	0x6ffffefb	/* dependency auditing */
+#define	DT_AUDIT	0x6ffffefc	/* object auditing */
+#define	DT_PLTPAD	0x6ffffefd	/* pltpadding (sparcv9) */
+#define	DT_MOVETAB	0x6ffffefe	/* move table */
+#define	DT_SYMINFO	0x6ffffeff	/* syminfo table */
+#define	DT_ADDRRNGHI	0x6ffffeff
+
+#define	DT_VERSYM	0x6ffffff0	/* Address of versym section. */
+#define	DT_RELACOUNT	0x6ffffff9	/* number of RELATIVE relocations */
+#define	DT_RELCOUNT	0x6ffffffa	/* number of RELATIVE relocations */
+#define	DT_FLAGS_1	0x6ffffffb	/* state flags - see DF_1_* defs */
+#define	DT_VERDEF	0x6ffffffc	/* Address of verdef section. */
+#define	DT_VERDEFNUM	0x6ffffffd	/* Number of elems in verdef section */
+#define	DT_VERNEED	0x6ffffffe	/* Address of verneed section. */
+#define	DT_VERNEEDNUM	0x6fffffff	/* Number of elems in verneed section */
+
+#define	DT_LOPROC	0x70000000	/* First processor-specific type. */
+
+#define	DT_ARM_SYMTABSZ			0x70000001
+#define	DT_ARM_PREEMPTMAP		0x70000002
+
+#define	DT_SPARC_REGISTER		0x70000001
+#define	DT_DEPRECATED_SPARC_REGISTER	0x7000001
+
+#define	DT_MIPS_RLD_VERSION		0x70000001
+#define	DT_MIPS_TIME_STAMP		0x70000002
+#define	DT_MIPS_ICHECKSUM		0x70000003
+#define	DT_MIPS_IVERSION		0x70000004
+#define	DT_MIPS_FLAGS			0x70000005
+#define	DT_MIPS_BASE_ADDRESS		0x70000006
+#define	DT_MIPS_CONFLICT		0x70000008
+#define	DT_MIPS_LIBLIST			0x70000009
+#define	DT_MIPS_LOCAL_GOTNO		0x7000000a
+#define	DT_MIPS_CONFLICTNO		0x7000000b
+#define	DT_MIPS_LIBLISTNO		0x70000010
+#define	DT_MIPS_SYMTABNO		0x70000011
+#define	DT_MIPS_UNREFEXTNO		0x70000012
+#define	DT_MIPS_GOTSYM			0x70000013
+#define	DT_MIPS_HIPAGENO		0x70000014
+#define	DT_MIPS_RLD_MAP			0x70000016
+#define	DT_MIPS_DELTA_CLASS		0x70000017
+#define	DT_MIPS_DELTA_CLASS_NO		0x70000018
+#define	DT_MIPS_DELTA_INSTANCE		0x70000019
+#define	DT_MIPS_DELTA_INSTANCE_NO	0x7000001A
+#define	DT_MIPS_DELTA_RELOC		0x7000001B
+#define	DT_MIPS_DELTA_RELOC_NO		0x7000001C
+#define	DT_MIPS_DELTA_SYM		0x7000001D
+#define	DT_MIPS_DELTA_SYM_NO		0x7000001E
+#define	DT_MIPS_DELTA_CLASSSYM		0x70000020
+#define	DT_MIPS_DELTA_CLASSSYM_NO	0x70000021
+#define	DT_MIPS_CXX_FLAGS		0x70000022
+#define	DT_MIPS_PIXIE_INIT		0x70000023
+#define	DT_MIPS_SYMBOL_LIB		0x70000024
+#define	DT_MIPS_LOCALPAGE_GOTIDX	0x70000025
+#define	DT_MIPS_LOCAL_GOTIDX		0x70000026
+#define	DT_MIPS_HIDDEN_GOTIDX		0x70000027
+#define	DT_MIPS_PROTECTED_GOTIDX	0x70000028
+#define	DT_MIPS_OPTIONS			0x70000029
+#define	DT_MIPS_INTERFACE		0x7000002A
+#define	DT_MIPS_DYNSTR_ALIGN		0x7000002B
+#define	DT_MIPS_INTERFACE_SIZE		0x7000002C
+#define	DT_MIPS_RLD_TEXT_RESOLVE_ADDR	0x7000002D
+#define	DT_MIPS_PERF_SUFFIX		0x7000002E
+#define	DT_MIPS_COMPACT_SIZE		0x7000002F
+#define	DT_MIPS_GP_VALUE		0x70000030
+#define	DT_MIPS_AUX_DYNAMIC		0x70000031
+#define	DT_MIPS_PLTGOT			0x70000032
+#define	DT_MIPS_RLD_OBJ_UPDATE		0x70000033
+#define	DT_MIPS_RWPLT			0x70000034
+#define	DT_MIPS_RLD_MAP_REL		0x70000035
+
+#define	DT_PPC_GOT			0x70000000
+#define	DT_PPC_TLSOPT			0x70000001
+
+#define	DT_PPC64_GLINK			0x70000000
+#define	DT_PPC64_OPD			0x70000001
+#define	DT_PPC64_OPDSZ			0x70000002
+#define	DT_PPC64_TLSOPT			0x70000003
+
+#define	DT_AUXILIARY	0x7ffffffd	/* shared library auxiliary name */
+#define	DT_USED		0x7ffffffe	/* ignored - same as needed */
+#define	DT_FILTER	0x7fffffff	/* shared library filter name */
+#define	DT_HIPROC	0x7fffffff	/* Last processor-specific type. */
+
+/* Values for DT_FLAGS */
+#define	DF_ORIGIN	0x0001	/* Indicates that the object being loaded may
+				   make reference to the $ORIGIN substitution
+				   string */
+#define	DF_SYMBOLIC	0x0002	/* Indicates "symbolic" linking. */
+#define	DF_TEXTREL	0x0004	/* Indicates there may be relocations in
+				   non-writable segments. */
+#define	DF_BIND_NOW	0x0008	/* Indicates that the dynamic linker should
+				   process all relocations for the object
+				   containing this entry before transferring
+				   control to the program. */
+#define	DF_STATIC_TLS	0x0010	/* Indicates that the shared object or
+				   executable contains code using a static
+				   thread-local storage scheme. */
+
+/* Values for DT_FLAGS_1 */
+#define	DF_1_BIND_NOW	0x00000001	/* Same as DF_BIND_NOW */
+#define	DF_1_GLOBAL	0x00000002	/* Set the RTLD_GLOBAL for object */
+#define	DF_1_NODELETE	0x00000008	/* Set the RTLD_NODELETE for object */
+#define	DF_1_LOADFLTR	0x00000010	/* Immediate loading of filtees */
+#define	DF_1_NOOPEN     0x00000040	/* Do not allow loading on dlopen() */
+#define	DF_1_ORIGIN	0x00000080	/* Process $ORIGIN */
+#define	DF_1_INTERPOSE	0x00000400	/* Interpose all objects but main */
+#define	DF_1_NODEFLIB	0x00000800	/* Do not search default paths */
+#define	DF_1_PIE	0x08000000	/* Is position-independent executable */
+
+/* Values for l_flags. */
+#define	LL_NONE			0x0	/* no flags */
+#define	LL_EXACT_MATCH		0x1	/* require an exact match */
+#define	LL_IGNORE_INT_VER	0x2	/* ignore version incompatibilities */
+#define	LL_REQUIRE_MINOR	0x4
+#define	LL_EXPORTS		0x8
+#define	LL_DELAY_LOAD		0x10
+#define	LL_DELTA		0x20
+
+/* Note section names */
+#define	ELF_NOTE_FREEBSD	"FreeBSD"
+#define	ELF_NOTE_NETBSD		"NetBSD"
+#define	ELF_NOTE_SOLARIS	"SUNW Solaris"
+#define	ELF_NOTE_GNU		"GNU"
+
+/* Values for n_type used in executables. */
+#define	NT_FREEBSD_ABI_TAG	1
+#define	NT_FREEBSD_NOINIT_TAG	2
+#define	NT_FREEBSD_ARCH_TAG	3
+#define	NT_FREEBSD_FEATURE_CTL	4
+
+/* NT_FREEBSD_FEATURE_CTL desc[0] bits */
+#define	NT_FREEBSD_FCTL_ASLR_DISABLE	0x00000001
+#define	NT_FREEBSD_FCTL_PROTMAX_DISABLE	0x00000002
+#define	NT_FREEBSD_FCTL_STKGAP_DISABLE	0x00000004
+#define	NT_FREEBSD_FCTL_WXNEEDED	0x00000008
+#define	NT_FREEBSD_FCTL_LA48		0x00000010
+#define	NT_FREEBSD_FCTL_ASG_DISABLE	0x00000020 /* ASLR STACK GAP Disable */
+
+/* Values for n_type.  Used in core files. */
+#define	NT_PRSTATUS	1	/* Process status. */
+#define	NT_FPREGSET	2	/* Floating point registers. */
+#define	NT_PRPSINFO	3	/* Process state info. */
+#define	NT_THRMISC	7	/* Thread miscellaneous info. */
+#define	NT_PROCSTAT_PROC	8	/* Procstat proc data. */
+#define	NT_PROCSTAT_FILES	9	/* Procstat files data. */
+#define	NT_PROCSTAT_VMMAP	10	/* Procstat vmmap data. */
+#define	NT_PROCSTAT_GROUPS	11	/* Procstat groups data. */
+#define	NT_PROCSTAT_UMASK	12	/* Procstat umask data. */
+#define	NT_PROCSTAT_RLIMIT	13	/* Procstat rlimit data. */
+#define	NT_PROCSTAT_OSREL	14	/* Procstat osreldate data. */
+#define	NT_PROCSTAT_PSSTRINGS	15	/* Procstat ps_strings data. */
+#define	NT_PROCSTAT_AUXV	16	/* Procstat auxv data. */
+#define	NT_PTLWPINFO		17	/* Thread ptrace miscellaneous info. */
+#define	NT_PPC_VMX	0x100	/* PowerPC Altivec/VMX registers */
+#define	NT_PPC_VSX	0x102	/* PowerPC VSX registers */
+#define	NT_X86_XSTATE	0x202	/* x86 XSAVE extended state. */
+#define	NT_ARM_VFP	0x400	/* ARM VFP registers */
+
+/* GNU note types. */
+#define	NT_GNU_ABI_TAG		1
+#define	NT_GNU_HWCAP		2
+#define	NT_GNU_BUILD_ID		3
+#define	NT_GNU_GOLD_VERSION	4
+#define	NT_GNU_PROPERTY_TYPE_0	5
+
+#define	GNU_PROPERTY_LOPROC			0xc0000000
+#define	GNU_PROPERTY_HIPROC			0xdfffffff
+
+#define	GNU_PROPERTY_X86_FEATURE_1_AND		0xc0000002
+
+#define	GNU_PROPERTY_X86_FEATURE_1_IBT		0x00000001
+#define	GNU_PROPERTY_X86_FEATURE_1_SHSTK	0x00000002
+
+/* Symbol Binding - ELFNN_ST_BIND - st_info */
+#define	STB_LOCAL	0	/* Local symbol */
+#define	STB_GLOBAL	1	/* Global symbol */
+#define	STB_WEAK	2	/* like global - lower precedence */
+#define	STB_LOOS	10	/* Start of operating system reserved range. */
+#define	STB_GNU_UNIQUE	10	/* Unique symbol (GNU) */
+#define	STB_HIOS	12	/* End of operating system reserved range. */
+#define	STB_LOPROC	13	/* reserved range for processor */
+#define	STB_HIPROC	15	/*   specific semantics. */
+
+/* Symbol type - ELFNN_ST_TYPE - st_info */
+#define	STT_NOTYPE	0	/* Unspecified type. */
+#define	STT_OBJECT	1	/* Data object. */
+#define	STT_FUNC	2	/* Function. */
+#define	STT_SECTION	3	/* Section. */
+#define	STT_FILE	4	/* Source file. */
+#define	STT_COMMON	5	/* Uninitialized common block. */
+#define	STT_TLS		6	/* TLS object. */
+#define	STT_NUM		7
+#define	STT_LOOS	10	/* Reserved range for operating system */
+#define	STT_GNU_IFUNC	10
+#define	STT_HIOS	12	/*   specific semantics. */
+#define	STT_LOPROC	13	/* Start of processor reserved range. */
+#define	STT_SPARC_REGISTER 13	/* SPARC register information. */
+#define	STT_HIPROC	15	/* End of processor reserved range. */
+
+/* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */
+#define	STV_DEFAULT	0x0	/* Default visibility (see binding). */
+#define	STV_INTERNAL	0x1	/* Special meaning in relocatable objects. */
+#define	STV_HIDDEN	0x2	/* Not visible. */
+#define	STV_PROTECTED	0x3	/* Visible but not preemptible. */
+#define	STV_EXPORTED	0x4
+#define	STV_SINGLETON	0x5
+#define	STV_ELIMINATE	0x6
+
+/* Special symbol table indexes. */
+#define	STN_UNDEF	0	/* Undefined symbol index. */
+
+/* Symbol versioning flags. */
+#define	VER_DEF_CURRENT	1
+#define	VER_DEF_IDX(x)	VER_NDX(x)
+
+#define	VER_FLG_BASE	0x01
+#define	VER_FLG_WEAK	0x02
+
+#define	VER_NEED_CURRENT	1
+#define	VER_NEED_WEAK	(1u << 15)
+#define	VER_NEED_HIDDEN	VER_NDX_HIDDEN
+#define	VER_NEED_IDX(x)	VER_NDX(x)
+
+#define	VER_NDX_LOCAL	0
+#define	VER_NDX_GLOBAL	1
+#define	VER_NDX_GIVEN	2
+
+#define	VER_NDX_HIDDEN	(1u << 15)
+#define	VER_NDX(x)	((x) & ~(1u << 15))
+
+#define	CA_SUNW_NULL	0
+#define	CA_SUNW_HW_1	1		/* first hardware capabilities entry */
+#define	CA_SUNW_SF_1	2		/* first software capabilities entry */
+
+/*
+ * Syminfo flag values
+ */
+#define	SYMINFO_FLG_DIRECT	0x0001	/* symbol ref has direct association */
+					/*	to object containing defn. */
+#define	SYMINFO_FLG_PASSTHRU	0x0002	/* ignored - see SYMINFO_FLG_FILTER */
+#define	SYMINFO_FLG_COPY	0x0004	/* symbol is a copy-reloc */
+#define	SYMINFO_FLG_LAZYLOAD	0x0008	/* object containing defn should be */
+					/*	lazily-loaded */
+#define	SYMINFO_FLG_DIRECTBIND	0x0010	/* ref should be bound directly to */
+					/*	object containing defn. */
+#define	SYMINFO_FLG_NOEXTDIRECT	0x0020	/* don't let an external reference */
+					/*	directly bind to this symbol */
+#define	SYMINFO_FLG_FILTER	0x0002	/* symbol ref is associated to a */
+#define	SYMINFO_FLG_AUXILIARY	0x0040	/* 	standard or auxiliary filter */
+
+/*
+ * Syminfo.si_boundto values.
+ */
+#define	SYMINFO_BT_SELF		0xffff	/* symbol bound to self */
+#define	SYMINFO_BT_PARENT	0xfffe	/* symbol bound to parent */
+#define	SYMINFO_BT_NONE		0xfffd	/* no special symbol binding */
+#define	SYMINFO_BT_EXTERN	0xfffc	/* symbol defined as external */
+#define	SYMINFO_BT_LOWRESERVE	0xff00	/* beginning of reserved entries */
+
+/*
+ * Syminfo version values.
+ */
+#define	SYMINFO_NONE		0	/* Syminfo version */
+#define	SYMINFO_CURRENT		1
+#define	SYMINFO_NUM		2
+
+/* Values for ch_type (compressed section headers). */
+#define	ELFCOMPRESS_ZLIB	1	/* ZLIB/DEFLATE */
+#define	ELFCOMPRESS_LOOS	0x60000000	/* OS-specific */
+#define	ELFCOMPRESS_HIOS	0x6fffffff
+#define	ELFCOMPRESS_LOPROC	0x70000000	/* Processor-specific */
+#define	ELFCOMPRESS_HIPROC	0x7fffffff
+
+/* Values for a_type. */
+#define	AT_NULL		0	/* Terminates the vector. */
+#define	AT_IGNORE	1	/* Ignored entry. */
+#define	AT_EXECFD	2	/* File descriptor of program to load. */
+#define	AT_PHDR		3	/* Program header of program already loaded. */
+#define	AT_PHENT	4	/* Size of each program header entry. */
+#define	AT_PHNUM	5	/* Number of program header entries. */
+#define	AT_PAGESZ	6	/* Page size in bytes. */
+#define	AT_BASE		7	/* Interpreter's base address. */
+#define	AT_FLAGS	8	/* Flags. */
+#define	AT_ENTRY	9	/* Where interpreter should transfer control. */
+#define	AT_NOTELF	10	/* Program is not ELF ?? */
+#define	AT_UID		11	/* Real uid. */
+#define	AT_EUID		12	/* Effective uid. */
+#define	AT_GID		13	/* Real gid. */
+#define	AT_EGID		14	/* Effective gid. */
+#define	AT_EXECPATH	15	/* Path to the executable. */
+#define	AT_CANARY	16	/* Canary for SSP. */
+#define	AT_CANARYLEN	17	/* Length of the canary. */
+#define	AT_OSRELDATE	18	/* OSRELDATE. */
+#define	AT_NCPUS	19	/* Number of CPUs. */
+#define	AT_PAGESIZES	20	/* Pagesizes. */
+#define	AT_PAGESIZESLEN	21	/* Number of pagesizes. */
+#define	AT_TIMEKEEP	22	/* Pointer to timehands. */
+#define	AT_STACKPROT	23	/* Initial stack protection. */
+#define	AT_EHDRFLAGS	24	/* e_flags field from elf hdr */
+#define	AT_HWCAP	25	/* CPU feature flags. */
+#define	AT_HWCAP2	26	/* CPU feature flags 2. */
+#define	AT_BSDFLAGS	27	/* ELF BSD Flags. */
+#define	AT_ARGC		28	/* Argument count */
+#define	AT_ARGV		29	/* Argument vector */
+#define	AT_ENVC		30	/* Environment count */
+#define	AT_ENVV		31	/* Environment vector */
+#define	AT_PS_STRINGS	32	/* struct ps_strings */
+#define	AT_FXRNG	33	/* Pointer to root RNG seed version. */
+
+#define	AT_COUNT	34	/* Count of defined aux entry types. */
+
+/*
+ * Relocation types.
+ *
+ * All machine architectures are defined here to allow tools on one to
+ * handle others.
+ */
+
+#define	R_386_NONE		0	/* No relocation. */
+#define	R_386_32		1	/* Add symbol value. */
+#define	R_386_PC32		2	/* Add PC-relative symbol value. */
+#define	R_386_GOT32		3	/* Add PC-relative GOT offset. */
+#define	R_386_PLT32		4	/* Add PC-relative PLT offset. */
+#define	R_386_COPY		5	/* Copy data from shared object. */
+#define	R_386_GLOB_DAT		6	/* Set GOT entry to data address. */
+#define	R_386_JMP_SLOT		7	/* Set GOT entry to code address. */
+#define	R_386_RELATIVE		8	/* Add load address of shared object. */
+#define	R_386_GOTOFF		9	/* Add GOT-relative symbol address. */
+#define	R_386_GOTPC		10	/* Add PC-relative GOT table address. */
+#define	R_386_32PLT		11
+#define	R_386_TLS_TPOFF		14	/* Negative offset in static TLS block */
+#define	R_386_TLS_IE		15	/* Absolute address of GOT for -ve static TLS */
+#define	R_386_TLS_GOTIE		16	/* GOT entry for negative static TLS block */
+#define	R_386_TLS_LE		17	/* Negative offset relative to static TLS */
+#define	R_386_TLS_GD		18	/* 32 bit offset to GOT (index,off) pair */
+#define	R_386_TLS_LDM		19	/* 32 bit offset to GOT (index,zero) pair */
+#define	R_386_16		20
+#define	R_386_PC16		21
+#define	R_386_8			22
+#define	R_386_PC8		23
+#define	R_386_TLS_GD_32		24	/* 32 bit offset to GOT (index,off) pair */
+#define	R_386_TLS_GD_PUSH	25	/* pushl instruction for Sun ABI GD sequence */
+#define	R_386_TLS_GD_CALL	26	/* call instruction for Sun ABI GD sequence */
+#define	R_386_TLS_GD_POP	27	/* popl instruction for Sun ABI GD sequence */
+#define	R_386_TLS_LDM_32	28	/* 32 bit offset to GOT (index,zero) pair */
+#define	R_386_TLS_LDM_PUSH	29	/* pushl instruction for Sun ABI LD sequence */
+#define	R_386_TLS_LDM_CALL	30	/* call instruction for Sun ABI LD sequence */
+#define	R_386_TLS_LDM_POP	31	/* popl instruction for Sun ABI LD sequence */
+#define	R_386_TLS_LDO_32	32	/* 32 bit offset from start of TLS block */
+#define	R_386_TLS_IE_32		33	/* 32 bit offset to GOT static TLS offset entry */
+#define	R_386_TLS_LE_32		34	/* 32 bit offset within static TLS block */
+#define	R_386_TLS_DTPMOD32	35	/* GOT entry containing TLS index */
+#define	R_386_TLS_DTPOFF32	36	/* GOT entry containing TLS offset */
+#define	R_386_TLS_TPOFF32	37	/* GOT entry of -ve static TLS offset */
+#define	R_386_SIZE32		38
+#define	R_386_TLS_GOTDESC	39
+#define	R_386_TLS_DESC_CALL	40
+#define	R_386_TLS_DESC		41
+#define	R_386_IRELATIVE		42	/* PLT entry resolved indirectly at runtime */
+#define	R_386_GOT32X		43
+
+#define	R_AARCH64_NONE		0	/* No relocation */
+#define	R_AARCH64_ABS64		257	/* Absolute offset */
+#define	R_AARCH64_ABS32		258	/* Absolute, 32-bit overflow check */
+#define	R_AARCH64_ABS16		259	/* Absolute, 16-bit overflow check */
+#define	R_AARCH64_PREL64	260	/* PC relative */
+#define	R_AARCH64_PREL32	261	/* PC relative, 32-bit overflow check */
+#define	R_AARCH64_PREL16	262	/* PC relative, 16-bit overflow check */
+#define	R_AARCH64_TSTBR14	279	/* TBZ/TBNZ immediate */
+#define	R_AARCH64_CONDBR19	280	/* Conditional branch immediate */
+#define	R_AARCH64_JUMP26	282	/* Branch immediate */
+#define	R_AARCH64_CALL26	283	/* Call immediate */
+#define	R_AARCH64_COPY		1024	/* Copy data from shared object */
+#define	R_AARCH64_GLOB_DAT	1025	/* Set GOT entry to data address */
+#define	R_AARCH64_JUMP_SLOT	1026	/* Set GOT entry to code address */
+#define	R_AARCH64_RELATIVE 	1027	/* Add load address of shared object */
+#define	R_AARCH64_TLS_DTPREL64	1028
+#define	R_AARCH64_TLS_DTPMOD64	1029
+#define	R_AARCH64_TLS_TPREL64 	1030
+#define	R_AARCH64_TLSDESC 	1031	/* Identify the TLS descriptor */
+#define	R_AARCH64_IRELATIVE	1032
+
+#define	R_ARM_NONE		0	/* No relocation. */
+#define	R_ARM_PC24		1
+#define	R_ARM_ABS32		2
+#define	R_ARM_REL32		3
+#define	R_ARM_PC13		4
+#define	R_ARM_ABS16		5
+#define	R_ARM_ABS12		6
+#define	R_ARM_THM_ABS5		7
+#define	R_ARM_ABS8		8
+#define	R_ARM_SBREL32		9
+#define	R_ARM_THM_PC22		10
+#define	R_ARM_THM_PC8		11
+#define	R_ARM_AMP_VCALL9	12
+#define	R_ARM_SWI24		13
+#define	R_ARM_THM_SWI8		14
+#define	R_ARM_XPC25		15
+#define	R_ARM_THM_XPC22		16
+/* TLS relocations */
+#define	R_ARM_TLS_DTPMOD32	17	/* ID of module containing symbol */
+#define	R_ARM_TLS_DTPOFF32	18	/* Offset in TLS block */
+#define	R_ARM_TLS_TPOFF32	19	/* Offset in static TLS block */
+#define	R_ARM_COPY		20	/* Copy data from shared object. */
+#define	R_ARM_GLOB_DAT		21	/* Set GOT entry to data address. */
+#define	R_ARM_JUMP_SLOT		22	/* Set GOT entry to code address. */
+#define	R_ARM_RELATIVE		23	/* Add load address of shared object. */
+#define	R_ARM_GOTOFF		24	/* Add GOT-relative symbol address. */
+#define	R_ARM_GOTPC		25	/* Add PC-relative GOT table address. */
+#define	R_ARM_GOT32		26	/* Add PC-relative GOT offset. */
+#define	R_ARM_PLT32		27	/* Add PC-relative PLT offset. */
+#define	R_ARM_GNU_VTENTRY	100
+#define	R_ARM_GNU_VTINHERIT	101
+#define	R_ARM_RSBREL32		250
+#define	R_ARM_THM_RPC22		251
+#define	R_ARM_RREL32		252
+#define	R_ARM_RABS32		253
+#define	R_ARM_RPC24		254
+#define	R_ARM_RBASE		255
+
+/*	Name			Value	   Field	Calculation */
+#define	R_IA_64_NONE		0	/* None */
+#define	R_IA_64_IMM14		0x21	/* immediate14	S + A */
+#define	R_IA_64_IMM22		0x22	/* immediate22	S + A */
+#define	R_IA_64_IMM64		0x23	/* immediate64	S + A */
+#define	R_IA_64_DIR32MSB	0x24	/* word32 MSB	S + A */
+#define	R_IA_64_DIR32LSB	0x25	/* word32 LSB	S + A */
+#define	R_IA_64_DIR64MSB	0x26	/* word64 MSB	S + A */
+#define	R_IA_64_DIR64LSB	0x27	/* word64 LSB	S + A */
+#define	R_IA_64_GPREL22		0x2a	/* immediate22	@gprel(S + A) */
+#define	R_IA_64_GPREL64I	0x2b	/* immediate64	@gprel(S + A) */
+#define	R_IA_64_GPREL32MSB	0x2c	/* word32 MSB	@gprel(S + A) */
+#define	R_IA_64_GPREL32LSB	0x2d	/* word32 LSB	@gprel(S + A) */
+#define	R_IA_64_GPREL64MSB	0x2e	/* word64 MSB	@gprel(S + A) */
+#define	R_IA_64_GPREL64LSB	0x2f	/* word64 LSB	@gprel(S + A) */
+#define	R_IA_64_LTOFF22		0x32	/* immediate22	@ltoff(S + A) */
+#define	R_IA_64_LTOFF64I	0x33	/* immediate64	@ltoff(S + A) */
+#define	R_IA_64_PLTOFF22	0x3a	/* immediate22	@pltoff(S + A) */
+#define	R_IA_64_PLTOFF64I	0x3b	/* immediate64	@pltoff(S + A) */
+#define	R_IA_64_PLTOFF64MSB	0x3e	/* word64 MSB	@pltoff(S + A) */
+#define	R_IA_64_PLTOFF64LSB	0x3f	/* word64 LSB	@pltoff(S + A) */
+#define	R_IA_64_FPTR64I		0x43	/* immediate64	@fptr(S + A) */
+#define	R_IA_64_FPTR32MSB	0x44	/* word32 MSB	@fptr(S + A) */
+#define	R_IA_64_FPTR32LSB	0x45	/* word32 LSB	@fptr(S + A) */
+#define	R_IA_64_FPTR64MSB	0x46	/* word64 MSB	@fptr(S + A) */
+#define	R_IA_64_FPTR64LSB	0x47	/* word64 LSB	@fptr(S + A) */
+#define	R_IA_64_PCREL60B	0x48	/* immediate60 form1 S + A - P */
+#define	R_IA_64_PCREL21B	0x49	/* immediate21 form1 S + A - P */
+#define	R_IA_64_PCREL21M	0x4a	/* immediate21 form2 S + A - P */
+#define	R_IA_64_PCREL21F	0x4b	/* immediate21 form3 S + A - P */
+#define	R_IA_64_PCREL32MSB	0x4c	/* word32 MSB	S + A - P */
+#define	R_IA_64_PCREL32LSB	0x4d	/* word32 LSB	S + A - P */
+#define	R_IA_64_PCREL64MSB	0x4e	/* word64 MSB	S + A - P */
+#define	R_IA_64_PCREL64LSB	0x4f	/* word64 LSB	S + A - P */
+#define	R_IA_64_LTOFF_FPTR22	0x52	/* immediate22	@ltoff(@fptr(S + A)) */
+#define	R_IA_64_LTOFF_FPTR64I	0x53	/* immediate64	@ltoff(@fptr(S + A)) */
+#define	R_IA_64_LTOFF_FPTR32MSB	0x54	/* word32 MSB	@ltoff(@fptr(S + A)) */
+#define	R_IA_64_LTOFF_FPTR32LSB	0x55	/* word32 LSB	@ltoff(@fptr(S + A)) */
+#define	R_IA_64_LTOFF_FPTR64MSB	0x56	/* word64 MSB	@ltoff(@fptr(S + A)) */
+#define	R_IA_64_LTOFF_FPTR64LSB	0x57	/* word64 LSB	@ltoff(@fptr(S + A)) */
+#define	R_IA_64_SEGREL32MSB	0x5c	/* word32 MSB	@segrel(S + A) */
+#define	R_IA_64_SEGREL32LSB	0x5d	/* word32 LSB	@segrel(S + A) */
+#define	R_IA_64_SEGREL64MSB	0x5e	/* word64 MSB	@segrel(S + A) */
+#define	R_IA_64_SEGREL64LSB	0x5f	/* word64 LSB	@segrel(S + A) */
+#define	R_IA_64_SECREL32MSB	0x64	/* word32 MSB	@secrel(S + A) */
+#define	R_IA_64_SECREL32LSB	0x65	/* word32 LSB	@secrel(S + A) */
+#define	R_IA_64_SECREL64MSB	0x66	/* word64 MSB	@secrel(S + A) */
+#define	R_IA_64_SECREL64LSB	0x67	/* word64 LSB	@secrel(S + A) */
+#define	R_IA_64_REL32MSB	0x6c	/* word32 MSB	BD + A */
+#define	R_IA_64_REL32LSB	0x6d	/* word32 LSB	BD + A */
+#define	R_IA_64_REL64MSB	0x6e	/* word64 MSB	BD + A */
+#define	R_IA_64_REL64LSB	0x6f	/* word64 LSB	BD + A */
+#define	R_IA_64_LTV32MSB	0x74	/* word32 MSB	S + A */
+#define	R_IA_64_LTV32LSB	0x75	/* word32 LSB	S + A */
+#define	R_IA_64_LTV64MSB	0x76	/* word64 MSB	S + A */
+#define	R_IA_64_LTV64LSB	0x77	/* word64 LSB	S + A */
+#define	R_IA_64_PCREL21BI	0x79	/* immediate21 form1 S + A - P */
+#define	R_IA_64_PCREL22		0x7a	/* immediate22	S + A - P */
+#define	R_IA_64_PCREL64I	0x7b	/* immediate64	S + A - P */
+#define	R_IA_64_IPLTMSB		0x80	/* function descriptor MSB special */
+#define	R_IA_64_IPLTLSB		0x81	/* function descriptor LSB speciaal */
+#define	R_IA_64_SUB		0x85	/* immediate64	A - S */
+#define	R_IA_64_LTOFF22X	0x86	/* immediate22	special */
+#define	R_IA_64_LDXMOV		0x87	/* immediate22	special */
+#define	R_IA_64_TPREL14		0x91	/* imm14	@tprel(S + A) */
+#define	R_IA_64_TPREL22		0x92	/* imm22	@tprel(S + A) */
+#define	R_IA_64_TPREL64I	0x93	/* imm64	@tprel(S + A) */
+#define	R_IA_64_TPREL64MSB	0x96	/* word64 MSB	@tprel(S + A) */
+#define	R_IA_64_TPREL64LSB	0x97	/* word64 LSB	@tprel(S + A) */
+#define	R_IA_64_LTOFF_TPREL22	0x9a	/* imm22	@ltoff(@tprel(S+A)) */
+#define	R_IA_64_DTPMOD64MSB	0xa6	/* word64 MSB	@dtpmod(S + A) */
+#define	R_IA_64_DTPMOD64LSB	0xa7	/* word64 LSB	@dtpmod(S + A) */
+#define	R_IA_64_LTOFF_DTPMOD22	0xaa	/* imm22	@ltoff(@dtpmod(S+A)) */
+#define	R_IA_64_DTPREL14	0xb1	/* imm14	@dtprel(S + A) */
+#define	R_IA_64_DTPREL22	0xb2	/* imm22	@dtprel(S + A) */
+#define	R_IA_64_DTPREL64I	0xb3	/* imm64	@dtprel(S + A) */
+#define	R_IA_64_DTPREL32MSB	0xb4	/* word32 MSB	@dtprel(S + A) */
+#define	R_IA_64_DTPREL32LSB	0xb5	/* word32 LSB	@dtprel(S + A) */
+#define	R_IA_64_DTPREL64MSB	0xb6	/* word64 MSB	@dtprel(S + A) */
+#define	R_IA_64_DTPREL64LSB	0xb7	/* word64 LSB	@dtprel(S + A) */
+#define	R_IA_64_LTOFF_DTPREL22	0xba	/* imm22	@ltoff(@dtprel(S+A)) */
+
+#define	R_MIPS_NONE	0	/* No reloc */
+#define	R_MIPS_16	1	/* Direct 16 bit */
+#define	R_MIPS_32	2	/* Direct 32 bit */
+#define	R_MIPS_REL32	3	/* PC relative 32 bit */
+#define	R_MIPS_26	4	/* Direct 26 bit shifted */
+#define	R_MIPS_HI16	5	/* High 16 bit */
+#define	R_MIPS_LO16	6	/* Low 16 bit */
+#define	R_MIPS_GPREL16	7	/* GP relative 16 bit */
+#define	R_MIPS_LITERAL	8	/* 16 bit literal entry */
+#define	R_MIPS_GOT16	9	/* 16 bit GOT entry */
+#define	R_MIPS_PC16	10	/* PC relative 16 bit */
+#define	R_MIPS_CALL16	11	/* 16 bit GOT entry for function */
+#define	R_MIPS_GPREL32	12	/* GP relative 32 bit */
+#define	R_MIPS_64	18	/* Direct 64 bit */
+#define	R_MIPS_GOT_DISP	19
+#define	R_MIPS_GOT_PAGE	20
+#define	R_MIPS_GOT_OFST	21
+#define	R_MIPS_GOT_HI16	22	/* GOT HI 16 bit */
+#define	R_MIPS_GOT_LO16	23	/* GOT LO 16 bit */
+#define	R_MIPS_SUB	24
+#define	R_MIPS_CALLHI16 30	/* upper 16 bit GOT entry for function */
+#define	R_MIPS_CALLLO16 31	/* lower 16 bit GOT entry for function */
+#define	R_MIPS_JALR	37
+#define	R_MIPS_TLS_GD	42
+#define	R_MIPS_COPY	126
+#define	R_MIPS_JUMP_SLOT	127
+
+#define	R_PPC_NONE		0	/* No relocation. */
+#define	R_PPC_ADDR32		1
+#define	R_PPC_ADDR24		2
+#define	R_PPC_ADDR16		3
+#define	R_PPC_ADDR16_LO		4
+#define	R_PPC_ADDR16_HI		5
+#define	R_PPC_ADDR16_HA		6
+#define	R_PPC_ADDR14		7
+#define	R_PPC_ADDR14_BRTAKEN	8
+#define	R_PPC_ADDR14_BRNTAKEN	9
+#define	R_PPC_REL24		10
+#define	R_PPC_REL14		11
+#define	R_PPC_REL14_BRTAKEN	12
+#define	R_PPC_REL14_BRNTAKEN	13
+#define	R_PPC_GOT16		14
+#define	R_PPC_GOT16_LO		15
+#define	R_PPC_GOT16_HI		16
+#define	R_PPC_GOT16_HA		17
+#define	R_PPC_PLTREL24		18
+#define	R_PPC_COPY		19
+#define	R_PPC_GLOB_DAT		20
+#define	R_PPC_JMP_SLOT		21
+#define	R_PPC_RELATIVE		22
+#define	R_PPC_LOCAL24PC		23
+#define	R_PPC_UADDR32		24
+#define	R_PPC_UADDR16		25
+#define	R_PPC_REL32		26
+#define	R_PPC_PLT32		27
+#define	R_PPC_PLTREL32		28
+#define	R_PPC_PLT16_LO		29
+#define	R_PPC_PLT16_HI		30
+#define	R_PPC_PLT16_HA		31
+#define	R_PPC_SDAREL16		32
+#define	R_PPC_SECTOFF		33
+#define	R_PPC_SECTOFF_LO	34
+#define	R_PPC_SECTOFF_HI	35
+#define	R_PPC_SECTOFF_HA	36
+#define	R_PPC_IRELATIVE		248
+
+/*
+ * 64-bit relocations
+ */
+#define	R_PPC64_ADDR64		38
+#define	R_PPC64_ADDR16_HIGHER	39
+#define	R_PPC64_ADDR16_HIGHERA	40
+#define	R_PPC64_ADDR16_HIGHEST	41
+#define	R_PPC64_ADDR16_HIGHESTA	42
+#define	R_PPC64_UADDR64		43
+#define	R_PPC64_REL64		44
+#define	R_PPC64_PLT64		45
+#define	R_PPC64_PLTREL64	46
+#define	R_PPC64_TOC16		47
+#define	R_PPC64_TOC16_LO	48
+#define	R_PPC64_TOC16_HI	49
+#define	R_PPC64_TOC16_HA	50
+#define	R_PPC64_TOC		51
+#define	R_PPC64_DTPMOD64	68
+#define	R_PPC64_TPREL64		73
+#define	R_PPC64_DTPREL64	78
+
+/*
+ * TLS relocations
+ */
+#define	R_PPC_TLS		67
+#define	R_PPC_DTPMOD32		68
+#define	R_PPC_TPREL16		69
+#define	R_PPC_TPREL16_LO	70
+#define	R_PPC_TPREL16_HI	71
+#define	R_PPC_TPREL16_HA	72
+#define	R_PPC_TPREL32		73
+#define	R_PPC_DTPREL16		74
+#define	R_PPC_DTPREL16_LO	75
+#define	R_PPC_DTPREL16_HI	76
+#define	R_PPC_DTPREL16_HA	77
+#define	R_PPC_DTPREL32		78
+#define	R_PPC_GOT_TLSGD16	79
+#define	R_PPC_GOT_TLSGD16_LO	80
+#define	R_PPC_GOT_TLSGD16_HI	81
+#define	R_PPC_GOT_TLSGD16_HA	82
+#define	R_PPC_GOT_TLSLD16	83
+#define	R_PPC_GOT_TLSLD16_LO	84
+#define	R_PPC_GOT_TLSLD16_HI	85
+#define	R_PPC_GOT_TLSLD16_HA	86
+#define	R_PPC_GOT_TPREL16	87
+#define	R_PPC_GOT_TPREL16_LO	88
+#define	R_PPC_GOT_TPREL16_HI	89
+#define	R_PPC_GOT_TPREL16_HA	90
+
+/*
+ * The remaining relocs are from the Embedded ELF ABI, and are not in the
+ *  SVR4 ELF ABI.
+ */
+
+#define	R_PPC_EMB_NADDR32	101
+#define	R_PPC_EMB_NADDR16	102
+#define	R_PPC_EMB_NADDR16_LO	103
+#define	R_PPC_EMB_NADDR16_HI	104
+#define	R_PPC_EMB_NADDR16_HA	105
+#define	R_PPC_EMB_SDAI16	106
+#define	R_PPC_EMB_SDA2I16	107
+#define	R_PPC_EMB_SDA2REL	108
+#define	R_PPC_EMB_SDA21		109
+#define	R_PPC_EMB_MRKREF	110
+#define	R_PPC_EMB_RELSEC16	111
+#define	R_PPC_EMB_RELST_LO	112
+#define	R_PPC_EMB_RELST_HI	113
+#define	R_PPC_EMB_RELST_HA	114
+#define	R_PPC_EMB_BIT_FLD	115
+#define	R_PPC_EMB_RELSDA	116
+
+/*
+ * RISC-V relocation types.
+ */
+
+/* Relocation types used by the dynamic linker. */
+#define	R_RISCV_NONE		0
+#define	R_RISCV_32		1
+#define	R_RISCV_64		2
+#define	R_RISCV_RELATIVE	3
+#define	R_RISCV_COPY		4
+#define	R_RISCV_JUMP_SLOT	5
+#define	R_RISCV_TLS_DTPMOD32	6
+#define	R_RISCV_TLS_DTPMOD64	7
+#define	R_RISCV_TLS_DTPREL32	8
+#define	R_RISCV_TLS_DTPREL64	9
+#define	R_RISCV_TLS_TPREL32	10
+#define	R_RISCV_TLS_TPREL64	11
+
+/* Relocation types not used by the dynamic linker. */
+#define	R_RISCV_BRANCH		16
+#define	R_RISCV_JAL		17
+#define	R_RISCV_CALL		18
+#define	R_RISCV_CALL_PLT	19
+#define	R_RISCV_GOT_HI20	20
+#define	R_RISCV_TLS_GOT_HI20	21
+#define	R_RISCV_TLS_GD_HI20	22
+#define	R_RISCV_PCREL_HI20	23
+#define	R_RISCV_PCREL_LO12_I	24
+#define	R_RISCV_PCREL_LO12_S	25
+#define	R_RISCV_HI20		26
+#define	R_RISCV_LO12_I		27
+#define	R_RISCV_LO12_S		28
+#define	R_RISCV_TPREL_HI20	29
+#define	R_RISCV_TPREL_LO12_I	30
+#define	R_RISCV_TPREL_LO12_S	31
+#define	R_RISCV_TPREL_ADD	32
+#define	R_RISCV_ADD8		33
+#define	R_RISCV_ADD16		34
+#define	R_RISCV_ADD32		35
+#define	R_RISCV_ADD64		36
+#define	R_RISCV_SUB8		37
+#define	R_RISCV_SUB16		38
+#define	R_RISCV_SUB32		39
+#define	R_RISCV_SUB64		40
+#define	R_RISCV_GNU_VTINHERIT	41
+#define	R_RISCV_GNU_VTENTRY	42
+#define	R_RISCV_ALIGN		43
+#define	R_RISCV_RVC_BRANCH	44
+#define	R_RISCV_RVC_JUMP	45
+#define	R_RISCV_RVC_LUI		46
+#define	R_RISCV_GPREL_I		47
+#define	R_RISCV_GPREL_S		48
+#define	R_RISCV_TPREL_I		49
+#define	R_RISCV_TPREL_S		50
+#define	R_RISCV_RELAX		51
+#define	R_RISCV_SUB6		52
+#define	R_RISCV_SET6		53
+#define	R_RISCV_SET8		54
+#define	R_RISCV_SET16		55
+#define	R_RISCV_SET32		56
+#define	R_RISCV_32_PCREL	57
+#define	R_RISCV_IRELATIVE	58
+
+#define	R_SPARC_NONE		0
+#define	R_SPARC_8		1
+#define	R_SPARC_16		2
+#define	R_SPARC_32		3
+#define	R_SPARC_DISP8		4
+#define	R_SPARC_DISP16		5
+#define	R_SPARC_DISP32		6
+#define	R_SPARC_WDISP30		7
+#define	R_SPARC_WDISP22		8
+#define	R_SPARC_HI22		9
+#define	R_SPARC_22		10
+#define	R_SPARC_13		11
+#define	R_SPARC_LO10		12
+#define	R_SPARC_GOT10		13
+#define	R_SPARC_GOT13		14
+#define	R_SPARC_GOT22		15
+#define	R_SPARC_PC10		16
+#define	R_SPARC_PC22		17
+#define	R_SPARC_WPLT30		18
+#define	R_SPARC_COPY		19
+#define	R_SPARC_GLOB_DAT	20
+#define	R_SPARC_JMP_SLOT	21
+#define	R_SPARC_RELATIVE	22
+#define	R_SPARC_UA32		23
+#define	R_SPARC_PLT32		24
+#define	R_SPARC_HIPLT22		25
+#define	R_SPARC_LOPLT10		26
+#define	R_SPARC_PCPLT32		27
+#define	R_SPARC_PCPLT22		28
+#define	R_SPARC_PCPLT10		29
+#define	R_SPARC_10		30
+#define	R_SPARC_11		31
+#define	R_SPARC_64		32
+#define	R_SPARC_OLO10		33
+#define	R_SPARC_HH22		34
+#define	R_SPARC_HM10		35
+#define	R_SPARC_LM22		36
+#define	R_SPARC_PC_HH22		37
+#define	R_SPARC_PC_HM10		38
+#define	R_SPARC_PC_LM22		39
+#define	R_SPARC_WDISP16		40
+#define	R_SPARC_WDISP19		41
+#define	R_SPARC_GLOB_JMP	42
+#define	R_SPARC_7		43
+#define	R_SPARC_5		44
+#define	R_SPARC_6		45
+#define	R_SPARC_DISP64		46
+#define	R_SPARC_PLT64		47
+#define	R_SPARC_HIX22		48
+#define	R_SPARC_LOX10		49
+#define	R_SPARC_H44		50
+#define	R_SPARC_M44		51
+#define	R_SPARC_L44		52
+#define	R_SPARC_REGISTER	53
+#define	R_SPARC_UA64		54
+#define	R_SPARC_UA16		55
+#define	R_SPARC_TLS_GD_HI22	56
+#define	R_SPARC_TLS_GD_LO10	57
+#define	R_SPARC_TLS_GD_ADD	58
+#define	R_SPARC_TLS_GD_CALL	59
+#define	R_SPARC_TLS_LDM_HI22	60
+#define	R_SPARC_TLS_LDM_LO10	61
+#define	R_SPARC_TLS_LDM_ADD	62
+#define	R_SPARC_TLS_LDM_CALL	63
+#define	R_SPARC_TLS_LDO_HIX22	64
+#define	R_SPARC_TLS_LDO_LOX10	65
+#define	R_SPARC_TLS_LDO_ADD	66
+#define	R_SPARC_TLS_IE_HI22	67
+#define	R_SPARC_TLS_IE_LO10	68
+#define	R_SPARC_TLS_IE_LD	69
+#define	R_SPARC_TLS_IE_LDX	70
+#define	R_SPARC_TLS_IE_ADD	71
+#define	R_SPARC_TLS_LE_HIX22	72
+#define	R_SPARC_TLS_LE_LOX10	73
+#define	R_SPARC_TLS_DTPMOD32	74
+#define	R_SPARC_TLS_DTPMOD64	75
+#define	R_SPARC_TLS_DTPOFF32	76
+#define	R_SPARC_TLS_DTPOFF64	77
+#define	R_SPARC_TLS_TPOFF32	78
+#define	R_SPARC_TLS_TPOFF64	79
+
+#define	R_X86_64_NONE		0	/* No relocation. */
+#define	R_X86_64_64		1	/* Add 64 bit symbol value. */
+#define	R_X86_64_PC32		2	/* PC-relative 32 bit signed sym value. */
+#define	R_X86_64_GOT32		3	/* PC-relative 32 bit GOT offset. */
+#define	R_X86_64_PLT32		4	/* PC-relative 32 bit PLT offset. */
+#define	R_X86_64_COPY		5	/* Copy data from shared object. */
+#define	R_X86_64_GLOB_DAT	6	/* Set GOT entry to data address. */
+#define	R_X86_64_JMP_SLOT	7	/* Set GOT entry to code address. */
+#define	R_X86_64_RELATIVE	8	/* Add load address of shared object. */
+#define	R_X86_64_GOTPCREL	9	/* Add 32 bit signed pcrel offset to GOT. */
+#define	R_X86_64_32		10	/* Add 32 bit zero extended symbol value */
+#define	R_X86_64_32S		11	/* Add 32 bit sign extended symbol value */
+#define	R_X86_64_16		12	/* Add 16 bit zero extended symbol value */
+#define	R_X86_64_PC16		13	/* Add 16 bit signed extended pc relative symbol value */
+#define	R_X86_64_8		14	/* Add 8 bit zero extended symbol value */
+#define	R_X86_64_PC8		15	/* Add 8 bit signed extended pc relative symbol value */
+#define	R_X86_64_DTPMOD64	16	/* ID of module containing symbol */
+#define	R_X86_64_DTPOFF64	17	/* Offset in TLS block */
+#define	R_X86_64_TPOFF64	18	/* Offset in static TLS block */
+#define	R_X86_64_TLSGD		19	/* PC relative offset to GD GOT entry */
+#define	R_X86_64_TLSLD		20	/* PC relative offset to LD GOT entry */
+#define	R_X86_64_DTPOFF32	21	/* Offset in TLS block */
+#define	R_X86_64_GOTTPOFF	22	/* PC relative offset to IE GOT entry */
+#define	R_X86_64_TPOFF32	23	/* Offset in static TLS block */
+#define	R_X86_64_PC64		24	/* PC-relative 64 bit signed sym value. */
+#define	R_X86_64_GOTOFF64	25
+#define	R_X86_64_GOTPC32	26
+#define	R_X86_64_GOT64		27
+#define	R_X86_64_GOTPCREL64	28
+#define	R_X86_64_GOTPC64	29
+#define	R_X86_64_GOTPLT64	30
+#define	R_X86_64_PLTOFF64	31
+#define	R_X86_64_SIZE32		32
+#define	R_X86_64_SIZE64		33
+#define	R_X86_64_GOTPC32_TLSDESC 34
+#define	R_X86_64_TLSDESC_CALL	35
+#define	R_X86_64_TLSDESC	36
+#define	R_X86_64_IRELATIVE	37
+#define	R_X86_64_RELATIVE64	38
+/* 39 and 40 were BND-related, already decomissioned */
+#define	R_X86_64_GOTPCRELX	41
+#define	R_X86_64_REX_GOTPCRELX	42
+
+#define	ELF_BSDF_SIGFASTBLK	0x0001	/* Kernel supports fast sigblock */
+
+#endif /* !_SYS_ELF_COMMON_H_ */
diff --git a/Utilities/cmjsoncpp/include/json/allocator.h b/Utilities/cmjsoncpp/include/json/allocator.h
index 9562436..a685da1 100644
--- a/Utilities/cmjsoncpp/include/json/allocator.h
+++ b/Utilities/cmjsoncpp/include/json/allocator.h
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_ALLOCATOR_H_INCLUDED
-#define CPPTL_JSON_ALLOCATOR_H_INCLUDED
+#ifndef JSON_ALLOCATOR_H_INCLUDED
+#define JSON_ALLOCATOR_H_INCLUDED
 
 #include <cstring>
 #include <memory>
@@ -14,89 +14,80 @@
 #endif
 
 namespace Json {
-template<typename T>
-class SecureAllocator {
-	public:
-		// Type definitions
-		using value_type      = T;
-		using pointer         = T*;
-		using const_pointer   = const T*;
-		using reference       = T&;
-		using const_reference = const T&;
-		using size_type       = std::size_t;
-		using difference_type = std::ptrdiff_t;
+template <typename T> class SecureAllocator {
+public:
+  // Type definitions
+  using value_type = T;
+  using pointer = T*;
+  using const_pointer = const T*;
+  using reference = T&;
+  using const_reference = const T&;
+  using size_type = std::size_t;
+  using difference_type = std::ptrdiff_t;
 
-		/**
-		 * Allocate memory for N items using the standard allocator.
-		 */
-		pointer allocate(size_type n) {
-			// allocate using "global operator new"
-			return static_cast<pointer>(::operator new(n * sizeof(T)));
-		}
+  /**
+   * Allocate memory for N items using the standard allocator.
+   */
+  pointer allocate(size_type n) {
+    // allocate using "global operator new"
+    return static_cast<pointer>(::operator new(n * sizeof(T)));
+  }
 
-		/**
-		 * Release memory which was allocated for N items at pointer P.
-		 *
-		 * The memory block is filled with zeroes before being released.
-		 * The pointer argument is tagged as "volatile" to prevent the
-		 * compiler optimizing out this critical step.
-		 */
-		void deallocate(volatile pointer p, size_type n) {
-			std::memset(p, 0, n * sizeof(T));
-			// free using "global operator delete"
-			::operator delete(p);
-		}
+  /**
+   * Release memory which was allocated for N items at pointer P.
+   *
+   * The memory block is filled with zeroes before being released.
+   * The pointer argument is tagged as "volatile" to prevent the
+   * compiler optimizing out this critical step.
+   */
+  void deallocate(volatile pointer p, size_type n) {
+    std::memset(p, 0, n * sizeof(T));
+    // free using "global operator delete"
+    ::operator delete(p);
+  }
 
-		/**
-		 * Construct an item in-place at pointer P.
-		 */
-		template<typename... Args>
-		void construct(pointer p, Args&&... args) {
-			// construct using "placement new" and "perfect forwarding"
-			::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
-		}
+  /**
+   * Construct an item in-place at pointer P.
+   */
+  template <typename... Args> void construct(pointer p, Args&&... args) {
+    // construct using "placement new" and "perfect forwarding"
+    ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
+  }
 
-		size_type max_size() const {
-			return size_t(-1) / sizeof(T);
-		}
+  size_type max_size() const { return size_t(-1) / sizeof(T); }
 
-		pointer address( reference x ) const {
-			return std::addressof(x);
-		}
+  pointer address(reference x) const { return std::addressof(x); }
 
-		const_pointer address( const_reference x ) const {
-			return std::addressof(x);
-		}
+  const_pointer address(const_reference x) const { return std::addressof(x); }
 
-		/**
-		 * Destroy an item in-place at pointer P.
-		 */
-		void destroy(pointer p) {
-			// destroy using "explicit destructor"
-			p->~T();
-		}
+  /**
+   * Destroy an item in-place at pointer P.
+   */
+  void destroy(pointer p) {
+    // destroy using "explicit destructor"
+    p->~T();
+  }
 
-		// Boilerplate
-		SecureAllocator() {}
-		template<typename U> SecureAllocator(const SecureAllocator<U>&) {}
-		template<typename U> struct rebind { using other = SecureAllocator<U>; };
+  // Boilerplate
+  SecureAllocator() {}
+  template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
+  template <typename U> struct rebind { using other = SecureAllocator<U>; };
 };
 
-
-template<typename T, typename U>
+template <typename T, typename U>
 bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
-	return true;
+  return true;
 }
 
-template<typename T, typename U>
+template <typename T, typename U>
 bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
-	return false;
+  return false;
 }
 
-} //namespace Json
+} // namespace Json
 
 #if !defined(__SUNPRO_CC)
 #pragma pack(pop)
 #endif
 
-#endif // CPPTL_JSON_ALLOCATOR_H_INCLUDED
+#endif // JSON_ALLOCATOR_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/assertions.h b/Utilities/cmjsoncpp/include/json/assertions.h
index f64913f..415c463 100644
--- a/Utilities/cmjsoncpp/include/json/assertions.h
+++ b/Utilities/cmjsoncpp/include/json/assertions.h
@@ -3,14 +3,14 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED
-#define CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#ifndef JSON_ASSERTIONS_H_INCLUDED
+#define JSON_ASSERTIONS_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
 #include "config.h"
 #endif // if !defined(JSON_IS_AMALGAMATION)
 
-#include <stdlib.h>
+#include <cstdlib>
 #include <sstream>
 
 /** It should not be possible for a maliciously designed file to
@@ -20,35 +20,42 @@
 #if JSON_USE_EXCEPTION
 
 // @todo <= add detail about condition in exception
-# define JSON_ASSERT(condition)                                                \
-  {if (!(condition)) {Json::throwLogicError( "assert json failed" );}}
+#define JSON_ASSERT(condition)                                                 \
+  do {                                                                         \
+    if (!(condition)) {                                                        \
+      Json::throwLogicError("assert json failed");                             \
+    }                                                                          \
+  } while (0)
 
-# define JSON_FAIL_MESSAGE(message)                                            \
-  {                                                                            \
-    JSONCPP_OSTRINGSTREAM oss; oss << message;                                    \
+#define JSON_FAIL_MESSAGE(message)                                             \
+  do {                                                                         \
+    OStringStream oss;                                                         \
+    oss << message;                                                            \
     Json::throwLogicError(oss.str());                                          \
     abort();                                                                   \
-  }
+  } while (0)
 
 #else // JSON_USE_EXCEPTION
 
-# define JSON_ASSERT(condition) assert(condition)
+#define JSON_ASSERT(condition) assert(condition)
 
 // The call to assert() will show the failure message in debug builds. In
 // release builds we abort, for a core-dump or debugger.
-# define JSON_FAIL_MESSAGE(message)                                            \
+#define JSON_FAIL_MESSAGE(message)                                             \
   {                                                                            \
-    JSONCPP_OSTRINGSTREAM oss; oss << message;                                    \
+    OStringStream oss;                                                         \
+    oss << message;                                                            \
     assert(false && oss.str().c_str());                                        \
     abort();                                                                   \
   }
 
-
 #endif
 
 #define JSON_ASSERT_MESSAGE(condition, message)                                \
-  if (!(condition)) {                                                          \
-    JSON_FAIL_MESSAGE(message);                                                \
-  }
+  do {                                                                         \
+    if (!(condition)) {                                                        \
+      JSON_FAIL_MESSAGE(message);                                              \
+    }                                                                          \
+  } while (0)
 
-#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED
+#endif // JSON_ASSERTIONS_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/config.h b/Utilities/cmjsoncpp/include/json/config.h
index 2cc8462..f03b746 100644
--- a/Utilities/cmjsoncpp/include/json/config.h
+++ b/Utilities/cmjsoncpp/include/json/config.h
@@ -6,26 +6,18 @@
 #ifndef JSON_CONFIG_H_INCLUDED
 #define JSON_CONFIG_H_INCLUDED
 
-// Include KWSys Large File Support configuration.
-#include <cmsys/Configure.h>
-
-#include <stddef.h>
-#include <string> //typedef String
-#include <stdint.h> //typedef int64_t, uint64_t
-
 #if defined(_MSC_VER)
 # pragma warning(push,1)
 #endif
 
-/// If defined, indicates that json library is embedded in CppTL library.
-//# define JSON_IN_CPPTL 1
-
-/// If defined, indicates that json may leverage CppTL library
-//#  define JSON_USE_CPPTL 1
-/// If defined, indicates that cpptl vector based map should be used instead of
-/// std::map
-/// as Value container.
-//#  define JSON_USE_CPPTL_SMALLMAP 1
+#include <cstddef>
+#include <cstdint>
+#include <istream>
+#include <memory>
+#include <ostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
 
 // If non-zero, the library uses exceptions to report bad input instead of C
 // assertion macros. The default is to use exceptions.
@@ -33,108 +25,73 @@
 #define JSON_USE_EXCEPTION 1
 #endif
 
-/// If defined, indicates that the source file is amalgated
+// Temporary, tracked for removal with issue #982.
+#ifndef JSON_USE_NULLREF
+#define JSON_USE_NULLREF 1
+#endif
+
+/// If defined, indicates that the source file is amalgamated
 /// to prevent private header inclusion.
-/// Remarks: it is automatically defined in the generated amalgated header.
+/// Remarks: it is automatically defined in the generated amalgamated header.
 // #define JSON_IS_AMALGAMATION
 
-#ifdef JSON_IN_CPPTL
-#include <cpptl/config.h>
-#ifndef JSON_USE_CPPTL
-#define JSON_USE_CPPTL 1
-#endif
-#endif
-
-#ifdef JSON_IN_CPPTL
-#define JSON_API CPPTL_API
-#elif defined(JSON_DLL_BUILD)
+// Export macros for DLL visibility
+#if defined(JSON_DLL_BUILD)
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #define JSON_API __declspec(dllexport)
 #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
+#elif defined(__GNUC__) || defined(__clang__)
+#define JSON_API __attribute__((visibility("default")))
 #endif // if defined(_MSC_VER)
+
 #elif defined(JSON_DLL)
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #define JSON_API __declspec(dllimport)
 #define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
 #endif // if defined(_MSC_VER)
-#endif // ifdef JSON_IN_CPPTL
+#endif // ifdef JSON_DLL_BUILD
+
 #if !defined(JSON_API)
 #define JSON_API
 #endif
 
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#error                                                                         \
+    "ERROR:  Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+// As recommended at
+// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
+extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+                                              const char* format, ...);
+#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
+#else
+#define jsoncpp_snprintf std::snprintf
+#endif
+
 // If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
 // integer
 // Storages, and 64 bits integer support is disabled.
 // #define JSON_NO_INT64 1
 
-#if defined(_MSC_VER) // MSVC
-#  if _MSC_VER <= 1200 // MSVC 6
-    // Microsoft Visual Studio 6 only support conversion from __int64 to double
-    // (no conversion from unsigned __int64).
-#    define JSON_USE_INT64_DOUBLE_CONVERSION 1
-    // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
-    // characters in the debug information)
-    // All projects I've ever seen with VS6 were using this globally (not bothering
-    // with pragma push/pop).
-#    pragma warning(disable : 4786)
-#  endif // MSVC 6
-
-#endif // defined(_MSC_VER)
-
-// In c++11 the override keyword allows you to explicity define that a function
-// is intended to override the base-class version.  This makes the code more
-// managable and fixes a set of common hard-to-find bugs.
-#if __cplusplus >= 201103L
-# define JSONCPP_OVERRIDE override
-# define JSONCPP_NOEXCEPT noexcept
-#elif defined(_MSC_VER) && _MSC_VER > 1600 && _MSC_VER < 1900
-# define JSONCPP_OVERRIDE override
-# define JSONCPP_NOEXCEPT throw()
-#elif defined(_MSC_VER) && _MSC_VER >= 1900
-# define JSONCPP_OVERRIDE override
-# define JSONCPP_NOEXCEPT noexcept
-#else
-# define JSONCPP_OVERRIDE
-# define JSONCPP_NOEXCEPT throw()
-#endif
-
-#ifndef JSON_HAS_RVALUE_REFERENCES
-
-#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif // MSVC >= 2010
+// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
+// C++11 should be used directly in JSONCPP.
+#define JSONCPP_OVERRIDE override
 
 #ifdef __clang__
-#if __has_feature(cxx_rvalue_references)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif  // has_feature
-
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
-#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
-#define JSON_HAS_RVALUE_REFERENCES 1
-#endif  // GXX_EXPERIMENTAL
-
-#endif // __clang__ || __GNUC__
-
-#endif // not defined JSON_HAS_RVALUE_REFERENCES
-
-#ifndef JSON_HAS_RVALUE_REFERENCES
-#define JSON_HAS_RVALUE_REFERENCES 0
+#if __has_extension(attribute_deprecated_with_message)
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
 #endif
-
-#ifdef __clang__
-#  if __has_extension(attribute_deprecated_with_message)
-#    define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
-#  endif
-#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc)
-#  if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
-#    define JSONCPP_DEPRECATED(message)  __attribute__ ((deprecated(message)))
-#  elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
-#    define JSONCPP_DEPRECATED(message)  __attribute__((__deprecated__))
-#  endif  // GNUC version
-#elif defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
-    /// Indicates that the following function is deprecated.
-#    define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
+#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
+#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
+#endif                  // GNUC version
+#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
+                        // MSVC)
+#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
 #endif // __clang__ || __GNUC__ || _MSC_VER
 
 #undef JSONCPP_DEPRECATED // no deprecations in CMake copy
@@ -142,53 +99,58 @@
 #define JSONCPP_DEPRECATED(message)
 #endif // if !defined(JSONCPP_DEPRECATED)
 
-#if __GNUC__ >= 6
-#  define JSON_USE_INT64_DOUBLE_CONVERSION 1
+#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
+#define JSON_USE_INT64_DOUBLE_CONVERSION 1
 #endif
 
 #if !defined(JSON_IS_AMALGAMATION)
 
-# include "version.h"
-
-# if JSONCPP_USING_SECURE_MEMORY
-#  include "allocator.h" //typedef Allocator
-# endif
+#include "allocator.h"
+#include "version.h"
 
 #endif // if !defined(JSON_IS_AMALGAMATION)
 
 namespace Json {
-typedef int Int;
-typedef unsigned int UInt;
+using Int = int;
+using UInt = unsigned int;
 #if defined(JSON_NO_INT64)
-typedef int LargestInt;
-typedef unsigned int LargestUInt;
+using LargestInt = int;
+using LargestUInt = unsigned int;
 #undef JSON_HAS_INT64
 #else                 // if defined(JSON_NO_INT64)
 // For Microsoft Visual use specific types as long long is not supported
 #if defined(_MSC_VER) // Microsoft Visual Studio
-typedef __int64 Int64;
-typedef unsigned __int64 UInt64;
+using Int64 = __int64;
+using UInt64 = unsigned __int64;
 #else                 // if defined(_MSC_VER) // Other platforms, use long long
-typedef int64_t Int64;
-typedef uint64_t UInt64;
-#endif // if defined(_MSC_VER)
-typedef Int64 LargestInt;
-typedef UInt64 LargestUInt;
+using Int64 = int64_t;
+using UInt64 = uint64_t;
+#endif                // if defined(_MSC_VER)
+using LargestInt = Int64;
+using LargestUInt = UInt64;
 #define JSON_HAS_INT64
 #endif // if defined(JSON_NO_INT64)
-#if JSONCPP_USING_SECURE_MEMORY
-#define JSONCPP_STRING        std::basic_string<char, std::char_traits<char>, Json::SecureAllocator<char> >
-#define JSONCPP_OSTRINGSTREAM std::basic_ostringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
-#define JSONCPP_OSTREAM       std::basic_ostream<char, std::char_traits<char>>
-#define JSONCPP_ISTRINGSTREAM std::basic_istringstream<char, std::char_traits<char>, Json::SecureAllocator<char> >
-#define JSONCPP_ISTREAM       std::istream
-#else
-#define JSONCPP_STRING        std::string
-#define JSONCPP_OSTRINGSTREAM std::ostringstream
-#define JSONCPP_OSTREAM       std::ostream
-#define JSONCPP_ISTRINGSTREAM std::istringstream
-#define JSONCPP_ISTREAM       std::istream
-#endif // if JSONCPP_USING_SECURE_MEMORY
-} // end namespace Json
+
+template <typename T>
+using Allocator =
+    typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
+                              std::allocator<T>>::type;
+using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
+using IStringStream =
+    std::basic_istringstream<String::value_type, String::traits_type,
+                             String::allocator_type>;
+using OStringStream =
+    std::basic_ostringstream<String::value_type, String::traits_type,
+                             String::allocator_type>;
+using IStream = std::istream;
+using OStream = std::ostream;
+} // namespace Json
+
+// Legacy names (formerly macros).
+using JSONCPP_STRING = Json::String;
+using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
+using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
+using JSONCPP_ISTREAM = Json::IStream;
+using JSONCPP_OSTREAM = Json::OStream;
 
 #endif // JSON_CONFIG_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/forwards.h b/Utilities/cmjsoncpp/include/json/forwards.h
index 70bbe19..affe33a 100644
--- a/Utilities/cmjsoncpp/include/json/forwards.h
+++ b/Utilities/cmjsoncpp/include/json/forwards.h
@@ -13,17 +13,23 @@
 namespace Json {
 
 // writer.h
+class StreamWriter;
+class StreamWriterBuilder;
+class Writer;
 class FastWriter;
 class StyledWriter;
+class StyledStreamWriter;
 
 // reader.h
 class Reader;
+class CharReader;
+class CharReaderBuilder;
 
-// features.h
+// json_features.h
 class Features;
 
 // value.h
-typedef unsigned int ArrayIndex;
+using ArrayIndex = unsigned int;
 class StaticString;
 class Path;
 class PathArgument;
diff --git a/Utilities/cmjsoncpp/include/json/json.h b/Utilities/cmjsoncpp/include/json/json.h
index 5964672..5c776a1 100644
--- a/Utilities/cmjsoncpp/include/json/json.h
+++ b/Utilities/cmjsoncpp/include/json/json.h
@@ -6,9 +6,10 @@
 #ifndef JSON_JSON_H_INCLUDED
 #define JSON_JSON_H_INCLUDED
 
-#include "value.h"
+#include "config.h"
+#include "json_features.h"
 #include "reader.h"
+#include "value.h"
 #include "writer.h"
-#include "features.h"
 
 #endif // JSON_JSON_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/features.h b/Utilities/cmjsoncpp/include/json/json_features.h
similarity index 87%
rename from Utilities/cmjsoncpp/include/json/features.h
rename to Utilities/cmjsoncpp/include/json/json_features.h
index 365abb3..6b99b4d 100644
--- a/Utilities/cmjsoncpp/include/json/features.h
+++ b/Utilities/cmjsoncpp/include/json/json_features.h
@@ -3,8 +3,8 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
-#define CPPTL_JSON_FEATURES_H_INCLUDED
+#ifndef JSON_FEATURES_H_INCLUDED
+#define JSON_FEATURES_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
 #include "forwards.h"
@@ -43,17 +43,17 @@
   Features();
 
   /// \c true if comments are allowed. Default: \c true.
-  bool allowComments_;
+  bool allowComments_{true};
 
   /// \c true if root must be either an array or an object value. Default: \c
   /// false.
-  bool strictRoot_;
+  bool strictRoot_{false};
 
   /// \c true if dropped null placeholders are allowed. Default: \c false.
-  bool allowDroppedNullPlaceholders_;
+  bool allowDroppedNullPlaceholders_{false};
 
   /// \c true if numeric object key are allowed. Default: \c false.
-  bool allowNumericKeys_;
+  bool allowNumericKeys_{false};
 };
 
 } // namespace Json
@@ -62,4 +62,4 @@
 #pragma pack(pop)
 #endif
 
-#endif // CPPTL_JSON_FEATURES_H_INCLUDED
+#endif // JSON_FEATURES_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/reader.h b/Utilities/cmjsoncpp/include/json/reader.h
index 667246a..7ad0be6 100644
--- a/Utilities/cmjsoncpp/include/json/reader.h
+++ b/Utilities/cmjsoncpp/include/json/reader.h
@@ -3,18 +3,18 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_READER_H_INCLUDED
-#define CPPTL_JSON_READER_H_INCLUDED
+#ifndef JSON_READER_H_INCLUDED
+#define JSON_READER_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
-#include "features.h"
+#include "json_features.h"
 #include "value.h"
 #endif // if !defined(JSON_IS_AMALGAMATION)
 #include <deque>
 #include <iosfwd>
+#include <istream>
 #include <stack>
 #include <string>
-#include <istream>
 
 // Disable warning C4251: <data member>: <type> needs to have dll-interface to
 // be used by...
@@ -30,132 +30,130 @@
 namespace Json {
 
 /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
- *Value.
+ * Value.
  *
  * deprecated Use CharReader and CharReaderBuilder.
  */
-class JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead") JSON_API Reader {
+
+class JSONCPP_DEPRECATED(
+    "Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
 public:
-  typedef char Char;
-  typedef const Char* Location;
+  using Char = char;
+  using Location = const Char*;
 
   /** \brief An error tagged with where in the JSON text it was encountered.
    *
    * The offsets give the [start, limit) range of bytes within the text. Note
    * that this is bytes, not codepoints.
-   *
    */
   struct StructuredError {
     ptrdiff_t offset_start;
     ptrdiff_t offset_limit;
-    JSONCPP_STRING message;
+    String message;
   };
 
-  /** \brief Constructs a Reader allowing all features
-   * for parsing.
+  /** \brief Constructs a Reader allowing all features for parsing.
    */
+  JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
   Reader();
 
-  /** \brief Constructs a Reader allowing the specified feature set
-   * for parsing.
+  /** \brief Constructs a Reader allowing the specified feature set for parsing.
    */
+  JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
   Reader(const Features& features);
 
   /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
    * document.
-   * \param document UTF-8 encoded string containing the document to read.
-   * \param root [out] Contains the root value of the document if it was
-   *             successfully parsed.
-   * \param collectComments \c true to collect comment and allow writing them
-   * back during
-   *                        serialization, \c false to discard comments.
-   *                        This parameter is ignored if
-   * Features::allowComments_
-   *                        is \c false.
+   *
+   * \param      document        UTF-8 encoded string containing the document
+   *                             to read.
+   * \param[out] root            Contains the root value of the document if it
+   *                             was successfully parsed.
+   * \param      collectComments \c true to collect comment and allow writing
+   *                             them back during serialization, \c false to
+   *                             discard comments.  This parameter is ignored
+   *                             if Features::allowComments_ is \c false.
    * \return \c true if the document was successfully parsed, \c false if an
    * error occurred.
    */
-  bool
-  parse(const std::string& document, Value& root, bool collectComments = true);
+  bool parse(const std::string& document, Value& root,
+             bool collectComments = true);
 
   /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
-   document.
-   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
-   document to read.
-   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
-   document to read.
-   *               Must be >= beginDoc.
-   * \param root [out] Contains the root value of the document if it was
-   *             successfully parsed.
-   * \param collectComments \c true to collect comment and allow writing them
-   back during
-   *                        serialization, \c false to discard comments.
-   *                        This parameter is ignored if
-   Features::allowComments_
-   *                        is \c false.
+   * document.
+   *
+   * \param      beginDoc        Pointer on the beginning of the UTF-8 encoded
+   *                             string of the document to read.
+   * \param      endDoc          Pointer on the end of the UTF-8 encoded string
+   *                             of the document to read.  Must be >= beginDoc.
+   * \param[out] root            Contains the root value of the document if it
+   *                             was successfully parsed.
+   * \param      collectComments \c true to collect comment and allow writing
+   *                             them back during serialization, \c false to
+   *                             discard comments.  This parameter is ignored
+   *                             if Features::allowComments_ is \c false.
    * \return \c true if the document was successfully parsed, \c false if an
-   error occurred.
+   * error occurred.
    */
-  bool parse(const char* beginDoc,
-             const char* endDoc,
-             Value& root,
+  bool parse(const char* beginDoc, const char* endDoc, Value& root,
              bool collectComments = true);
 
   /// \brief Parse from input stream.
   /// \see Json::operator>>(std::istream&, Json::Value&).
-  bool parse(JSONCPP_ISTREAM& is, Value& root, bool collectComments = true);
+  bool parse(IStream& is, Value& root, bool collectComments = true);
 
   /** \brief Returns a user friendly string that list errors in the parsed
    * document.
-   * \return Formatted error message with the list of errors with their location
-   * in
-   *         the parsed document. An empty string is returned if no error
-   * occurred
-   *         during parsing.
+   *
+   * \return Formatted error message with the list of errors with their
+   * location in the parsed document. An empty string is returned if no error
+   * occurred during parsing.
    * deprecated Use getFormattedErrorMessages() instead (typo fix).
    */
   JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
-  JSONCPP_STRING getFormatedErrorMessages() const;
+  String getFormatedErrorMessages() const;
 
   /** \brief Returns a user friendly string that list errors in the parsed
    * document.
-   * \return Formatted error message with the list of errors with their location
-   * in
-   *         the parsed document. An empty string is returned if no error
-   * occurred
-   *         during parsing.
+   *
+   * \return Formatted error message with the list of errors with their
+   * location in the parsed document. An empty string is returned if no error
+   * occurred during parsing.
    */
-  JSONCPP_STRING getFormattedErrorMessages() const;
+  String getFormattedErrorMessages() const;
 
-  /** \brief Returns a vector of structured erros encounted while parsing.
+  /** \brief Returns a vector of structured errors encountered while parsing.
+   *
    * \return A (possibly empty) vector of StructuredError objects. Currently
-   *         only one error can be returned, but the caller should tolerate
-   * multiple
-   *         errors.  This can occur if the parser recovers from a non-fatal
-   *         parse error and then encounters additional errors.
+   * only one error can be returned, but the caller should tolerate multiple
+   * errors.  This can occur if the parser recovers from a non-fatal parse
+   * error and then encounters additional errors.
    */
   std::vector<StructuredError> getStructuredErrors() const;
 
   /** \brief Add a semantic error message.
-   * \param value JSON Value location associated with the error
+   *
+   * \param value   JSON Value location associated with the error
    * \param message The error message.
-   * \return \c true if the error was successfully added, \c false if the
-   * Value offset exceeds the document size.
+   * \return \c true if the error was successfully added, \c false if the Value
+   * offset exceeds the document size.
    */
-  bool pushError(const Value& value, const JSONCPP_STRING& message);
+  bool pushError(const Value& value, const String& message);
 
   /** \brief Add a semantic error message with extra context.
-   * \param value JSON Value location associated with the error
+   *
+   * \param value   JSON Value location associated with the error
    * \param message The error message.
-   * \param extra Additional JSON Value location to contextualize the error
+   * \param extra   Additional JSON Value location to contextualize the error
    * \return \c true if the error was successfully added, \c false if either
    * Value offset exceeds the document size.
    */
-  bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra);
+  bool pushError(const Value& value, const String& message, const Value& extra);
 
   /** \brief Return whether there are any errors.
-   * \return \c true if there are no errors to report \c false if
-   * errors have occurred.
+   *
+   * \return \c true if there are no errors to report \c false if errors have
+   * occurred.
    */
   bool good() const;
 
@@ -187,15 +185,15 @@
   class ErrorInfo {
   public:
     Token token_;
-    JSONCPP_STRING message_;
+    String message_;
     Location extra_;
   };
 
-  typedef std::deque<ErrorInfo> Errors;
+  using Errors = std::deque<ErrorInfo>;
 
   bool readToken(Token& token);
   void skipSpaces();
-  bool match(Location pattern, int patternLength);
+  bool match(const Char* pattern, int patternLength);
   bool readComment();
   bool readCStyleComment();
   bool readCppStyleComment();
@@ -207,142 +205,138 @@
   bool decodeNumber(Token& token);
   bool decodeNumber(Token& token, Value& decoded);
   bool decodeString(Token& token);
-  bool decodeString(Token& token, JSONCPP_STRING& decoded);
+  bool decodeString(Token& token, String& decoded);
   bool decodeDouble(Token& token);
   bool decodeDouble(Token& token, Value& decoded);
-  bool decodeUnicodeCodePoint(Token& token,
-                              Location& current,
-                              Location end,
+  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
                               unsigned int& unicode);
-  bool decodeUnicodeEscapeSequence(Token& token,
-                                   Location& current,
-                                   Location end,
-                                   unsigned int& unicode);
-  bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0);
+  bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+                                   Location end, unsigned int& unicode);
+  bool addError(const String& message, Token& token, Location extra = nullptr);
   bool recoverFromError(TokenType skipUntilToken);
-  bool addErrorAndRecover(const JSONCPP_STRING& message,
-                          Token& token,
+  bool addErrorAndRecover(const String& message, Token& token,
                           TokenType skipUntilToken);
   void skipUntilSpace();
   Value& currentValue();
   Char getNextChar();
-  void
-  getLocationLineAndColumn(Location location, int& line, int& column) const;
-  JSONCPP_STRING getLocationLineAndColumn(Location location) const;
+  void getLocationLineAndColumn(Location location, int& line,
+                                int& column) const;
+  String getLocationLineAndColumn(Location location) const;
   void addComment(Location begin, Location end, CommentPlacement placement);
   void skipCommentTokens(Token& token);
 
   static bool containsNewLine(Location begin, Location end);
-  static JSONCPP_STRING normalizeEOL(Location begin, Location end);
+  static String normalizeEOL(Location begin, Location end);
 
-  typedef std::stack<Value*> Nodes;
+  using Nodes = std::stack<Value*>;
   Nodes nodes_;
   Errors errors_;
-  JSONCPP_STRING document_;
-  Location begin_;
-  Location end_;
-  Location current_;
-  Location lastValueEnd_;
-  Value* lastValue_;
-  JSONCPP_STRING commentsBefore_;
+  String document_;
+  Location begin_{};
+  Location end_{};
+  Location current_{};
+  Location lastValueEnd_{};
+  Value* lastValue_{};
+  String commentsBefore_;
   Features features_;
-  bool collectComments_;
-};  // Reader
+  bool collectComments_{};
+}; // Reader
 
 /** Interface for reading JSON from a char array.
  */
 class JSON_API CharReader {
 public:
-  virtual ~CharReader() {}
+  virtual ~CharReader() = default;
   /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
-   document.
-   * The document must be a UTF-8 encoded string containing the document to read.
+   * document. The document must be a UTF-8 encoded string containing the
+   * document to read.
    *
-   * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the
-   document to read.
-   * \param endDoc Pointer on the end of the UTF-8 encoded string of the
-   document to read.
-   *        Must be >= beginDoc.
-   * \param root [out] Contains the root value of the document if it was
-   *             successfully parsed.
-   * \param errs [out] Formatted error messages (if not NULL)
-   *        a user friendly string that lists errors in the parsed
-   * document.
+   * \param      beginDoc Pointer on the beginning of the UTF-8 encoded string
+   *                      of the document to read.
+   * \param      endDoc   Pointer on the end of the UTF-8 encoded string of the
+   *                      document to read. Must be >= beginDoc.
+   * \param[out] root     Contains the root value of the document if it was
+   *                      successfully parsed.
+   * \param[out] errs     Formatted error messages (if not NULL) a user
+   *                      friendly string that lists errors in the parsed
+   *                      document.
    * \return \c true if the document was successfully parsed, \c false if an
-   error occurred.
+   * error occurred.
    */
-  virtual bool parse(
-      char const* beginDoc, char const* endDoc,
-      Value* root, JSONCPP_STRING* errs) = 0;
+  virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
+                     String* errs) = 0;
 
   class JSON_API Factory {
   public:
-    virtual ~Factory() {}
+    virtual ~Factory() = default;
     /** \brief Allocate a CharReader via operator new().
      * \throw std::exception if something goes wrong (e.g. invalid settings)
      */
     virtual CharReader* newCharReader() const = 0;
-  };  // Factory
-};  // CharReader
+  }; // Factory
+};   // CharReader
 
 /** \brief Build a CharReader implementation.
-
-Usage:
-\code
-  using namespace Json;
-  CharReaderBuilder builder;
-  builder["collectComments"] = false;
-  Value value;
-  JSONCPP_STRING errs;
-  bool ok = parseFromStream(builder, std::cin, &value, &errs);
-\endcode
-*/
+ *
+ * Usage:
+ *   \code
+ *   using namespace Json;
+ *   CharReaderBuilder builder;
+ *   builder["collectComments"] = false;
+ *   Value value;
+ *   String errs;
+ *   bool ok = parseFromStream(builder, std::cin, &value, &errs);
+ *   \endcode
+ */
 class JSON_API CharReaderBuilder : public CharReader::Factory {
 public:
   // Note: We use a Json::Value so that we can add data-members to this class
   // without a major version bump.
   /** Configuration of this builder.
-    These are case-sensitive.
-    Available settings (case-sensitive):
-    - `"collectComments": false or true`
-      - true to collect comment and allow writing them
-        back during serialization, false to discard comments.
-        This parameter is ignored if allowComments is false.
-    - `"allowComments": false or true`
-      - true if comments are allowed.
-    - `"strictRoot": false or true`
-      - true if root must be either an array or an object value
-    - `"allowDroppedNullPlaceholders": false or true`
-      - true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
-    - `"allowNumericKeys": false or true`
-      - true if numeric object keys are allowed.
-    - `"allowSingleQuotes": false or true`
-      - true if '' are allowed for strings (both keys and values)
-    - `"stackLimit": integer`
-      - Exceeding stackLimit (recursive depth of `readValue()`) will
-        cause an exception.
-      - This is a security issue (seg-faults caused by deeply nested JSON),
-        so the default is low.
-    - `"failIfExtra": false or true`
-      - If true, `parse()` returns false when extra non-whitespace trails
-        the JSON value in the input string.
-    - `"rejectDupKeys": false or true`
-      - If true, `parse()` returns false when a key is duplicated within an object.
-    - `"allowSpecialFloats": false or true`
-      - If true, special float values (NaNs and infinities) are allowed 
-        and their values are lossfree restorable.
-
-    You can examine 'settings_` yourself
-    to see the defaults. You can also write and read them just like any
-    JSON Value.
-    \sa setDefaults()
-    */
+   * These are case-sensitive.
+   * Available settings (case-sensitive):
+   * - `"collectComments": false or true`
+   *   - true to collect comment and allow writing them back during
+   *     serialization, false to discard comments.  This parameter is ignored
+   *     if allowComments is false.
+   * - `"allowComments": false or true`
+   *   - true if comments are allowed.
+   * - `"allowTrailingCommas": false or true`
+   *   - true if trailing commas in objects and arrays are allowed.
+   * - `"strictRoot": false or true`
+   *   - true if root must be either an array or an object value
+   * - `"allowDroppedNullPlaceholders": false or true`
+   *   - true if dropped null placeholders are allowed. (See
+   *     StreamWriterBuilder.)
+   * - `"allowNumericKeys": false or true`
+   *   - true if numeric object keys are allowed.
+   * - `"allowSingleQuotes": false or true`
+   *   - true if '' are allowed for strings (both keys and values)
+   * - `"stackLimit": integer`
+   *   - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
+   *     exception.
+   *   - This is a security issue (seg-faults caused by deeply nested JSON), so
+   *     the default is low.
+   * - `"failIfExtra": false or true`
+   *   - If true, `parse()` returns false when extra non-whitespace trails the
+   *     JSON value in the input string.
+   * - `"rejectDupKeys": false or true`
+   *   - If true, `parse()` returns false when a key is duplicated within an
+   *     object.
+   * - `"allowSpecialFloats": false or true`
+   *   - If true, special float values (NaNs and infinities) are allowed and
+   *     their values are lossfree restorable.
+   *
+   * You can examine 'settings_` yourself to see the defaults. You can also
+   * write and read them just like any JSON Value.
+   * \sa setDefaults()
+   */
   Json::Value settings_;
 
   CharReaderBuilder();
-  ~CharReaderBuilder() JSONCPP_OVERRIDE;
+  ~CharReaderBuilder() override;
 
-  CharReader* newCharReader() const JSONCPP_OVERRIDE;
+  CharReader* newCharReader() const override;
 
   /** \return true if 'settings' are legal and consistent;
    *   otherwise, indicate bad settings via 'invalid'.
@@ -351,7 +345,7 @@
 
   /** A simple way to update a specific setting.
    */
-  Value& operator[](JSONCPP_STRING key);
+  Value& operator[](const String& key);
 
   /** Called by ctor, but you can use this to reset settings_.
    * \pre 'settings' != NULL (but Json::null is fine)
@@ -368,39 +362,37 @@
 };
 
 /** Consume entire stream and use its begin/end.
-  * Someday we might have a real StreamReader, but for now this
-  * is convenient.
-  */
-bool JSON_API parseFromStream(
-    CharReader::Factory const&,
-    JSONCPP_ISTREAM&,
-    Value* root, std::string* errs);
+ * Someday we might have a real StreamReader, but for now this
+ * is convenient.
+ */
+bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
+                              String* errs);
 
 /** \brief Read from 'sin' into 'root'.
-
- Always keep comments from the input JSON.
-
- This can be used to read a file into a particular sub-object.
- For example:
- \code
- Json::Value root;
- cin >> root["dir"]["file"];
- cout << root;
- \endcode
- Result:
- \verbatim
- {
- "dir": {
-     "file": {
-     // The input stream JSON would be nested here.
-     }
- }
- }
- \endverbatim
- \throw std::exception on parse error.
- \see Json::operator<<()
-*/
-JSON_API JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM&, Value&);
+ *
+ * Always keep comments from the input JSON.
+ *
+ * This can be used to read a file into a particular sub-object.
+ * For example:
+ *   \code
+ *   Json::Value root;
+ *   cin >> root["dir"]["file"];
+ *   cout << root;
+ *   \endcode
+ * Result:
+ * \verbatim
+ * {
+ * "dir": {
+ *    "file": {
+ *    // The input stream JSON would be nested here.
+ *    }
+ * }
+ * }
+ * \endverbatim
+ * \throw std::exception on parse error.
+ * \see Json::operator<<()
+ */
+JSON_API IStream& operator>>(IStream&, Value&);
 
 } // namespace Json
 
@@ -412,4 +404,4 @@
 #pragma warning(pop)
 #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
 
-#endif // CPPTL_JSON_READER_H_INCLUDED
+#endif // JSON_READER_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/value.h b/Utilities/cmjsoncpp/include/json/value.h
index 7b90c5a..952d423 100644
--- a/Utilities/cmjsoncpp/include/json/value.h
+++ b/Utilities/cmjsoncpp/include/json/value.h
@@ -3,37 +3,48 @@
 // recognized in your jurisdiction.
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
-#ifndef CPPTL_JSON_H_INCLUDED
-#define CPPTL_JSON_H_INCLUDED
+#ifndef JSON_H_INCLUDED
+#define JSON_H_INCLUDED
 
 #if !defined(JSON_IS_AMALGAMATION)
 #include "forwards.h"
 #endif // if !defined(JSON_IS_AMALGAMATION)
-#include <string>
-#include <vector>
-#include <exception>
 
-#ifndef JSON_USE_CPPTL_SMALLMAP
-#include <map>
-#else
-#include <cpptl/smallmap.h>
-#endif
-#ifdef JSON_USE_CPPTL
-#include <cpptl/forwards.h>
-#endif
-
-//Conditional NORETURN attribute on the throw functions would:
+// Conditional NORETURN attribute on the throw functions would:
 // a) suppress false positives from static code analysis
 // b) possibly improve optimization opportunities.
 #if !defined(JSONCPP_NORETURN)
-#  if defined(_MSC_VER)
-#    define JSONCPP_NORETURN __declspec(noreturn)
-#  elif defined(__GNUC__)
-#    define JSONCPP_NORETURN __attribute__ ((__noreturn__))
-#  else
-#    define JSONCPP_NORETURN
-#  endif
+#if defined(_MSC_VER) && _MSC_VER == 1800
+#define JSONCPP_NORETURN __declspec(noreturn)
+#else
+#define JSONCPP_NORETURN [[noreturn]]
 #endif
+#endif
+
+// Support for '= delete' with template declarations was a late addition
+// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
+// even though these declare themselves to be c++11 compilers.
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#if defined(__clang__) && defined(__apple_build_version__)
+#if __apple_build_version__ <= 8000042
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#elif defined(__clang__)
+#if __clang_major__ == 3 && __clang_minor__ <= 8
+#define JSONCPP_TEMPLATE_DELETE
+#endif
+#endif
+#if !defined(JSONCPP_TEMPLATE_DELETE)
+#define JSONCPP_TEMPLATE_DELETE = delete
+#endif
+#endif
+
+#include <array>
+#include <exception>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
 
 // Disable warning C4251: <data member>: <type> needs to have dll-interface to
 // be used by...
@@ -50,17 +61,19 @@
  */
 namespace Json {
 
+#if JSON_USE_EXCEPTION
 /** Base class for all exceptions we throw.
  *
  * We use nothing but these internally. Of course, STL can throw others.
  */
 class JSON_API Exception : public std::exception {
 public:
-  Exception(JSONCPP_STRING const& msg);
-  ~Exception() JSONCPP_NOEXCEPT JSONCPP_OVERRIDE;
-  char const* what() const JSONCPP_NOEXCEPT JSONCPP_OVERRIDE;
+  Exception(String msg);
+  ~Exception() noexcept override;
+  char const* what() const noexcept override;
+
 protected:
-  JSONCPP_STRING msg_;
+  String msg_;
 };
 
 /** Exceptions which the user cannot easily avoid.
@@ -71,7 +84,7 @@
  */
 class JSON_API RuntimeError : public Exception {
 public:
-  RuntimeError(JSONCPP_STRING const& msg);
+  RuntimeError(String const& msg);
 };
 
 /** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
@@ -82,13 +95,14 @@
  */
 class JSON_API LogicError : public Exception {
 public:
-  LogicError(JSONCPP_STRING const& msg);
+  LogicError(String const& msg);
 };
+#endif
 
 /// used internally
-JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg);
+JSONCPP_NORETURN void throwRuntimeError(String const& msg);
 /// used internally
-JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg);
+JSONCPP_NORETURN void throwLogicError(String const& msg);
 
 /** \brief Type of the value held by a Value object.
  */
@@ -111,14 +125,16 @@
   numberOfCommentPlacement
 };
 
-//# ifdef JSON_USE_CPPTL
-//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
-//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
-//# endif
+/** \brief Type of precision for formatting of real values.
+ */
+enum PrecisionType {
+  significantDigits = 0, ///< we set max number of significant digits in string
+  decimalPlaces          ///< we set max number of digits after "." in string
+};
 
 /** \brief Lightweight wrapper to tag static string.
  *
- * Value constructor and objectValue member assignement takes advantage of the
+ * Value constructor and objectValue member assignment takes advantage of the
  * StaticString and avoid the cost of string duplication when storing the
  * string or the member name.
  *
@@ -167,7 +183,7 @@
  * The get() methods can be used to obtain default value in the case the
  * required element does not exist.
  *
- * It is possible to iterate over the list of a #objectValue values using
+ * It is possible to iterate over the list of member keys of an object using
  * the getMemberNames() method.
  *
  * \note #Value string-length fit in size_t, but keys must be < 2^30.
@@ -178,73 +194,86 @@
  */
 class JSON_API Value {
   friend class ValueIteratorBase;
-public:
-  typedef std::vector<JSONCPP_STRING> Members;
-  typedef ValueIterator iterator;
-  typedef ValueConstIterator const_iterator;
-  typedef Json::UInt UInt;
-  typedef Json::Int Int;
-#if defined(JSON_HAS_INT64)
-  typedef Json::UInt64 UInt64;
-  typedef Json::Int64 Int64;
-#endif // defined(JSON_HAS_INT64)
-  typedef Json::LargestInt LargestInt;
-  typedef Json::LargestUInt LargestUInt;
-  typedef Json::ArrayIndex ArrayIndex;
 
-  static const Value& null;  ///< We regret this reference to a global instance; prefer the simpler Value().
-  static const Value& nullRef;  ///< just a kludge for binary-compatibility; same as null
-  static Value const& nullSingleton(); ///< Prefer this to null or nullRef.
+public:
+  using Members = std::vector<String>;
+  using iterator = ValueIterator;
+  using const_iterator = ValueConstIterator;
+  using UInt = Json::UInt;
+  using Int = Json::Int;
+#if defined(JSON_HAS_INT64)
+  using UInt64 = Json::UInt64;
+  using Int64 = Json::Int64;
+#endif // defined(JSON_HAS_INT64)
+  using LargestInt = Json::LargestInt;
+  using LargestUInt = Json::LargestUInt;
+  using ArrayIndex = Json::ArrayIndex;
+
+  // Required for boost integration, e. g. BOOST_TEST
+  using value_type = std::string;
+
+#if JSON_USE_NULLREF
+  // Binary compatibility kludges, do not use.
+  static const Value& null;
+  static const Value& nullRef;
+#endif
+
+  // null and nullRef are deprecated, use this instead.
+  static Value const& nullSingleton();
 
   /// Minimum signed integer value that can be stored in a Json::Value.
-  static const LargestInt minLargestInt;
+  static constexpr LargestInt minLargestInt =
+      LargestInt(~(LargestUInt(-1) / 2));
   /// Maximum signed integer value that can be stored in a Json::Value.
-  static const LargestInt maxLargestInt;
+  static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
   /// Maximum unsigned integer value that can be stored in a Json::Value.
-  static const LargestUInt maxLargestUInt;
+  static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
 
   /// Minimum signed int value that can be stored in a Json::Value.
-  static const Int minInt;
+  static constexpr Int minInt = Int(~(UInt(-1) / 2));
   /// Maximum signed int value that can be stored in a Json::Value.
-  static const Int maxInt;
+  static constexpr Int maxInt = Int(UInt(-1) / 2);
   /// Maximum unsigned int value that can be stored in a Json::Value.
-  static const UInt maxUInt;
+  static constexpr UInt maxUInt = UInt(-1);
 
 #if defined(JSON_HAS_INT64)
   /// Minimum signed 64 bits int value that can be stored in a Json::Value.
-  static const Int64 minInt64;
+  static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
   /// Maximum signed 64 bits int value that can be stored in a Json::Value.
-  static const Int64 maxInt64;
+  static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
   /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
-  static const UInt64 maxUInt64;
+  static constexpr UInt64 maxUInt64 = UInt64(-1);
 #endif // defined(JSON_HAS_INT64)
-
+  /// Default precision for real value for string representation.
+  static constexpr UInt defaultRealPrecision = 17;
+  // The constant is hard-coded because some compiler have trouble
+  // converting Value::maxUInt64 to a double correctly (AIX/xlC).
+  // Assumes that UInt64 is a 64 bits integer.
+  static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
+// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
+// when using gcc and clang backend compilers.  CZString
+// cannot be defined as private.  See issue #486
+#ifdef __NVCC__
+public:
+#else
 private:
+#endif
 #ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
   class CZString {
   public:
-    enum DuplicationPolicy {
-      noDuplication = 0,
-      duplicate,
-      duplicateOnCopy
-    };
+    enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
     CZString(ArrayIndex index);
     CZString(char const* str, unsigned length, DuplicationPolicy allocate);
     CZString(CZString const& other);
-#if JSON_HAS_RVALUE_REFERENCES
     CZString(CZString&& other);
-#endif
     ~CZString();
     CZString& operator=(const CZString& other);
-
-#if JSON_HAS_RVALUE_REFERENCES
     CZString& operator=(CZString&& other);
-#endif
 
     bool operator<(CZString const& other) const;
     bool operator==(CZString const& other) const;
     ArrayIndex index() const;
-    //const char* c_str() const; ///< deprecated
+    // const char* c_str() const; ///< deprecated
     char const* data() const;
     unsigned length() const;
     bool isStaticString() const;
@@ -253,11 +282,11 @@
     void swap(CZString& other);
 
     struct StringStorage {
-      unsigned policy_: 2;
-      unsigned length_: 30; // 1GB max
+      unsigned policy_ : 2;
+      unsigned length_ : 30; // 1GB max
     };
 
-    char const* cstr_;  // actually, a prefixed string, unless policy is noDup
+    char const* cstr_; // actually, a prefixed string, unless policy is noDup
     union {
       ArrayIndex index_;
       StringStorage storage_;
@@ -265,29 +294,26 @@
   };
 
 public:
-#ifndef JSON_USE_CPPTL_SMALLMAP
   typedef std::map<CZString, Value> ObjectValues;
-#else
-  typedef CppTL::SmallMap<CZString, Value> ObjectValues;
-#endif // ifndef JSON_USE_CPPTL_SMALLMAP
 #endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
 
 public:
-  /** \brief Create a default Value of the given type.
-
-    This is a very useful constructor.
-    To create an empty array, pass arrayValue.
-    To create an empty object, pass objectValue.
-    Another Value can then be set to this one by assignment.
-This is useful since clear() and resize() will not alter types.
-
-    Examples:
-\code
-Json::Value null_value; // null
-Json::Value arr_value(Json::arrayValue); // []
-Json::Value obj_value(Json::objectValue); // {}
-\endcode
-  */
+  /**
+   * \brief Create a default Value of the given type.
+   *
+   * This is a very useful constructor.
+   * To create an empty array, pass arrayValue.
+   * To create an empty object, pass objectValue.
+   * Another Value can then be set to this one by assignment.
+   * This is useful since clear() and resize() will not alter types.
+   *
+   * Examples:
+   *   \code
+   *   Json::Value null_value; // null
+   *   Json::Value arr_value(Json::arrayValue); // []
+   *   Json::Value obj_value(Json::objectValue); // {}
+   *   \endcode
+   */
   Value(ValueType type = nullValue);
   Value(Int value);
   Value(UInt value);
@@ -298,38 +324,35 @@
   Value(double value);
   Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
   Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
-  /** \brief Constructs a value from a static string.
-
+  /**
+   * \brief Constructs a value from a static string.
+   *
    * Like other value string constructor but do not duplicate the string for
-   * internal storage. The given string must remain alive after the call to this
-   * constructor.
+   * internal storage. The given string must remain alive after the call to
+   * this constructor.
+   *
    * \note This works only for null-terminated strings. (We cannot change the
-   *   size of this class, so we have nowhere to store the length,
-   *   which might be computed later for various operations.)
+   * size of this class, so we have nowhere to store the length, which might be
+   * computed later for various operations.)
    *
    * Example of usage:
-   * \code
-   * static StaticString foo("some text");
-   * Json::Value aValue(foo);
-   * \endcode
+   *   \code
+   *   static StaticString foo("some text");
+   *   Json::Value aValue(foo);
+   *   \endcode
    */
   Value(const StaticString& value);
-  Value(const JSONCPP_STRING& value); ///< Copy data() til size(). Embedded zeroes too.
-#ifdef JSON_USE_CPPTL
-  Value(const CppTL::ConstString& value);
-#endif
+  Value(const String& value);
   Value(bool value);
-  /// Deep copy.
+  Value(std::nullptr_t ptr) = delete;
   Value(const Value& other);
-#if JSON_HAS_RVALUE_REFERENCES
-  /// Move constructor
   Value(Value&& other);
-#endif
   ~Value();
 
-  /// Deep copy, then swap(other).
-  /// \note Over-write existing comments. To preserve comments, use #swapPayload().
-  Value& operator=(Value other);
+  /// \note Overwrite existing comments. To preserve comments, use
+  /// #swapPayload().
+  Value& operator=(const Value& other);
+  Value& operator=(Value&& other);
 
   /// Swap everything.
   void swap(Value& other);
@@ -354,17 +377,14 @@
 
   const char* asCString() const; ///< Embedded zeroes could cause you trouble!
 #if JSONCPP_USING_SECURE_MEMORY
-  unsigned getCStringLength() const; //Allows you to understand the length of the CString
+  unsigned getCStringLength() const; // Allows you to understand the length of
+                                     // the CString
 #endif
-  JSONCPP_STRING asString() const; ///< Embedded zeroes are possible.
+  String asString() const; ///< Embedded zeroes are possible.
   /** Get raw char* of string-value.
    *  \return false if !string. (Seg-fault if str or end are NULL.)
    */
-  bool getString(
-      char const** begin, char const** end) const;
-#ifdef JSON_USE_CPPTL
-  CppTL::ConstString asConstString() const;
-#endif
+  bool getString(char const** begin, char const** end) const;
   Int asInt() const;
   UInt asUInt() const;
 #if defined(JSON_HAS_INT64)
@@ -390,6 +410,10 @@
   bool isArray() const;
   bool isObject() const;
 
+  /// The `as<T>` and `is<T>` member function templates and specializations.
+  template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
+  template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
+
   bool isConvertibleTo(ValueType other) const;
 
   /// Number of values in array or object
@@ -399,50 +423,41 @@
   /// otherwise, false.
   bool empty() const;
 
-  /// Return isNull()
-  bool operator!() const;
+  /// Return !isNull()
+  explicit operator bool() const;
 
   /// Remove all object members and array elements.
   /// \pre type() is arrayValue, objectValue, or nullValue
   /// \post type() is unchanged
   void clear();
 
-  /// Resize the array to size elements.
+  /// Resize the array to newSize elements.
   /// New elements are initialized to null.
   /// May only be called on nullValue or arrayValue.
   /// \pre type() is arrayValue or nullValue
   /// \post type() is arrayValue
-  void resize(ArrayIndex size);
+  void resize(ArrayIndex newSize);
 
-  /// Access an array element (zero based index ).
-  /// If the array contains less than index element, then null value are
-  /// inserted
-  /// in the array so that its size is index+1.
+  //@{
+  /// Access an array element (zero based index). If the array contains less
+  /// than index element, then null value are inserted in the array so that
+  /// its size is index+1.
   /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
+  /// this from the operator[] which takes a string.)
   Value& operator[](ArrayIndex index);
-
-  /// Access an array element (zero based index ).
-  /// If the array contains less than index element, then null value are
-  /// inserted
-  /// in the array so that its size is index+1.
-  /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
   Value& operator[](int index);
+  //@}
 
-  /// Access an array element (zero based index )
+  //@{
+  /// Access an array element (zero based index).
   /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
+  /// this from the operator[] which takes a string.)
   const Value& operator[](ArrayIndex index) const;
-
-  /// Access an array element (zero based index )
-  /// (You may need to say 'value[0u]' to get your compiler to distinguish
-  ///  this from the operator[] which takes a string.)
   const Value& operator[](int index) const;
+  //@}
 
   /// If the array contains at least index+1 elements, returns the element
-  /// value,
-  /// otherwise returns defaultValue.
+  /// value, otherwise returns defaultValue.
   Value get(ArrayIndex index, const Value& defaultValue) const;
   /// Return true if index < size().
   bool isValidIndex(ArrayIndex index) const;
@@ -450,61 +465,51 @@
   ///
   /// Equivalent to jsonvalue[jsonvalue.size()] = value;
   Value& append(const Value& value);
-
-#if JSON_HAS_RVALUE_REFERENCES
   Value& append(Value&& value);
-#endif
+
+  /// \brief Insert value in array at specific index
+  bool insert(ArrayIndex index, const Value& newValue);
+  bool insert(ArrayIndex index, Value&& newValue);
 
   /// Access an object value by name, create a null member if it does not exist.
   /// \note Because of our implementation, keys are limited to 2^30 -1 chars.
-  ///  Exceeding that will cause an exception.
+  /// Exceeding that will cause an exception.
   Value& operator[](const char* key);
   /// Access an object value by name, returns null if there is no member with
   /// that name.
   const Value& operator[](const char* key) const;
   /// Access an object value by name, create a null member if it does not exist.
   /// \param key may contain embedded nulls.
-  Value& operator[](const JSONCPP_STRING& key);
+  Value& operator[](const String& key);
   /// Access an object value by name, returns null if there is no member with
   /// that name.
   /// \param key may contain embedded nulls.
-  const Value& operator[](const JSONCPP_STRING& key) const;
+  const Value& operator[](const String& key) const;
   /** \brief Access an object value by name, create a null member if it does not
-   exist.
-
-   * If the object has no entry for that name, then the member name used to store
-   * the new entry is not duplicated.
+   * exist.
+   *
+   * If the object has no entry for that name, then the member name used to
+   * store the new entry is not duplicated.
    * Example of use:
-   * \code
-   * Json::Value object;
-   * static const StaticString code("code");
-   * object[code] = 1234;
-   * \endcode
+   *   \code
+   *   Json::Value object;
+   *   static const StaticString code("code");
+   *   object[code] = 1234;
+   *   \endcode
    */
   Value& operator[](const StaticString& key);
-#ifdef JSON_USE_CPPTL
-  /// Access an object value by name, create a null member if it does not exist.
-  Value& operator[](const CppTL::ConstString& key);
-  /// Access an object value by name, returns null if there is no member with
-  /// that name.
-  const Value& operator[](const CppTL::ConstString& key) const;
-#endif
   /// Return the member named key if it exist, defaultValue otherwise.
   /// \note deep copy
   Value get(const char* key, const Value& defaultValue) const;
   /// Return the member named key if it exist, defaultValue otherwise.
   /// \note deep copy
   /// \note key may contain embedded nulls.
-  Value get(const char* begin, const char* end, const Value& defaultValue) const;
+  Value get(const char* begin, const char* end,
+            const Value& defaultValue) const;
   /// Return the member named key if it exist, defaultValue otherwise.
   /// \note deep copy
   /// \param key may contain embedded nulls.
-  Value get(const JSONCPP_STRING& key, const Value& defaultValue) const;
-#ifdef JSON_USE_CPPTL
-  /// Return the member named key if it exist, defaultValue otherwise.
-  /// \note deep copy
-  Value get(const CppTL::ConstString& key, const Value& defaultValue) const;
-#endif
+  Value get(const String& key, const Value& defaultValue) const;
   /// Most general and efficient version of isMember()const, get()const,
   /// and operator[]const
   /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
@@ -512,53 +517,44 @@
   /// Most general and efficient version of object-mutators.
   /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
   /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
-  Value const* demand(char const* begin, char const* end);
+  Value* demand(char const* begin, char const* end);
   /// \brief Remove and return the named member.
   ///
   /// Do nothing if it did not exist.
-  /// \return the removed Value, or null.
   /// \pre type() is objectValue or nullValue
   /// \post type() is unchanged
-  /// deprecated
-  JSONCPP_DEPRECATED("")
-  Value removeMember(const char* key);
+  void removeMember(const char* key);
   /// Same as removeMember(const char*)
   /// \param key may contain embedded nulls.
-  /// deprecated
-  JSONCPP_DEPRECATED("")
-  Value removeMember(const JSONCPP_STRING& key);
+  void removeMember(const String& key);
   /// Same as removeMember(const char* begin, const char* end, Value* removed),
   /// but 'key' is null-terminated.
   bool removeMember(const char* key, Value* removed);
   /** \brief Remove the named map member.
-
-      Update 'removed' iff removed.
-      \param key may contain embedded nulls.
-      \return true iff removed (no exceptions)
-  */
-  bool removeMember(JSONCPP_STRING const& key, Value* removed);
-  /// Same as removeMember(JSONCPP_STRING const& key, Value* removed)
+   *
+   *  Update 'removed' iff removed.
+   *  \param key may contain embedded nulls.
+   *  \return true iff removed (no exceptions)
+   */
+  bool removeMember(String const& key, Value* removed);
+  /// Same as removeMember(String const& key, Value* removed)
   bool removeMember(const char* begin, const char* end, Value* removed);
   /** \brief Remove the indexed array element.
-
-      O(n) expensive operations.
-      Update 'removed' iff removed.
-      \return true iff removed (no exceptions)
-  */
-  bool removeIndex(ArrayIndex i, Value* removed);
+   *
+   *  O(n) expensive operations.
+   *  Update 'removed' iff removed.
+   *  \return true if removed (no exceptions)
+   */
+  bool removeIndex(ArrayIndex index, Value* removed);
 
   /// Return true if the object has a member named key.
   /// \note 'key' must be null-terminated.
   bool isMember(const char* key) const;
   /// Return true if the object has a member named key.
   /// \param key may contain embedded nulls.
-  bool isMember(const JSONCPP_STRING& key) const;
-  /// Same as isMember(JSONCPP_STRING const& key)const
+  bool isMember(const String& key) const;
+  /// Same as isMember(String const& key)const
   bool isMember(const char* begin, const char* end) const;
-#ifdef JSON_USE_CPPTL
-  /// Return true if the object has a member named key.
-  bool isMember(const CppTL::ConstString& key) const;
-#endif
 
   /// \brief Return a list of the member names.
   ///
@@ -567,23 +563,22 @@
   /// \post if type() was nullValue, it remains nullValue
   Members getMemberNames() const;
 
-  //# ifdef JSON_USE_CPPTL
-  //      EnumMemberNames enumMemberNames() const;
-  //      EnumValues enumValues() const;
-  //# endif
-
   /// deprecated Always pass len.
-  JSONCPP_DEPRECATED("Use setComment(JSONCPP_STRING const&) instead.")
-  void setComment(const char* comment, CommentPlacement placement);
+  JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
+  void setComment(const char* comment, CommentPlacement placement) {
+    setComment(String(comment, strlen(comment)), placement);
+  }
   /// Comments must be //... or /* ... */
-  void setComment(const char* comment, size_t len, CommentPlacement placement);
+  void setComment(const char* comment, size_t len, CommentPlacement placement) {
+    setComment(String(comment, len), placement);
+  }
   /// Comments must be //... or /* ... */
-  void setComment(const JSONCPP_STRING& comment, CommentPlacement placement);
+  void setComment(String comment, CommentPlacement placement);
   bool hasComment(CommentPlacement placement) const;
   /// Include delimiters and embedded newlines.
-  JSONCPP_STRING getComment(CommentPlacement placement) const;
+  String getComment(CommentPlacement placement) const;
 
-  JSONCPP_STRING toStyledString() const;
+  String toStyledString() const;
 
   const_iterator begin() const;
   const_iterator end() const;
@@ -599,20 +594,20 @@
   ptrdiff_t getOffsetLimit() const;
 
 private:
+  void setType(ValueType v) {
+    bits_.value_type_ = static_cast<unsigned char>(v);
+  }
+  bool isAllocated() const { return bits_.allocated_; }
+  void setIsAllocated(bool v) { bits_.allocated_ = v; }
+
   void initBasic(ValueType type, bool allocated = false);
+  void dupPayload(const Value& other);
+  void releasePayload();
+  void dupMeta(const Value& other);
 
   Value& resolveReference(const char* key);
   Value& resolveReference(const char* key, const char* end);
 
-  struct CommentInfo {
-    CommentInfo();
-    ~CommentInfo();
-
-    void setComment(const char* text, size_t len);
-
-    char* comment_;
-  };
-
   // struct MemberNamesTransform
   //{
   //   typedef const char *result_type;
@@ -627,13 +622,33 @@
     LargestUInt uint_;
     double real_;
     bool bool_;
-    char* string_;  // actually ptr to unsigned, followed by str, unless !allocated_
+    char* string_; // if allocated_, ptr to { unsigned, char[] }.
     ObjectValues* map_;
   } value_;
-  ValueType type_ : 8;
-  unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless.
-                               // If not allocated_, string_ must be null-terminated.
-  CommentInfo* comments_;
+
+  struct {
+    // Really a ValueType, but types should agree for bitfield packing.
+    unsigned int value_type_ : 8;
+    // Unless allocated_, string_ must be null-terminated.
+    unsigned int allocated_ : 1;
+  } bits_;
+
+  class Comments {
+  public:
+    Comments() = default;
+    Comments(const Comments& that);
+    Comments(Comments&& that);
+    Comments& operator=(const Comments& that);
+    Comments& operator=(Comments&& that);
+    bool has(CommentPlacement slot) const;
+    String get(CommentPlacement slot) const;
+    void set(CommentPlacement slot, String comment);
+
+  private:
+    using Array = std::array<String, numberOfCommentPlacement>;
+    std::unique_ptr<Array> ptr_;
+  };
+  Comments comments_;
 
   // [start, limit) byte offsets in the source JSON text from which this Value
   // was extracted.
@@ -641,6 +656,36 @@
   ptrdiff_t limit_;
 };
 
+template <> inline bool Value::as<bool>() const { return asBool(); }
+template <> inline bool Value::is<bool>() const { return isBool(); }
+
+template <> inline Int Value::as<Int>() const { return asInt(); }
+template <> inline bool Value::is<Int>() const { return isInt(); }
+
+template <> inline UInt Value::as<UInt>() const { return asUInt(); }
+template <> inline bool Value::is<UInt>() const { return isUInt(); }
+
+#if defined(JSON_HAS_INT64)
+template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
+template <> inline bool Value::is<Int64>() const { return isInt64(); }
+
+template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
+template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
+#endif
+
+template <> inline double Value::as<double>() const { return asDouble(); }
+template <> inline bool Value::is<double>() const { return isDouble(); }
+
+template <> inline String Value::as<String>() const { return asString(); }
+template <> inline bool Value::is<String>() const { return isString(); }
+
+/// These `as` specializations are type conversions, and do not have a
+/// corresponding `is`.
+template <> inline float Value::as<float>() const { return asFloat(); }
+template <> inline const char* Value::as<const char*>() const {
+  return asCString();
+}
+
 /** \brief Experimental and untested: represents an element of the "path" to
  * access a node.
  */
@@ -651,17 +696,13 @@
   PathArgument();
   PathArgument(ArrayIndex index);
   PathArgument(const char* key);
-  PathArgument(const JSONCPP_STRING& key);
+  PathArgument(String key);
 
 private:
-  enum Kind {
-    kindNone = 0,
-    kindIndex,
-    kindKey
-  };
-  JSONCPP_STRING key_;
-  ArrayIndex index_;
-  Kind kind_;
+  enum Kind { kindNone = 0, kindIndex, kindKey };
+  String key_;
+  ArrayIndex index_{};
+  Kind kind_{kindNone};
 };
 
 /** \brief Experimental and untested: represents a "path" to access a node.
@@ -673,12 +714,11 @@
  * - ".name1.name2.name3"
  * - ".[0][1][2].name1[3]"
  * - ".%" => member name is provided as parameter
- * - ".[%]" => index is provied as parameter
+ * - ".[%]" => index is provided as parameter
  */
 class JSON_API Path {
 public:
-  Path(const JSONCPP_STRING& path,
-       const PathArgument& a1 = PathArgument(),
+  Path(const String& path, const PathArgument& a1 = PathArgument(),
        const PathArgument& a2 = PathArgument(),
        const PathArgument& a3 = PathArgument(),
        const PathArgument& a4 = PathArgument(),
@@ -691,15 +731,13 @@
   Value& make(Value& root) const;
 
 private:
-  typedef std::vector<const PathArgument*> InArgs;
-  typedef std::vector<PathArgument> Args;
+  using InArgs = std::vector<const PathArgument*>;
+  using Args = std::vector<PathArgument>;
 
-  void makePath(const JSONCPP_STRING& path, const InArgs& in);
-  void addPathInArg(const JSONCPP_STRING& path,
-                    const InArgs& in,
-                    InArgs::const_iterator& itInArg,
-                    PathArgument::Kind kind);
-  void invalidPath(const JSONCPP_STRING& path, int location);
+  void makePath(const String& path, const InArgs& in);
+  void addPathInArg(const String& path, const InArgs& in,
+                    InArgs::const_iterator& itInArg, PathArgument::Kind kind);
+  static void invalidPath(const String& path, int location);
 
   Args args_;
 };
@@ -709,10 +747,10 @@
  */
 class JSON_API ValueIteratorBase {
 public:
-  typedef std::bidirectional_iterator_tag iterator_category;
-  typedef unsigned int size_t;
-  typedef int difference_type;
-  typedef ValueIteratorBase SelfType;
+  using iterator_category = std::bidirectional_iterator_tag;
+  using size_t = unsigned int;
+  using difference_type = int;
+  using SelfType = ValueIteratorBase;
 
   bool operator==(const SelfType& other) const { return isEqual(other); }
 
@@ -726,17 +764,19 @@
   /// Value.
   Value key() const;
 
-  /// Return the index of the referenced Value, or -1 if it is not an arrayValue.
+  /// Return the index of the referenced Value, or -1 if it is not an
+  /// arrayValue.
   UInt index() const;
 
   /// Return the member name of the referenced Value, or "" if it is not an
   /// objectValue.
   /// \note Avoid `c_str()` on result, as embedded zeroes are possible.
-  JSONCPP_STRING name() const;
+  String name() const;
 
   /// Return the member name of the referenced Value. "" if it is not an
   /// objectValue.
-  /// deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls.
+  /// deprecated This cannot be used for UTF-8 strings, since there can be
+  /// embedded nulls.
   JSONCPP_DEPRECATED("Use `key = name();` instead.")
   char const* memberName() const;
   /// Return the member name of the referenced Value, or NULL if it is not an
@@ -745,7 +785,14 @@
   char const* memberName(char const** end) const;
 
 protected:
-  Value& deref() const;
+  /*! Internal utility functions to assist with implementing
+   *   other iterator functions. The const and non-const versions
+   *   of the "deref" protected methods expose the protected
+   *   current_ member variable in a way that can often be
+   *   optimized away by the compiler.
+   */
+  const Value& deref() const;
+  Value& deref();
 
   void increment();
 
@@ -760,7 +807,7 @@
 private:
   Value::ObjectValues::iterator current_;
   // Indicates that iterator is for a null value.
-  bool isNull_;
+  bool isNull_{true};
 
 public:
   // For some reason, BORLAND needs these at the end, rather
@@ -776,20 +823,21 @@
   friend class Value;
 
 public:
-  typedef const Value value_type;
-  //typedef unsigned int size_t;
-  //typedef int difference_type;
-  typedef const Value& reference;
-  typedef const Value* pointer;
-  typedef ValueConstIterator SelfType;
+  using value_type = const Value;
+  // typedef unsigned int size_t;
+  // typedef int difference_type;
+  using reference = const Value&;
+  using pointer = const Value*;
+  using SelfType = ValueConstIterator;
 
   ValueConstIterator();
   ValueConstIterator(ValueIterator const& other);
 
 private:
-/*! internal Use by Value to create an iterator.
- */
+  /*! internal Use by Value to create an iterator.
+   */
   explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
+
 public:
   SelfType& operator=(const ValueIteratorBase& other);
 
@@ -826,21 +874,22 @@
   friend class Value;
 
 public:
-  typedef Value value_type;
-  typedef unsigned int size_t;
-  typedef int difference_type;
-  typedef Value& reference;
-  typedef Value* pointer;
-  typedef ValueIterator SelfType;
+  using value_type = Value;
+  using size_t = unsigned int;
+  using difference_type = int;
+  using reference = Value&;
+  using pointer = Value*;
+  using SelfType = ValueIterator;
 
   ValueIterator();
   explicit ValueIterator(const ValueConstIterator& other);
   ValueIterator(const ValueIterator& other);
 
 private:
-/*! internal Use by Value to create an iterator.
- */
+  /*! internal Use by Value to create an iterator.
+   */
   explicit ValueIterator(const Value::ObjectValues::iterator& current);
+
 public:
   SelfType& operator=(const SelfType& other);
 
@@ -866,20 +915,19 @@
     return *this;
   }
 
-  reference operator*() const { return deref(); }
-
-  pointer operator->() const { return &deref(); }
+  /*! The return value of non-const iterators can be
+   *  changed, so the these functions are not const
+   *  because the returned references/pointers can be used
+   *  to change state of the base class.
+   */
+  reference operator*() { return deref(); }
+  pointer operator->() { return &deref(); }
 };
 
+inline void swap(Value& a, Value& b) { a.swap(b); }
+
 } // namespace Json
 
-
-namespace std {
-/// Specialize std::swap() for Json::Value.
-template<>
-inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); }
-}
-
 #if !defined(__SUNPRO_CC)
 #pragma pack(pop)
 #endif
@@ -888,4 +936,4 @@
 #pragma warning(pop)
 #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
 
-#endif // CPPTL_JSON_H_INCLUDED
+#endif // JSON_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/include/json/version.h b/Utilities/cmjsoncpp/include/json/version.h
index d953961..5b9783d 100644
--- a/Utilities/cmjsoncpp/include/json/version.h
+++ b/Utilities/cmjsoncpp/include/json/version.h
@@ -1,14 +1,22 @@
-// DO NOT EDIT. This file (and "version") is generated by CMake.
-// Run CMake configure step to update it.
 #ifndef JSON_VERSION_H_INCLUDED
-# define JSON_VERSION_H_INCLUDED
+#define JSON_VERSION_H_INCLUDED
 
-# define JSONCPP_VERSION_STRING "1.8.2"
-# define JSONCPP_VERSION_MAJOR 1
-# define JSONCPP_VERSION_MINOR 8
-# define JSONCPP_VERSION_PATCH 2
-# define JSONCPP_VERSION_QUALIFIER
-# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8))
+// Note: version must be updated in three places when doing a release. This
+// annoying process ensures that amalgamate, CMake, and meson all report the
+// correct version.
+// 1. /meson.build
+// 2. /include/json/version.h
+// 3. /CMakeLists.txt
+// IMPORTANT: also update the SOVERSION!!
+
+#define JSONCPP_VERSION_STRING "1.9.4"
+#define JSONCPP_VERSION_MAJOR 1
+#define JSONCPP_VERSION_MINOR 9
+#define JSONCPP_VERSION_PATCH 3
+#define JSONCPP_VERSION_QUALIFIER
+#define JSONCPP_VERSION_HEXA                                                   \
+  ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) |             \
+   (JSONCPP_VERSION_PATCH << 8))
 
 #ifdef JSONCPP_USING_SECURE_MEMORY
 #undef JSONCPP_USING_SECURE_MEMORY
diff --git a/Utilities/cmjsoncpp/include/json/writer.h b/Utilities/cmjsoncpp/include/json/writer.h
index d3ae62b..cc6b78c 100644
--- a/Utilities/cmjsoncpp/include/json/writer.h
+++ b/Utilities/cmjsoncpp/include/json/writer.h
@@ -9,14 +9,13 @@
 #if !defined(JSON_IS_AMALGAMATION)
 #include "value.h"
 #endif // if !defined(JSON_IS_AMALGAMATION)
-#include <iosfwd>
-#include <vector>
-#include <string>
 #include <ostream>
+#include <string>
+#include <vector>
 
 // Disable warning C4251: <data member>: <type> needs to have dll-interface to
 // be used by...
-#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
+#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
 #pragma warning(push)
 #pragma warning(disable : 4251)
 #endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
@@ -30,31 +29,31 @@
 class Value;
 
 /**
-
-Usage:
-\code
-  using namespace Json;
-  void writeToStdout(StreamWriter::Factory const& factory, Value const& value) {
-    std::unique_ptr<StreamWriter> const writer(
-      factory.newStreamWriter());
-    writer->write(value, &std::cout);
-    std::cout << std::endl;  // add lf and flush
-  }
-\endcode
-*/
+ *
+ * Usage:
+ *  \code
+ *  using namespace Json;
+ *  void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
+ * { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
+ *    writer->write(value, &std::cout);
+ *    std::cout << std::endl;  // add lf and flush
+ *  }
+ *  \endcode
+ */
 class JSON_API StreamWriter {
 protected:
-  JSONCPP_OSTREAM* sout_;  // not owned; will not delete
+  OStream* sout_; // not owned; will not delete
 public:
   StreamWriter();
   virtual ~StreamWriter();
   /** Write Value into document as configured in sub-class.
-      Do not take ownership of sout, but maintain a reference during function.
-      \pre sout != NULL
-      \return zero on success (For now, we always return zero, so check the stream instead.)
-      \throw std::exception possibly, depending on configuration
+   *   Do not take ownership of sout, but maintain a reference during function.
+   *   \pre sout != NULL
+   *   \return zero on success (For now, we always return zero, so check the
+   *   stream instead.) \throw std::exception possibly, depending on
+   * configuration
    */
-  virtual int write(Value const& root, JSONCPP_OSTREAM* sout) = 0;
+  virtual int write(Value const& root, OStream* sout) = 0;
 
   /** \brief A simple abstract factory.
    */
@@ -65,64 +64,69 @@
      * \throw std::exception if something goes wrong (e.g. invalid settings)
      */
     virtual StreamWriter* newStreamWriter() const = 0;
-  };  // Factory
-};  // StreamWriter
+  }; // Factory
+};   // StreamWriter
 
 /** \brief Write into stringstream, then return string, for convenience.
  * A StreamWriter will be created from the factory, used, and then deleted.
  */
-JSONCPP_STRING JSON_API writeString(StreamWriter::Factory const& factory, Value const& root);
-
+String JSON_API writeString(StreamWriter::Factory const& factory,
+                            Value const& root);
 
 /** \brief Build a StreamWriter implementation.
 
-Usage:
-\code
-  using namespace Json;
-  Value value = ...;
-  StreamWriterBuilder builder;
-  builder["commentStyle"] = "None";
-  builder["indentation"] = "   ";  // or whatever you like
-  std::unique_ptr<Json::StreamWriter> writer(
-      builder.newStreamWriter());
-  writer->write(value, &std::cout);
-  std::cout << std::endl;  // add lf and flush
-\endcode
+* Usage:
+*   \code
+*   using namespace Json;
+*   Value value = ...;
+*   StreamWriterBuilder builder;
+*   builder["commentStyle"] = "None";
+*   builder["indentation"] = "   ";  // or whatever you like
+*   std::unique_ptr<Json::StreamWriter> writer(
+*      builder.newStreamWriter());
+*   writer->write(value, &std::cout);
+*   std::cout << std::endl;  // add lf and flush
+*   \endcode
 */
 class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
 public:
   // Note: We use a Json::Value so that we can add data-members to this class
   // without a major version bump.
   /** Configuration of this builder.
-    Available settings (case-sensitive):
-    - "commentStyle": "None" or "All"
-    - "indentation":  "<anything>"
-    - "enableYAMLCompatibility": false or true
-      - slightly change the whitespace around colons
-    - "dropNullPlaceholders": false or true
-      - Drop the "null" string from the writer's output for nullValues.
-        Strictly speaking, this is not valid JSON. But when the output is being
-        fed to a browser's Javascript, it makes for smaller output and the
-        browser can handle the output just fine.
-    - "useSpecialFloats": false or true
-      - If true, outputs non-finite floating point values in the following way:
-        NaN values as "NaN", positive infinity as "Infinity", and negative infinity
-        as "-Infinity".
+   *  Available settings (case-sensitive):
+   *  - "commentStyle": "None" or "All"
+   *  - "indentation":  "<anything>".
+   *  - Setting this to an empty string also omits newline characters.
+   *  - "enableYAMLCompatibility": false or true
+   *  - slightly change the whitespace around colons
+   *  - "dropNullPlaceholders": false or true
+   *  - Drop the "null" string from the writer's output for nullValues.
+   *    Strictly speaking, this is not valid JSON. But when the output is being
+   *    fed to a browser's JavaScript, it makes for smaller output and the
+   *    browser can handle the output just fine.
+   *  - "useSpecialFloats": false or true
+   *  - If true, outputs non-finite floating point values in the following way:
+   *    NaN values as "NaN", positive infinity as "Infinity", and negative
+   *  infinity as "-Infinity".
+   *  - "precision": int
+   *  - Number of precision digits for formatting of real values.
+   *  - "precisionType": "significant"(default) or "decimal"
+   *  - Type of precision for formatting of real values.
 
-    You can examine 'settings_` yourself
-    to see the defaults. You can also write and read them just like any
-    JSON Value.
-    \sa setDefaults()
-    */
+   *  You can examine 'settings_` yourself
+   *  to see the defaults. You can also write and read them just like any
+   *  JSON Value.
+   *  \sa setDefaults()
+   */
   Json::Value settings_;
 
   StreamWriterBuilder();
-  ~StreamWriterBuilder() JSONCPP_OVERRIDE;
+  ~StreamWriterBuilder() override;
 
   /**
    * \throw std::exception if something goes wrong (e.g. invalid settings)
    */
-  StreamWriter* newStreamWriter() const JSONCPP_OVERRIDE;
+  StreamWriter* newStreamWriter() const override;
 
   /** \return true if 'settings' are legal and consistent;
    *   otherwise, indicate bad settings via 'invalid'.
@@ -130,7 +134,7 @@
   bool validate(Json::Value* invalid) const;
   /** A simple way to update a specific setting.
    */
-  Value& operator[](JSONCPP_STRING key);
+  Value& operator[](const String& key);
 
   /** Called by ctor, but you can use this to reset settings_.
    * \pre 'settings' != NULL (but Json::null is fine)
@@ -147,7 +151,7 @@
 public:
   virtual ~Writer();
 
-  virtual JSONCPP_STRING write(const Value& root) = 0;
+  virtual String write(const Value& root) = 0;
 };
 
 /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
@@ -155,21 +159,25 @@
  *
  * The JSON document is written in a single line. It is not intended for 'human'
  *consumption,
- * but may be usefull to support feature such as RPC where bandwith is limited.
+ * but may be useful to support feature such as RPC where bandwidth is limited.
  * \sa Reader, Value
  * deprecated Use StreamWriterBuilder.
  */
-class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter : public Writer {
-
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter
+    : public Writer {
 public:
   FastWriter();
-  ~FastWriter() JSONCPP_OVERRIDE {}
+  ~FastWriter() override = default;
 
   void enableYAMLCompatibility();
 
   /** \brief Drop the "null" string from the writer's output for nullValues.
    * Strictly speaking, this is not valid JSON. But when the output is being
-   * fed to a browser's Javascript, it makes for smaller output and the
+   * fed to a browser's JavaScript, it makes for smaller output and the
    * browser can handle the output just fine.
    */
   void dropNullPlaceholders();
@@ -177,16 +185,19 @@
   void omitEndingLineFeed();
 
 public: // overridden from Writer
-  JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE;
+  String write(const Value& root) override;
 
 private:
   void writeValue(const Value& value);
 
-  JSONCPP_STRING document_;
-  bool yamlCompatiblityEnabled_;
-  bool dropNullPlaceholders_;
-  bool omitEndingLineFeed_;
+  String document_;
+  bool yamlCompatibilityEnabled_{false};
+  bool dropNullPlaceholders_{false};
+  bool omitEndingLineFeed_{false};
 };
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
 
 /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
  *human friendly way.
@@ -212,41 +223,49 @@
  * \sa Reader, Value, Value::setComment()
  * deprecated Use StreamWriterBuilder.
  */
-class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledWriter : public Writer {
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
+    StyledWriter : public Writer {
 public:
   StyledWriter();
-  ~StyledWriter() JSONCPP_OVERRIDE {}
+  ~StyledWriter() override = default;
 
 public: // overridden from Writer
   /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
    * \param root Value to serialize.
    * \return String containing the JSON document that represents the root value.
    */
-  JSONCPP_STRING write(const Value& root) JSONCPP_OVERRIDE;
+  String write(const Value& root) override;
 
 private:
   void writeValue(const Value& value);
   void writeArrayValue(const Value& value);
-  bool isMultineArray(const Value& value);
-  void pushValue(const JSONCPP_STRING& value);
+  bool isMultilineArray(const Value& value);
+  void pushValue(const String& value);
   void writeIndent();
-  void writeWithIndent(const JSONCPP_STRING& value);
+  void writeWithIndent(const String& value);
   void indent();
   void unindent();
   void writeCommentBeforeValue(const Value& root);
   void writeCommentAfterValueOnSameLine(const Value& root);
-  bool hasCommentForValue(const Value& value);
-  static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text);
+  static bool hasCommentForValue(const Value& value);
+  static String normalizeEOL(const String& text);
 
-  typedef std::vector<JSONCPP_STRING> ChildValues;
+  using ChildValues = std::vector<String>;
 
   ChildValues childValues_;
-  JSONCPP_STRING document_;
-  JSONCPP_STRING indentString_;
-  unsigned int rightMargin_;
-  unsigned int indentSize_;
-  bool addChildValues_;
+  String document_;
+  String indentString_;
+  unsigned int rightMargin_{74};
+  unsigned int indentSize_{3};
+  bool addChildValues_{false};
 };
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
 
 /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
  human friendly way,
@@ -273,13 +292,18 @@
  * \sa Reader, Value, Value::setComment()
  * deprecated Use StreamWriterBuilder.
  */
-class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API StyledStreamWriter {
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable : 4996) // Deriving from deprecated class
+#endif
+class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
+    StyledStreamWriter {
 public:
-/**
- * \param indentation Each level will be indented by this amount extra.
- */
-  StyledStreamWriter(JSONCPP_STRING indentation = "\t");
-  ~StyledStreamWriter() {}
+  /**
+   * \param indentation Each level will be indented by this amount extra.
+   */
+  StyledStreamWriter(String indentation = "\t");
+  ~StyledStreamWriter() = default;
 
 public:
   /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
@@ -288,46 +312,51 @@
    * \note There is no point in deriving from Writer, since write() should not
    * return a value.
    */
-  void write(JSONCPP_OSTREAM& out, const Value& root);
+  void write(OStream& out, const Value& root);
 
 private:
   void writeValue(const Value& value);
   void writeArrayValue(const Value& value);
-  bool isMultineArray(const Value& value);
-  void pushValue(const JSONCPP_STRING& value);
+  bool isMultilineArray(const Value& value);
+  void pushValue(const String& value);
   void writeIndent();
-  void writeWithIndent(const JSONCPP_STRING& value);
+  void writeWithIndent(const String& value);
   void indent();
   void unindent();
   void writeCommentBeforeValue(const Value& root);
   void writeCommentAfterValueOnSameLine(const Value& root);
-  bool hasCommentForValue(const Value& value);
-  static JSONCPP_STRING normalizeEOL(const JSONCPP_STRING& text);
+  static bool hasCommentForValue(const Value& value);
+  static String normalizeEOL(const String& text);
 
-  typedef std::vector<JSONCPP_STRING> ChildValues;
+  using ChildValues = std::vector<String>;
 
   ChildValues childValues_;
-  JSONCPP_OSTREAM* document_;
-  JSONCPP_STRING indentString_;
-  unsigned int rightMargin_;
-  JSONCPP_STRING indentation_;
+  OStream* document_;
+  String indentString_;
+  unsigned int rightMargin_{74};
+  String indentation_;
   bool addChildValues_ : 1;
   bool indented_ : 1;
 };
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
 
 #if defined(JSON_HAS_INT64)
-JSONCPP_STRING JSON_API valueToString(Int value);
-JSONCPP_STRING JSON_API valueToString(UInt value);
+String JSON_API valueToString(Int value);
+String JSON_API valueToString(UInt value);
 #endif // if defined(JSON_HAS_INT64)
-JSONCPP_STRING JSON_API valueToString(LargestInt value);
-JSONCPP_STRING JSON_API valueToString(LargestUInt value);
-JSONCPP_STRING JSON_API valueToString(double value);
-JSONCPP_STRING JSON_API valueToString(bool value);
-JSONCPP_STRING JSON_API valueToQuotedString(const char* value);
+String JSON_API valueToString(LargestInt value);
+String JSON_API valueToString(LargestUInt value);
+String JSON_API valueToString(
+    double value, unsigned int precision = Value::defaultRealPrecision,
+    PrecisionType precisionType = PrecisionType::significantDigits);
+String JSON_API valueToString(bool value);
+String JSON_API valueToQuotedString(const char* value);
 
 /// \brief Output using the StyledStreamWriter.
 /// \see Json::operator>>()
-JSON_API JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM&, const Value& root);
+JSON_API OStream& operator<<(OStream&, const Value& root);
 
 } // namespace Json
 
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
index 8291cc6..e6caf40 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
+++ b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp
@@ -5,69 +5,65 @@
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
 #if !defined(JSON_IS_AMALGAMATION)
+#include "json_tool.h"
 #include <json/assertions.h>
 #include <json/reader.h>
 #include <json/value.h>
-#include "json_tool.h"
 #endif // if !defined(JSON_IS_AMALGAMATION)
-#include <utility>
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iostream>
 #include <istream>
-#include <sstream>
+#include <limits>
 #include <memory>
 #include <set>
-#include <limits>
+#include <sstream>
+#include <utility>
 
-#if defined(_MSC_VER)
-#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above 
-#define snprintf sprintf_s
-#elif _MSC_VER >= 1900 // VC++ 14.0 and above
-#define snprintf std::snprintf
-#else
-#define snprintf _snprintf
-#endif
-#elif defined(__ANDROID__) || defined(__QNXNTO__)
-#define snprintf snprintf
-#elif __cplusplus >= 201103L
-#if !defined(__MINGW32__) && !defined(__CYGWIN__)
-#define snprintf std::snprintf
-#endif
-#endif
+#include <cstdio>
+#if __cplusplus >= 201103L
 
-#if defined(__QNXNTO__)
+#if !defined(sscanf)
 #define sscanf std::sscanf
 #endif
 
-#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
+#endif //__cplusplus
+
+#if defined(_MSC_VER)
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
+#endif //_MSC_VER
+
+#if defined(_MSC_VER)
 // Disable warning about strdup being deprecated.
 #pragma warning(disable : 4996)
 #endif
 
-// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit
+// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile
+// time to change the stack limit
 #if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
 #define JSONCPP_DEPRECATED_STACK_LIMIT 1000
 #endif
 
-static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
+static size_t const stackLimit_g =
+    JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue()
 
 namespace Json {
 
 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
-typedef std::unique_ptr<CharReader> CharReaderPtr;
+using CharReaderPtr = std::unique_ptr<CharReader>;
 #else
-typedef std::auto_ptr<CharReader>   CharReaderPtr;
+using CharReaderPtr = std::auto_ptr<CharReader>;
 #endif
 
 // Implementation of class Features
 // ////////////////////////////////
 
-Features::Features()
-    : allowComments_(true), strictRoot_(false),
-      allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
+Features::Features() = default;
 
-Features Features::all() { return Features(); }
+Features Features::all() { return {}; }
 
 Features Features::strictMode() {
   Features features;
@@ -82,49 +78,38 @@
 // ////////////////////////////////
 
 bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
+  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
 }
 
 // Class Reader
 // //////////////////////////////////////////////////////////////////
 
-Reader::Reader()
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(), features_(Features::all()),
-      collectComments_() {}
+Reader::Reader() : features_(Features::all()) {}
 
-Reader::Reader(const Features& features)
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(), features_(features), collectComments_() {
-}
+Reader::Reader(const Features& features) : features_(features) {}
 
-bool
-Reader::parse(const std::string& document, Value& root, bool collectComments) {
+bool Reader::parse(const std::string& document, Value& root,
+                   bool collectComments) {
   document_.assign(document.begin(), document.end());
   const char* begin = document_.c_str();
   const char* end = begin + document_.length();
   return parse(begin, end, root, collectComments);
 }
 
-bool Reader::parse(std::istream& sin, Value& root, bool collectComments) {
-  // std::istream_iterator<char> begin(sin);
+bool Reader::parse(std::istream& is, Value& root, bool collectComments) {
+  // std::istream_iterator<char> begin(is);
   // std::istream_iterator<char> end;
   // Those would allow streamed input from a file, if parse() were a
   // template function.
 
-  // Since JSONCPP_STRING is reference-counted, this at least does not
+  // Since String is reference-counted, this at least does not
   // create an extra copy.
-  JSONCPP_STRING doc;
-  std::getline(sin, doc, (char)EOF);
+  String doc;
+  std::getline(is, doc, static_cast<char> EOF);
   return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
 }
 
-bool Reader::parse(const char* beginDoc,
-                   const char* endDoc,
-                   Value& root,
+bool Reader::parse(const char* beginDoc, const char* endDoc, Value& root,
                    bool collectComments) {
   if (!features_.allowComments_) {
     collectComments = false;
@@ -134,8 +119,8 @@
   end_ = endDoc;
   collectComments_ = collectComments;
   current_ = begin_;
-  lastValueEnd_ = 0;
-  lastValue_ = 0;
+  lastValueEnd_ = nullptr;
+  lastValue_ = nullptr;
   commentsBefore_.clear();
   errors_.clear();
   while (!nodes_.empty())
@@ -165,9 +150,11 @@
 
 bool Reader::readValue() {
   // readValue() may call itself only if it calls readObject() or ReadArray().
-  // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). 
-  // parse() executes one nodes_.push(), so > instead of >=.
-  if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
+  // These methods execute nodes_.push() just before and nodes_.pop)() just
+  // after calling readValue(). parse() executes one nodes_.push(), so > instead
+  // of >=.
+  if (nodes_.size() > stackLimit_g)
+    throwRuntimeError("Exceeded stackLimit in readValue().");
 
   Token token;
   skipCommentTokens(token);
@@ -193,30 +180,24 @@
   case tokenString:
     successful = decodeString(token);
     break;
-  case tokenTrue:
-    {
+  case tokenTrue: {
     Value v(true);
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenFalse:
-    {
+  } break;
+  case tokenFalse: {
     Value v(false);
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenNull:
-    {
+  } break;
+  case tokenNull: {
     Value v;
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
+  } break;
   case tokenArraySeparator:
   case tokenObjectEnd:
   case tokenArrayEnd:
@@ -322,7 +303,7 @@
   if (!ok)
     token.type_ = tokenError;
   token.end_ = current_;
-  return true;
+  return ok;
 }
 
 void Reader::skipSpaces() {
@@ -335,7 +316,7 @@
   }
 }
 
-bool Reader::match(Location pattern, int patternLength) {
+bool Reader::match(const Char* pattern, int patternLength) {
   if (end_ - current_ < patternLength)
     return false;
   int index = patternLength;
@@ -369,16 +350,16 @@
   return true;
 }
 
-JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
-  JSONCPP_STRING normalized;
+String Reader::normalizeEOL(Reader::Location begin, Reader::Location end) {
+  String normalized;
   normalized.reserve(static_cast<size_t>(end - begin));
   Reader::Location current = begin;
   while (current != end) {
     char c = *current++;
     if (c == '\r') {
       if (current != end && *current == '\n')
-         // convert dos EOL
-         ++current;
+        // convert dos EOL
+        ++current;
       // convert Mac EOL
       normalized += '\n';
     } else {
@@ -388,12 +369,12 @@
   return normalized;
 }
 
-void
-Reader::addComment(Location begin, Location end, CommentPlacement placement) {
+void Reader::addComment(Location begin, Location end,
+                        CommentPlacement placement) {
   assert(collectComments_);
-  const JSONCPP_STRING& normalized = normalizeEOL(begin, end);
+  const String& normalized = normalizeEOL(begin, end);
   if (placement == commentAfterOnSameLine) {
-    assert(lastValue_ != 0);
+    assert(lastValue_ != nullptr);
     lastValue_->setComment(normalized, placement);
   } else {
     commentsBefore_ += normalized;
@@ -426,7 +407,7 @@
 }
 
 void Reader::readNumber() {
-  const char *p = current_;
+  Location p = current_;
   char c = '0'; // stopgap for already consumed character
   // integral part
   while (c >= '0' && c <= '9')
@@ -459,12 +440,12 @@
   return c == '"';
 }
 
-bool Reader::readObject(Token& tokenStart) {
+bool Reader::readObject(Token& token) {
   Token tokenName;
-  JSONCPP_STRING name;
+  String name;
   Value init(objectValue);
   currentValue().swapPayload(init);
-  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  currentValue().setOffsetStart(token.start_ - begin_);
   while (readToken(tokenName)) {
     bool initialTokenOk = true;
     while (tokenName.type_ == tokenComment && initialTokenOk)
@@ -481,15 +462,15 @@
       Value numberName;
       if (!decodeNumber(tokenName, numberName))
         return recoverFromError(tokenObjectEnd);
-      name = JSONCPP_STRING(numberName.asCString());
+      name = numberName.asString();
     } else {
       break;
     }
 
     Token colon;
     if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
-      return addErrorAndRecover(
-          "Missing ':' after object member name", colon, tokenObjectEnd);
+      return addErrorAndRecover("Missing ':' after object member name", colon,
+                                tokenObjectEnd);
     }
     Value& value = currentValue()[name];
     nodes_.push(&value);
@@ -502,8 +483,8 @@
     if (!readToken(comma) ||
         (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
          comma.type_ != tokenComment)) {
-      return addErrorAndRecover(
-          "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+      return addErrorAndRecover("Missing ',' or '}' in object declaration",
+                                comma, tokenObjectEnd);
     }
     bool finalizeTokenOk = true;
     while (comma.type_ == tokenComment && finalizeTokenOk)
@@ -511,14 +492,14 @@
     if (comma.type_ == tokenObjectEnd)
       return true;
   }
-  return addErrorAndRecover(
-      "Missing '}' or object member name", tokenName, tokenObjectEnd);
+  return addErrorAndRecover("Missing '}' or object member name", tokenName,
+                            tokenObjectEnd);
 }
 
-bool Reader::readArray(Token& tokenStart) {
+bool Reader::readArray(Token& token) {
   Value init(arrayValue);
   currentValue().swapPayload(init);
-  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  currentValue().setOffsetStart(token.start_ - begin_);
   skipSpaces();
   if (current_ != end_ && *current_ == ']') // empty array
   {
@@ -535,19 +516,19 @@
     if (!ok) // error already set
       return recoverFromError(tokenArrayEnd);
 
-    Token token;
+    Token currentToken;
     // Accept Comment after last item in the array.
-    ok = readToken(token);
-    while (token.type_ == tokenComment && ok) {
-      ok = readToken(token);
+    ok = readToken(currentToken);
+    while (currentToken.type_ == tokenComment && ok) {
+      ok = readToken(currentToken);
     }
-    bool badTokenType =
-        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+                         currentToken.type_ != tokenArrayEnd);
     if (!ok || badTokenType) {
-      return addErrorAndRecover(
-          "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+      return addErrorAndRecover("Missing ',' or ']' in array declaration",
+                                currentToken, tokenArrayEnd);
     }
-    if (token.type_ == tokenArrayEnd)
+    if (currentToken.type_ == tokenArrayEnd)
       break;
   }
   return true;
@@ -571,7 +552,8 @@
   bool isNegative = *current == '-';
   if (isNegative)
     ++current;
-  // TODO: Help the compiler do the div and mod at compile time or get rid of them.
+  // TODO: Help the compiler do the div and mod at compile time or get rid of
+  // them.
   Value::LargestUInt maxIntegerValue =
       isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1
                  : Value::maxLargestUInt;
@@ -581,7 +563,7 @@
     Char c = *current++;
     if (c < '0' || c > '9')
       return decodeDouble(token, decoded);
-    Value::UInt digit(static_cast<Value::UInt>(c - '0'));
+    auto digit(static_cast<Value::UInt>(c - '0'));
     if (value >= threshold) {
       // We've hit or exceeded the max value divided by 10 (rounded down). If
       // a) we've only just touched the limit, b) this is the last digit, and
@@ -617,18 +599,17 @@
 
 bool Reader::decodeDouble(Token& token, Value& decoded) {
   double value = 0;
-  JSONCPP_STRING buffer(token.start_, token.end_);
-  JSONCPP_ISTRINGSTREAM is(buffer);
+  String buffer(token.start_, token.end_);
+  IStringStream is(buffer);
   if (!(is >> value))
-    return addError("'" + JSONCPP_STRING(token.start_, token.end_) +
-                        "' is not a number.",
-                    token);
+    return addError(
+        "'" + String(token.start_, token.end_) + "' is not a number.", token);
   decoded = value;
   return true;
 }
 
 bool Reader::decodeString(Token& token) {
-  JSONCPP_STRING decoded_string;
+  String decoded_string;
   if (!decodeString(token, decoded_string))
     return false;
   Value decoded(decoded_string);
@@ -638,7 +619,7 @@
   return true;
 }
 
-bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) {
+bool Reader::decodeString(Token& token, String& decoded) {
   decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
   Location current = token.start_ + 1; // skip '"'
   Location end = token.end_ - 1;       // do not include '"'
@@ -646,7 +627,7 @@
     Char c = *current++;
     if (c == '"')
       break;
-    else if (c == '\\') {
+    if (c == '\\') {
       if (current == end)
         return addError("Empty escape sequence in string", token, current);
       Char escape = *current++;
@@ -691,10 +672,8 @@
   return true;
 }
 
-bool Reader::decodeUnicodeCodePoint(Token& token,
-                                    Location& current,
-                                    Location end,
-                                    unsigned int& unicode) {
+bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
+                                    Location end, unsigned int& unicode) {
 
   if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
     return false;
@@ -703,10 +682,9 @@
     if (end - current < 6)
       return addError(
           "additional six characters expected to parse unicode surrogate pair.",
-          token,
-          current);
-    unsigned int surrogatePair;
+          token, current);
     if (*(current++) == '\\' && *(current++) == 'u') {
+      unsigned int surrogatePair;
       if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
         unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
       } else
@@ -714,20 +692,17 @@
     } else
       return addError("expecting another \\u token to begin the second half of "
                       "a unicode surrogate pair",
-                      token,
-                      current);
+                      token, current);
   }
   return true;
 }
 
-bool Reader::decodeUnicodeEscapeSequence(Token& token,
-                                         Location& current,
+bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
                                          Location end,
                                          unsigned int& ret_unicode) {
   if (end - current < 4)
     return addError(
-        "Bad unicode escape sequence in string: four digits expected.",
-        token,
+        "Bad unicode escape sequence in string: four digits expected.", token,
         current);
   int unicode = 0;
   for (int index = 0; index < 4; ++index) {
@@ -742,15 +717,13 @@
     else
       return addError(
           "Bad unicode escape sequence in string: hexadecimal digit expected.",
-          token,
-          current);
+          token, current);
   }
   ret_unicode = static_cast<unsigned int>(unicode);
   return true;
 }
 
-bool
-Reader::addError(const JSONCPP_STRING& message, Token& token, Location extra) {
+bool Reader::addError(const String& message, Token& token, Location extra) {
   ErrorInfo info;
   info.token_ = token;
   info.message_ = message;
@@ -772,8 +745,7 @@
   return false;
 }
 
-bool Reader::addErrorAndRecover(const JSONCPP_STRING& message,
-                                Token& token,
+bool Reader::addErrorAndRecover(const String& message, Token& token,
                                 TokenType skipUntilToken) {
   addError(message, token);
   return recoverFromError(skipUntilToken);
@@ -787,8 +759,7 @@
   return *current_++;
 }
 
-void Reader::getLocationLineAndColumn(Location location,
-                                      int& line,
+void Reader::getLocationLineAndColumn(Location location, int& line,
                                       int& column) const {
   Location current = begin_;
   Location lastLineStart = current;
@@ -810,25 +781,22 @@
   ++line;
 }
 
-JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const {
+String Reader::getLocationLineAndColumn(Location location) const {
   int line, column;
   getLocationLineAndColumn(location, line, column);
   char buffer[18 + 16 + 16 + 1];
-  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
   return buffer;
 }
 
 // Deprecated. Preserved for backward compatibility
-JSONCPP_STRING Reader::getFormatedErrorMessages() const {
+String Reader::getFormatedErrorMessages() const {
   return getFormattedErrorMessages();
 }
 
-JSONCPP_STRING Reader::getFormattedErrorMessages() const {
-  JSONCPP_STRING formattedMessage;
-  for (Errors::const_iterator itError = errors_.begin();
-       itError != errors_.end();
-       ++itError) {
-    const ErrorInfo& error = *itError;
+String Reader::getFormattedErrorMessages() const {
+  String formattedMessage;
+  for (const auto& error : errors_) {
     formattedMessage +=
         "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
     formattedMessage += "  " + error.message_ + "\n";
@@ -841,10 +809,7 @@
 
 std::vector<Reader::StructuredError> Reader::getStructuredErrors() const {
   std::vector<Reader::StructuredError> allErrors;
-  for (Errors::const_iterator itError = errors_.begin();
-       itError != errors_.end();
-       ++itError) {
-    const ErrorInfo& error = *itError;
+  for (const auto& error : errors_) {
     Reader::StructuredError structured;
     structured.offset_start = error.token_.start_ - begin_;
     structured.offset_limit = error.token_.end_ - begin_;
@@ -854,28 +819,27 @@
   return allErrors;
 }
 
-bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) {
+bool Reader::pushError(const Value& value, const String& message) {
   ptrdiff_t const length = end_ - begin_;
-  if(value.getOffsetStart() > length
-    || value.getOffsetLimit() > length)
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length)
     return false;
   Token token;
   token.type_ = tokenError;
   token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = end_ + value.getOffsetLimit();
+  token.end_ = begin_ + value.getOffsetLimit();
   ErrorInfo info;
   info.token_ = token;
   info.message_ = message;
-  info.extra_ = 0;
+  info.extra_ = nullptr;
   errors_.push_back(info);
   return true;
 }
 
-bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) {
+bool Reader::pushError(const Value& value, const String& message,
+                       const Value& extra) {
   ptrdiff_t const length = end_ - begin_;
-  if(value.getOffsetStart() > length
-    || value.getOffsetLimit() > length
-    || extra.getOffsetLimit() > length)
+  if (value.getOffsetStart() > length || value.getOffsetLimit() > length ||
+      extra.getOffsetLimit() > length)
     return false;
   Token token;
   token.type_ = tokenError;
@@ -889,15 +853,15 @@
   return true;
 }
 
-bool Reader::good() const {
-  return !errors_.size();
-}
+bool Reader::good() const { return errors_.empty(); }
 
-// exact copy of Features
+// Originally copied from the Features class (now deprecated), used internally
+// for features implementation.
 class OurFeatures {
 public:
   static OurFeatures all();
   bool allowComments_;
+  bool allowTrailingCommas_;
   bool strictRoot_;
   bool allowDroppedNullPlaceholders_;
   bool allowNumericKeys_;
@@ -905,42 +869,36 @@
   bool failIfExtra_;
   bool rejectDupKeys_;
   bool allowSpecialFloats_;
-  int stackLimit_;
-};  // OurFeatures
+  bool skipBom_;
+  size_t stackLimit_;
+}; // OurFeatures
 
-// exact copy of Implementation of class Features
-// ////////////////////////////////
-
-OurFeatures OurFeatures::all() { return OurFeatures(); }
+OurFeatures OurFeatures::all() { return {}; }
 
 // Implementation of class Reader
 // ////////////////////////////////
 
-// exact copy of Reader, renamed to OurReader
+// Originally copied from the Reader class (now deprecated), used internally
+// for implementing JSON reading.
 class OurReader {
 public:
-  typedef char Char;
-  typedef const Char* Location;
+  using Char = char;
+  using Location = const Char*;
   struct StructuredError {
     ptrdiff_t offset_start;
     ptrdiff_t offset_limit;
-    JSONCPP_STRING message;
+    String message;
   };
 
-  OurReader(OurFeatures const& features);
-  bool parse(const char* beginDoc,
-             const char* endDoc,
-             Value& root,
+  explicit OurReader(OurFeatures const& features);
+  bool parse(const char* beginDoc, const char* endDoc, Value& root,
              bool collectComments = true);
-  JSONCPP_STRING getFormattedErrorMessages() const;
+  String getFormattedErrorMessages() const;
   std::vector<StructuredError> getStructuredErrors() const;
-  bool pushError(const Value& value, const JSONCPP_STRING& message);
-  bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra);
-  bool good() const;
 
 private:
-  OurReader(OurReader const&);  // no impl
-  void operator=(OurReader const&);  // no impl
+  OurReader(OurReader const&);      // no impl
+  void operator=(OurReader const&); // no impl
 
   enum TokenType {
     tokenEndOfStream = 0,
@@ -972,17 +930,18 @@
   class ErrorInfo {
   public:
     Token token_;
-    JSONCPP_STRING message_;
+    String message_;
     Location extra_;
   };
 
-  typedef std::deque<ErrorInfo> Errors;
+  using Errors = std::deque<ErrorInfo>;
 
   bool readToken(Token& token);
   void skipSpaces();
-  bool match(Location pattern, int patternLength);
+  void skipBom(bool skipBom);
+  bool match(const Char* pattern, int patternLength);
   bool readComment();
-  bool readCStyleComment();
+  bool readCStyleComment(bool* containsNewLineResult);
   bool readCppStyleComment();
   bool readString();
   bool readStringSingleQuote();
@@ -993,68 +952,57 @@
   bool decodeNumber(Token& token);
   bool decodeNumber(Token& token, Value& decoded);
   bool decodeString(Token& token);
-  bool decodeString(Token& token, JSONCPP_STRING& decoded);
+  bool decodeString(Token& token, String& decoded);
   bool decodeDouble(Token& token);
   bool decodeDouble(Token& token, Value& decoded);
-  bool decodeUnicodeCodePoint(Token& token,
-                              Location& current,
-                              Location end,
+  bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
                               unsigned int& unicode);
-  bool decodeUnicodeEscapeSequence(Token& token,
-                                   Location& current,
-                                   Location end,
-                                   unsigned int& unicode);
-  bool addError(const JSONCPP_STRING& message, Token& token, Location extra = 0);
+  bool decodeUnicodeEscapeSequence(Token& token, Location& current,
+                                   Location end, unsigned int& unicode);
+  bool addError(const String& message, Token& token, Location extra = nullptr);
   bool recoverFromError(TokenType skipUntilToken);
-  bool addErrorAndRecover(const JSONCPP_STRING& message,
-                          Token& token,
+  bool addErrorAndRecover(const String& message, Token& token,
                           TokenType skipUntilToken);
   void skipUntilSpace();
   Value& currentValue();
   Char getNextChar();
-  void
-  getLocationLineAndColumn(Location location, int& line, int& column) const;
-  JSONCPP_STRING getLocationLineAndColumn(Location location) const;
+  void getLocationLineAndColumn(Location location, int& line,
+                                int& column) const;
+  String getLocationLineAndColumn(Location location) const;
   void addComment(Location begin, Location end, CommentPlacement placement);
   void skipCommentTokens(Token& token);
 
-  static JSONCPP_STRING normalizeEOL(Location begin, Location end);
+  static String normalizeEOL(Location begin, Location end);
   static bool containsNewLine(Location begin, Location end);
 
-  typedef std::stack<Value*> Nodes;
-  Nodes nodes_;
-  Errors errors_;
-  JSONCPP_STRING document_;
-  Location begin_;
-  Location end_;
-  Location current_;
-  Location lastValueEnd_;
-  Value* lastValue_;
-  JSONCPP_STRING commentsBefore_;
+  using Nodes = std::stack<Value*>;
+
+  Nodes nodes_{};
+  Errors errors_{};
+  String document_{};
+  Location begin_ = nullptr;
+  Location end_ = nullptr;
+  Location current_ = nullptr;
+  Location lastValueEnd_ = nullptr;
+  Value* lastValue_ = nullptr;
+  bool lastValueHasAComment_ = false;
+  String commentsBefore_{};
 
   OurFeatures const features_;
-  bool collectComments_;
-};  // OurReader
+  bool collectComments_ = false;
+}; // OurReader
 
 // complete copy of Read impl, for OurReader
 
-bool OurReader::containsNewLine(OurReader::Location begin, OurReader::Location end) {
-  for (; begin < end; ++begin)
-    if (*begin == '\n' || *begin == '\r')
-      return true;
-  return false;
+bool OurReader::containsNewLine(OurReader::Location begin,
+                                OurReader::Location end) {
+  return std::any_of(begin, end, [](char b) { return b == '\n' || b == '\r'; });
 }
 
-OurReader::OurReader(OurFeatures const& features)
-    : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
-      lastValue_(), commentsBefore_(),
-      features_(features), collectComments_() {
-}
+OurReader::OurReader(OurFeatures const& features) : features_(features) {}
 
-bool OurReader::parse(const char* beginDoc,
-                   const char* endDoc,
-                   Value& root,
-                   bool collectComments) {
+bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
+                      bool collectComments) {
   if (!features_.allowComments_) {
     collectComments = false;
   }
@@ -1063,22 +1011,23 @@
   end_ = endDoc;
   collectComments_ = collectComments;
   current_ = begin_;
-  lastValueEnd_ = 0;
-  lastValue_ = 0;
+  lastValueEnd_ = nullptr;
+  lastValue_ = nullptr;
   commentsBefore_.clear();
   errors_.clear();
   while (!nodes_.empty())
     nodes_.pop();
   nodes_.push(&root);
 
+  // skip byte order mark if it exists at the beginning of the UTF-8 text.
+  skipBom(features_.skipBom_);
   bool successful = readValue();
+  nodes_.pop();
   Token token;
   skipCommentTokens(token);
-  if (features_.failIfExtra_) {
-    if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) {
-      addError("Extra non-whitespace after JSON value.", token);
-      return false;
-    }
+  if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
+    addError("Extra non-whitespace after JSON value.", token);
+    return false;
   }
   if (collectComments_ && !commentsBefore_.empty())
     root.setComment(commentsBefore_, commentAfter);
@@ -1100,7 +1049,8 @@
 
 bool OurReader::readValue() {
   //  To preserve the old behaviour we cast size_t to int.
-  if (static_cast<int>(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
+  if (nodes_.size() > features_.stackLimit_)
+    throwRuntimeError("Exceeded stackLimit in readValue().");
   Token token;
   skipCommentTokens(token);
   bool successful = true;
@@ -1125,54 +1075,42 @@
   case tokenString:
     successful = decodeString(token);
     break;
-  case tokenTrue:
-    {
+  case tokenTrue: {
     Value v(true);
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenFalse:
-    {
+  } break;
+  case tokenFalse: {
     Value v(false);
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenNull:
-    {
+  } break;
+  case tokenNull: {
     Value v;
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenNaN:
-    {
+  } break;
+  case tokenNaN: {
     Value v(std::numeric_limits<double>::quiet_NaN());
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenPosInf:
-    {
+  } break;
+  case tokenPosInf: {
     Value v(std::numeric_limits<double>::infinity());
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
-  case tokenNegInf:
-    {
+  } break;
+  case tokenNegInf: {
     Value v(-std::numeric_limits<double>::infinity());
     currentValue().swapPayload(v);
     currentValue().setOffsetStart(token.start_ - begin_);
     currentValue().setOffsetLimit(token.end_ - begin_);
-    }
-    break;
+  } break;
   case tokenArraySeparator:
   case tokenObjectEnd:
   case tokenArrayEnd:
@@ -1194,6 +1132,7 @@
 
   if (collectComments_) {
     lastValueEnd_ = current_;
+    lastValueHasAComment_ = false;
     lastValue_ = &currentValue();
   }
 
@@ -1234,10 +1173,13 @@
     break;
   case '\'':
     if (features_.allowSingleQuotes_) {
-    token.type_ = tokenString;
-    ok = readStringSingleQuote();
+      token.type_ = tokenString;
+      ok = readStringSingleQuote();
+    } else {
+      // If we don't allow single quotes, this is a failure case.
+      ok = false;
+    }
     break;
-    } // else continue
   case '/':
     token.type_ = tokenComment;
     ok = readComment();
@@ -1263,6 +1205,14 @@
       ok = features_.allowSpecialFloats_ && match("nfinity", 7);
     }
     break;
+  case '+':
+    if (readNumber(true)) {
+      token.type_ = tokenNumber;
+    } else {
+      token.type_ = tokenPosInf;
+      ok = features_.allowSpecialFloats_ && match("nfinity", 7);
+    }
+    break;
   case 't':
     token.type_ = tokenTrue;
     ok = match("rue", 3);
@@ -1307,7 +1257,7 @@
   if (!ok)
     token.type_ = tokenError;
   token.end_ = current_;
-  return true;
+  return ok;
 }
 
 void OurReader::skipSpaces() {
@@ -1320,7 +1270,17 @@
   }
 }
 
-bool OurReader::match(Location pattern, int patternLength) {
+void OurReader::skipBom(bool skipBom) {
+  // The default behavior is to skip BOM.
+  if (skipBom) {
+    if ((end_ - begin_) >= 3 && strncmp(begin_, "\xEF\xBB\xBF", 3) == 0) {
+      begin_ += 3;
+      current_ = begin_;
+    }
+  }
+}
+
+bool OurReader::match(const Char* pattern, int patternLength) {
   if (end_ - current_ < patternLength)
     return false;
   int index = patternLength;
@@ -1332,21 +1292,32 @@
 }
 
 bool OurReader::readComment() {
-  Location commentBegin = current_ - 1;
-  Char c = getNextChar();
+  const Location commentBegin = current_ - 1;
+  const Char c = getNextChar();
   bool successful = false;
-  if (c == '*')
-    successful = readCStyleComment();
-  else if (c == '/')
+  bool cStyleWithEmbeddedNewline = false;
+
+  const bool isCStyleComment = (c == '*');
+  const bool isCppStyleComment = (c == '/');
+  if (isCStyleComment) {
+    successful = readCStyleComment(&cStyleWithEmbeddedNewline);
+  } else if (isCppStyleComment) {
     successful = readCppStyleComment();
+  }
+
   if (!successful)
     return false;
 
   if (collectComments_) {
     CommentPlacement placement = commentBefore;
-    if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
-      if (c != '*' || !containsNewLine(commentBegin, current_))
-        placement = commentAfterOnSameLine;
+
+    if (!lastValueHasAComment_) {
+      if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
+        if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
+          placement = commentAfterOnSameLine;
+          lastValueHasAComment_ = true;
+        }
+      }
     }
 
     addComment(commentBegin, current_, placement);
@@ -1354,16 +1325,17 @@
   return true;
 }
 
-JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Location end) {
-  JSONCPP_STRING normalized;
+String OurReader::normalizeEOL(OurReader::Location begin,
+                               OurReader::Location end) {
+  String normalized;
   normalized.reserve(static_cast<size_t>(end - begin));
   OurReader::Location current = begin;
   while (current != end) {
     char c = *current++;
     if (c == '\r') {
       if (current != end && *current == '\n')
-         // convert dos EOL
-         ++current;
+        // convert dos EOL
+        ++current;
       // convert Mac EOL
       normalized += '\n';
     } else {
@@ -1373,24 +1345,29 @@
   return normalized;
 }
 
-void
-OurReader::addComment(Location begin, Location end, CommentPlacement placement) {
+void OurReader::addComment(Location begin, Location end,
+                           CommentPlacement placement) {
   assert(collectComments_);
-  const JSONCPP_STRING& normalized = normalizeEOL(begin, end);
+  const String& normalized = normalizeEOL(begin, end);
   if (placement == commentAfterOnSameLine) {
-    assert(lastValue_ != 0);
+    assert(lastValue_ != nullptr);
     lastValue_->setComment(normalized, placement);
   } else {
     commentsBefore_ += normalized;
   }
 }
 
-bool OurReader::readCStyleComment() {
+bool OurReader::readCStyleComment(bool* containsNewLineResult) {
+  *containsNewLineResult = false;
+
   while ((current_ + 1) < end_) {
     Char c = getNextChar();
     if (c == '*' && *current_ == '/')
       break;
+    if (c == '\n')
+      *containsNewLineResult = true;
   }
+
   return getNextChar() == '/';
 }
 
@@ -1411,7 +1388,7 @@
 }
 
 bool OurReader::readNumber(bool checkInf) {
-  const char *p = current_;
+  Location p = current_;
   if (checkInf && p != end_ && *p == 'I') {
     current_ = ++p;
     return false;
@@ -1448,7 +1425,6 @@
   return c == '"';
 }
 
-
 bool OurReader::readStringSingleQuote() {
   Char c = 0;
   while (current_ != end_) {
@@ -1461,19 +1437,21 @@
   return c == '\'';
 }
 
-bool OurReader::readObject(Token& tokenStart) {
+bool OurReader::readObject(Token& token) {
   Token tokenName;
-  JSONCPP_STRING name;
+  String name;
   Value init(objectValue);
   currentValue().swapPayload(init);
-  currentValue().setOffsetStart(tokenStart.start_ - begin_);
+  currentValue().setOffsetStart(token.start_ - begin_);
   while (readToken(tokenName)) {
     bool initialTokenOk = true;
     while (tokenName.type_ == tokenComment && initialTokenOk)
       initialTokenOk = readToken(tokenName);
     if (!initialTokenOk)
       break;
-    if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object
+    if (tokenName.type_ == tokenObjectEnd &&
+        (name.empty() ||
+         features_.allowTrailingCommas_)) // empty object or trailing comma
       return true;
     name.clear();
     if (tokenName.type_ == tokenString) {
@@ -1487,17 +1465,17 @@
     } else {
       break;
     }
+    if (name.length() >= (1U << 30))
+      throwRuntimeError("keylength >= 2^30");
+    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
+      String msg = "Duplicate key: '" + name + "'";
+      return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
+    }
 
     Token colon;
     if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
-      return addErrorAndRecover(
-          "Missing ':' after object member name", colon, tokenObjectEnd);
-    }
-    if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30");
-    if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
-      JSONCPP_STRING msg = "Duplicate key: '" + name + "'";
-      return addErrorAndRecover(
-          msg, tokenName, tokenObjectEnd);
+      return addErrorAndRecover("Missing ':' after object member name", colon,
+                                tokenObjectEnd);
     }
     Value& value = currentValue()[name];
     nodes_.push(&value);
@@ -1510,8 +1488,8 @@
     if (!readToken(comma) ||
         (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
          comma.type_ != tokenComment)) {
-      return addErrorAndRecover(
-          "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
+      return addErrorAndRecover("Missing ',' or '}' in object declaration",
+                                comma, tokenObjectEnd);
     }
     bool finalizeTokenOk = true;
     while (comma.type_ == tokenComment && finalizeTokenOk)
@@ -1519,23 +1497,27 @@
     if (comma.type_ == tokenObjectEnd)
       return true;
   }
-  return addErrorAndRecover(
-      "Missing '}' or object member name", tokenName, tokenObjectEnd);
+  return addErrorAndRecover("Missing '}' or object member name", tokenName,
+                            tokenObjectEnd);
 }
 
-bool OurReader::readArray(Token& tokenStart) {
+bool OurReader::readArray(Token& token) {
   Value init(arrayValue);
   currentValue().swapPayload(init);
-  currentValue().setOffsetStart(tokenStart.start_ - begin_);
-  skipSpaces();
-  if (current_ != end_ && *current_ == ']') // empty array
-  {
-    Token endArray;
-    readToken(endArray);
-    return true;
-  }
+  currentValue().setOffsetStart(token.start_ - begin_);
   int index = 0;
   for (;;) {
+    skipSpaces();
+    if (current_ != end_ && *current_ == ']' &&
+        (index == 0 ||
+         (features_.allowTrailingCommas_ &&
+          !features_.allowDroppedNullPlaceholders_))) // empty array or trailing
+                                                      // comma
+    {
+      Token endArray;
+      readToken(endArray);
+      return true;
+    }
     Value& value = currentValue()[index++];
     nodes_.push(&value);
     bool ok = readValue();
@@ -1543,19 +1525,19 @@
     if (!ok) // error already set
       return recoverFromError(tokenArrayEnd);
 
-    Token token;
+    Token currentToken;
     // Accept Comment after last item in the array.
-    ok = readToken(token);
-    while (token.type_ == tokenComment && ok) {
-      ok = readToken(token);
+    ok = readToken(currentToken);
+    while (currentToken.type_ == tokenComment && ok) {
+      ok = readToken(currentToken);
     }
-    bool badTokenType =
-        (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
+    bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
+                         currentToken.type_ != tokenArrayEnd);
     if (!ok || badTokenType) {
-      return addErrorAndRecover(
-          "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
+      return addErrorAndRecover("Missing ',' or ']' in array declaration",
+                                currentToken, tokenArrayEnd);
     }
-    if (token.type_ == tokenArrayEnd)
+    if (currentToken.type_ == tokenArrayEnd)
       break;
   }
   return true;
@@ -1576,38 +1558,78 @@
   // larger than the maximum supported value of an integer then
   // we decode the number as a double.
   Location current = token.start_;
-  bool isNegative = *current == '-';
-  if (isNegative)
+  const bool isNegative = *current == '-';
+  if (isNegative) {
     ++current;
-  // TODO: Help the compiler do the div and mod at compile time or get rid of them.
-  Value::LargestUInt maxIntegerValue =
-      isNegative ? Value::LargestUInt(Value::minLargestInt)
-                 : Value::maxLargestUInt;
-  Value::LargestUInt threshold = maxIntegerValue / 10;
+  }
+
+  // We assume we can represent the largest and smallest integer types as
+  // unsigned integers with separate sign. This is only true if they can fit
+  // into an unsigned integer.
+  static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
+                "Int must be smaller than UInt");
+
+  // We need to convert minLargestInt into a positive number. The easiest way
+  // to do this conversion is to assume our "threshold" value of minLargestInt
+  // divided by 10 can fit in maxLargestInt when absolute valued. This should
+  // be a safe assumption.
+  static_assert(Value::minLargestInt <= -Value::maxLargestInt,
+                "The absolute value of minLargestInt must be greater than or "
+                "equal to maxLargestInt");
+  static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
+                "The absolute value of minLargestInt must be only 1 magnitude "
+                "larger than maxLargest Int");
+
+  static constexpr Value::LargestUInt positive_threshold =
+      Value::maxLargestUInt / 10;
+  static constexpr Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
+
+  // For the negative values, we have to be more careful. Since typically
+  // -Value::minLargestInt will cause an overflow, we first divide by 10 and
+  // then take the inverse. This assumes that minLargestInt is only a single
+  // power of 10 different in magnitude, which we check above. For the last
+  // digit, we take the modulus before negating for the same reason.
+  static constexpr auto negative_threshold =
+      Value::LargestUInt(-(Value::minLargestInt / 10));
+  static constexpr auto negative_last_digit =
+      Value::UInt(-(Value::minLargestInt % 10));
+
+  const Value::LargestUInt threshold =
+      isNegative ? negative_threshold : positive_threshold;
+  const Value::UInt max_last_digit =
+      isNegative ? negative_last_digit : positive_last_digit;
+
   Value::LargestUInt value = 0;
   while (current < token.end_) {
     Char c = *current++;
     if (c < '0' || c > '9')
       return decodeDouble(token, decoded);
-    Value::UInt digit(static_cast<Value::UInt>(c - '0'));
+
+    const auto digit(static_cast<Value::UInt>(c - '0'));
     if (value >= threshold) {
       // We've hit or exceeded the max value divided by 10 (rounded down). If
-      // a) we've only just touched the limit, b) this is the last digit, and
+      // a) we've only just touched the limit, meaing value == threshold,
+      // b) this is the last digit, or
       // c) it's small enough to fit in that rounding delta, we're okay.
       // Otherwise treat this number as a double to avoid overflow.
       if (value > threshold || current != token.end_ ||
-          digit > maxIntegerValue % 10) {
+          digit > max_last_digit) {
         return decodeDouble(token, decoded);
       }
     }
     value = value * 10 + digit;
   }
-  if (isNegative)
-    decoded = -Value::LargestInt(value);
-  else if (value <= Value::LargestUInt(Value::maxInt))
+
+  if (isNegative) {
+    // We use the same magnitude assumption here, just in case.
+    const auto last_digit = static_cast<Value::UInt>(value % 10);
+    decoded = -Value::LargestInt(value / 10) * 10 - last_digit;
+  } else if (value <= Value::LargestUInt(Value::maxLargestInt)) {
     decoded = Value::LargestInt(value);
-  else
+  } else {
     decoded = value;
+  }
+
   return true;
 }
 
@@ -1623,44 +1645,18 @@
 
 bool OurReader::decodeDouble(Token& token, Value& decoded) {
   double value = 0;
-  const int bufferSize = 32;
-  int count;
-  ptrdiff_t const length = token.end_ - token.start_;
-
-  // Sanity check to avoid buffer overflow exploits.
-  if (length < 0) {
-    return addError("Unable to parse token length", token);
+  const String buffer(token.start_, token.end_);
+  IStringStream is(buffer);
+  if (!(is >> value)) {
+    return addError(
+        "'" + String(token.start_, token.end_) + "' is not a number.", token);
   }
-  size_t const ulength = static_cast<size_t>(length);
-
-  // Avoid using a string constant for the format control string given to
-  // sscanf, as this can cause hard to debug crashes on OS X. See here for more
-  // info:
-  //
-  //     http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html
-  char format[] = "%lf";
-
-  if (length <= bufferSize) {
-    Char buffer[bufferSize + 1];
-    memcpy(buffer, token.start_, ulength);
-    buffer[length] = 0;
-    fixNumericLocaleInput(buffer, buffer + length);
-    count = sscanf(buffer, format, &value);
-  } else {
-    JSONCPP_STRING buffer(token.start_, token.end_);
-    count = sscanf(buffer.c_str(), format, &value);
-  }
-
-  if (count != 1)
-    return addError("'" + JSONCPP_STRING(token.start_, token.end_) +
-                        "' is not a number.",
-                    token);
   decoded = value;
   return true;
 }
 
 bool OurReader::decodeString(Token& token) {
-  JSONCPP_STRING decoded_string;
+  String decoded_string;
   if (!decodeString(token, decoded_string))
     return false;
   Value decoded(decoded_string);
@@ -1670,7 +1666,7 @@
   return true;
 }
 
-bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) {
+bool OurReader::decodeString(Token& token, String& decoded) {
   decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
   Location current = token.start_ + 1; // skip '"'
   Location end = token.end_ - 1;       // do not include '"'
@@ -1678,7 +1674,7 @@
     Char c = *current++;
     if (c == '"')
       break;
-    else if (c == '\\') {
+    if (c == '\\') {
       if (current == end)
         return addError("Empty escape sequence in string", token, current);
       Char escape = *current++;
@@ -1723,10 +1719,8 @@
   return true;
 }
 
-bool OurReader::decodeUnicodeCodePoint(Token& token,
-                                    Location& current,
-                                    Location end,
-                                    unsigned int& unicode) {
+bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
+                                       Location end, unsigned int& unicode) {
 
   unicode = 0; // Convince clang-analyzer that this is initialized before use.
   if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
@@ -1736,10 +1730,9 @@
     if (end - current < 6)
       return addError(
           "additional six characters expected to parse unicode surrogate pair.",
-          token,
-          current);
-    unsigned int surrogatePair;
+          token, current);
     if (*(current++) == '\\' && *(current++) == 'u') {
+      unsigned int surrogatePair;
       if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
         unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
       } else
@@ -1747,20 +1740,17 @@
     } else
       return addError("expecting another \\u token to begin the second half of "
                       "a unicode surrogate pair",
-                      token,
-                      current);
+                      token, current);
   }
   return true;
 }
 
-bool OurReader::decodeUnicodeEscapeSequence(Token& token,
-                                         Location& current,
-                                         Location end,
-                                         unsigned int& ret_unicode) {
+bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
+                                            Location end,
+                                            unsigned int& ret_unicode) {
   if (end - current < 4)
     return addError(
-        "Bad unicode escape sequence in string: four digits expected.",
-        token,
+        "Bad unicode escape sequence in string: four digits expected.", token,
         current);
   int unicode = 0;
   for (int index = 0; index < 4; ++index) {
@@ -1775,15 +1765,13 @@
     else
       return addError(
           "Bad unicode escape sequence in string: hexadecimal digit expected.",
-          token,
-          current);
+          token, current);
   }
   ret_unicode = static_cast<unsigned int>(unicode);
   return true;
 }
 
-bool
-OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) {
+bool OurReader::addError(const String& message, Token& token, Location extra) {
   ErrorInfo info;
   info.token_ = token;
   info.message_ = message;
@@ -1805,9 +1793,8 @@
   return false;
 }
 
-bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message,
-                                Token& token,
-                                TokenType skipUntilToken) {
+bool OurReader::addErrorAndRecover(const String& message, Token& token,
+                                   TokenType skipUntilToken) {
   addError(message, token);
   return recoverFromError(skipUntilToken);
 }
@@ -1820,9 +1807,8 @@
   return *current_++;
 }
 
-void OurReader::getLocationLineAndColumn(Location location,
-                                      int& line,
-                                      int& column) const {
+void OurReader::getLocationLineAndColumn(Location location, int& line,
+                                         int& column) const {
   Location current = begin_;
   Location lastLineStart = current;
   line = 0;
@@ -1843,20 +1829,17 @@
   ++line;
 }
 
-JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const {
+String OurReader::getLocationLineAndColumn(Location location) const {
   int line, column;
   getLocationLineAndColumn(location, line, column);
   char buffer[18 + 16 + 16 + 1];
-  snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
+  jsoncpp_snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column);
   return buffer;
 }
 
-JSONCPP_STRING OurReader::getFormattedErrorMessages() const {
-  JSONCPP_STRING formattedMessage;
-  for (Errors::const_iterator itError = errors_.begin();
-       itError != errors_.end();
-       ++itError) {
-    const ErrorInfo& error = *itError;
+String OurReader::getFormattedErrorMessages() const {
+  String formattedMessage;
+  for (const auto& error : errors_) {
     formattedMessage +=
         "* " + getLocationLineAndColumn(error.token_.start_) + "\n";
     formattedMessage += "  " + error.message_ + "\n";
@@ -1869,10 +1852,7 @@
 
 std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
   std::vector<OurReader::StructuredError> allErrors;
-  for (Errors::const_iterator itError = errors_.begin();
-       itError != errors_.end();
-       ++itError) {
-    const ErrorInfo& error = *itError;
+  for (const auto& error : errors_) {
     OurReader::StructuredError structured;
     structured.offset_start = error.token_.start_ - begin_;
     structured.offset_limit = error.token_.end_ - begin_;
@@ -1882,59 +1862,15 @@
   return allErrors;
 }
 
-bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) {
-  ptrdiff_t length = end_ - begin_;
-  if(value.getOffsetStart() > length
-    || value.getOffsetLimit() > length)
-    return false;
-  Token token;
-  token.type_ = tokenError;
-  token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = end_ + value.getOffsetLimit();
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = 0;
-  errors_.push_back(info);
-  return true;
-}
-
-bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) {
-  ptrdiff_t length = end_ - begin_;
-  if(value.getOffsetStart() > length
-    || value.getOffsetLimit() > length
-    || extra.getOffsetLimit() > length)
-    return false;
-  Token token;
-  token.type_ = tokenError;
-  token.start_ = begin_ + value.getOffsetStart();
-  token.end_ = begin_ + value.getOffsetLimit();
-  ErrorInfo info;
-  info.token_ = token;
-  info.message_ = message;
-  info.extra_ = begin_ + extra.getOffsetStart();
-  errors_.push_back(info);
-  return true;
-}
-
-bool OurReader::good() const {
-  return !errors_.size();
-}
-
-
 class OurCharReader : public CharReader {
   bool const collectComments_;
   OurReader reader_;
+
 public:
-  OurCharReader(
-    bool collectComments,
-    OurFeatures const& features)
-  : collectComments_(collectComments)
-  , reader_(features)
-  {}
-  bool parse(
-      char const* beginDoc, char const* endDoc,
-      Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE {
+  OurCharReader(bool collectComments, OurFeatures const& features)
+      : collectComments_(collectComments), reader_(features) {}
+  bool parse(char const* beginDoc, char const* endDoc, Value* root,
+             String* errs) override {
     bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
     if (errs) {
       *errs = reader_.getFormattedErrorMessages();
@@ -1943,67 +1879,64 @@
   }
 };
 
-CharReaderBuilder::CharReaderBuilder()
-{
-  setDefaults(&settings_);
-}
-CharReaderBuilder::~CharReaderBuilder()
-{}
-CharReader* CharReaderBuilder::newCharReader() const
-{
+CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
+CharReaderBuilder::~CharReaderBuilder() = default;
+CharReader* CharReaderBuilder::newCharReader() const {
   bool collectComments = settings_["collectComments"].asBool();
   OurFeatures features = OurFeatures::all();
   features.allowComments_ = settings_["allowComments"].asBool();
+  features.allowTrailingCommas_ = settings_["allowTrailingCommas"].asBool();
   features.strictRoot_ = settings_["strictRoot"].asBool();
-  features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
+  features.allowDroppedNullPlaceholders_ =
+      settings_["allowDroppedNullPlaceholders"].asBool();
   features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
   features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool();
-  features.stackLimit_ = settings_["stackLimit"].asInt();
+
+  // Stack limit is always a size_t, so we get this as an unsigned int
+  // regardless of it we have 64-bit integer support enabled.
+  features.stackLimit_ = static_cast<size_t>(settings_["stackLimit"].asUInt());
   features.failIfExtra_ = settings_["failIfExtra"].asBool();
   features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool();
   features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool();
+  features.skipBom_ = settings_["skipBom"].asBool();
   return new OurCharReader(collectComments, features);
 }
-static void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys)
-{
-  valid_keys->clear();
-  valid_keys->insert("collectComments");
-  valid_keys->insert("allowComments");
-  valid_keys->insert("strictRoot");
-  valid_keys->insert("allowDroppedNullPlaceholders");
-  valid_keys->insert("allowNumericKeys");
-  valid_keys->insert("allowSingleQuotes");
-  valid_keys->insert("stackLimit");
-  valid_keys->insert("failIfExtra");
-  valid_keys->insert("rejectDupKeys");
-  valid_keys->insert("allowSpecialFloats");
-}
-bool CharReaderBuilder::validate(Json::Value* invalid) const
-{
-  Json::Value my_invalid;
-  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
-  Json::Value& inv = *invalid;
-  std::set<JSONCPP_STRING> valid_keys;
-  getValidReaderKeys(&valid_keys);
-  Value::Members keys = settings_.getMemberNames();
-  size_t n = keys.size();
-  for (size_t i = 0; i < n; ++i) {
-    JSONCPP_STRING const& key = keys[i];
-    if (valid_keys.find(key) == valid_keys.end()) {
-      inv[key] = settings_[key];
-    }
+
+bool CharReaderBuilder::validate(Json::Value* invalid) const {
+  static const auto& valid_keys = *new std::set<String>{
+      "collectComments",
+      "allowComments",
+      "allowTrailingCommas",
+      "strictRoot",
+      "allowDroppedNullPlaceholders",
+      "allowNumericKeys",
+      "allowSingleQuotes",
+      "stackLimit",
+      "failIfExtra",
+      "rejectDupKeys",
+      "allowSpecialFloats",
+      "skipBom",
+  };
+  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+    auto key = si.name();
+    if (valid_keys.count(key))
+      continue;
+    if (invalid)
+      (*invalid)[std::move(key)] = *si;
+    else
+      return false;
   }
-  return 0u == inv.size();
+  return invalid ? invalid->empty() : true;
 }
-Value& CharReaderBuilder::operator[](JSONCPP_STRING key)
-{
+
+Value& CharReaderBuilder::operator[](const String& key) {
   return settings_[key];
 }
 // static
-void CharReaderBuilder::strictMode(Json::Value* settings)
-{
-//! [CharReaderBuilderStrictMode]
+void CharReaderBuilder::strictMode(Json::Value* settings) {
+  //! [CharReaderBuilderStrictMode]
   (*settings)["allowComments"] = false;
+  (*settings)["allowTrailingCommas"] = false;
   (*settings)["strictRoot"] = true;
   (*settings)["allowDroppedNullPlaceholders"] = false;
   (*settings)["allowNumericKeys"] = false;
@@ -2012,14 +1945,15 @@
   (*settings)["failIfExtra"] = true;
   (*settings)["rejectDupKeys"] = true;
   (*settings)["allowSpecialFloats"] = false;
-//! [CharReaderBuilderStrictMode]
+  (*settings)["skipBom"] = true;
+  //! [CharReaderBuilderStrictMode]
 }
 // static
-void CharReaderBuilder::setDefaults(Json::Value* settings)
-{
-//! [CharReaderBuilderDefaults]
+void CharReaderBuilder::setDefaults(Json::Value* settings) {
+  //! [CharReaderBuilderDefaults]
   (*settings)["collectComments"] = true;
   (*settings)["allowComments"] = true;
+  (*settings)["allowTrailingCommas"] = true;
   (*settings)["strictRoot"] = false;
   (*settings)["allowDroppedNullPlaceholders"] = false;
   (*settings)["allowNumericKeys"] = false;
@@ -2028,19 +1962,18 @@
   (*settings)["failIfExtra"] = false;
   (*settings)["rejectDupKeys"] = false;
   (*settings)["allowSpecialFloats"] = false;
-//! [CharReaderBuilderDefaults]
+  (*settings)["skipBom"] = true;
+  //! [CharReaderBuilderDefaults]
 }
 
 //////////////////////////////////
 // global functions
 
-bool parseFromStream(
-    CharReader::Factory const& fact, JSONCPP_ISTREAM& sin,
-    Value* root, JSONCPP_STRING* errs)
-{
-  JSONCPP_OSTRINGSTREAM ssin;
+bool parseFromStream(CharReader::Factory const& fact, IStream& sin, Value* root,
+                     String* errs) {
+  OStringStream ssin;
   ssin << sin.rdbuf();
-  JSONCPP_STRING doc = ssin.str();
+  String doc = ssin.str();
   char const* begin = doc.data();
   char const* end = begin + doc.size();
   // Note that we do not actually need a null-terminator.
@@ -2048,15 +1981,11 @@
   return reader->parse(begin, end, root, errs);
 }
 
-JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) {
+IStream& operator>>(IStream& sin, Value& root) {
   CharReaderBuilder b;
-  JSONCPP_STRING errs;
+  String errs;
   bool ok = parseFromStream(b, sin, &root, &errs);
   if (!ok) {
-    fprintf(stderr,
-            "Error from reader: %s",
-            errs.c_str());
-
     throwRuntimeError(errs);
   }
   return sin;
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_tool.h b/Utilities/cmjsoncpp/src/lib_json/json_tool.h
index 4316178..2d7b7d9 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_tool.h
+++ b/Utilities/cmjsoncpp/src/lib_json/json_tool.h
@@ -6,6 +6,9 @@
 #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
 #define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
 
+#if !defined(JSON_IS_AMALGAMATION)
+#include <json/config.h>
+#endif
 
 // Also support old flag NO_LOCALE_SUPPORT
 #ifdef NO_LOCALE_SUPPORT
@@ -23,7 +26,7 @@
  */
 
 namespace Json {
-static char getDecimalPoint() {
+static inline char getDecimalPoint() {
 #ifdef JSONCPP_NO_LOCALE_SUPPORT
   return '\0';
 #else
@@ -33,8 +36,8 @@
 }
 
 /// Converts a unicode code-point to UTF-8.
-static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) {
-  JSONCPP_STRING result;
+static inline String codePointToUTF8(unsigned int cp) {
+  String result;
 
   // based on description from http://en.wikipedia.org/wiki/UTF-8
 
@@ -61,9 +64,6 @@
   return result;
 }
 
-/// Returns true if ch is a control character (in range [1,31]).
-static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; }
-
 enum {
   /// Constant that specify the size of the buffer that must be passed to
   /// uintToString.
@@ -71,10 +71,10 @@
 };
 
 // Defines a char buffer for use with uintToString().
-typedef char UIntToStringBuffer[uintToStringBufferSize];
+using UIntToStringBuffer = char[uintToStringBufferSize];
 
 /** Converts an unsigned integer to string.
- * @param value Unsigned interger to convert to string
+ * @param value Unsigned integer to convert to string
  * @param current Input/Output string buffer.
  *        Must have at least uintToStringBufferSize chars free.
  */
@@ -91,27 +91,44 @@
  * We had a sophisticated way, but it did not work in WinCE.
  * @see https://github.com/open-source-parsers/jsoncpp/pull/9
  */
-static inline void fixNumericLocale(char* begin, char* end) {
-  while (begin < end) {
+template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
+  for (; begin != end; ++begin) {
     if (*begin == ',') {
       *begin = '.';
     }
-    ++begin;
   }
+  return begin;
 }
 
-static inline void fixNumericLocaleInput(char* begin, char* end) {
+template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
   char decimalPoint = getDecimalPoint();
-  if (decimalPoint != '\0' && decimalPoint != '.') {
-    while (begin < end) {
-      if (*begin == '.') {
-        *begin = decimalPoint;
-      }
-      ++begin;
+  if (decimalPoint == '\0' || decimalPoint == '.') {
+    return;
+  }
+  for (; begin != end; ++begin) {
+    if (*begin == '.') {
+      *begin = decimalPoint;
     }
   }
 }
 
-} // namespace Json {
+/**
+ * Return iterator that would be the new end of the range [begin,end), if we
+ * were to delete zeros in the end of string, but not the last zero before '.'.
+ */
+template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
+  for (; begin != end; --end) {
+    if (*(end - 1) != '0') {
+      return end;
+    }
+    // Don't delete the last zero before the decimal point.
+    if (begin != (end - 1) && *(end - 2) == '.') {
+      return end;
+    }
+  }
+  return end;
+}
+
+} // namespace Json
 
 #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_value.cpp b/Utilities/cmjsoncpp/src/lib_json/json_value.cpp
index f271e57..d59b7d9 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_value.cpp
+++ b/Utilities/cmjsoncpp/src/lib_json/json_value.cpp
@@ -8,20 +8,54 @@
 #include <json/value.h>
 #include <json/writer.h>
 #endif // if !defined(JSON_IS_AMALGAMATION)
-#include <math.h>
+#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
 #include <sstream>
 #include <utility>
-#include <string.h>
-#include <assert.h>
-#ifdef JSON_USE_CPPTL
-#include <cpptl/conststring.h>
+
+// Provide implementation equivalent of std::snprintf for older _MSC compilers
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#include <stdarg.h>
+static int msvc_pre1900_c99_vsnprintf(char* outBuf, size_t size,
+                                      const char* format, va_list ap) {
+  int count = -1;
+  if (size != 0)
+    count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
+  if (count == -1)
+    count = _vscprintf(format, ap);
+  return count;
+}
+
+int JSON_API msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
+                                       const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  const int count = msvc_pre1900_c99_vsnprintf(outBuf, size, format, ap);
+  va_end(ap);
+  return count;
+}
 #endif
-#include <cstddef> // size_t
-#include <algorithm> // min()
+
+// Disable warning C4702 : unreachable code
+#if defined(_MSC_VER)
+#pragma warning(disable : 4702)
+#endif
 
 #define JSON_ASSERT_UNREACHABLE assert(false)
 
 namespace Json {
+template <typename T>
+static std::unique_ptr<T> cloneUnique(const std::unique_ptr<T>& p) {
+  std::unique_ptr<T> r;
+  if (p) {
+    r = std::unique_ptr<T>(new T(*p));
+  }
+  return r;
+}
 
 // This is a walkaround to avoid the static initialization of Value::null.
 // kNull must be word-aligned to avoid crashing on ARM.  We use an alignment of
@@ -31,50 +65,34 @@
 #else
 #define ALIGNAS(byte_alignment)
 #endif
-//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 };
-//const unsigned char& kNullRef = kNull[0];
-//const Value& Value::null = reinterpret_cast<const Value&>(kNullRef);
-//const Value& Value::nullRef = null;
 
 // static
-Value const& Value::nullSingleton()
-{
- static Value const nullStatic;
- return nullStatic;
+Value const& Value::nullSingleton() {
+  static Value const nullStatic;
+  return nullStatic;
 }
 
-// for backwards compatibility, we'll leave these global references around, but DO NOT
-// use them in JSONCPP library code any more!
+#if JSON_USE_NULLREF
+// for backwards compatibility, we'll leave these global references around, but
+// DO NOT use them in JSONCPP library code any more!
+// static
 Value const& Value::null = Value::nullSingleton();
-Value const& Value::nullRef = Value::nullSingleton();
 
-const Int Value::minInt = Int(~(UInt(-1) / 2));
-const Int Value::maxInt = Int(UInt(-1) / 2);
-const UInt Value::maxUInt = UInt(-1);
-#if defined(JSON_HAS_INT64)
-const Int64 Value::minInt64 = Int64(~(UInt64(-1) / 2));
-const Int64 Value::maxInt64 = Int64(UInt64(-1) / 2);
-const UInt64 Value::maxUInt64 = UInt64(-1);
-// The constant is hard-coded because some compiler have trouble
-// converting Value::maxUInt64 to a double correctly (AIX/xlC).
-// Assumes that UInt64 is a 64 bits integer.
-static const double maxUInt64AsDouble = 18446744073709551615.0;
-#endif // defined(JSON_HAS_INT64)
-const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2));
-const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2);
-const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
+// static
+Value const& Value::nullRef = Value::nullSingleton();
+#endif
 
 #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
 template <typename T, typename U>
 static inline bool InRange(double d, T min, U max) {
   // The casts can lose precision, but we are looking only for
   // an approximate range. Might fail on edge cases though. ~cdunn
-  //return d >= static_cast<double>(min) && d <= static_cast<double>(max);
-  return d >= min && d <= max;
+  return d >= static_cast<double>(min) && d <= static_cast<double>(max);
 }
 #else  // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
 static inline double integerToDouble(Json::UInt64 value) {
-  return static_cast<double>(Int64(value / 2)) * 2.0 + static_cast<double>(Int64(value & 1));
+  return static_cast<double>(Int64(value / 2)) * 2.0 +
+         static_cast<double>(Int64(value & 1));
 }
 
 template <typename T> static inline double integerToDouble(T value) {
@@ -94,19 +112,16 @@
  *               computed using strlen(value).
  * @return Pointer on the duplicate instance of string.
  */
-static inline char* duplicateStringValue(const char* value,
-                                         size_t length)
-{
+static inline char* duplicateStringValue(const char* value, size_t length) {
   // Avoid an integer overflow in the call to malloc below by limiting length
   // to a sane value.
   if (length >= static_cast<size_t>(Value::maxInt))
     length = Value::maxInt - 1;
 
-  char* newString = static_cast<char*>(malloc(length + 1));
-  if (newString == NULL) {
-    throwRuntimeError(
-        "in Json::Value::duplicateStringValue(): "
-        "Failed to allocate string value buffer");
+  auto newString = static_cast<char*>(malloc(length + 1));
+  if (newString == nullptr) {
+    throwRuntimeError("in Json::Value::duplicateStringValue(): "
+                      "Failed to allocate string value buffer");
   }
   memcpy(newString, value, length);
   newString[length] = 0;
@@ -115,31 +130,28 @@
 
 /* Record the length as a prefix.
  */
-static inline char* duplicateAndPrefixStringValue(
-    const char* value,
-    unsigned int length)
-{
+static inline char* duplicateAndPrefixStringValue(const char* value,
+                                                  unsigned int length) {
   // Avoid an integer overflow in the call to malloc below by limiting length
   // to a sane value.
-  JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - sizeof(unsigned) - 1U,
+  JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -
+                                    sizeof(unsigned) - 1U,
                       "in Json::Value::duplicateAndPrefixStringValue(): "
                       "length too big for prefixing");
-  unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U;
-  char* newString = static_cast<char*>(malloc(actualLength));
-  if (newString == 0) {
-    throwRuntimeError(
-        "in Json::Value::duplicateAndPrefixStringValue(): "
-        "Failed to allocate string value buffer");
+  size_t actualLength = sizeof(length) + length + 1;
+  auto newString = static_cast<char*>(malloc(actualLength));
+  if (newString == nullptr) {
+    throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): "
+                      "Failed to allocate string value buffer");
   }
   *reinterpret_cast<unsigned*>(newString) = length;
   memcpy(newString + sizeof(unsigned), value, length);
-  newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later
+  newString[actualLength - 1U] =
+      0; // to avoid buffer over-run accidents by users later
   return newString;
 }
-inline static void decodePrefixedString(
-    bool isPrefixed, char const* prefixed,
-    unsigned* length, char const** value)
-{
+inline static void decodePrefixedString(bool isPrefixed, char const* prefixed,
+                                        unsigned* length, char const** value) {
   if (!isPrefixed) {
     *length = static_cast<unsigned>(strlen(prefixed));
     *value = prefixed;
@@ -148,7 +160,8 @@
     *value = prefixed + sizeof(unsigned);
   }
 }
-/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue().
+/** Free the string duplicated by
+ * duplicateStringValue()/duplicateAndPrefixStringValue().
  */
 #if JSONCPP_USING_SECURE_MEMORY
 static inline void releasePrefixedStringValue(char* value) {
@@ -161,17 +174,13 @@
 }
 static inline void releaseStringValue(char* value, unsigned length) {
   // length==0 => we allocated the strings memory
-  size_t size = (length==0) ? strlen(value) : length;
+  size_t size = (length == 0) ? strlen(value) : length;
   memset(value, 0, size);
   free(value);
 }
-#else // !JSONCPP_USING_SECURE_MEMORY
-static inline void releasePrefixedStringValue(char* value) {
-  free(value);
-}
-static inline void releaseStringValue(char* value, unsigned) {
-  free(value);
-}
+#else  // !JSONCPP_USING_SECURE_MEMORY
+static inline void releasePrefixedStringValue(char* value) { free(value); }
+static inline void releaseStringValue(char* value, unsigned) { free(value); }
 #endif // JSONCPP_USING_SECURE_MEMORY
 
 } // namespace Json
@@ -190,58 +199,28 @@
 
 namespace Json {
 
-Exception::Exception(JSONCPP_STRING const& msg)
-  : msg_(msg)
-{}
-Exception::~Exception() JSONCPP_NOEXCEPT
-{}
-char const* Exception::what() const JSONCPP_NOEXCEPT
-{
-  return msg_.c_str();
-}
-RuntimeError::RuntimeError(JSONCPP_STRING const& msg)
-  : Exception(msg)
-{}
-LogicError::LogicError(JSONCPP_STRING const& msg)
-  : Exception(msg)
-{}
-JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg)
-{
+#if JSON_USE_EXCEPTION
+Exception::Exception(String msg) : msg_(std::move(msg)) {}
+Exception::~Exception() noexcept = default;
+char const* Exception::what() const noexcept { return msg_.c_str(); }
+RuntimeError::RuntimeError(String const& msg) : Exception(msg) {}
+LogicError::LogicError(String const& msg) : Exception(msg) {}
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
   throw RuntimeError(msg);
 }
-JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg)
-{
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
   throw LogicError(msg);
 }
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CommentInfo
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-Value::CommentInfo::CommentInfo() : comment_(0)
-{}
-
-Value::CommentInfo::~CommentInfo() {
-  if (comment_)
-    releaseStringValue(comment_, 0u);
+#else // !JSON_USE_EXCEPTION
+JSONCPP_NORETURN void throwRuntimeError(String const& msg) {
+  std::cerr << msg << std::endl;
+  abort();
 }
-
-void Value::CommentInfo::setComment(const char* text, size_t len) {
-  if (comment_) {
-    releaseStringValue(comment_, 0u);
-    comment_ = 0;
-  }
-  JSON_ASSERT(text != 0);
-  JSON_ASSERT_MESSAGE(
-      text[0] == '\0' || text[0] == '/',
-      "in Json::Value::setComment(): Comments must start with /");
-  // It seems that /**/ style comments are acceptable as well.
-  comment_ = duplicateStringValue(text, len);
+JSONCPP_NORETURN void throwLogicError(String const& msg) {
+  std::cerr << msg << std::endl;
+  abort();
 }
+#endif
 
 // //////////////////////////////////////////////////////////////////
 // //////////////////////////////////////////////////////////////////
@@ -254,36 +233,44 @@
 // Notes: policy_ indicates if the string was allocated when
 // a string is stored.
 
-Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {}
+Value::CZString::CZString(ArrayIndex index) : cstr_(nullptr), index_(index) {}
 
-Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate)
+Value::CZString::CZString(char const* str, unsigned length,
+                          DuplicationPolicy allocate)
     : cstr_(str) {
   // allocate != duplicate
   storage_.policy_ = allocate & 0x3;
-  storage_.length_ = ulength & 0x3FFFFFFF;
+  storage_.length_ = length & 0x3FFFFFFF;
 }
 
 Value::CZString::CZString(const CZString& other) {
-  cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0
-				 ? duplicateStringValue(other.cstr_, other.storage_.length_)
-				 : other.cstr_);
-  storage_.policy_ = static_cast<unsigned>(other.cstr_
-                 ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication
-                     ? noDuplication : duplicate)
-                 : static_cast<DuplicationPolicy>(other.storage_.policy_)) & 3U;
+  cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != nullptr
+               ? duplicateStringValue(other.cstr_, other.storage_.length_)
+               : other.cstr_);
+  storage_.policy_ =
+      static_cast<unsigned>(
+          other.cstr_
+              ? (static_cast<DuplicationPolicy>(other.storage_.policy_) ==
+                         noDuplication
+                     ? noDuplication
+                     : duplicate)
+              : static_cast<DuplicationPolicy>(other.storage_.policy_)) &
+      3U;
   storage_.length_ = other.storage_.length_;
 }
 
-#if JSON_HAS_RVALUE_REFERENCES
 Value::CZString::CZString(CZString&& other)
-  : cstr_(other.cstr_), index_(other.index_) {
+    : cstr_(other.cstr_), index_(other.index_) {
   other.cstr_ = nullptr;
 }
-#endif
 
 Value::CZString::~CZString() {
   if (cstr_ && storage_.policy_ == duplicate) {
-	  releaseStringValue(const_cast<char*>(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary
+    releaseStringValue(const_cast<char*>(cstr_),
+                       storage_.length_ + 1U); // +1 for null terminating
+                                               // character for sake of
+                                               // completeness but not actually
+                                               // necessary
   }
 }
 
@@ -298,36 +285,39 @@
   return *this;
 }
 
-#if JSON_HAS_RVALUE_REFERENCES
 Value::CZString& Value::CZString::operator=(CZString&& other) {
   cstr_ = other.cstr_;
   index_ = other.index_;
   other.cstr_ = nullptr;
   return *this;
 }
-#endif
 
 bool Value::CZString::operator<(const CZString& other) const {
-  if (!cstr_) return index_ < other.index_;
-  //return strcmp(cstr_, other.cstr_) < 0;
+  if (!cstr_)
+    return index_ < other.index_;
+  // return strcmp(cstr_, other.cstr_) < 0;
   // Assume both are strings.
   unsigned this_len = this->storage_.length_;
   unsigned other_len = other.storage_.length_;
   unsigned min_len = std::min<unsigned>(this_len, other_len);
   JSON_ASSERT(this->cstr_ && other.cstr_);
   int comp = memcmp(this->cstr_, other.cstr_, min_len);
-  if (comp < 0) return true;
-  if (comp > 0) return false;
+  if (comp < 0)
+    return true;
+  if (comp > 0)
+    return false;
   return (this_len < other_len);
 }
 
 bool Value::CZString::operator==(const CZString& other) const {
-  if (!cstr_) return index_ == other.index_;
-  //return strcmp(cstr_, other.cstr_) == 0;
+  if (!cstr_)
+    return index_ == other.index_;
+  // return strcmp(cstr_, other.cstr_) == 0;
   // Assume both are strings.
   unsigned this_len = this->storage_.length_;
   unsigned other_len = other.storage_.length_;
-  if (this_len != other_len) return false;
+  if (this_len != other_len)
+    return false;
   JSON_ASSERT(this->cstr_ && other.cstr_);
   int comp = memcmp(this->cstr_, other.cstr_, this_len);
   return comp == 0;
@@ -335,10 +325,12 @@
 
 ArrayIndex Value::CZString::index() const { return index_; }
 
-//const char* Value::CZString::c_str() const { return cstr_; }
+// const char* Value::CZString::c_str() const { return cstr_; }
 const char* Value::CZString::data() const { return cstr_; }
 unsigned Value::CZString::length() const { return storage_.length_; }
-bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; }
+bool Value::CZString::isStaticString() const {
+  return storage_.policy_ == noDuplication;
+}
 
 // //////////////////////////////////////////////////////////////////
 // //////////////////////////////////////////////////////////////////
@@ -352,10 +344,10 @@
  * memset( this, 0, sizeof(Value) )
  * This optimization is used in ValueInternalMap fast allocator.
  */
-Value::Value(ValueType vtype) {
+Value::Value(ValueType type) {
   static char const emptyString[] = "";
-  initBasic(vtype);
-  switch (vtype) {
+  initBasic(type);
+  switch (type) {
   case nullValue:
     break;
   case intValue:
@@ -408,20 +400,22 @@
 
 Value::Value(const char* value) {
   initBasic(stringValue, true);
-  JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor");
-  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value)));
+  JSON_ASSERT_MESSAGE(value != nullptr,
+                      "Null Value Passed to Value Constructor");
+  value_.string_ = duplicateAndPrefixStringValue(
+      value, static_cast<unsigned>(strlen(value)));
 }
 
-Value::Value(const char* beginValue, const char* endValue) {
+Value::Value(const char* begin, const char* end) {
   initBasic(stringValue, true);
   value_.string_ =
-      duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue));
+      duplicateAndPrefixStringValue(begin, static_cast<unsigned>(end - begin));
 }
 
-Value::Value(const JSONCPP_STRING& value) {
+Value::Value(const String& value) {
   initBasic(stringValue, true);
-  value_.string_ =
-      duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length()));
+  value_.string_ = duplicateAndPrefixStringValue(
+      value.data(), static_cast<unsigned>(value.length()));
 }
 
 Value::Value(const StaticString& value) {
@@ -429,114 +423,44 @@
   value_.string_ = const_cast<char*>(value.c_str());
 }
 
-#ifdef JSON_USE_CPPTL
-Value::Value(const CppTL::ConstString& value) {
-  initBasic(stringValue, true);
-  value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length()));
-}
-#endif
-
 Value::Value(bool value) {
   initBasic(booleanValue);
   value_.bool_ = value;
 }
 
-Value::Value(Value const& other)
-    : type_(other.type_), allocated_(false)
-      ,
-      comments_(0), start_(other.start_), limit_(other.limit_)
-{
-  switch (type_) {
-  case nullValue:
-  case intValue:
-  case uintValue:
-  case realValue:
-  case booleanValue:
-    value_ = other.value_;
-    break;
-  case stringValue:
-    if (other.value_.string_ && other.allocated_) {
-      unsigned len;
-      char const* str;
-      decodePrefixedString(other.allocated_, other.value_.string_,
-          &len, &str);
-      value_.string_ = duplicateAndPrefixStringValue(str, len);
-      allocated_ = true;
-    } else {
-      value_.string_ = other.value_.string_;
-      allocated_ = false;
-    }
-    break;
-  case arrayValue:
-  case objectValue:
-    value_.map_ = new ObjectValues(*other.value_.map_);
-    break;
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-  if (other.comments_) {
-    comments_ = new CommentInfo[numberOfCommentPlacement];
-    for (int comment = 0; comment < numberOfCommentPlacement; ++comment) {
-      const CommentInfo& otherComment = other.comments_[comment];
-      if (otherComment.comment_)
-        comments_[comment].setComment(
-            otherComment.comment_, strlen(otherComment.comment_));
-    }
-  }
+Value::Value(const Value& other) {
+  dupPayload(other);
+  dupMeta(other);
 }
 
-#if JSON_HAS_RVALUE_REFERENCES
-// Move constructor
 Value::Value(Value&& other) {
   initBasic(nullValue);
   swap(other);
 }
-#endif
 
 Value::~Value() {
-  switch (type_) {
-  case nullValue:
-  case intValue:
-  case uintValue:
-  case realValue:
-  case booleanValue:
-    break;
-  case stringValue:
-    if (allocated_)
-      releasePrefixedStringValue(value_.string_);
-    break;
-  case arrayValue:
-  case objectValue:
-    delete value_.map_;
-    break;
-  default:
-    JSON_ASSERT_UNREACHABLE;
-  }
-
-  delete[] comments_;
-
+  releasePayload();
   value_.uint_ = 0;
 }
 
-Value& Value::operator=(Value other) {
-  swap(other);
+Value& Value::operator=(const Value& other) {
+  Value(other).swap(*this);
+  return *this;
+}
+
+Value& Value::operator=(Value&& other) {
+  other.swap(*this);
   return *this;
 }
 
 void Value::swapPayload(Value& other) {
-  ValueType temp = type_;
-  type_ = other.type_;
-  other.type_ = temp;
+  std::swap(bits_, other.bits_);
   std::swap(value_, other.value_);
-  int temp2 = allocated_;
-  allocated_ = other.allocated_;
-  other.allocated_ = temp2 & 0x1;
 }
 
 void Value::copyPayload(const Value& other) {
-  type_ = other.type_;
-  value_ = other.value_;
-  allocated_ = other.allocated_;
+  releasePayload();
+  dupPayload(other);
 }
 
 void Value::swap(Value& other) {
@@ -548,12 +472,12 @@
 
 void Value::copy(const Value& other) {
   copyPayload(other);
-  comments_ = other.comments_;
-  start_ = other.start_;
-  limit_ = other.limit_;
+  dupMeta(other);
 }
 
-ValueType Value::type() const { return type_; }
+ValueType Value::type() const {
+  return static_cast<ValueType>(bits_.value_type_);
+}
 
 int Value::compare(const Value& other) const {
   if (*this < other)
@@ -564,10 +488,10 @@
 }
 
 bool Value::operator<(const Value& other) const {
-  int typeDelta = type_ - other.type_;
+  int typeDelta = type() - other.type();
   if (typeDelta)
-    return typeDelta < 0 ? true : false;
-  switch (type_) {
+    return typeDelta < 0;
+  switch (type()) {
   case nullValue:
     return false;
   case intValue:
@@ -578,30 +502,33 @@
     return value_.real_ < other.value_.real_;
   case booleanValue:
     return value_.bool_ < other.value_.bool_;
-  case stringValue:
-  {
-    if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
-      if (other.value_.string_) return true;
-      else return false;
+  case stringValue: {
+    if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
+      return other.value_.string_ != nullptr;
     }
     unsigned this_len;
     unsigned other_len;
     char const* this_str;
     char const* other_str;
-    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
-    decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
+    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                         &this_str);
+    decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+                         &other_str);
     unsigned min_len = std::min<unsigned>(this_len, other_len);
     JSON_ASSERT(this_str && other_str);
     int comp = memcmp(this_str, other_str, min_len);
-    if (comp < 0) return true;
-    if (comp > 0) return false;
+    if (comp < 0)
+      return true;
+    if (comp > 0)
+      return false;
     return (this_len < other_len);
   }
   case arrayValue:
   case objectValue: {
-    int delta = int(value_.map_->size() - other.value_.map_->size());
-    if (delta)
-      return delta < 0;
+    auto thisSize = value_.map_->size();
+    auto otherSize = other.value_.map_->size();
+    if (thisSize != otherSize)
+      return thisSize < otherSize;
     return (*value_.map_) < (*other.value_.map_);
   }
   default:
@@ -617,14 +544,9 @@
 bool Value::operator>(const Value& other) const { return other < *this; }
 
 bool Value::operator==(const Value& other) const {
-  // if ( type_ != other.type_ )
-  // GCC 2.95.3 says:
-  // attempt to take address of bit-field structure member `Json::Value::type_'
-  // Beats me, but a temp solves the problem.
-  int temp = other.type_;
-  if (type_ != temp)
+  if (type() != other.type())
     return false;
-  switch (type_) {
+  switch (type()) {
   case nullValue:
     return true;
   case intValue:
@@ -635,18 +557,20 @@
     return value_.real_ == other.value_.real_;
   case booleanValue:
     return value_.bool_ == other.value_.bool_;
-  case stringValue:
-  {
-    if ((value_.string_ == 0) || (other.value_.string_ == 0)) {
+  case stringValue: {
+    if ((value_.string_ == nullptr) || (other.value_.string_ == nullptr)) {
       return (value_.string_ == other.value_.string_);
     }
     unsigned this_len;
     unsigned other_len;
     char const* this_str;
     char const* other_str;
-    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
-    decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str);
-    if (this_len != other_len) return false;
+    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                         &this_str);
+    decodePrefixedString(other.isAllocated(), other.value_.string_, &other_len,
+                         &other_str);
+    if (this_len != other_len)
+      return false;
     JSON_ASSERT(this_str && other_str);
     int comp = memcmp(this_str, other_str, this_len);
     return comp == 0;
@@ -664,47 +588,55 @@
 bool Value::operator!=(const Value& other) const { return !(*this == other); }
 
 const char* Value::asCString() const {
-  JSON_ASSERT_MESSAGE(type_ == stringValue,
+  JSON_ASSERT_MESSAGE(type() == stringValue,
                       "in Json::Value::asCString(): requires stringValue");
-  if (value_.string_ == 0) return 0;
+  if (value_.string_ == nullptr)
+    return nullptr;
   unsigned this_len;
   char const* this_str;
-  decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+  decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                       &this_str);
   return this_str;
 }
 
 #if JSONCPP_USING_SECURE_MEMORY
 unsigned Value::getCStringLength() const {
-  JSON_ASSERT_MESSAGE(type_ == stringValue,
-	                  "in Json::Value::asCString(): requires stringValue");
-  if (value_.string_ == 0) return 0;
+  JSON_ASSERT_MESSAGE(type() == stringValue,
+                      "in Json::Value::asCString(): requires stringValue");
+  if (value_.string_ == 0)
+    return 0;
   unsigned this_len;
   char const* this_str;
-  decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
+  decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                       &this_str);
   return this_len;
 }
 #endif
 
-bool Value::getString(char const** str, char const** cend) const {
-  if (type_ != stringValue) return false;
-  if (value_.string_ == 0) return false;
+bool Value::getString(char const** begin, char const** end) const {
+  if (type() != stringValue)
+    return false;
+  if (value_.string_ == nullptr)
+    return false;
   unsigned length;
-  decodePrefixedString(this->allocated_, this->value_.string_, &length, str);
-  *cend = *str + length;
+  decodePrefixedString(this->isAllocated(), this->value_.string_, &length,
+                       begin);
+  *end = *begin + length;
   return true;
 }
 
-JSONCPP_STRING Value::asString() const {
-  switch (type_) {
+String Value::asString() const {
+  switch (type()) {
   case nullValue:
     return "";
-  case stringValue:
-  {
-    if (value_.string_ == 0) return "";
+  case stringValue: {
+    if (value_.string_ == nullptr)
+      return "";
     unsigned this_len;
     char const* this_str;
-    decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str);
-    return JSONCPP_STRING(this_str, this_len);
+    decodePrefixedString(this->isAllocated(), this->value_.string_, &this_len,
+                         &this_str);
+    return String(this_str, this_len);
   }
   case booleanValue:
     return value_.bool_ ? "true" : "false";
@@ -719,18 +651,8 @@
   }
 }
 
-#ifdef JSON_USE_CPPTL
-CppTL::ConstString Value::asConstString() const {
-  unsigned len;
-  char const* str;
-  decodePrefixedString(allocated_, value_.string_,
-      &len, &str);
-  return CppTL::ConstString(str, len);
-}
-#endif
-
 Value::Int Value::asInt() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
     JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range");
     return Int(value_.int_);
@@ -752,7 +674,7 @@
 }
 
 Value::UInt Value::asUInt() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
     JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range");
     return UInt(value_.int_);
@@ -776,7 +698,7 @@
 #if defined(JSON_HAS_INT64)
 
 Value::Int64 Value::asInt64() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
     return Int64(value_.int_);
   case uintValue:
@@ -797,7 +719,7 @@
 }
 
 Value::UInt64 Value::asUInt64() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
     JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range");
     return UInt64(value_.int_);
@@ -835,7 +757,7 @@
 }
 
 double Value::asDouble() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
     return static_cast<double>(value_.int_);
   case uintValue:
@@ -857,7 +779,7 @@
 }
 
 float Value::asFloat() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
     return static_cast<float>(value_.int_);
   case uintValue:
@@ -872,7 +794,7 @@
   case nullValue:
     return 0.0;
   case booleanValue:
-    return value_.bool_ ? 1.0f : 0.0f;
+    return value_.bool_ ? 1.0F : 0.0F;
   default:
     break;
   }
@@ -880,18 +802,20 @@
 }
 
 bool Value::asBool() const {
-  switch (type_) {
+  switch (type()) {
   case booleanValue:
     return value_.bool_;
   case nullValue:
     return false;
   case intValue:
-    return value_.int_ ? true : false;
+    return value_.int_ != 0;
   case uintValue:
-    return value_.uint_ ? true : false;
-  case realValue:
-    // This is kind of strange. Not recommended.
-    return (value_.real_ != 0.0) ? true : false;
+    return value_.uint_ != 0;
+  case realValue: {
+    // According to JavaScript language zero or NaN is regarded as false
+    const auto value_classification = std::fpclassify(value_.real_);
+    return value_classification != FP_ZERO && value_classification != FP_NAN;
+  }
   default:
     break;
   }
@@ -902,30 +826,30 @@
   switch (other) {
   case nullValue:
     return (isNumeric() && asDouble() == 0.0) ||
-           (type_ == booleanValue && value_.bool_ == false) ||
-           (type_ == stringValue && asString().empty()) ||
-           (type_ == arrayValue && value_.map_->size() == 0) ||
-           (type_ == objectValue && value_.map_->size() == 0) ||
-           type_ == nullValue;
+           (type() == booleanValue && !value_.bool_) ||
+           (type() == stringValue && asString().empty()) ||
+           (type() == arrayValue && value_.map_->empty()) ||
+           (type() == objectValue && value_.map_->empty()) ||
+           type() == nullValue;
   case intValue:
     return isInt() ||
-           (type_ == realValue && InRange(value_.real_, minInt, maxInt)) ||
-           type_ == booleanValue || type_ == nullValue;
+           (type() == realValue && InRange(value_.real_, minInt, maxInt)) ||
+           type() == booleanValue || type() == nullValue;
   case uintValue:
     return isUInt() ||
-           (type_ == realValue && InRange(value_.real_, 0, maxUInt)) ||
-           type_ == booleanValue || type_ == nullValue;
+           (type() == realValue && InRange(value_.real_, 0, maxUInt)) ||
+           type() == booleanValue || type() == nullValue;
   case realValue:
-    return isNumeric() || type_ == booleanValue || type_ == nullValue;
+    return isNumeric() || type() == booleanValue || type() == nullValue;
   case booleanValue:
-    return isNumeric() || type_ == booleanValue || type_ == nullValue;
+    return isNumeric() || type() == booleanValue || type() == nullValue;
   case stringValue:
-    return isNumeric() || type_ == booleanValue || type_ == stringValue ||
-           type_ == nullValue;
+    return isNumeric() || type() == booleanValue || type() == stringValue ||
+           type() == nullValue;
   case arrayValue:
-    return type_ == arrayValue || type_ == nullValue;
+    return type() == arrayValue || type() == nullValue;
   case objectValue:
-    return type_ == objectValue || type_ == nullValue;
+    return type() == objectValue || type() == nullValue;
   }
   JSON_ASSERT_UNREACHABLE;
   return false;
@@ -933,7 +857,7 @@
 
 /// Number of values in array or object
 ArrayIndex Value::size() const {
-  switch (type_) {
+  switch (type()) {
   case nullValue:
   case intValue:
   case uintValue:
@@ -957,20 +881,19 @@
 
 bool Value::empty() const {
   if (isNull() || isArray() || isObject())
-    return size() == 0u;
-  else
-    return false;
+    return size() == 0U;
+  return false;
 }
 
-bool Value::operator!() const { return isNull(); }
+Value::operator bool() const { return !isNull(); }
 
 void Value::clear() {
-  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue ||
-                          type_ == objectValue,
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue ||
+                          type() == objectValue,
                       "in Json::Value::clear(): requires complex value");
   start_ = 0;
   limit_ = 0;
-  switch (type_) {
+  switch (type()) {
   case arrayValue:
   case objectValue:
     value_.map_->clear();
@@ -981,15 +904,15 @@
 }
 
 void Value::resize(ArrayIndex newSize) {
-  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == arrayValue,
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
                       "in Json::Value::resize(): requires arrayValue");
-  if (type_ == nullValue)
+  if (type() == nullValue)
     *this = Value(arrayValue);
   ArrayIndex oldSize = size();
   if (newSize == 0)
     clear();
   else if (newSize > oldSize)
-    (*this)[newSize - 1];
+    this->operator[](newSize - 1);
   else {
     for (ArrayIndex index = newSize; index < oldSize; ++index) {
       value_.map_->erase(index);
@@ -1000,12 +923,12 @@
 
 Value& Value::operator[](ArrayIndex index) {
   JSON_ASSERT_MESSAGE(
-      type_ == nullValue || type_ == arrayValue,
+      type() == nullValue || type() == arrayValue,
       "in Json::Value::operator[](ArrayIndex): requires arrayValue");
-  if (type_ == nullValue)
+  if (type() == nullValue)
     *this = Value(arrayValue);
   CZString key(index);
-  ObjectValues::iterator it = value_.map_->lower_bound(key);
+  auto it = value_.map_->lower_bound(key);
   if (it != value_.map_->end() && (*it).first == key)
     return (*it).second;
 
@@ -1023,9 +946,9 @@
 
 const Value& Value::operator[](ArrayIndex index) const {
   JSON_ASSERT_MESSAGE(
-      type_ == nullValue || type_ == arrayValue,
+      type() == nullValue || type() == arrayValue,
       "in Json::Value::operator[](ArrayIndex)const: requires arrayValue");
-  if (type_ == nullValue)
+  if (type() == nullValue)
     return nullSingleton();
   CZString key(index);
   ObjectValues::const_iterator it = value_.map_->find(key);
@@ -1041,26 +964,85 @@
   return (*this)[ArrayIndex(index)];
 }
 
-void Value::initBasic(ValueType vtype, bool allocated) {
-  type_ = vtype;
-  allocated_ = allocated;
-  comments_ = 0;
+void Value::initBasic(ValueType type, bool allocated) {
+  setType(type);
+  setIsAllocated(allocated);
+  comments_ = Comments{};
   start_ = 0;
   limit_ = 0;
 }
 
+void Value::dupPayload(const Value& other) {
+  setType(other.type());
+  setIsAllocated(false);
+  switch (type()) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+    value_ = other.value_;
+    break;
+  case stringValue:
+    if (other.value_.string_ && other.isAllocated()) {
+      unsigned len;
+      char const* str;
+      decodePrefixedString(other.isAllocated(), other.value_.string_, &len,
+                           &str);
+      value_.string_ = duplicateAndPrefixStringValue(str, len);
+      setIsAllocated(true);
+    } else {
+      value_.string_ = other.value_.string_;
+    }
+    break;
+  case arrayValue:
+  case objectValue:
+    value_.map_ = new ObjectValues(*other.value_.map_);
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+}
+
+void Value::releasePayload() {
+  switch (type()) {
+  case nullValue:
+  case intValue:
+  case uintValue:
+  case realValue:
+  case booleanValue:
+    break;
+  case stringValue:
+    if (isAllocated())
+      releasePrefixedStringValue(value_.string_);
+    break;
+  case arrayValue:
+  case objectValue:
+    delete value_.map_;
+    break;
+  default:
+    JSON_ASSERT_UNREACHABLE;
+  }
+}
+
+void Value::dupMeta(const Value& other) {
+  comments_ = other.comments_;
+  start_ = other.start_;
+  limit_ = other.limit_;
+}
+
 // Access an object value by name, create a null member if it does not exist.
 // @pre Type of '*this' is object or null.
 // @param key is null-terminated.
 Value& Value::resolveReference(const char* key) {
   JSON_ASSERT_MESSAGE(
-      type_ == nullValue || type_ == objectValue,
+      type() == nullValue || type() == objectValue,
       "in Json::Value::resolveReference(): requires objectValue");
-  if (type_ == nullValue)
+  if (type() == nullValue)
     *this = Value(objectValue);
-  CZString actualKey(
-      key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE!
-  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+  CZString actualKey(key, static_cast<unsigned>(strlen(key)),
+                     CZString::noDuplication); // NOTE!
+  auto it = value_.map_->lower_bound(actualKey);
   if (it != value_.map_->end() && (*it).first == actualKey)
     return (*it).second;
 
@@ -1071,16 +1053,15 @@
 }
 
 // @param key is not null-terminated.
-Value& Value::resolveReference(char const* key, char const* cend)
-{
+Value& Value::resolveReference(char const* key, char const* end) {
   JSON_ASSERT_MESSAGE(
-      type_ == nullValue || type_ == objectValue,
+      type() == nullValue || type() == objectValue,
       "in Json::Value::resolveReference(key, end): requires objectValue");
-  if (type_ == nullValue)
+  if (type() == nullValue)
     *this = Value(objectValue);
-  CZString actualKey(
-      key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy);
-  ObjectValues::iterator it = value_.map_->lower_bound(actualKey);
+  CZString actualKey(key, static_cast<unsigned>(end - key),
+                     CZString::duplicateOnCopy);
+  auto it = value_.map_->lower_bound(actualKey);
   if (it != value_.map_->end() && (*it).first == actualKey)
     return (*it).second;
 
@@ -1097,27 +1078,35 @@
 
 bool Value::isValidIndex(ArrayIndex index) const { return index < size(); }
 
-Value const* Value::find(char const* key, char const* cend) const
-{
-  JSON_ASSERT_MESSAGE(
-      type_ == nullValue || type_ == objectValue,
-      "in Json::Value::find(key, end, found): requires objectValue or nullValue");
-  if (type_ == nullValue) return NULL;
-  CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
+Value const* Value::find(char const* begin, char const* end) const {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+                      "in Json::Value::find(begin, end): requires "
+                      "objectValue or nullValue");
+  if (type() == nullValue)
+    return nullptr;
+  CZString actualKey(begin, static_cast<unsigned>(end - begin),
+                     CZString::noDuplication);
   ObjectValues::const_iterator it = value_.map_->find(actualKey);
-  if (it == value_.map_->end()) return NULL;
+  if (it == value_.map_->end())
+    return nullptr;
   return &(*it).second;
 }
-const Value& Value::operator[](const char* key) const
-{
+Value* Value::demand(char const* begin, char const* end) {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
+                      "in Json::Value::demand(begin, end): requires "
+                      "objectValue or nullValue");
+  return &resolveReference(begin, end);
+}
+const Value& Value::operator[](const char* key) const {
   Value const* found = find(key, key + strlen(key));
-  if (!found) return nullSingleton();
+  if (!found)
+    return nullSingleton();
   return *found;
 }
-Value const& Value::operator[](JSONCPP_STRING const& key) const
-{
+Value const& Value::operator[](const String& key) const {
   Value const* found = find(key.data(), key.data() + key.length());
-  if (!found) return nullSingleton();
+  if (!found)
+    return nullSingleton();
   return *found;
 }
 
@@ -1125,7 +1114,7 @@
   return resolveReference(key, key + strlen(key));
 }
 
-Value& Value::operator[](const JSONCPP_STRING& key) {
+Value& Value::operator[](const String& key) {
   return resolveReference(key.data(), key.data() + key.length());
 }
 
@@ -1133,179 +1122,140 @@
   return resolveReference(key.c_str());
 }
 
-#ifdef JSON_USE_CPPTL
-Value& Value::operator[](const CppTL::ConstString& key) {
-  return resolveReference(key.c_str(), key.end_c_str());
+Value& Value::append(const Value& value) { return append(Value(value)); }
+
+Value& Value::append(Value&& value) {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+                      "in Json::Value::append: requires arrayValue");
+  if (type() == nullValue) {
+    *this = Value(arrayValue);
+  }
+  return this->value_.map_->emplace(size(), std::move(value)).first->second;
 }
-Value const& Value::operator[](CppTL::ConstString const& key) const
-{
-  Value const* found = find(key.c_str(), key.end_c_str());
-  if (!found) return nullSingleton();
-  return *found;
+
+bool Value::insert(ArrayIndex index, const Value& newValue) {
+  return insert(index, Value(newValue));
 }
-#endif
 
-Value& Value::append(const Value& value) { return (*this)[size()] = value; }
+bool Value::insert(ArrayIndex index, Value&& newValue) {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == arrayValue,
+                      "in Json::Value::insert: requires arrayValue");
+  ArrayIndex length = size();
+  if (index > length) {
+    return false;
+  }
+  for (ArrayIndex i = length; i > index; i--) {
+    (*this)[i] = std::move((*this)[i - 1]);
+  }
+  (*this)[index] = std::move(newValue);
+  return true;
+}
 
-#if JSON_HAS_RVALUE_REFERENCES
-  Value& Value::append(Value&& value) { return (*this)[size()] = value; }
-#endif
-
-Value Value::get(char const* key, char const* cend, Value const& defaultValue) const
-{
-  Value const* found = find(key, cend);
+Value Value::get(char const* begin, char const* end,
+                 Value const& defaultValue) const {
+  Value const* found = find(begin, end);
   return !found ? defaultValue : *found;
 }
-Value Value::get(char const* key, Value const& defaultValue) const
-{
+Value Value::get(char const* key, Value const& defaultValue) const {
   return get(key, key + strlen(key), defaultValue);
 }
-Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const
-{
+Value Value::get(String const& key, Value const& defaultValue) const {
   return get(key.data(), key.data() + key.length(), defaultValue);
 }
 
-
-bool Value::removeMember(const char* key, const char* cend, Value* removed)
-{
-  if (type_ != objectValue) {
+bool Value::removeMember(const char* begin, const char* end, Value* removed) {
+  if (type() != objectValue) {
     return false;
   }
-  CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication);
-  ObjectValues::iterator it = value_.map_->find(actualKey);
+  CZString actualKey(begin, static_cast<unsigned>(end - begin),
+                     CZString::noDuplication);
+  auto it = value_.map_->find(actualKey);
   if (it == value_.map_->end())
     return false;
-  *removed = it->second;
+  if (removed)
+    *removed = std::move(it->second);
   value_.map_->erase(it);
   return true;
 }
-bool Value::removeMember(const char* key, Value* removed)
-{
+bool Value::removeMember(const char* key, Value* removed) {
   return removeMember(key, key + strlen(key), removed);
 }
-bool Value::removeMember(JSONCPP_STRING const& key, Value* removed)
-{
+bool Value::removeMember(String const& key, Value* removed) {
   return removeMember(key.data(), key.data() + key.length(), removed);
 }
-Value Value::removeMember(const char* key)
-{
-  JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue,
+void Value::removeMember(const char* key) {
+  JSON_ASSERT_MESSAGE(type() == nullValue || type() == objectValue,
                       "in Json::Value::removeMember(): requires objectValue");
-  if (type_ == nullValue)
-    return nullSingleton();
+  if (type() == nullValue)
+    return;
 
-  Value removed;  // null
-  removeMember(key, key + strlen(key), &removed);
-  return removed; // still null if removeMember() did nothing
+  CZString actualKey(key, unsigned(strlen(key)), CZString::noDuplication);
+  value_.map_->erase(actualKey);
 }
-Value Value::removeMember(const JSONCPP_STRING& key)
-{
-  return removeMember(key.c_str());
-}
+void Value::removeMember(const String& key) { removeMember(key.c_str()); }
 
 bool Value::removeIndex(ArrayIndex index, Value* removed) {
-  if (type_ != arrayValue) {
+  if (type() != arrayValue) {
     return false;
   }
   CZString key(index);
-  ObjectValues::iterator it = value_.map_->find(key);
+  auto it = value_.map_->find(key);
   if (it == value_.map_->end()) {
     return false;
   }
-  *removed = it->second;
+  if (removed)
+    *removed = it->second;
   ArrayIndex oldSize = size();
   // shift left all items left, into the place of the "removed"
-  for (ArrayIndex i = index; i < (oldSize - 1); ++i){
+  for (ArrayIndex i = index; i < (oldSize - 1); ++i) {
     CZString keey(i);
     (*value_.map_)[keey] = (*this)[i + 1];
   }
   // erase the last one ("leftover")
   CZString keyLast(oldSize - 1);
-  ObjectValues::iterator itLast = value_.map_->find(keyLast);
+  auto itLast = value_.map_->find(keyLast);
   value_.map_->erase(itLast);
   return true;
 }
 
-#ifdef JSON_USE_CPPTL
-Value Value::get(const CppTL::ConstString& key,
-                 const Value& defaultValue) const {
-  return get(key.c_str(), key.end_c_str(), defaultValue);
+bool Value::isMember(char const* begin, char const* end) const {
+  Value const* value = find(begin, end);
+  return nullptr != value;
 }
-#endif
-
-bool Value::isMember(char const* key, char const* cend) const
-{
-  Value const* value = find(key, cend);
-  return NULL != value;
-}
-bool Value::isMember(char const* key) const
-{
+bool Value::isMember(char const* key) const {
   return isMember(key, key + strlen(key));
 }
-bool Value::isMember(JSONCPP_STRING const& key) const
-{
+bool Value::isMember(String const& key) const {
   return isMember(key.data(), key.data() + key.length());
 }
 
-#ifdef JSON_USE_CPPTL
-bool Value::isMember(const CppTL::ConstString& key) const {
-  return isMember(key.c_str(), key.end_c_str());
-}
-#endif
-
 Value::Members Value::getMemberNames() const {
   JSON_ASSERT_MESSAGE(
-      type_ == nullValue || type_ == objectValue,
+      type() == nullValue || type() == objectValue,
       "in Json::Value::getMemberNames(), value must be objectValue");
-  if (type_ == nullValue)
+  if (type() == nullValue)
     return Value::Members();
   Members members;
   members.reserve(value_.map_->size());
   ObjectValues::const_iterator it = value_.map_->begin();
   ObjectValues::const_iterator itEnd = value_.map_->end();
   for (; it != itEnd; ++it) {
-    members.push_back(JSONCPP_STRING((*it).first.data(),
-                                  (*it).first.length()));
+    members.push_back(String((*it).first.data(), (*it).first.length()));
   }
   return members;
 }
-//
-//# ifdef JSON_USE_CPPTL
-// EnumMemberNames
-// Value::enumMemberNames() const
-//{
-//   if ( type_ == objectValue )
-//   {
-//      return CppTL::Enum::any(  CppTL::Enum::transform(
-//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
-//         MemberNamesTransform() ) );
-//   }
-//   return EnumMemberNames();
-//}
-//
-//
-// EnumValues
-// Value::enumValues() const
-//{
-//   if ( type_ == objectValue  ||  type_ == arrayValue )
-//      return CppTL::Enum::anyValues( *(value_.map_),
-//                                     CppTL::Type<const Value &>() );
-//   return EnumValues();
-//}
-//
-//# endif
 
 static bool IsIntegral(double d) {
   double integral_part;
   return modf(d, &integral_part) == 0.0;
 }
 
-bool Value::isNull() const { return type_ == nullValue; }
+bool Value::isNull() const { return type() == nullValue; }
 
-bool Value::isBool() const { return type_ == booleanValue; }
+bool Value::isBool() const { return type() == booleanValue; }
 
 bool Value::isInt() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
 #if defined(JSON_HAS_INT64)
     return value_.int_ >= minInt && value_.int_ <= maxInt;
@@ -1324,7 +1274,7 @@
 }
 
 bool Value::isUInt() const {
-  switch (type_) {
+  switch (type()) {
   case intValue:
 #if defined(JSON_HAS_INT64)
     return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt);
@@ -1348,7 +1298,7 @@
 
 bool Value::isInt64() const {
 #if defined(JSON_HAS_INT64)
-  switch (type_) {
+  switch (type()) {
   case intValue:
     return true;
   case uintValue:
@@ -1368,7 +1318,7 @@
 
 bool Value::isUInt64() const {
 #if defined(JSON_HAS_INT64)
-  switch (type_) {
+  switch (type()) {
   case intValue:
     return value_.int_ >= 0;
   case uintValue:
@@ -1387,61 +1337,92 @@
 }
 
 bool Value::isIntegral() const {
-  switch (type_) {
-    case intValue:
-    case uintValue:
-      return true;
-    case realValue:
+  switch (type()) {
+  case intValue:
+  case uintValue:
+    return true;
+  case realValue:
 #if defined(JSON_HAS_INT64)
-      // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
-      // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
-      // require the value to be strictly less than the limit.
-      return value_.real_ >= double(minInt64) && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
+    // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a
+    // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we
+    // require the value to be strictly less than the limit.
+    return value_.real_ >= double(minInt64) &&
+           value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_);
 #else
-      return value_.real_ >= minInt && value_.real_ <= maxUInt && IsIntegral(value_.real_);
+    return value_.real_ >= minInt && value_.real_ <= maxUInt &&
+           IsIntegral(value_.real_);
 #endif // JSON_HAS_INT64
-    default:
-      break;
+  default:
+    break;
   }
   return false;
 }
 
-bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; }
+bool Value::isDouble() const {
+  return type() == intValue || type() == uintValue || type() == realValue;
+}
 
 bool Value::isNumeric() const { return isDouble(); }
 
-bool Value::isString() const { return type_ == stringValue; }
+bool Value::isString() const { return type() == stringValue; }
 
-bool Value::isArray() const { return type_ == arrayValue; }
+bool Value::isArray() const { return type() == arrayValue; }
 
-bool Value::isObject() const { return type_ == objectValue; }
+bool Value::isObject() const { return type() == objectValue; }
 
-void Value::setComment(const char* comment, size_t len, CommentPlacement placement) {
-  if (!comments_)
-    comments_ = new CommentInfo[numberOfCommentPlacement];
-  if ((len > 0) && (comment[len-1] == '\n')) {
-    // Always discard trailing newline, to aid indentation.
-    len -= 1;
+Value::Comments::Comments(const Comments& that)
+    : ptr_{cloneUnique(that.ptr_)} {}
+
+Value::Comments::Comments(Comments&& that) : ptr_{std::move(that.ptr_)} {}
+
+Value::Comments& Value::Comments::operator=(const Comments& that) {
+  ptr_ = cloneUnique(that.ptr_);
+  return *this;
+}
+
+Value::Comments& Value::Comments::operator=(Comments&& that) {
+  ptr_ = std::move(that.ptr_);
+  return *this;
+}
+
+bool Value::Comments::has(CommentPlacement slot) const {
+  return ptr_ && !(*ptr_)[slot].empty();
+}
+
+String Value::Comments::get(CommentPlacement slot) const {
+  if (!ptr_)
+    return {};
+  return (*ptr_)[slot];
+}
+
+void Value::Comments::set(CommentPlacement slot, String comment) {
+  if (!ptr_) {
+    ptr_ = std::unique_ptr<Array>(new Array());
   }
-  comments_[placement].setComment(comment, len);
+  // check comments array boundry.
+  if (slot < CommentPlacement::numberOfCommentPlacement) {
+    (*ptr_)[slot] = std::move(comment);
+  }
 }
 
-void Value::setComment(const char* comment, CommentPlacement placement) {
-  setComment(comment, strlen(comment), placement);
-}
-
-void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) {
-  setComment(comment.c_str(), comment.length(), placement);
+void Value::setComment(String comment, CommentPlacement placement) {
+  if (!comment.empty() && (comment.back() == '\n')) {
+    // Always discard trailing newline, to aid indentation.
+    comment.pop_back();
+  }
+  JSON_ASSERT(!comment.empty());
+  JSON_ASSERT_MESSAGE(
+      comment[0] == '\0' || comment[0] == '/',
+      "in Json::Value::setComment(): Comments must start with /");
+  comments_.set(placement, std::move(comment));
 }
 
 bool Value::hasComment(CommentPlacement placement) const {
-  return comments_ != 0 && comments_[placement].comment_ != 0;
+  return comments_.has(placement);
 }
 
-JSONCPP_STRING Value::getComment(CommentPlacement placement) const {
-  if (hasComment(placement))
-    return comments_[placement].comment_;
-  return "";
+String Value::getComment(CommentPlacement placement) const {
+  return comments_.get(placement);
 }
 
 void Value::setOffsetStart(ptrdiff_t start) { start_ = start; }
@@ -1452,18 +1433,18 @@
 
 ptrdiff_t Value::getOffsetLimit() const { return limit_; }
 
-JSONCPP_STRING Value::toStyledString() const {
+String Value::toStyledString() const {
   StreamWriterBuilder builder;
 
-  JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : "";
+  String out = this->hasComment(commentBefore) ? "\n" : "";
   out += Json::writeString(builder, *this);
-  out += "\n";
+  out += '\n';
 
   return out;
 }
 
 Value::const_iterator Value::begin() const {
-  switch (type_) {
+  switch (type()) {
   case arrayValue:
   case objectValue:
     if (value_.map_)
@@ -1472,11 +1453,11 @@
   default:
     break;
   }
-  return const_iterator();
+  return {};
 }
 
 Value::const_iterator Value::end() const {
-  switch (type_) {
+  switch (type()) {
   case arrayValue:
   case objectValue:
     if (value_.map_)
@@ -1485,11 +1466,11 @@
   default:
     break;
   }
-  return const_iterator();
+  return {};
 }
 
 Value::iterator Value::begin() {
-  switch (type_) {
+  switch (type()) {
   case arrayValue:
   case objectValue:
     if (value_.map_)
@@ -1502,7 +1483,7 @@
 }
 
 Value::iterator Value::end() {
-  switch (type_) {
+  switch (type()) {
   case arrayValue:
   case objectValue:
     if (value_.map_)
@@ -1517,25 +1498,20 @@
 // class PathArgument
 // //////////////////////////////////////////////////////////////////
 
-PathArgument::PathArgument() : key_(), index_(), kind_(kindNone) {}
+PathArgument::PathArgument() = default;
 
 PathArgument::PathArgument(ArrayIndex index)
-    : key_(), index_(index), kind_(kindIndex) {}
+    : index_(index), kind_(kindIndex) {}
 
-PathArgument::PathArgument(const char* key)
-    : key_(key), index_(), kind_(kindKey) {}
+PathArgument::PathArgument(const char* key) : key_(key), kind_(kindKey) {}
 
-PathArgument::PathArgument(const JSONCPP_STRING& key)
-    : key_(key.c_str()), index_(), kind_(kindKey) {}
+PathArgument::PathArgument(String key) : key_(std::move(key)), kind_(kindKey) {}
 
 // class Path
 // //////////////////////////////////////////////////////////////////
 
-Path::Path(const JSONCPP_STRING& path,
-           const PathArgument& a1,
-           const PathArgument& a2,
-           const PathArgument& a3,
-           const PathArgument& a4,
+Path::Path(const String& path, const PathArgument& a1, const PathArgument& a2,
+           const PathArgument& a3, const PathArgument& a4,
            const PathArgument& a5) {
   InArgs in;
   in.reserve(5);
@@ -1547,10 +1523,10 @@
   makePath(path, in);
 }
 
-void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) {
+void Path::makePath(const String& path, const InArgs& in) {
   const char* current = path.c_str();
   const char* end = current + path.length();
-  InArgs::const_iterator itInArg = in.begin();
+  auto itInArg = in.begin();
   while (current != end) {
     if (*current == '[') {
       ++current;
@@ -1573,13 +1549,12 @@
       const char* beginName = current;
       while (current != end && !strchr("[.", *current))
         ++current;
-      args_.push_back(JSONCPP_STRING(beginName, current));
+      args_.push_back(String(beginName, current));
     }
   }
 }
 
-void Path::addPathInArg(const JSONCPP_STRING& /*path*/,
-                        const InArgs& in,
+void Path::addPathInArg(const String& /*path*/, const InArgs& in,
                         InArgs::const_iterator& itInArg,
                         PathArgument::Kind kind) {
   if (itInArg == in.end()) {
@@ -1591,30 +1566,29 @@
   }
 }
 
-void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) {
+void Path::invalidPath(const String& /*path*/, int /*location*/) {
   // Error: invalid path.
 }
 
 const Value& Path::resolve(const Value& root) const {
   const Value* node = &root;
-  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
-    const PathArgument& arg = *it;
+  for (const auto& arg : args_) {
     if (arg.kind_ == PathArgument::kindIndex) {
       if (!node->isArray() || !node->isValidIndex(arg.index_)) {
-        // Error: unable to resolve path (array value expected at position...
-        return Value::null;
+        // Error: unable to resolve path (array value expected at position... )
+        return Value::nullSingleton();
       }
       node = &((*node)[arg.index_]);
     } else if (arg.kind_ == PathArgument::kindKey) {
       if (!node->isObject()) {
         // Error: unable to resolve path (object value expected at position...)
-        return Value::null;
+        return Value::nullSingleton();
       }
       node = &((*node)[arg.key_]);
       if (node == &Value::nullSingleton()) {
         // Error: unable to resolve path (object has no member named '' at
         // position...)
-        return Value::null;
+        return Value::nullSingleton();
       }
     }
   }
@@ -1623,8 +1597,7 @@
 
 Value Path::resolve(const Value& root, const Value& defaultValue) const {
   const Value* node = &root;
-  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
-    const PathArgument& arg = *it;
+  for (const auto& arg : args_) {
     if (arg.kind_ == PathArgument::kindIndex) {
       if (!node->isArray() || !node->isValidIndex(arg.index_))
         return defaultValue;
@@ -1642,8 +1615,7 @@
 
 Value& Path::make(Value& root) const {
   Value* node = &root;
-  for (Args::const_iterator it = args_.begin(); it != args_.end(); ++it) {
-    const PathArgument& arg = *it;
+  for (const auto& arg : args_) {
     if (arg.kind_ == PathArgument::kindIndex) {
       if (!node->isArray()) {
         // Error: node is not an array at position ...
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl b/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl
index 5243bfe..d6128b8 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl
+++ b/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl
@@ -15,31 +15,21 @@
 // //////////////////////////////////////////////////////////////////
 // //////////////////////////////////////////////////////////////////
 
-ValueIteratorBase::ValueIteratorBase()
-    : current_(), isNull_(true) {
-}
+ValueIteratorBase::ValueIteratorBase() : current_() {}
 
 ValueIteratorBase::ValueIteratorBase(
     const Value::ObjectValues::iterator& current)
     : current_(current), isNull_(false) {}
 
-Value& ValueIteratorBase::deref() const {
-  return current_->second;
-}
+Value& ValueIteratorBase::deref() { return current_->second; }
+const Value& ValueIteratorBase::deref() const { return current_->second; }
 
-void ValueIteratorBase::increment() {
-  ++current_;
-}
+void ValueIteratorBase::increment() { ++current_; }
 
-void ValueIteratorBase::decrement() {
-  --current_;
-}
+void ValueIteratorBase::decrement() { --current_; }
 
 ValueIteratorBase::difference_type
 ValueIteratorBase::computeDistance(const SelfType& other) const {
-#ifdef JSON_USE_CPPTL_SMALLMAP
-  return other.current_ - current_;
-#else
   // Iterator for null value are initialized using the default
   // constructor, which initialize current_ to the default
   // std::map::iterator. As begin() and end() are two instance
@@ -60,7 +50,6 @@
     ++myDistance;
   }
   return myDistance;
-#endif
 }
 
 bool ValueIteratorBase::isEqual(const SelfType& other) const {
@@ -92,12 +81,13 @@
   return Value::UInt(-1);
 }
 
-JSONCPP_STRING ValueIteratorBase::name() const {
+String ValueIteratorBase::name() const {
   char const* keey;
   char const* end;
   keey = memberName(&end);
-  if (!keey) return JSONCPP_STRING();
-  return JSONCPP_STRING(keey, end);
+  if (!keey)
+    return String();
+  return String(keey, end);
 }
 
 char const* ValueIteratorBase::memberName() const {
@@ -108,8 +98,8 @@
 char const* ValueIteratorBase::memberName(char const** end) const {
   const char* cname = (*current_).first.data();
   if (!cname) {
-    *end = NULL;
-    return NULL;
+    *end = nullptr;
+    return nullptr;
   }
   *end = cname + (*current_).first.length();
   return cname;
@@ -123,7 +113,7 @@
 // //////////////////////////////////////////////////////////////////
 // //////////////////////////////////////////////////////////////////
 
-ValueConstIterator::ValueConstIterator() {}
+ValueConstIterator::ValueConstIterator() = default;
 
 ValueConstIterator::ValueConstIterator(
     const Value::ObjectValues::iterator& current)
@@ -146,7 +136,7 @@
 // //////////////////////////////////////////////////////////////////
 // //////////////////////////////////////////////////////////////////
 
-ValueIterator::ValueIterator() {}
+ValueIterator::ValueIterator() = default;
 
 ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
     : ValueIteratorBase(current) {}
@@ -156,8 +146,7 @@
   throwRuntimeError("ConstIterator to Iterator should never be allowed.");
 }
 
-ValueIterator::ValueIterator(const ValueIterator& other)
-    : ValueIteratorBase(other) {}
+ValueIterator::ValueIterator(const ValueIterator& other) = default;
 
 ValueIterator& ValueIterator::operator=(const SelfType& other) {
   copy(other);
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
index fc86505..8bf02db 100644
--- a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
+++ b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp
@@ -4,106 +4,81 @@
 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
 
 #if !defined(JSON_IS_AMALGAMATION)
-#include <json/writer.h>
 #include "json_tool.h"
+#include <json/writer.h>
 #endif // if !defined(JSON_IS_AMALGAMATION)
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstring>
 #include <iomanip>
 #include <memory>
+#include <set>
 #include <sstream>
 #include <utility>
-#include <set>
-#include <cassert>
-#include <cstring>
+
+#if __cplusplus >= 201103L
+#include <cmath>
 #include <cstdio>
 
-#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0
+#if !defined(isnan)
+#define isnan std::isnan
+#endif
+
+#if !defined(isfinite)
+#define isfinite std::isfinite
+#endif
+
+#else
+#include <cmath>
+#include <cstdio>
+
+#if defined(_MSC_VER)
+#if !defined(isnan)
+#include <float.h>
+#define isnan _isnan
+#endif
+
+#if !defined(isfinite)
 #include <float.h>
 #define isfinite _finite
-#elif defined(__sun) && defined(__SVR4) //Solaris
+#endif
+
+#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
+
+#endif //_MSC_VER
+
+#if defined(__sun) && defined(__SVR4) // Solaris
 #if !defined(isfinite)
 #include <ieeefp.h>
 #define isfinite finite
 #endif
-#elif defined(_AIX)
+#endif
+
+#if defined(__hpux)
 #if !defined(isfinite)
-#include <math.h>
-#define isfinite finite
-#endif
-#elif defined(__hpux)
-#if !defined(isfinite) && !defined(__GNUC__)
 #if defined(__ia64) && !defined(finite)
-#define isfinite(x) ((sizeof(x) == sizeof(float) ? \
-                     _Isfinitef(x) : _IsFinite(x)))
-#else
-#include <math.h>
+#define isfinite(x)                                                            \
+  ((sizeof(x) == sizeof(float) ? _Isfinitef(x) : _IsFinite(x)))
+#endif
+#endif
+#endif
+
+#if !defined(isnan)
+// IEEE standard states that NaN values will not compare to themselves
+#define isnan(x) (x != x)
+#endif
+
+#if !defined(__APPLE__)
+#if !defined(isfinite)
 #define isfinite finite
 #endif
 #endif
-#else
-#include <cmath>
-#if !(defined(__QNXNTO__)) // QNX already defines isfinite
-#define isfinite std::isfinite
-#endif
 #endif
 
 #if defined(_MSC_VER)
-#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above
-#define snprintf sprintf_s
-#elif _MSC_VER >= 1900 // VC++ 14.0 and above
-#define snprintf std::snprintf
-#else
-#define snprintf _snprintf
-#endif
-#elif defined(__ANDROID__) || defined(__QNXNTO__)
-#define snprintf snprintf
-#elif __cplusplus >= 201103L
-#if !defined(__MINGW32__) && !defined(__CYGWIN__)
-#define snprintf std::snprintf
-#endif
-#endif
-
-#if defined(__BORLANDC__)  
-#include <float.h>
-#define isfinite _finite
-#define snprintf _snprintf
-#endif
-
-// Solaris
-#if defined(__sun)
-# include <ieeefp.h>
-# if !defined(isfinite)
-#  define isfinite finite
-# endif
-#endif
-
-// AIX
-#if defined(_AIX)
-# if !defined(isfinite)
-#  define isfinite finite
-# endif
-#endif
-
-// HP-UX
-#if defined(__hpux)
-# if !defined(isfinite)
-#  if defined(__ia64) && !defined(finite) && !defined(__GNUC__)
-#   define isfinite(x) ((sizeof(x) == sizeof(float) ? \
-                        _Isfinitef(x) : _Isfinite(x)))
-#  else
-#   include <math.h>
-#   define isfinite finite
-#  endif
-# endif
-#endif
-
-// Ancient glibc
-#if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 2
-# if !defined(isfinite)
-#  define isfinite __finite
-# endif
-#endif
-
-#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
 // Disable warning about strdup being deprecated.
 #pragma warning(disable : 4996)
 #endif
@@ -111,30 +86,12 @@
 namespace Json {
 
 #if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
-typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
+using StreamWriterPtr = std::unique_ptr<StreamWriter>;
 #else
-typedef std::auto_ptr<StreamWriter>   StreamWriterPtr;
+using StreamWriterPtr = std::auto_ptr<StreamWriter>;
 #endif
 
-static bool containsControlCharacter(const char* str) {
-  while (*str) {
-    if (isControlCharacter(*(str++)))
-      return true;
-  }
-  return false;
-}
-
-static bool containsControlCharacter0(const char* str, unsigned len) {
-  char const* end = str + len;
-  while (end != str) {
-    if (isControlCharacter(*str) || 0==*str)
-      return true;
-    ++str;
-  }
-  return false;
-}
-
-JSONCPP_STRING valueToString(LargestInt value) {
+String valueToString(LargestInt value) {
   UIntToStringBuffer buffer;
   char* current = buffer + sizeof(buffer);
   if (value == Value::minLargestInt) {
@@ -150,7 +107,7 @@
   return current;
 }
 
-JSONCPP_STRING valueToString(LargestUInt value) {
+String valueToString(LargestUInt value) {
   UIntToStringBuffer buffer;
   char* current = buffer + sizeof(buffer);
   uintToString(value, current);
@@ -160,147 +117,171 @@
 
 #if defined(JSON_HAS_INT64)
 
-JSONCPP_STRING valueToString(Int value) {
-  return valueToString(LargestInt(value));
-}
+String valueToString(Int value) { return valueToString(LargestInt(value)); }
 
-JSONCPP_STRING valueToString(UInt value) {
-  return valueToString(LargestUInt(value));
-}
+String valueToString(UInt value) { return valueToString(LargestUInt(value)); }
 
 #endif // # if defined(JSON_HAS_INT64)
 
 namespace {
-JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) {
-  // Allocate a buffer that is more than large enough to store the 16 digits of
-  // precision requested below.
-  char buffer[36];
-  int len = -1;
-
-  char formatString[15];
-  snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
-
+String valueToString(double value, bool useSpecialFloats,
+                     unsigned int precision, PrecisionType precisionType) {
   // Print into the buffer. We need not request the alternative representation
-  // that always has a decimal point because JSON doesn't distingish the
+  // that always has a decimal point because JSON doesn't distinguish the
   // concepts of reals and integers.
-  if (isfinite(value)) {
-    len = snprintf(buffer, sizeof(buffer), formatString, value);
-    fixNumericLocale(buffer, buffer + len);
-
-    // try to ensure we preserve the fact that this was given to us as a double on input
-    if (!strchr(buffer, '.') && !strchr(buffer, 'e')) {
-      strcat(buffer, ".0");
-    }
-
-  } else {
-    // IEEE standard states that NaN values will not compare to themselves
-    if (value != value) {
-      len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null");
-    } else if (value < 0) {
-      len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999");
-    } else {
-      len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999");
-    }
+  if (!isfinite(value)) {
+    static const char* const reps[2][3] = {{"NaN", "-Infinity", "Infinity"},
+                                           {"null", "-1e+9999", "1e+9999"}};
+    return reps[useSpecialFloats ? 0 : 1]
+               [isnan(value) ? 0 : (value < 0) ? 1 : 2];
   }
-  assert(len >= 0);
+
+  String buffer(size_t(36), '\0');
+  while (true) {
+    int len = jsoncpp_snprintf(
+        &*buffer.begin(), buffer.size(),
+        (precisionType == PrecisionType::significantDigits) ? "%.*g" : "%.*f",
+        precision, value);
+    assert(len >= 0);
+    auto wouldPrint = static_cast<size_t>(len);
+    if (wouldPrint >= buffer.size()) {
+      buffer.resize(wouldPrint + 1);
+      continue;
+    }
+    buffer.resize(wouldPrint);
+    break;
+  }
+
+  buffer.erase(fixNumericLocale(buffer.begin(), buffer.end()), buffer.end());
+
+  // strip the zero padding from the right
+  if (precisionType == PrecisionType::decimalPlaces) {
+    buffer.erase(fixZerosInTheEnd(buffer.begin(), buffer.end()), buffer.end());
+  }
+
+  // try to ensure we preserve the fact that this was given to us as a double on
+  // input
+  if (buffer.find('.') == buffer.npos && buffer.find('e') == buffer.npos) {
+    buffer += ".0";
+  }
   return buffer;
 }
+} // namespace
+
+String valueToString(double value, unsigned int precision,
+                     PrecisionType precisionType) {
+  return valueToString(value, false, precision, precisionType);
 }
 
-JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); }
+String valueToString(bool value) { return value ? "true" : "false"; }
 
-JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; }
+static bool doesAnyCharRequireEscaping(char const* s, size_t n) {
+  assert(s || !n);
 
-JSONCPP_STRING valueToQuotedString(const char* value) {
-  if (value == NULL)
-    return "";
-  // Not sure how to handle unicode...
-  if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL &&
-      !containsControlCharacter(value))
-    return JSONCPP_STRING("\"") + value + "\"";
-  // We have to walk value and escape any special characters.
-  // Appending to JSONCPP_STRING is not efficient, but this should be rare.
-  // (Note: forward slashes are *not* rare, but I am not escaping them.)
-  JSONCPP_STRING::size_type maxsize =
-      strlen(value) * 2 + 3; // allescaped+quotes+NULL
-  JSONCPP_STRING result;
-  result.reserve(maxsize); // to avoid lots of mallocs
-  result += "\"";
-  for (const char* c = value; *c != 0; ++c) {
-    switch (*c) {
-    case '\"':
-      result += "\\\"";
-      break;
-    case '\\':
-      result += "\\\\";
-      break;
-    case '\b':
-      result += "\\b";
-      break;
-    case '\f':
-      result += "\\f";
-      break;
-    case '\n':
-      result += "\\n";
-      break;
-    case '\r':
-      result += "\\r";
-      break;
-    case '\t':
-      result += "\\t";
-      break;
-    // case '/':
-    // Even though \/ is considered a legal escape in JSON, a bare
-    // slash is also legal, so I see no reason to escape it.
-    // (I hope I am not misunderstanding something.
-    // blep notes: actually escaping \/ may be useful in javascript to avoid </
-    // sequence.
-    // Should add a flag to allow this compatibility mode and prevent this
-    // sequence from occurring.
-    default:
-      if (isControlCharacter(*c)) {
-        JSONCPP_OSTRINGSTREAM oss;
-        oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
-            << std::setw(4) << static_cast<int>(*c);
-        result += oss.str();
-      } else {
-        result += *c;
-      }
-      break;
-    }
+  return std::any_of(s, s + n, [](unsigned char c) {
+    return c == '\\' || c == '"' || c < 0x20 || c > 0x7F;
+  });
+}
+
+static unsigned int utf8ToCodepoint(const char*& s, const char* e) {
+  const unsigned int REPLACEMENT_CHARACTER = 0xFFFD;
+
+  unsigned int firstByte = static_cast<unsigned char>(*s);
+
+  if (firstByte < 0x80)
+    return firstByte;
+
+  if (firstByte < 0xE0) {
+    if (e - s < 2)
+      return REPLACEMENT_CHARACTER;
+
+    unsigned int calculated =
+        ((firstByte & 0x1F) << 6) | (static_cast<unsigned int>(s[1]) & 0x3F);
+    s += 1;
+    // oversized encoded characters are invalid
+    return calculated < 0x80 ? REPLACEMENT_CHARACTER : calculated;
   }
-  result += "\"";
+
+  if (firstByte < 0xF0) {
+    if (e - s < 3)
+      return REPLACEMENT_CHARACTER;
+
+    unsigned int calculated = ((firstByte & 0x0F) << 12) |
+                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 6) |
+                              (static_cast<unsigned int>(s[2]) & 0x3F);
+    s += 2;
+    // surrogates aren't valid codepoints itself
+    // shouldn't be UTF-8 encoded
+    if (calculated >= 0xD800 && calculated <= 0xDFFF)
+      return REPLACEMENT_CHARACTER;
+    // oversized encoded characters are invalid
+    return calculated < 0x800 ? REPLACEMENT_CHARACTER : calculated;
+  }
+
+  if (firstByte < 0xF8) {
+    if (e - s < 4)
+      return REPLACEMENT_CHARACTER;
+
+    unsigned int calculated = ((firstByte & 0x07) << 18) |
+                              ((static_cast<unsigned int>(s[1]) & 0x3F) << 12) |
+                              ((static_cast<unsigned int>(s[2]) & 0x3F) << 6) |
+                              (static_cast<unsigned int>(s[3]) & 0x3F);
+    s += 3;
+    // oversized encoded characters are invalid
+    return calculated < 0x10000 ? REPLACEMENT_CHARACTER : calculated;
+  }
+
+  return REPLACEMENT_CHARACTER;
+}
+
+static const char hex2[] = "000102030405060708090a0b0c0d0e0f"
+                           "101112131415161718191a1b1c1d1e1f"
+                           "202122232425262728292a2b2c2d2e2f"
+                           "303132333435363738393a3b3c3d3e3f"
+                           "404142434445464748494a4b4c4d4e4f"
+                           "505152535455565758595a5b5c5d5e5f"
+                           "606162636465666768696a6b6c6d6e6f"
+                           "707172737475767778797a7b7c7d7e7f"
+                           "808182838485868788898a8b8c8d8e8f"
+                           "909192939495969798999a9b9c9d9e9f"
+                           "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
+                           "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
+                           "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+                           "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
+                           "e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+                           "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
+
+static String toHex16Bit(unsigned int x) {
+  const unsigned int hi = (x >> 8) & 0xff;
+  const unsigned int lo = x & 0xff;
+  String result(4, ' ');
+  result[0] = hex2[2 * hi];
+  result[1] = hex2[2 * hi + 1];
+  result[2] = hex2[2 * lo];
+  result[3] = hex2[2 * lo + 1];
   return result;
 }
 
-// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp
-static char const* strnpbrk(char const* s, char const* accept, size_t n) {
-  assert((s || !n) && accept);
-
-  char const* const end = s + n;
-  for (char const* cur = s; cur < end; ++cur) {
-    int const c = *cur;
-    for (char const* a = accept; *a; ++a) {
-      if (*a == c) {
-        return cur;
-      }
-    }
-  }
-  return NULL;
+static void appendRaw(String& result, unsigned ch) {
+  result += static_cast<char>(ch);
 }
-static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) {
-  if (value == NULL)
+
+static void appendHex(String& result, unsigned ch) {
+  result.append("\\u").append(toHex16Bit(ch));
+}
+
+static String valueToQuotedStringN(const char* value, unsigned length,
+                                   bool emitUTF8 = false) {
+  if (value == nullptr)
     return "";
-  // Not sure how to handle unicode...
-  if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL &&
-      !containsControlCharacter0(value, length))
-    return JSONCPP_STRING("\"") + value + "\"";
+
+  if (!doesAnyCharRequireEscaping(value, length))
+    return String("\"") + value + "\"";
   // We have to walk value and escape any special characters.
-  // Appending to JSONCPP_STRING is not efficient, but this should be rare.
+  // Appending to String is not efficient, but this should be rare.
   // (Note: forward slashes are *not* rare, but I am not escaping them.)
-  JSONCPP_STRING::size_type maxsize =
-      length * 2 + 3; // allescaped+quotes+NULL
-  JSONCPP_STRING result;
+  String::size_type maxsize = length * 2 + 3; // allescaped+quotes+NULL
+  String result;
   result.reserve(maxsize); // to avoid lots of mallocs
   result += "\"";
   char const* end = value + length;
@@ -335,44 +316,63 @@
     // sequence.
     // Should add a flag to allow this compatibility mode and prevent this
     // sequence from occurring.
-    default:
-      if ((isControlCharacter(*c)) || (*c == 0)) {
-        JSONCPP_OSTRINGSTREAM oss;
-        oss << "\\u" << std::hex << std::uppercase << std::setfill('0')
-            << std::setw(4) << static_cast<int>(*c);
-        result += oss.str();
+    default: {
+      if (emitUTF8) {
+        unsigned codepoint = static_cast<unsigned char>(*c);
+        if (codepoint < 0x20) {
+          appendHex(result, codepoint);
+        } else {
+          appendRaw(result, codepoint);
+        }
       } else {
-        result += *c;
+        unsigned codepoint = utf8ToCodepoint(c, end); // modifies `c`
+        if (codepoint < 0x20) {
+          appendHex(result, codepoint);
+        } else if (codepoint < 0x80) {
+          appendRaw(result, codepoint);
+        } else if (codepoint < 0x10000) {
+          // Basic Multilingual Plane
+          appendHex(result, codepoint);
+        } else {
+          // Extended Unicode. Encode 20 bits as a surrogate pair.
+          codepoint -= 0x10000;
+          appendHex(result, 0xd800 + ((codepoint >> 10) & 0x3ff));
+          appendHex(result, 0xdc00 + (codepoint & 0x3ff));
+        }
       }
-      break;
+    } break;
     }
   }
   result += "\"";
   return result;
 }
 
+String valueToQuotedString(const char* value) {
+  return valueToQuotedStringN(value, static_cast<unsigned int>(strlen(value)));
+}
+
 // Class Writer
 // //////////////////////////////////////////////////////////////////
-Writer::~Writer() {}
+Writer::~Writer() = default;
 
 // Class FastWriter
 // //////////////////////////////////////////////////////////////////
 
 FastWriter::FastWriter()
-    : yamlCompatiblityEnabled_(false), dropNullPlaceholders_(false),
-      omitEndingLineFeed_(false) {}
 
-void FastWriter::enableYAMLCompatibility() { yamlCompatiblityEnabled_ = true; }
+    = default;
+
+void FastWriter::enableYAMLCompatibility() { yamlCompatibilityEnabled_ = true; }
 
 void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; }
 
 void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; }
 
-JSONCPP_STRING FastWriter::write(const Value& root) {
+String FastWriter::write(const Value& root) {
   document_.clear();
   writeValue(root);
   if (!omitEndingLineFeed_)
-    document_ += "\n";
+    document_ += '\n';
   return document_;
 }
 
@@ -391,13 +391,13 @@
   case realValue:
     document_ += valueToString(value.asDouble());
     break;
-  case stringValue:
-  {
+  case stringValue: {
     // Is NULL possible for value.string_? No.
     char const* str;
     char const* end;
     bool ok = value.getString(&str, &end);
-    if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str));
+    if (ok)
+      document_ += valueToQuotedStringN(str, static_cast<unsigned>(end - str));
     break;
   }
   case booleanValue:
@@ -416,13 +416,13 @@
   case objectValue: {
     Value::Members members(value.getMemberNames());
     document_ += '{';
-    for (Value::Members::iterator it = members.begin(); it != members.end();
-         ++it) {
-      const JSONCPP_STRING& name = *it;
+    for (auto it = members.begin(); it != members.end(); ++it) {
+      const String& name = *it;
       if (it != members.begin())
         document_ += ',';
-      document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()));
-      document_ += yamlCompatiblityEnabled_ ? ": " : ":";
+      document_ += valueToQuotedStringN(name.data(),
+                                        static_cast<unsigned>(name.length()));
+      document_ += yamlCompatibilityEnabled_ ? ": " : ":";
       writeValue(value[name]);
     }
     document_ += '}';
@@ -433,17 +433,16 @@
 // Class StyledWriter
 // //////////////////////////////////////////////////////////////////
 
-StyledWriter::StyledWriter()
-    : rightMargin_(74), indentSize_(3), addChildValues_() {}
+StyledWriter::StyledWriter() = default;
 
-JSONCPP_STRING StyledWriter::write(const Value& root) {
+String StyledWriter::write(const Value& root) {
   document_.clear();
   addChildValues_ = false;
   indentString_.clear();
   writeCommentBeforeValue(root);
   writeValue(root);
   writeCommentAfterValueOnSameLine(root);
-  document_ += "\n";
+  document_ += '\n';
   return document_;
 }
 
@@ -461,14 +460,15 @@
   case realValue:
     pushValue(valueToString(value.asDouble()));
     break;
-  case stringValue:
-  {
+  case stringValue: {
     // Is NULL possible for value.string_? No.
     char const* str;
     char const* end;
     bool ok = value.getString(&str, &end);
-    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
-    else pushValue("");
+    if (ok)
+      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
+    else
+      pushValue("");
     break;
   }
   case booleanValue:
@@ -484,9 +484,9 @@
     else {
       writeWithIndent("{");
       indent();
-      Value::Members::iterator it = members.begin();
+      auto it = members.begin();
       for (;;) {
-        const JSONCPP_STRING& name = *it;
+        const String& name = *it;
         const Value& childValue = value[name];
         writeCommentBeforeValue(childValue);
         writeWithIndent(valueToQuotedString(name.c_str()));
@@ -511,7 +511,7 @@
   if (size == 0)
     pushValue("[]");
   else {
-    bool isArrayMultiLine = isMultineArray(value);
+    bool isArrayMultiLine = isMultilineArray(value);
     if (isArrayMultiLine) {
       writeWithIndent("[");
       indent();
@@ -549,14 +549,14 @@
   }
 }
 
-bool StyledWriter::isMultineArray(const Value& value) {
+bool StyledWriter::isMultilineArray(const Value& value) {
   ArrayIndex const size = value.size();
   bool isMultiLine = size * 3 >= rightMargin_;
   childValues_.clear();
   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
     const Value& childValue = value[index];
     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
-                        childValue.size() > 0);
+                   !childValue.empty());
   }
   if (!isMultiLine) // check if line length > max line length
   {
@@ -576,7 +576,7 @@
   return isMultiLine;
 }
 
-void StyledWriter::pushValue(const JSONCPP_STRING& value) {
+void StyledWriter::pushValue(const String& value) {
   if (addChildValues_)
     childValues_.push_back(value);
   else
@@ -594,12 +594,12 @@
   document_ += indentString_;
 }
 
-void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) {
+void StyledWriter::writeWithIndent(const String& value) {
   writeIndent();
   document_ += value;
 }
 
-void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); }
+void StyledWriter::indent() { indentString_ += String(indentSize_, ' '); }
 
 void StyledWriter::unindent() {
   assert(indentString_.size() >= indentSize_);
@@ -610,20 +610,19 @@
   if (!root.hasComment(commentBefore))
     return;
 
-  document_ += "\n";
+  document_ += '\n';
   writeIndent();
-  const JSONCPP_STRING& comment = root.getComment(commentBefore);
-  JSONCPP_STRING::const_iterator iter = comment.begin();
+  const String& comment = root.getComment(commentBefore);
+  String::const_iterator iter = comment.begin();
   while (iter != comment.end()) {
     document_ += *iter;
-    if (*iter == '\n' &&
-       (iter != comment.end() && *(iter + 1) == '/'))
+    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
       writeIndent();
     ++iter;
   }
 
   // Comments are stripped of trailing newlines, so add one here
-  document_ += "\n";
+  document_ += '\n';
 }
 
 void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) {
@@ -631,9 +630,9 @@
     document_ += " " + root.getComment(commentAfterOnSameLine);
 
   if (root.hasComment(commentAfter)) {
-    document_ += "\n";
+    document_ += '\n';
     document_ += root.getComment(commentAfter);
-    document_ += "\n";
+    document_ += '\n';
   }
 }
 
@@ -646,22 +645,23 @@
 // Class StyledStreamWriter
 // //////////////////////////////////////////////////////////////////
 
-StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation)
-    : document_(NULL), rightMargin_(74), indentation_(indentation),
-      addChildValues_() {}
+StyledStreamWriter::StyledStreamWriter(String indentation)
+    : document_(nullptr), indentation_(std::move(indentation)),
+      addChildValues_(), indented_(false) {}
 
-void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) {
+void StyledStreamWriter::write(OStream& out, const Value& root) {
   document_ = &out;
   addChildValues_ = false;
   indentString_.clear();
   indented_ = true;
   writeCommentBeforeValue(root);
-  if (!indented_) writeIndent();
+  if (!indented_)
+    writeIndent();
   indented_ = true;
   writeValue(root);
   writeCommentAfterValueOnSameLine(root);
   *document_ << "\n";
-  document_ = NULL; // Forget the stream, for safety.
+  document_ = nullptr; // Forget the stream, for safety.
 }
 
 void StyledStreamWriter::writeValue(const Value& value) {
@@ -678,14 +678,15 @@
   case realValue:
     pushValue(valueToString(value.asDouble()));
     break;
-  case stringValue:
-  {
+  case stringValue: {
     // Is NULL possible for value.string_? No.
     char const* str;
     char const* end;
     bool ok = value.getString(&str, &end);
-    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
-    else pushValue("");
+    if (ok)
+      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str)));
+    else
+      pushValue("");
     break;
   }
   case booleanValue:
@@ -701,9 +702,9 @@
     else {
       writeWithIndent("{");
       indent();
-      Value::Members::iterator it = members.begin();
+      auto it = members.begin();
       for (;;) {
-        const JSONCPP_STRING& name = *it;
+        const String& name = *it;
         const Value& childValue = value[name];
         writeCommentBeforeValue(childValue);
         writeWithIndent(valueToQuotedString(name.c_str()));
@@ -728,7 +729,7 @@
   if (size == 0)
     pushValue("[]");
   else {
-    bool isArrayMultiLine = isMultineArray(value);
+    bool isArrayMultiLine = isMultilineArray(value);
     if (isArrayMultiLine) {
       writeWithIndent("[");
       indent();
@@ -740,7 +741,8 @@
         if (hasChildValue)
           writeWithIndent(childValues_[index]);
         else {
-          if (!indented_) writeIndent();
+          if (!indented_)
+            writeIndent();
           indented_ = true;
           writeValue(childValue);
           indented_ = false;
@@ -768,14 +770,14 @@
   }
 }
 
-bool StyledStreamWriter::isMultineArray(const Value& value) {
+bool StyledStreamWriter::isMultilineArray(const Value& value) {
   ArrayIndex const size = value.size();
   bool isMultiLine = size * 3 >= rightMargin_;
   childValues_.clear();
   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
     const Value& childValue = value[index];
     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
-                        childValue.size() > 0);
+                   !childValue.empty());
   }
   if (!isMultiLine) // check if line length > max line length
   {
@@ -795,7 +797,7 @@
   return isMultiLine;
 }
 
-void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) {
+void StyledStreamWriter::pushValue(const String& value) {
   if (addChildValues_)
     childValues_.push_back(value);
   else
@@ -810,8 +812,9 @@
   *document_ << '\n' << indentString_;
 }
 
-void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) {
-  if (!indented_) writeIndent();
+void StyledStreamWriter::writeWithIndent(const String& value) {
+  if (!indented_)
+    writeIndent();
   *document_ << value;
   indented_ = false;
 }
@@ -827,13 +830,13 @@
   if (!root.hasComment(commentBefore))
     return;
 
-  if (!indented_) writeIndent();
-  const JSONCPP_STRING& comment = root.getComment(commentBefore);
-  JSONCPP_STRING::const_iterator iter = comment.begin();
+  if (!indented_)
+    writeIndent();
+  const String& comment = root.getComment(commentBefore);
+  String::const_iterator iter = comment.begin();
   while (iter != comment.end()) {
     *document_ << *iter;
-    if (*iter == '\n' &&
-       (iter != comment.end() && *(iter + 1) == '/'))
+    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
       // writeIndent();  // would include newline
       *document_ << indentString_;
     ++iter;
@@ -865,84 +868,73 @@
 struct CommentStyle {
   /// Decide whether to write comments.
   enum Enum {
-    None,  ///< Drop all comments.
-    Most,  ///< Recover odd behavior of previous versions (not implemented yet).
-    All  ///< Keep all comments.
+    None, ///< Drop all comments.
+    Most, ///< Recover odd behavior of previous versions (not implemented yet).
+    All   ///< Keep all comments.
   };
 };
 
-struct BuiltStyledStreamWriter : public StreamWriter
-{
-  BuiltStyledStreamWriter(
-      JSONCPP_STRING const& indentation,
-      CommentStyle::Enum cs,
-      JSONCPP_STRING const& colonSymbol,
-      JSONCPP_STRING const& nullSymbol,
-      JSONCPP_STRING const& endingLineFeedSymbol,
-      bool useSpecialFloats,
-      unsigned int precision);
-  int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE;
+struct BuiltStyledStreamWriter : public StreamWriter {
+  BuiltStyledStreamWriter(String indentation, CommentStyle::Enum cs,
+                          String colonSymbol, String nullSymbol,
+                          String endingLineFeedSymbol, bool useSpecialFloats,
+                          bool emitUTF8, unsigned int precision,
+                          PrecisionType precisionType);
+  int write(Value const& root, OStream* sout) override;
+
 private:
   void writeValue(Value const& value);
   void writeArrayValue(Value const& value);
-  bool isMultineArray(Value const& value);
-  void pushValue(JSONCPP_STRING const& value);
+  bool isMultilineArray(Value const& value);
+  void pushValue(String const& value);
   void writeIndent();
-  void writeWithIndent(JSONCPP_STRING const& value);
+  void writeWithIndent(String const& value);
   void indent();
   void unindent();
   void writeCommentBeforeValue(Value const& root);
   void writeCommentAfterValueOnSameLine(Value const& root);
   static bool hasCommentForValue(const Value& value);
 
-  typedef std::vector<JSONCPP_STRING> ChildValues;
+  using ChildValues = std::vector<String>;
 
   ChildValues childValues_;
-  JSONCPP_STRING indentString_;
+  String indentString_;
   unsigned int rightMargin_;
-  JSONCPP_STRING indentation_;
+  String indentation_;
   CommentStyle::Enum cs_;
-  JSONCPP_STRING colonSymbol_;
-  JSONCPP_STRING nullSymbol_;
-  JSONCPP_STRING endingLineFeedSymbol_;
+  String colonSymbol_;
+  String nullSymbol_;
+  String endingLineFeedSymbol_;
   bool addChildValues_ : 1;
   bool indented_ : 1;
   bool useSpecialFloats_ : 1;
+  bool emitUTF8_ : 1;
   unsigned int precision_;
+  PrecisionType precisionType_;
 };
 BuiltStyledStreamWriter::BuiltStyledStreamWriter(
-      JSONCPP_STRING const& indentation,
-      CommentStyle::Enum cs,
-      JSONCPP_STRING const& colonSymbol,
-      JSONCPP_STRING const& nullSymbol,
-      JSONCPP_STRING const& endingLineFeedSymbol,
-      bool useSpecialFloats,
-      unsigned int precision)
-  : rightMargin_(74)
-  , indentation_(indentation)
-  , cs_(cs)
-  , colonSymbol_(colonSymbol)
-  , nullSymbol_(nullSymbol)
-  , endingLineFeedSymbol_(endingLineFeedSymbol)
-  , addChildValues_(false)
-  , indented_(false)
-  , useSpecialFloats_(useSpecialFloats)
-  , precision_(precision)
-{
-}
-int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout)
-{
+    String indentation, CommentStyle::Enum cs, String colonSymbol,
+    String nullSymbol, String endingLineFeedSymbol, bool useSpecialFloats,
+    bool emitUTF8, unsigned int precision, PrecisionType precisionType)
+    : rightMargin_(74), indentation_(std::move(indentation)), cs_(cs),
+      colonSymbol_(std::move(colonSymbol)), nullSymbol_(std::move(nullSymbol)),
+      endingLineFeedSymbol_(std::move(endingLineFeedSymbol)),
+      addChildValues_(false), indented_(false),
+      useSpecialFloats_(useSpecialFloats), emitUTF8_(emitUTF8),
+      precision_(precision), precisionType_(precisionType) {}
+int BuiltStyledStreamWriter::write(Value const& root, OStream* sout) {
   sout_ = sout;
   addChildValues_ = false;
   indented_ = true;
   indentString_.clear();
   writeCommentBeforeValue(root);
-  if (!indented_) writeIndent();
+  if (!indented_)
+    writeIndent();
   indented_ = true;
   writeValue(root);
   writeCommentAfterValueOnSameLine(root);
   *sout_ << endingLineFeedSymbol_;
-  sout_ = NULL;
+  sout_ = nullptr;
   return 0;
 }
 void BuiltStyledStreamWriter::writeValue(Value const& value) {
@@ -957,16 +949,19 @@
     pushValue(valueToString(value.asLargestUInt()));
     break;
   case realValue:
-    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_));
+    pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_,
+                            precisionType_));
     break;
-  case stringValue:
-  {
+  case stringValue: {
     // Is NULL is possible for value.string_? No.
     char const* str;
     char const* end;
     bool ok = value.getString(&str, &end);
-    if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str)));
-    else pushValue("");
+    if (ok)
+      pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end - str),
+                                     emitUTF8_));
+    else
+      pushValue("");
     break;
   }
   case booleanValue:
@@ -982,12 +977,13 @@
     else {
       writeWithIndent("{");
       indent();
-      Value::Members::iterator it = members.begin();
+      auto it = members.begin();
       for (;;) {
-        JSONCPP_STRING const& name = *it;
+        String const& name = *it;
         Value const& childValue = value[name];
         writeCommentBeforeValue(childValue);
-        writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())));
+        writeWithIndent(valueToQuotedStringN(
+            name.data(), static_cast<unsigned>(name.length()), emitUTF8_));
         *sout_ << colonSymbol_;
         writeValue(childValue);
         if (++it == members.end()) {
@@ -1009,7 +1005,7 @@
   if (size == 0)
     pushValue("[]");
   else {
-    bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value);
+    bool isMultiLine = (cs_ == CommentStyle::All) || isMultilineArray(value);
     if (isMultiLine) {
       writeWithIndent("[");
       indent();
@@ -1021,7 +1017,8 @@
         if (hasChildValue)
           writeWithIndent(childValues_[index]);
         else {
-          if (!indented_) writeIndent();
+          if (!indented_)
+            writeIndent();
           indented_ = true;
           writeValue(childValue);
           indented_ = false;
@@ -1039,26 +1036,28 @@
     {
       assert(childValues_.size() == size);
       *sout_ << "[";
-      if (!indentation_.empty()) *sout_ << " ";
+      if (!indentation_.empty())
+        *sout_ << " ";
       for (unsigned index = 0; index < size; ++index) {
         if (index > 0)
           *sout_ << ((!indentation_.empty()) ? ", " : ",");
         *sout_ << childValues_[index];
       }
-      if (!indentation_.empty()) *sout_ << " ";
+      if (!indentation_.empty())
+        *sout_ << " ";
       *sout_ << "]";
     }
   }
 }
 
-bool BuiltStyledStreamWriter::isMultineArray(Value const& value) {
+bool BuiltStyledStreamWriter::isMultilineArray(Value const& value) {
   ArrayIndex const size = value.size();
   bool isMultiLine = size * 3 >= rightMargin_;
   childValues_.clear();
   for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) {
     Value const& childValue = value[index];
     isMultiLine = ((childValue.isArray() || childValue.isObject()) &&
-                        childValue.size() > 0);
+                   !childValue.empty());
   }
   if (!isMultiLine) // check if line length > max line length
   {
@@ -1078,7 +1077,7 @@
   return isMultiLine;
 }
 
-void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) {
+void BuiltStyledStreamWriter::pushValue(String const& value) {
   if (addChildValues_)
     childValues_.push_back(value);
   else
@@ -1097,8 +1096,9 @@
   }
 }
 
-void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) {
-  if (!indented_) writeIndent();
+void BuiltStyledStreamWriter::writeWithIndent(String const& value) {
+  if (!indented_)
+    writeIndent();
   *sout_ << value;
   indented_ = false;
 }
@@ -1111,17 +1111,18 @@
 }
 
 void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) {
-  if (cs_ == CommentStyle::None) return;
+  if (cs_ == CommentStyle::None)
+    return;
   if (!root.hasComment(commentBefore))
     return;
 
-  if (!indented_) writeIndent();
-  const JSONCPP_STRING& comment = root.getComment(commentBefore);
-  JSONCPP_STRING::const_iterator iter = comment.begin();
+  if (!indented_)
+    writeIndent();
+  const String& comment = root.getComment(commentBefore);
+  String::const_iterator iter = comment.begin();
   while (iter != comment.end()) {
     *sout_ << *iter;
-    if (*iter == '\n' &&
-       (iter != comment.end() && *(iter + 1) == '/'))
+    if (*iter == '\n' && ((iter + 1) != comment.end() && *(iter + 1) == '/'))
       // writeIndent();  // would write extra newline
       *sout_ << indentString_;
     ++iter;
@@ -1129,8 +1130,10 @@
   indented_ = false;
 }
 
-void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) {
-  if (cs_ == CommentStyle::None) return;
+void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(
+    Value const& root) {
+  if (cs_ == CommentStyle::None)
+    return;
   if (root.hasComment(commentAfterOnSameLine))
     *sout_ << " " + root.getComment(commentAfterOnSameLine);
 
@@ -1150,28 +1153,19 @@
 ///////////////
 // StreamWriter
 
-StreamWriter::StreamWriter()
-    : sout_(NULL)
-{
-}
-StreamWriter::~StreamWriter()
-{
-}
-StreamWriter::Factory::~Factory()
-{}
-StreamWriterBuilder::StreamWriterBuilder()
-{
-  setDefaults(&settings_);
-}
-StreamWriterBuilder::~StreamWriterBuilder()
-{}
-StreamWriter* StreamWriterBuilder::newStreamWriter() const
-{
-  JSONCPP_STRING indentation = settings_["indentation"].asString();
-  JSONCPP_STRING cs_str = settings_["commentStyle"].asString();
-  bool eyc = settings_["enableYAMLCompatibility"].asBool();
-  bool dnp = settings_["dropNullPlaceholders"].asBool();
-  bool usf = settings_["useSpecialFloats"].asBool(); 
+StreamWriter::StreamWriter() : sout_(nullptr) {}
+StreamWriter::~StreamWriter() = default;
+StreamWriter::Factory::~Factory() = default;
+StreamWriterBuilder::StreamWriterBuilder() { setDefaults(&settings_); }
+StreamWriterBuilder::~StreamWriterBuilder() = default;
+StreamWriter* StreamWriterBuilder::newStreamWriter() const {
+  const String indentation = settings_["indentation"].asString();
+  const String cs_str = settings_["commentStyle"].asString();
+  const String pt_str = settings_["precisionType"].asString();
+  const bool eyc = settings_["enableYAMLCompatibility"].asBool();
+  const bool dnp = settings_["dropNullPlaceholders"].asBool();
+  const bool usf = settings_["useSpecialFloats"].asBool();
+  const bool emitUTF8 = settings_["emitUTF8"].asBool();
   unsigned int pre = settings_["precision"].asUInt();
   CommentStyle::Enum cs = CommentStyle::All;
   if (cs_str == "All") {
@@ -1181,74 +1175,80 @@
   } else {
     throwRuntimeError("commentStyle must be 'All' or 'None'");
   }
-  JSONCPP_STRING colonSymbol = " : ";
+  PrecisionType precisionType(significantDigits);
+  if (pt_str == "significant") {
+    precisionType = PrecisionType::significantDigits;
+  } else if (pt_str == "decimal") {
+    precisionType = PrecisionType::decimalPlaces;
+  } else {
+    throwRuntimeError("precisionType must be 'significant' or 'decimal'");
+  }
+  String colonSymbol = " : ";
   if (eyc) {
     colonSymbol = ": ";
   } else if (indentation.empty()) {
     colonSymbol = ":";
   }
-  JSONCPP_STRING nullSymbol = "null";
+  String nullSymbol = "null";
   if (dnp) {
     nullSymbol.clear();
   }
-  if (pre > 17) pre = 17;
-  JSONCPP_STRING endingLineFeedSymbol;
-  return new BuiltStyledStreamWriter(
-      indentation, cs,
-      colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre);
+  if (pre > 17)
+    pre = 17;
+  String endingLineFeedSymbol;
+  return new BuiltStyledStreamWriter(indentation, cs, colonSymbol, nullSymbol,
+                                     endingLineFeedSymbol, usf, emitUTF8, pre,
+                                     precisionType);
 }
-static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys)
-{
-  valid_keys->clear();
-  valid_keys->insert("indentation");
-  valid_keys->insert("commentStyle");
-  valid_keys->insert("enableYAMLCompatibility");
-  valid_keys->insert("dropNullPlaceholders");
-  valid_keys->insert("useSpecialFloats");
-  valid_keys->insert("precision");
-}
-bool StreamWriterBuilder::validate(Json::Value* invalid) const
-{
-  Json::Value my_invalid;
-  if (!invalid) invalid = &my_invalid;  // so we do not need to test for NULL
-  Json::Value& inv = *invalid;
-  std::set<JSONCPP_STRING> valid_keys;
-  getValidWriterKeys(&valid_keys);
-  Value::Members keys = settings_.getMemberNames();
-  size_t n = keys.size();
-  for (size_t i = 0; i < n; ++i) {
-    JSONCPP_STRING const& key = keys[i];
-    if (valid_keys.find(key) == valid_keys.end()) {
-      inv[key] = settings_[key];
-    }
+
+bool StreamWriterBuilder::validate(Json::Value* invalid) const {
+  static const auto& valid_keys = *new std::set<String>{
+      "indentation",
+      "commentStyle",
+      "enableYAMLCompatibility",
+      "dropNullPlaceholders",
+      "useSpecialFloats",
+      "emitUTF8",
+      "precision",
+      "precisionType",
+  };
+  for (auto si = settings_.begin(); si != settings_.end(); ++si) {
+    auto key = si.name();
+    if (valid_keys.count(key))
+      continue;
+    if (invalid)
+      (*invalid)[std::move(key)] = *si;
+    else
+      return false;
   }
-  return 0u == inv.size();
+  return invalid ? invalid->empty() : true;
 }
-Value& StreamWriterBuilder::operator[](JSONCPP_STRING key)
-{
+
+Value& StreamWriterBuilder::operator[](const String& key) {
   return settings_[key];
 }
 // static
-void StreamWriterBuilder::setDefaults(Json::Value* settings)
-{
+void StreamWriterBuilder::setDefaults(Json::Value* settings) {
   //! [StreamWriterBuilderDefaults]
   (*settings)["commentStyle"] = "All";
   (*settings)["indentation"] = "\t";
   (*settings)["enableYAMLCompatibility"] = false;
   (*settings)["dropNullPlaceholders"] = false;
   (*settings)["useSpecialFloats"] = false;
+  (*settings)["emitUTF8"] = false;
   (*settings)["precision"] = 17;
+  (*settings)["precisionType"] = "significant";
   //! [StreamWriterBuilderDefaults]
 }
 
-JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) {
-  JSONCPP_OSTRINGSTREAM sout;
-  StreamWriterPtr const writer(builder.newStreamWriter());
+String writeString(StreamWriter::Factory const& factory, Value const& root) {
+  OStringStream sout;
+  StreamWriterPtr const writer(factory.newStreamWriter());
   writer->write(root, &sout);
   return sout.str();
 }
 
-JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) {
+OStream& operator<<(OStream& sout, Value const& root) {
   StreamWriterBuilder builder;
   StreamWriterPtr const writer(builder.newStreamWriter());
   writer->write(root, &sout);
diff --git a/Utilities/cmlibarchive/CMakeLists.txt b/Utilities/cmlibarchive/CMakeLists.txt
index 79452ff..ba65470 100644
--- a/Utilities/cmlibarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/CMakeLists.txt
@@ -171,32 +171,32 @@
   # This is added into CMAKE_C_FLAGS when CMAKE_BUILD_TYPE is "Debug"
   # Enable level 4 C4062: The enumerate has no associated handler in a switch
   #                       statement and there is no default that can catch it.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4062")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14062")
   # Enable level 4 C4254: A larger bit field was assigned to a smaller bit
   #                       field.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4254")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14254")
   # Enable level 4 C4295: An array was initialized but the last character in
   #                       the array is not a null; accessing the array may
   #                       produce unexpected results.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4295")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14295")
   # Enable level 4 C4296: An unsigned variable was used in a comparison
   #                       operation with zero.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4296")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14296")
   # Enable level 4 C4389: An operation involved signed and unsigned variables.
   #                       This could result in a loss of data.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4389")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14389")
   # Enable level 4 C4505: The given function is local and not referenced in
   #                       the body of the module; therefore, the function is
   #                       dead code.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4505")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14505")
   # Enable level 4 C4514: The optimizer removed an inline function that is not
   #                       called.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4514")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14514")
   # Enable level 4 C4702: Unreachable code.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4702")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14702")
   # Enable level 4 C4706: The test value in a conditional expression was the
   #                       result of an assignment.
-  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /we4706")
+  SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /w14706")
   # /Oi option enables built-in functions.
   SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Oi")
   #################################################################
@@ -1404,6 +1404,7 @@
 CHECK_FUNCTION_EXISTS_GLIBC(strdup HAVE_STRDUP)
 CHECK_FUNCTION_EXISTS_GLIBC(strerror HAVE_STRERROR)
 CHECK_FUNCTION_EXISTS_GLIBC(strncpy_s HAVE_STRNCPY_S)
+CHECK_FUNCTION_EXISTS_GLIBC(strnlen HAVE_STRNLEN)
 CHECK_FUNCTION_EXISTS_GLIBC(strrchr HAVE_STRRCHR)
 CHECK_FUNCTION_EXISTS_GLIBC(symlink HAVE_SYMLINK)
 CHECK_FUNCTION_EXISTS_GLIBC(timegm HAVE_TIMEGM)
@@ -1475,15 +1476,19 @@
   "#include <sys/sysmacros.h>\nint main() { return major(256); }"
   MAJOR_IN_SYSMACROS)
 
+IF(ENABLE_LZMA)
 CMAKE_PUSH_CHECK_STATE()
 SET(CMAKE_REQUIRED_LIBRARIES ${LIBLZMA_LIBRARIES})
 SET(CMAKE_REQUIRED_INCLUDES ${LIBLZMA_INCLUDE_DIR})
 
 CHECK_C_SOURCE_COMPILES(
   "#include <lzma.h>\n#if LZMA_VERSION < 50020000\n#error unsupported\n#endif\nint main(void){lzma_stream_encoder_mt(0, 0); return 0;}"
-HAVE_LZMA_STREAM_ENCODER_MT)
+  HAVE_LZMA_STREAM_ENCODER_MT)
 
 CMAKE_POP_CHECK_STATE()
+ELSE()
+  SET(HAVE_LZMA_STREAM_ENCODER_MT 0)
+ENDIF(ENABLE_LZMA)
 
 IF(HAVE_STRERROR_R)
   SET(HAVE_DECL_STRERROR_R 1)
@@ -2007,6 +2012,11 @@
   ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
 ENDIF(MSVC)
 
+IF(APPLE)
+  # CC_MD5_Init() functions are deprecated on macOS 10.15, but we want to use them
+  ADD_DEFINITIONS(-Wno-deprecated-declarations)
+ENDIF(APPLE)
+
 IF(0) # CMake does not build libarchive's tests.
 IF(ENABLE_TEST)
   ADD_CUSTOM_TARGET(run_all_tests)
diff --git a/Utilities/cmlibarchive/COPYING b/Utilities/cmlibarchive/COPYING
index 14bbefa..1b97235 100644
--- a/Utilities/cmlibarchive/COPYING
+++ b/Utilities/cmlibarchive/COPYING
@@ -15,7 +15,6 @@
 * The following source files are also subject in whole or in part to
   a 3-clause UC Regents copyright; please read the individual source
   files for details:
-   libarchive/archive_entry.c
    libarchive/archive_read_support_filter_compress.c
    libarchive/archive_write_add_filter_compress.c
    libarchive/mtree.5
diff --git a/Utilities/cmlibarchive/build/cmake/CreatePkgConfigFile.cmake b/Utilities/cmlibarchive/build/cmake/CreatePkgConfigFile.cmake
index fc8529a..bc5a43f 100644
--- a/Utilities/cmlibarchive/build/cmake/CreatePkgConfigFile.cmake
+++ b/Utilities/cmlibarchive/build/cmake/CreatePkgConfigFile.cmake
@@ -24,10 +24,10 @@
 #	 thus there's a good chance it'll make some binutils versions unhappy...
 #	 This only affects Libs.private (looked up for static builds) though.
 CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build/pkgconfig/libarchive.pc.in
-		${CMAKE_CURRENT_SOURCE_DIR}/build/pkgconfig/libarchive.pc
+		${CMAKE_CURRENT_BINARY_DIR}/build/pkgconfig/libarchive.pc
 		@ONLY)
 # And install it, of course ;).
 IF(ENABLE_INSTALL)
-  INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/build/pkgconfig/libarchive.pc
+  INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/build/pkgconfig/libarchive.pc
           DESTINATION "lib/pkgconfig")
 ENDIF()
diff --git a/Utilities/cmlibarchive/build/cmake/config.h.in b/Utilities/cmlibarchive/build/cmake/config.h.in
index f38601f..9bd2667 100644
--- a/Utilities/cmlibarchive/build/cmake/config.h.in
+++ b/Utilities/cmlibarchive/build/cmake/config.h.in
@@ -1,4 +1,5 @@
 /* config.h.  Generated from build/cmake/config.h.in by cmake configure */
+#define __LIBARCHIVE_CONFIG_H_INCLUDED 1
 #if defined(__osf__)
 # define _OSF_SOURCE
 #endif
@@ -742,6 +743,9 @@
 /* Define to 1 if you have the `strchr' function. */
 #cmakedefine HAVE_STRCHR 1
 
+/* Define to 1 if you have the `strnlen' function. */
+#cmakedefine HAVE_STRNLEN 1
+
 /* Define to 1 if you have the `strdup' function. */
 #cmakedefine HAVE_STRDUP 1
 
diff --git a/Utilities/cmlibarchive/build/version b/Utilities/cmlibarchive/build/version
index 78be3ab..205791c 100644
--- a/Utilities/cmlibarchive/build/version
+++ b/Utilities/cmlibarchive/build/version
@@ -1 +1 @@
-3004002
+3005001
diff --git a/Utilities/cmlibarchive/libarchive/archive.h b/Utilities/cmlibarchive/libarchive/archive.h
index 574e087..a1f0b87 100644
--- a/Utilities/cmlibarchive/libarchive/archive.h
+++ b/Utilities/cmlibarchive/libarchive/archive.h
@@ -36,7 +36,7 @@
  * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
  */
 /* Note: Compiler will complain if this does not match archive_entry.h! */
-#define	ARCHIVE_VERSION_NUMBER 3004002
+#define	ARCHIVE_VERSION_NUMBER 3005001
 
 #include <sys/stat.h>
 #include <stddef.h>  /* for wchar_t */
@@ -152,7 +152,7 @@
 /*
  * Textual name/version of the library, useful for version displays.
  */
-#define	ARCHIVE_VERSION_ONLY_STRING "3.4.2"
+#define	ARCHIVE_VERSION_ONLY_STRING "3.5.1"
 #define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
 __LA_DECL const char *	archive_version_string(void);
 
@@ -243,6 +243,8 @@
 
 typedef int	archive_close_callback(struct archive *, void *_client_data);
 
+typedef int	archive_free_callback(struct archive *, void *_client_data);
+
 /* Switches from one client data object to the next/prev client data object.
  * This is useful for reading from different data blocks such as a set of files
  * that make up one large file.
@@ -415,6 +417,7 @@
 #endif
 
 __LA_DECL int archive_read_support_filter_all(struct archive *);
+__LA_DECL int archive_read_support_filter_by_code(struct archive *, int);
 __LA_DECL int archive_read_support_filter_bzip2(struct archive *);
 __LA_DECL int archive_read_support_filter_compress(struct archive *);
 __LA_DECL int archive_read_support_filter_gzip(struct archive *);
@@ -814,9 +817,13 @@
 __LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);
 __LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);
 __LA_DECL int archive_write_zip_set_compression_store(struct archive *);
+/* Deprecated; use archive_write_open2 instead */
 __LA_DECL int archive_write_open(struct archive *, void *,
 		     archive_open_callback *, archive_write_callback *,
 		     archive_close_callback *);
+__LA_DECL int archive_write_open2(struct archive *, void *,
+		     archive_open_callback *, archive_write_callback *,
+		     archive_close_callback *, archive_free_callback *);
 __LA_DECL int archive_write_open_fd(struct archive *, int _fd);
 __LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
 __LA_DECL int archive_write_open_filename_w(struct archive *,
diff --git a/Utilities/cmlibarchive/libarchive/archive_acl.c b/Utilities/cmlibarchive/libarchive/archive_acl.c
index 952e20d..ead7e36 100644
--- a/Utilities/cmlibarchive/libarchive/archive_acl.c
+++ b/Utilities/cmlibarchive/libarchive/archive_acl.c
@@ -595,7 +595,7 @@
 				else
 					length += sizeof(uid_t) * 3 + 1;
 			} else {
-				r = archive_mstring_get_mbs_l(&ap->name, &name,
+				r = archive_mstring_get_mbs_l(a, &ap->name, &name,
 				    &len, sc);
 				if (r != 0)
 					return (0);
@@ -968,7 +968,7 @@
 		else
 			prefix = NULL;
 		r = archive_mstring_get_mbs_l(
-		    &ap->name, &name, &len, sc);
+		    NULL, &ap->name, &name, &len, sc);
 		if (r != 0) {
 			free(s);
 			return (NULL);
@@ -1402,14 +1402,14 @@
 	if (start >= end)
 		return (0);
 	while (start < end) {
-		if (*start < '0' || *start > '9')
+		if (*start < L'0' || *start > L'9')
 			return (0);
 		if (n > (INT_MAX / 10) ||
-		    (n == INT_MAX / 10 && (*start - '0') > INT_MAX % 10)) {
+		    (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
 			n = INT_MAX;
 		} else {
 			n *= 10;
-			n += *start - '0';
+			n += *start - L'0';
 		}
 		start++;
 	}
diff --git a/Utilities/cmlibarchive/libarchive/archive_check_magic.c b/Utilities/cmlibarchive/libarchive/archive_check_magic.c
index 288ce23..1f40072 100644
--- a/Utilities/cmlibarchive/libarchive/archive_check_magic.c
+++ b/Utilities/cmlibarchive/libarchive/archive_check_magic.c
@@ -54,7 +54,7 @@
 	ssize_t written;
 
 	while (s > 0) {
-		written = write(2, m, strlen(m));
+		written = write(2, m, s);
 		if (written <= 0)
 			return;
 		m += written;
diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor.c b/Utilities/cmlibarchive/libarchive/archive_cryptor.c
index 8ab2b09..d4bca90 100644
--- a/Utilities/cmlibarchive/libarchive/archive_cryptor.c
+++ b/Utilities/cmlibarchive/libarchive/archive_cryptor.c
@@ -347,8 +347,31 @@
 static int
 aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
 {
+#if NETTLE_VERSION_MAJOR < 3
 	aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key);
 	aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce);
+#else
+	switch(ctx->key_len) {
+	case AES128_KEY_SIZE:
+		aes128_set_encrypt_key(&ctx->ctx.c128, ctx->key);
+		aes128_encrypt(&ctx->ctx.c128, AES_BLOCK_SIZE, ctx->encr_buf,
+		    ctx->nonce);
+		break;
+	case AES192_KEY_SIZE:
+		aes192_set_encrypt_key(&ctx->ctx.c192, ctx->key);
+		aes192_encrypt(&ctx->ctx.c192, AES_BLOCK_SIZE, ctx->encr_buf,
+		    ctx->nonce);
+		break;
+	case AES256_KEY_SIZE:
+		aes256_set_encrypt_key(&ctx->ctx.c256, ctx->key);
+		aes256_encrypt(&ctx->ctx.c256, AES_BLOCK_SIZE, ctx->encr_buf,
+		    ctx->nonce);
+		break;
+	default:
+		return -1;
+		break;
+	}
+#endif
 	return 0;
 }
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h
index 64a2055..16b6d16 100644
--- a/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_cryptor_private.h
@@ -104,9 +104,18 @@
 #include <nettle/pbkdf2.h>
 #endif
 #include <nettle/aes.h>
+#include <nettle/version.h>
 
 typedef struct {
+#if NETTLE_VERSION_MAJOR < 3
 	struct aes_ctx	ctx;
+#else
+	union {
+		struct aes128_ctx c128;
+		struct aes192_ctx c192;
+		struct aes256_ctx c256;
+	}		ctx;
+#endif
 	uint8_t		key[AES_MAX_KEY_SIZE];
 	unsigned	key_len;
 	uint8_t		nonce[AES_BLOCK_SIZE];
diff --git a/Utilities/cmlibarchive/libarchive/archive_digest.c b/Utilities/cmlibarchive/libarchive/archive_digest.c
index 34c58ac..410df01 100644
--- a/Utilities/cmlibarchive/libarchive/archive_digest.c
+++ b/Utilities/cmlibarchive/libarchive/archive_digest.c
@@ -109,14 +109,14 @@
 #if defined(ARCHIVE_CRYPTO_MD5_LIBC)
 
 static int
-__archive_libc_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   MD5Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   MD5Update(ctx, indata, insize);
@@ -124,7 +124,7 @@
 }
 
 static int
-__archive_libc_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   MD5Final(md, ctx);
   return (ARCHIVE_OK);
@@ -133,14 +133,14 @@
 #elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
 
 static int
-__archive_libmd_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   MD5Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libmd_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   MD5Update(ctx, indata, insize);
@@ -148,7 +148,7 @@
 }
 
 static int
-__archive_libmd_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   MD5Final(md, ctx);
   return (ARCHIVE_OK);
@@ -157,14 +157,14 @@
 #elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
 
 static int
-__archive_libsystem_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   CC_MD5_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libsystem_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   CC_MD5_Update(ctx, indata, insize);
@@ -172,7 +172,7 @@
 }
 
 static int
-__archive_libsystem_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   CC_MD5_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -181,7 +181,7 @@
 #elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
 
 static int
-__archive_mbedtls_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   mbedtls_md5_init(ctx);
   if (mbedtls_md5_starts_ret(ctx) == 0)
@@ -191,7 +191,7 @@
 }
 
 static int
-__archive_mbedtls_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)
@@ -201,7 +201,7 @@
 }
 
 static int
-__archive_mbedtls_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   if (mbedtls_md5_finish_ret(ctx, md) == 0) {
     mbedtls_md5_free(ctx);
@@ -215,14 +215,14 @@
 #elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
 
 static int
-__archive_nettle_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   md5_init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_nettle_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   md5_update(ctx, insize, indata);
@@ -230,7 +230,7 @@
 }
 
 static int
-__archive_nettle_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   md5_digest(ctx, MD5_DIGEST_SIZE, md);
   return (ARCHIVE_OK);
@@ -239,7 +239,7 @@
 #elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
 
 static int
-__archive_openssl_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   if ((*ctx = EVP_MD_CTX_new()) == NULL)
 	return (ARCHIVE_FAILED);
@@ -248,7 +248,7 @@
 }
 
 static int
-__archive_openssl_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   EVP_DigestUpdate(*ctx, indata, insize);
@@ -256,7 +256,7 @@
 }
 
 static int
-__archive_openssl_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
    * this is meant to cope with that. Real fix is probably to fix
@@ -273,20 +273,20 @@
 #elif defined(ARCHIVE_CRYPTO_MD5_WIN)
 
 static int
-__archive_windowsapi_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
   return (win_crypto_init(ctx, CALG_MD5));
 }
 
 static int
-__archive_windowsapi_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
   return (win_crypto_Update(ctx, indata, insize));
 }
 
 static int
-__archive_windowsapi_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
   return (win_crypto_Final(md, 16, ctx));
 }
@@ -294,14 +294,14 @@
 #else
 
 static int
-__archive_stub_md5init(archive_md5_ctx *ctx)
+__archive_md5init(archive_md5_ctx *ctx)
 {
 	(void)ctx; /* UNUSED */
 	return (ARCHIVE_FAILED);
 }
 
 static int
-__archive_stub_md5update(archive_md5_ctx *ctx, const void *indata,
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
     size_t insize)
 {
 	(void)ctx; /* UNUSED */
@@ -311,7 +311,7 @@
 }
 
 static int
-__archive_stub_md5final(archive_md5_ctx *ctx, void *md)
+__archive_md5final(archive_md5_ctx *ctx, void *md)
 {
 	(void)ctx; /* UNUSED */
 	(void)md; /* UNUSED */
@@ -324,14 +324,14 @@
 #if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
 
 static int
-__archive_libc_ripemd160init(archive_rmd160_ctx *ctx)
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
 {
   RMD160Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
     size_t insize)
 {
   RMD160Update(ctx, indata, insize);
@@ -339,7 +339,7 @@
 }
 
 static int
-__archive_libc_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
 {
   RMD160Final(md, ctx);
   return (ARCHIVE_OK);
@@ -348,14 +348,14 @@
 #elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
 
 static int
-__archive_libmd_ripemd160init(archive_rmd160_ctx *ctx)
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
 {
   RIPEMD160_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libmd_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
     size_t insize)
 {
   RIPEMD160_Update(ctx, indata, insize);
@@ -363,7 +363,7 @@
 }
 
 static int
-__archive_libmd_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
 {
   RIPEMD160_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -372,7 +372,7 @@
 #elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
 
 static int
-__archive_mbedtls_ripemd160init(archive_rmd160_ctx *ctx)
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
 {
   mbedtls_ripemd160_init(ctx);
   if (mbedtls_ripemd160_starts_ret(ctx) == 0)
@@ -382,7 +382,7 @@
 }
 
 static int
-__archive_mbedtls_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
     size_t insize)
 {
   if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)
@@ -392,7 +392,7 @@
 }
 
 static int
-__archive_mbedtls_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
 {
   if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {
     mbedtls_ripemd160_free(ctx);
@@ -406,14 +406,14 @@
 #elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
 
 static int
-__archive_nettle_ripemd160init(archive_rmd160_ctx *ctx)
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
 {
   ripemd160_init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_nettle_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
     size_t insize)
 {
   ripemd160_update(ctx, insize, indata);
@@ -421,7 +421,7 @@
 }
 
 static int
-__archive_nettle_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
 {
   ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md);
   return (ARCHIVE_OK);
@@ -430,7 +430,7 @@
 #elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
 
 static int
-__archive_openssl_ripemd160init(archive_rmd160_ctx *ctx)
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
 {
   if ((*ctx = EVP_MD_CTX_new()) == NULL)
 	return (ARCHIVE_FAILED);
@@ -439,7 +439,7 @@
 }
 
 static int
-__archive_openssl_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
     size_t insize)
 {
   EVP_DigestUpdate(*ctx, indata, insize);
@@ -447,7 +447,7 @@
 }
 
 static int
-__archive_openssl_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
 {
   if (*ctx) {
     EVP_DigestFinal(*ctx, md, NULL);
@@ -460,14 +460,14 @@
 #else
 
 static int
-__archive_stub_ripemd160init(archive_rmd160_ctx *ctx)
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
 {
 	(void)ctx; /* UNUSED */
 	return (ARCHIVE_FAILED);
 }
 
 static int
-__archive_stub_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
     size_t insize)
 {
 	(void)ctx; /* UNUSED */
@@ -477,7 +477,7 @@
 }
 
 static int
-__archive_stub_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
 {
 	(void)ctx; /* UNUSED */
 	(void)md; /* UNUSED */
@@ -490,14 +490,14 @@
 #if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
 
 static int
-__archive_libc_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   SHA1Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA1Update(ctx, indata, insize);
@@ -505,7 +505,7 @@
 }
 
 static int
-__archive_libc_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   SHA1Final(md, ctx);
   return (ARCHIVE_OK);
@@ -514,14 +514,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
 
 static int
-__archive_libmd_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   SHA1_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libmd_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA1_Update(ctx, indata, insize);
@@ -529,7 +529,7 @@
 }
 
 static int
-__archive_libmd_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   SHA1_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -538,14 +538,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
 
 static int
-__archive_libsystem_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   CC_SHA1_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libsystem_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   CC_SHA1_Update(ctx, indata, insize);
@@ -553,7 +553,7 @@
 }
 
 static int
-__archive_libsystem_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   CC_SHA1_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -562,7 +562,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
 
 static int
-__archive_mbedtls_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   mbedtls_sha1_init(ctx);
   if (mbedtls_sha1_starts_ret(ctx) == 0)
@@ -572,7 +572,7 @@
 }
 
 static int
-__archive_mbedtls_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)
@@ -582,7 +582,7 @@
 }
 
 static int
-__archive_mbedtls_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   if (mbedtls_sha1_finish_ret(ctx, md) == 0) {
     mbedtls_sha1_free(ctx);
@@ -596,14 +596,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
 
 static int
-__archive_nettle_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   sha1_init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_nettle_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   sha1_update(ctx, insize, indata);
@@ -611,7 +611,7 @@
 }
 
 static int
-__archive_nettle_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   sha1_digest(ctx, SHA1_DIGEST_SIZE, md);
   return (ARCHIVE_OK);
@@ -620,7 +620,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
 
 static int
-__archive_openssl_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   if ((*ctx = EVP_MD_CTX_new()) == NULL)
 	return (ARCHIVE_FAILED);
@@ -629,7 +629,7 @@
 }
 
 static int
-__archive_openssl_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   EVP_DigestUpdate(*ctx, indata, insize);
@@ -637,7 +637,7 @@
 }
 
 static int
-__archive_openssl_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
    * this is meant to cope with that. Real fix is probably to fix
@@ -654,20 +654,20 @@
 #elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
 
 static int
-__archive_windowsapi_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
   return (win_crypto_init(ctx, CALG_SHA1));
 }
 
 static int
-__archive_windowsapi_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
   return (win_crypto_Update(ctx, indata, insize));
 }
 
 static int
-__archive_windowsapi_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
   return (win_crypto_Final(md, 20, ctx));
 }
@@ -675,14 +675,14 @@
 #else
 
 static int
-__archive_stub_sha1init(archive_sha1_ctx *ctx)
+__archive_sha1init(archive_sha1_ctx *ctx)
 {
 	(void)ctx; /* UNUSED */
 	return (ARCHIVE_FAILED);
 }
 
 static int
-__archive_stub_sha1update(archive_sha1_ctx *ctx, const void *indata,
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
     size_t insize)
 {
 	(void)ctx; /* UNUSED */
@@ -692,7 +692,7 @@
 }
 
 static int
-__archive_stub_sha1final(archive_sha1_ctx *ctx, void *md)
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
 {
 	(void)ctx; /* UNUSED */
 	(void)md; /* UNUSED */
@@ -705,14 +705,14 @@
 #if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
 
 static int
-__archive_libc_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   SHA256_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA256_Update(ctx, indata, insize);
@@ -720,7 +720,7 @@
 }
 
 static int
-__archive_libc_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   SHA256_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -729,14 +729,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
 
 static int
-__archive_libc2_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   SHA256Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc2_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA256Update(ctx, indata, insize);
@@ -744,7 +744,7 @@
 }
 
 static int
-__archive_libc2_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   SHA256Final(md, ctx);
   return (ARCHIVE_OK);
@@ -753,14 +753,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
 
 static int
-__archive_libc3_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   SHA256Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc3_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA256Update(ctx, indata, insize);
@@ -768,7 +768,7 @@
 }
 
 static int
-__archive_libc3_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   SHA256Final(md, ctx);
   return (ARCHIVE_OK);
@@ -777,14 +777,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
 
 static int
-__archive_libmd_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   SHA256_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libmd_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA256_Update(ctx, indata, insize);
@@ -792,7 +792,7 @@
 }
 
 static int
-__archive_libmd_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   SHA256_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -801,14 +801,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
 
 static int
-__archive_libsystem_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   CC_SHA256_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libsystem_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   CC_SHA256_Update(ctx, indata, insize);
@@ -816,7 +816,7 @@
 }
 
 static int
-__archive_libsystem_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   CC_SHA256_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -825,7 +825,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
 
 static int
-__archive_mbedtls_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   mbedtls_sha256_init(ctx);
   if (mbedtls_sha256_starts_ret(ctx, 0) == 0)
@@ -835,7 +835,7 @@
 }
 
 static int
-__archive_mbedtls_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)
@@ -845,7 +845,7 @@
 }
 
 static int
-__archive_mbedtls_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   if (mbedtls_sha256_finish_ret(ctx, md) == 0) {
     mbedtls_sha256_free(ctx);
@@ -859,14 +859,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
 
 static int
-__archive_nettle_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   sha256_init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_nettle_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   sha256_update(ctx, insize, indata);
@@ -874,7 +874,7 @@
 }
 
 static int
-__archive_nettle_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   sha256_digest(ctx, SHA256_DIGEST_SIZE, md);
   return (ARCHIVE_OK);
@@ -883,7 +883,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
 
 static int
-__archive_openssl_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   if ((*ctx = EVP_MD_CTX_new()) == NULL)
 	return (ARCHIVE_FAILED);
@@ -892,7 +892,7 @@
 }
 
 static int
-__archive_openssl_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   EVP_DigestUpdate(*ctx, indata, insize);
@@ -900,7 +900,7 @@
 }
 
 static int
-__archive_openssl_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   if (*ctx) {
     EVP_DigestFinal(*ctx, md, NULL);
@@ -913,20 +913,20 @@
 #elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
 
 static int
-__archive_windowsapi_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
   return (win_crypto_init(ctx, CALG_SHA_256));
 }
 
 static int
-__archive_windowsapi_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
   return (win_crypto_Update(ctx, indata, insize));
 }
 
 static int
-__archive_windowsapi_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
   return (win_crypto_Final(md, 32, ctx));
 }
@@ -934,14 +934,14 @@
 #else
 
 static int
-__archive_stub_sha256init(archive_sha256_ctx *ctx)
+__archive_sha256init(archive_sha256_ctx *ctx)
 {
 	(void)ctx; /* UNUSED */
 	return (ARCHIVE_FAILED);
 }
 
 static int
-__archive_stub_sha256update(archive_sha256_ctx *ctx, const void *indata,
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
     size_t insize)
 {
 	(void)ctx; /* UNUSED */
@@ -951,7 +951,7 @@
 }
 
 static int
-__archive_stub_sha256final(archive_sha256_ctx *ctx, void *md)
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
 {
 	(void)ctx; /* UNUSED */
 	(void)md; /* UNUSED */
@@ -964,14 +964,14 @@
 #if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
 
 static int
-__archive_libc_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   SHA384_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA384_Update(ctx, indata, insize);
@@ -979,7 +979,7 @@
 }
 
 static int
-__archive_libc_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   SHA384_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -988,14 +988,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
 
 static int
-__archive_libc2_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   SHA384Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc2_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA384Update(ctx, indata, insize);
@@ -1003,7 +1003,7 @@
 }
 
 static int
-__archive_libc2_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   SHA384Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1012,14 +1012,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
 
 static int
-__archive_libc3_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   SHA384Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc3_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA384Update(ctx, indata, insize);
@@ -1027,7 +1027,7 @@
 }
 
 static int
-__archive_libc3_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   SHA384Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1036,14 +1036,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
 
 static int
-__archive_libsystem_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   CC_SHA384_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libsystem_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   CC_SHA384_Update(ctx, indata, insize);
@@ -1051,7 +1051,7 @@
 }
 
 static int
-__archive_libsystem_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   CC_SHA384_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1060,7 +1060,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
 
 static int
-__archive_mbedtls_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   mbedtls_sha512_init(ctx);
   if (mbedtls_sha512_starts_ret(ctx, 1) == 0)
@@ -1070,7 +1070,7 @@
 }
 
 static int
-__archive_mbedtls_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
@@ -1080,7 +1080,7 @@
 }
 
 static int
-__archive_mbedtls_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
     mbedtls_sha512_free(ctx);
@@ -1094,14 +1094,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
 
 static int
-__archive_nettle_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   sha384_init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_nettle_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   sha384_update(ctx, insize, indata);
@@ -1109,7 +1109,7 @@
 }
 
 static int
-__archive_nettle_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   sha384_digest(ctx, SHA384_DIGEST_SIZE, md);
   return (ARCHIVE_OK);
@@ -1118,7 +1118,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
 
 static int
-__archive_openssl_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   if ((*ctx = EVP_MD_CTX_new()) == NULL)
 	return (ARCHIVE_FAILED);
@@ -1127,7 +1127,7 @@
 }
 
 static int
-__archive_openssl_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   EVP_DigestUpdate(*ctx, indata, insize);
@@ -1135,7 +1135,7 @@
 }
 
 static int
-__archive_openssl_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   if (*ctx) {
     EVP_DigestFinal(*ctx, md, NULL);
@@ -1148,20 +1148,20 @@
 #elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
 
 static int
-__archive_windowsapi_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
   return (win_crypto_init(ctx, CALG_SHA_384));
 }
 
 static int
-__archive_windowsapi_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
   return (win_crypto_Update(ctx, indata, insize));
 }
 
 static int
-__archive_windowsapi_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
   return (win_crypto_Final(md, 48, ctx));
 }
@@ -1169,14 +1169,14 @@
 #else
 
 static int
-__archive_stub_sha384init(archive_sha384_ctx *ctx)
+__archive_sha384init(archive_sha384_ctx *ctx)
 {
 	(void)ctx; /* UNUSED */
 	return (ARCHIVE_FAILED);
 }
 
 static int
-__archive_stub_sha384update(archive_sha384_ctx *ctx, const void *indata,
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
     size_t insize)
 {
 	(void)ctx; /* UNUSED */
@@ -1186,7 +1186,7 @@
 }
 
 static int
-__archive_stub_sha384final(archive_sha384_ctx *ctx, void *md)
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
 {
 	(void)ctx; /* UNUSED */
 	(void)md; /* UNUSED */
@@ -1199,14 +1199,14 @@
 #if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
 
 static int
-__archive_libc_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   SHA512_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA512_Update(ctx, indata, insize);
@@ -1214,7 +1214,7 @@
 }
 
 static int
-__archive_libc_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   SHA512_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1223,14 +1223,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
 
 static int
-__archive_libc2_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   SHA512Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc2_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA512Update(ctx, indata, insize);
@@ -1238,7 +1238,7 @@
 }
 
 static int
-__archive_libc2_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   SHA512Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1247,14 +1247,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
 
 static int
-__archive_libc3_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   SHA512Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libc3_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA512Update(ctx, indata, insize);
@@ -1262,7 +1262,7 @@
 }
 
 static int
-__archive_libc3_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   SHA512Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1271,14 +1271,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
 
 static int
-__archive_libmd_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   SHA512_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libmd_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   SHA512_Update(ctx, indata, insize);
@@ -1286,7 +1286,7 @@
 }
 
 static int
-__archive_libmd_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   SHA512_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1295,14 +1295,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
 
 static int
-__archive_libsystem_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   CC_SHA512_Init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_libsystem_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   CC_SHA512_Update(ctx, indata, insize);
@@ -1310,7 +1310,7 @@
 }
 
 static int
-__archive_libsystem_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   CC_SHA512_Final(md, ctx);
   return (ARCHIVE_OK);
@@ -1319,7 +1319,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
 
 static int
-__archive_mbedtls_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   mbedtls_sha512_init(ctx);
   if (mbedtls_sha512_starts_ret(ctx, 0) == 0)
@@ -1329,7 +1329,7 @@
 }
 
 static int
-__archive_mbedtls_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
@@ -1339,7 +1339,7 @@
 }
 
 static int
-__archive_mbedtls_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
     mbedtls_sha512_free(ctx);
@@ -1353,14 +1353,14 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
 
 static int
-__archive_nettle_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   sha512_init(ctx);
   return (ARCHIVE_OK);
 }
 
 static int
-__archive_nettle_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   sha512_update(ctx, insize, indata);
@@ -1368,7 +1368,7 @@
 }
 
 static int
-__archive_nettle_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   sha512_digest(ctx, SHA512_DIGEST_SIZE, md);
   return (ARCHIVE_OK);
@@ -1377,7 +1377,7 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
 
 static int
-__archive_openssl_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   if ((*ctx = EVP_MD_CTX_new()) == NULL)
 	return (ARCHIVE_FAILED);
@@ -1386,7 +1386,7 @@
 }
 
 static int
-__archive_openssl_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   EVP_DigestUpdate(*ctx, indata, insize);
@@ -1394,7 +1394,7 @@
 }
 
 static int
-__archive_openssl_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   if (*ctx) {
     EVP_DigestFinal(*ctx, md, NULL);
@@ -1407,20 +1407,20 @@
 #elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
 
 static int
-__archive_windowsapi_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
   return (win_crypto_init(ctx, CALG_SHA_512));
 }
 
 static int
-__archive_windowsapi_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
   return (win_crypto_Update(ctx, indata, insize));
 }
 
 static int
-__archive_windowsapi_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
   return (win_crypto_Final(md, 64, ctx));
 }
@@ -1428,14 +1428,14 @@
 #else
 
 static int
-__archive_stub_sha512init(archive_sha512_ctx *ctx)
+__archive_sha512init(archive_sha512_ctx *ctx)
 {
 	(void)ctx; /* UNUSED */
 	return (ARCHIVE_FAILED);
 }
 
 static int
-__archive_stub_sha512update(archive_sha512_ctx *ctx, const void *indata,
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
     size_t insize)
 {
 	(void)ctx; /* UNUSED */
@@ -1445,7 +1445,7 @@
 }
 
 static int
-__archive_stub_sha512final(archive_sha512_ctx *ctx, void *md)
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
 {
 	(void)ctx; /* UNUSED */
 	(void)md; /* UNUSED */
@@ -1468,224 +1468,32 @@
 const struct archive_digest __archive_digest =
 {
 /* MD5 */
-#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
-  &__archive_libc_md5init,
-  &__archive_libc_md5update,
-  &__archive_libc_md5final,
-#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
-  &__archive_libmd_md5init,
-  &__archive_libmd_md5update,
-  &__archive_libmd_md5final,
-#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
-  &__archive_libsystem_md5init,
-  &__archive_libsystem_md5update,
-  &__archive_libsystem_md5final,
-#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
-  &__archive_mbedtls_md5init,
-  &__archive_mbedtls_md5update,
-  &__archive_mbedtls_md5final,
-#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
-  &__archive_nettle_md5init,
-  &__archive_nettle_md5update,
-  &__archive_nettle_md5final,
-#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
-  &__archive_openssl_md5init,
-  &__archive_openssl_md5update,
-  &__archive_openssl_md5final,
-#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
-  &__archive_windowsapi_md5init,
-  &__archive_windowsapi_md5update,
-  &__archive_windowsapi_md5final,
-#elif !defined(ARCHIVE_MD5_COMPILE_TEST)
-  &__archive_stub_md5init,
-  &__archive_stub_md5update,
-  &__archive_stub_md5final,
-#endif
+  &__archive_md5init,
+  &__archive_md5update,
+  &__archive_md5final,
 
 /* RIPEMD160 */
-#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
-  &__archive_libc_ripemd160init,
-  &__archive_libc_ripemd160update,
-  &__archive_libc_ripemd160final,
-#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
-  &__archive_libmd_ripemd160init,
-  &__archive_libmd_ripemd160update,
-  &__archive_libmd_ripemd160final,
-#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
-  &__archive_mbedtls_ripemd160init,
-  &__archive_mbedtls_ripemd160update,
-  &__archive_mbedtls_ripemd160final,
-#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
-  &__archive_nettle_ripemd160init,
-  &__archive_nettle_ripemd160update,
-  &__archive_nettle_ripemd160final,
-#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
-  &__archive_openssl_ripemd160init,
-  &__archive_openssl_ripemd160update,
-  &__archive_openssl_ripemd160final,
-#elif !defined(ARCHIVE_RMD160_COMPILE_TEST)
-  &__archive_stub_ripemd160init,
-  &__archive_stub_ripemd160update,
-  &__archive_stub_ripemd160final,
-#endif
+  &__archive_ripemd160init,
+  &__archive_ripemd160update,
+  &__archive_ripemd160final,
 
 /* SHA1 */
-#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
-  &__archive_libc_sha1init,
-  &__archive_libc_sha1update,
-  &__archive_libc_sha1final,
-#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
-  &__archive_libmd_sha1init,
-  &__archive_libmd_sha1update,
-  &__archive_libmd_sha1final,
-#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
-  &__archive_libsystem_sha1init,
-  &__archive_libsystem_sha1update,
-  &__archive_libsystem_sha1final,
-#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
-  &__archive_mbedtls_sha1init,
-  &__archive_mbedtls_sha1update,
-  &__archive_mbedtls_sha1final,
-#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
-  &__archive_nettle_sha1init,
-  &__archive_nettle_sha1update,
-  &__archive_nettle_sha1final,
-#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
-  &__archive_openssl_sha1init,
-  &__archive_openssl_sha1update,
-  &__archive_openssl_sha1final,
-#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
-  &__archive_windowsapi_sha1init,
-  &__archive_windowsapi_sha1update,
-  &__archive_windowsapi_sha1final,
-#elif !defined(ARCHIVE_SHA1_COMPILE_TEST)
-  &__archive_stub_sha1init,
-  &__archive_stub_sha1update,
-  &__archive_stub_sha1final,
-#endif
+  &__archive_sha1init,
+  &__archive_sha1update,
+  &__archive_sha1final,
 
 /* SHA256 */
-#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
-  &__archive_libc_sha256init,
-  &__archive_libc_sha256update,
-  &__archive_libc_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
-  &__archive_libc2_sha256init,
-  &__archive_libc2_sha256update,
-  &__archive_libc2_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
-  &__archive_libc3_sha256init,
-  &__archive_libc3_sha256update,
-  &__archive_libc3_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
-  &__archive_libmd_sha256init,
-  &__archive_libmd_sha256update,
-  &__archive_libmd_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
-  &__archive_libsystem_sha256init,
-  &__archive_libsystem_sha256update,
-  &__archive_libsystem_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
-  &__archive_mbedtls_sha256init,
-  &__archive_mbedtls_sha256update,
-  &__archive_mbedtls_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
-  &__archive_nettle_sha256init,
-  &__archive_nettle_sha256update,
-  &__archive_nettle_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
-  &__archive_openssl_sha256init,
-  &__archive_openssl_sha256update,
-  &__archive_openssl_sha256final,
-#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
-  &__archive_windowsapi_sha256init,
-  &__archive_windowsapi_sha256update,
-  &__archive_windowsapi_sha256final,
-#elif !defined(ARCHIVE_SHA256_COMPILE_TEST)
-  &__archive_stub_sha256init,
-  &__archive_stub_sha256update,
-  &__archive_stub_sha256final,
-#endif
+  &__archive_sha256init,
+  &__archive_sha256update,
+  &__archive_sha256final,
 
 /* SHA384 */
-#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
-  &__archive_libc_sha384init,
-  &__archive_libc_sha384update,
-  &__archive_libc_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
-  &__archive_libc2_sha384init,
-  &__archive_libc2_sha384update,
-  &__archive_libc2_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
-  &__archive_libc3_sha384init,
-  &__archive_libc3_sha384update,
-  &__archive_libc3_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
-  &__archive_libsystem_sha384init,
-  &__archive_libsystem_sha384update,
-  &__archive_libsystem_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
-  &__archive_mbedtls_sha384init,
-  &__archive_mbedtls_sha384update,
-  &__archive_mbedtls_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
-  &__archive_nettle_sha384init,
-  &__archive_nettle_sha384update,
-  &__archive_nettle_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
-  &__archive_openssl_sha384init,
-  &__archive_openssl_sha384update,
-  &__archive_openssl_sha384final,
-#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
-  &__archive_windowsapi_sha384init,
-  &__archive_windowsapi_sha384update,
-  &__archive_windowsapi_sha384final,
-#elif !defined(ARCHIVE_SHA384_COMPILE_TEST)
-  &__archive_stub_sha384init,
-  &__archive_stub_sha384update,
-  &__archive_stub_sha384final,
-#endif
+  &__archive_sha384init,
+  &__archive_sha384update,
+  &__archive_sha384final,
 
 /* SHA512 */
-#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
-  &__archive_libc_sha512init,
-  &__archive_libc_sha512update,
-  &__archive_libc_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
-  &__archive_libc2_sha512init,
-  &__archive_libc2_sha512update,
-  &__archive_libc2_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
-  &__archive_libc3_sha512init,
-  &__archive_libc3_sha512update,
-  &__archive_libc3_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
-  &__archive_libmd_sha512init,
-  &__archive_libmd_sha512update,
-  &__archive_libmd_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
-  &__archive_libsystem_sha512init,
-  &__archive_libsystem_sha512update,
-  &__archive_libsystem_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
-  &__archive_mbedtls_sha512init,
-  &__archive_mbedtls_sha512update,
-  &__archive_mbedtls_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
-  &__archive_nettle_sha512init,
-  &__archive_nettle_sha512update,
-  &__archive_nettle_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
-  &__archive_openssl_sha512init,
-  &__archive_openssl_sha512update,
-  &__archive_openssl_sha512final
-#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
-  &__archive_windowsapi_sha512init,
-  &__archive_windowsapi_sha512update,
-  &__archive_windowsapi_sha512final
-#elif !defined(ARCHIVE_SHA512_COMPILE_TEST)
-  &__archive_stub_sha512init,
-  &__archive_stub_sha512update,
-  &__archive_stub_sha512final
-#endif
+  &__archive_sha512init,
+  &__archive_sha512update,
+  &__archive_sha512final
 };
diff --git a/Utilities/cmlibarchive/libarchive/archive_digest_private.h b/Utilities/cmlibarchive/libarchive/archive_digest_private.h
index 15312ee..9b3bd66 100644
--- a/Utilities/cmlibarchive/libarchive/archive_digest_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_digest_private.h
@@ -30,6 +30,10 @@
 #ifndef __LIBARCHIVE_BUILD
 #error This header is only to be used internally to libarchive.
 #endif
+#ifndef __LIBARCHIVE_CONFIG_H_INCLUDED
+#error "Should have include config.h first!"
+#endif
+
 /*
  * Crypto support in various Operating Systems:
  *
diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.c b/Utilities/cmlibarchive/libarchive/archive_entry.c
index a15e98c..ca7a4bd 100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry.c
+++ b/Utilities/cmlibarchive/libarchive/archive_entry.c
@@ -208,6 +208,19 @@
 
 	/* Copy encryption status */
 	entry2->encryption = entry->encryption;
+
+	/* Copy digests */
+#define copy_digest(_e2, _e, _t) \
+	memcpy(_e2->digest._t, _e->digest._t, sizeof(_e2->digest._t))
+
+	copy_digest(entry2, entry, md5);
+	copy_digest(entry2, entry, rmd160);
+	copy_digest(entry2, entry, sha1);
+	copy_digest(entry2, entry, sha256);
+	copy_digest(entry2, entry, sha384);
+	copy_digest(entry2, entry, sha512);
+
+#undef copy_digest
 	
 	/* Copy ACL data over. */
 	archive_acl_copy(&entry2->acl, &entry->acl);
@@ -353,7 +366,7 @@
 		return minor(entry->ae_stat.aest_dev);
 }
 
-mode_t
+__LA_MODE_T
 archive_entry_filetype(struct archive_entry *entry)
 {
 	return (AE_IFMT & entry->acl.mode);
@@ -450,7 +463,7 @@
 _archive_entry_gname_l(struct archive_entry *entry,
     const char **p, size_t *len, struct archive_string_conv *sc)
 {
-	return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc));
+	return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_gname, p, len, sc));
 }
 
 const char *
@@ -504,7 +517,7 @@
 		*len = 0;
 		return (0);
 	}
-	return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc));
+	return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_hardlink, p, len, sc));
 }
 
 la_int64_t
@@ -525,7 +538,7 @@
 	return (entry->ae_stat.aest_ino);
 }
 
-mode_t
+__LA_MODE_T
 archive_entry_mode(struct archive_entry *entry)
 {
 	return (entry->acl.mode);
@@ -595,10 +608,10 @@
 _archive_entry_pathname_l(struct archive_entry *entry,
     const char **p, size_t *len, struct archive_string_conv *sc)
 {
-	return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc));
+	return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_pathname, p, len, sc));
 }
 
-mode_t
+__LA_MODE_T
 archive_entry_perm(struct archive_entry *entry)
 {
 	return (~AE_IFMT & entry->acl.mode);
@@ -723,7 +736,7 @@
 		*len = 0;
 		return (0);
 	}
-	return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc));
+	return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_symlink, p, len, sc));
 }
 
 la_int64_t
@@ -769,7 +782,7 @@
 _archive_entry_uname_l(struct archive_entry *entry,
     const char **p, size_t *len, struct archive_string_conv *sc)
 {
-	return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc));
+	return (archive_mstring_get_mbs_l(entry->archive, &entry->ae_uname, p, len, sc));
 }
 
 int
@@ -1416,6 +1429,62 @@
   }
 }
 
+/* Digest handling */
+const unsigned char *
+archive_entry_digest(struct archive_entry *entry, int type)
+{
+	switch (type) {
+	case ARCHIVE_ENTRY_DIGEST_MD5:
+		return entry->digest.md5;
+	case ARCHIVE_ENTRY_DIGEST_RMD160:
+		return entry->digest.rmd160;
+	case ARCHIVE_ENTRY_DIGEST_SHA1:
+		return entry->digest.sha1;
+	case ARCHIVE_ENTRY_DIGEST_SHA256:
+		return entry->digest.sha256;
+	case ARCHIVE_ENTRY_DIGEST_SHA384:
+		return entry->digest.sha384;
+	case ARCHIVE_ENTRY_DIGEST_SHA512:
+		return entry->digest.sha512;
+	default:
+		return NULL;
+	}
+}
+
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+    const unsigned char *digest)
+{
+#define copy_digest(_e, _t, _d)\
+	memcpy(_e->digest._t, _d, sizeof(_e->digest._t))
+
+	switch (type) {
+	case ARCHIVE_ENTRY_DIGEST_MD5:
+		copy_digest(entry, md5, digest);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_RMD160:
+		copy_digest(entry, rmd160, digest);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA1:
+		copy_digest(entry, sha1, digest);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA256:
+		copy_digest(entry, sha256, digest);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA384:
+		copy_digest(entry, sha384, digest);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA512:
+		copy_digest(entry, sha512, digest);
+		break;
+	default:
+		return ARCHIVE_WARN;
+	}
+
+	return ARCHIVE_OK;
+#undef copy_digest
+}
+
 /*
  * ACL management.  The following would, of course, be a lot simpler
  * if: 1) the last draft of POSIX.1e were a really thorough and
diff --git a/Utilities/cmlibarchive/libarchive/archive_entry.h b/Utilities/cmlibarchive/libarchive/archive_entry.h
index 0053faa..21e89d2 100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry.h
+++ b/Utilities/cmlibarchive/libarchive/archive_entry.h
@@ -30,7 +30,7 @@
 #define	ARCHIVE_ENTRY_H_INCLUDED
 
 /* Note: Compiler will complain if this does not match archive.h! */
-#define	ARCHIVE_VERSION_NUMBER 3004002
+#define	ARCHIVE_VERSION_NUMBER 3005001
 
 /*
  * Note: archive_entry.h is for use outside of libarchive; the
@@ -394,6 +394,19 @@
 __LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_t);
 
 /*
+ * Digest routine. This is used to query the raw hex digest for the
+ * given entry. The type of digest is provided as an argument.
+ */
+#define ARCHIVE_ENTRY_DIGEST_MD5              0x00000001
+#define ARCHIVE_ENTRY_DIGEST_RMD160           0x00000002
+#define ARCHIVE_ENTRY_DIGEST_SHA1             0x00000003
+#define ARCHIVE_ENTRY_DIGEST_SHA256           0x00000004
+#define ARCHIVE_ENTRY_DIGEST_SHA384           0x00000005
+#define ARCHIVE_ENTRY_DIGEST_SHA512           0x00000006
+
+__LA_DECL const unsigned char * archive_entry_digest(struct archive_entry *, int /* type */);
+
+/*
  * ACL routines.  This used to simply store and return text-format ACL
  * strings, but that proved insufficient for a number of reasons:
  *   = clients need control over uname/uid and gname/gid mappings
diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_private.h b/Utilities/cmlibarchive/libarchive/archive_entry_private.h
index 2b9a084..cf4deb2 100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_entry_private.h
@@ -50,6 +50,15 @@
 	int64_t	 length;
 };
 
+struct ae_digest {
+	unsigned char md5[16];
+	unsigned char rmd160[20];
+	unsigned char sha1[20];
+	unsigned char sha256[32];
+	unsigned char sha384[48];
+	unsigned char sha512[64];
+};
+
 /*
  * Description of an archive entry.
  *
@@ -162,6 +171,9 @@
 	void *mac_metadata;
 	size_t mac_metadata_size;
 
+	/* Digest support. */
+	struct ae_digest digest;
+
 	/* ACL support. */
 	struct archive_acl    acl;
 
@@ -181,4 +193,8 @@
 	int ae_symlink_type;
 };
 
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+    const unsigned char *digest);
+
 #endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/Utilities/cmlibarchive/libarchive/archive_entry_stat.3 b/Utilities/cmlibarchive/libarchive/archive_entry_stat.3
index aa5c8e0..29a53f7 100644
--- a/Utilities/cmlibarchive/libarchive/archive_entry_stat.3
+++ b/Utilities/cmlibarchive/libarchive/archive_entry_stat.3
@@ -215,9 +215,9 @@
 set and unset the size, respectively.
 .Pp
 The number of references (hardlinks) can be obtained by calling
-.Fn archive_entry_nlinks
+.Fn archive_entry_nlink
 and set with
-.Fn archive_entry_set_nlinks .
+.Fn archive_entry_set_nlink .
 .Ss Identifying unique files
 The functions
 .Fn archive_entry_dev
diff --git a/Utilities/cmlibarchive/libarchive/archive_ppmd7.c b/Utilities/cmlibarchive/libarchive/archive_ppmd7.c
index 4029395..cc3f778 100644
--- a/Utilities/cmlibarchive/libarchive/archive_ppmd7.c
+++ b/Utilities/cmlibarchive/libarchive/archive_ppmd7.c
@@ -4,7 +4,7 @@
 
 #include "archive_platform.h"
 
-#include <memory.h>
+#include <stdlib.h>
 
 #include "archive_ppmd7_private.h"
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_read.c b/Utilities/cmlibarchive/libarchive/archive_read.c
index 4a933b2..c59f051 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read.c
@@ -892,15 +892,16 @@
 			len = a->read_data_remaining;
 			if (len > s)
 				len = s;
-			if (len)
+			if (len) {
 				memcpy(dest, a->read_data_block, len);
-			s -= len;
-			a->read_data_block += len;
-			a->read_data_remaining -= len;
-			a->read_data_output_offset += len;
-			a->read_data_offset += len;
-			dest += len;
-			bytes_read += len;
+				s -= len;
+				a->read_data_block += len;
+				a->read_data_remaining -= len;
+				a->read_data_output_offset += len;
+				a->read_data_offset += len;
+				dest += len;
+				bytes_read += len;
+			}
 		}
 	}
 	a->read_data_is_posix_read = 0;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c
index cf821b5..f0b1ab9 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_add_passphrase.c
@@ -57,6 +57,10 @@
 {
 	p->next = a->passphrases.first;
 	a->passphrases.first = p;
+	if (&a->passphrases.first == a->passphrases.last) {
+		a->passphrases.last = &p->next;
+		p->next = NULL;
+	}
 }
 
 static struct archive_read_passphrase *
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c
index 2a8cec8..9c9cf38 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_entry_from_file.c
@@ -103,6 +103,10 @@
 
 static int setup_mac_metadata(struct archive_read_disk *,
     struct archive_entry *, int *fd);
+#ifdef ARCHIVE_XATTR_FREEBSD
+static int setup_xattrs_namespace(struct archive_read_disk *,
+    struct archive_entry *, int *, int);
+#endif
 static int setup_xattrs(struct archive_read_disk *,
     struct archive_entry *, int *fd);
 static int setup_sparse(struct archive_read_disk *,
@@ -701,14 +705,13 @@
 }
 
 static int
-setup_xattrs(struct archive_read_disk *a,
-    struct archive_entry *entry, int *fd)
+setup_xattrs_namespace(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd, int namespace)
 {
 	char buff[512];
 	char *list, *p;
 	ssize_t list_size;
 	const char *path;
-	int namespace = EXTATTR_NAMESPACE_USER;
 
 	path = NULL;
 
@@ -727,6 +730,8 @@
 
 	if (list_size == -1 && errno == EOPNOTSUPP)
 		return (ARCHIVE_OK);
+	if (list_size == -1 && errno == EPERM)
+		return (ARCHIVE_OK);
 	if (list_size == -1) {
 		archive_set_error(&a->archive, errno,
 			"Couldn't list extended attributes");
@@ -760,7 +765,17 @@
 		size_t len = 255 & (int)*p;
 		char *name;
 
-		strcpy(buff, "user.");
+		if (namespace == EXTATTR_NAMESPACE_SYSTEM) {
+			if (!strcmp(p + 1, "nfs4.acl") ||
+			    !strcmp(p + 1, "posix1e.acl_access") ||
+			    !strcmp(p + 1, "posix1e.acl_default")) {
+				p += 1 + len;
+				continue;
+			}
+			strcpy(buff, "system.");
+		} else {
+			strcpy(buff, "user.");
+		}
 		name = buff + strlen(buff);
 		memcpy(name, p + 1, len);
 		name[len] = '\0';
@@ -772,6 +787,31 @@
 	return (ARCHIVE_OK);
 }
 
+static int
+setup_xattrs(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	int namespaces[2];
+	int i, res;
+
+	namespaces[0] = EXTATTR_NAMESPACE_USER;
+	namespaces[1] = EXTATTR_NAMESPACE_SYSTEM;
+
+	for (i = 0; i < 2; i++) {
+		res = setup_xattrs_namespace(a, entry, fd,
+		    namespaces[i]);
+		switch (res) {
+			case (ARCHIVE_OK):
+			case (ARCHIVE_WARN):
+				break;
+			default:
+				return (res);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
 #else
 
 /*
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c
index 52fec7b..2898206 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_disk_posix.c
@@ -1658,7 +1658,7 @@
 setup_current_filesystem(struct archive_read_disk *a)
 {
 	struct tree *t = a->tree;
-	struct statvfs sfs;
+	struct statvfs svfs;
 	int r, xr = 0;
 
 	t->current_filesystem->synthetic = -1;
@@ -1667,16 +1667,16 @@
 		return (ARCHIVE_FAILED);
 	}
 	if (tree_current_is_symblic_link_target(t)) {
-		r = statvfs(tree_current_access_path(t), &sfs);
+		r = statvfs(tree_current_access_path(t), &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, -1, tree_current_access_path(t));
 	} else {
 #ifdef HAVE_FSTATVFS
-		r = fstatvfs(tree_current_dir_fd(t), &sfs);
+		r = fstatvfs(tree_current_dir_fd(t), &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
 #else
-		r = statvfs(".", &sfs);
+		r = statvfs(".", &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, -1, ".");
 #endif
@@ -1688,30 +1688,30 @@
 	} else if (xr == 1) {
 		/* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN
 		 * for pathconf() function. */
-		t->current_filesystem->xfer_align = sfs.f_frsize;
+		t->current_filesystem->xfer_align = svfs.f_frsize;
 		t->current_filesystem->max_xfer_size = -1;
 #if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)
-		t->current_filesystem->min_xfer_size = sfs.f_iosize;
-		t->current_filesystem->incr_xfer_size = sfs.f_iosize;
+		t->current_filesystem->min_xfer_size = svfs.f_iosize;
+		t->current_filesystem->incr_xfer_size = svfs.f_iosize;
 #else
-		t->current_filesystem->min_xfer_size = sfs.f_bsize;
-		t->current_filesystem->incr_xfer_size = sfs.f_bsize;
+		t->current_filesystem->min_xfer_size = svfs.f_bsize;
+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;
 #endif
 	}
-	if (sfs.f_flag & ST_LOCAL)
+	if (svfs.f_flag & ST_LOCAL)
 		t->current_filesystem->remote = 0;
 	else
 		t->current_filesystem->remote = 1;
 
 #if defined(ST_NOATIME)
-	if (sfs.f_flag & ST_NOATIME)
+	if (svfs.f_flag & ST_NOATIME)
 		t->current_filesystem->noatime = 1;
 	else
 #endif
 		t->current_filesystem->noatime = 0;
 
 	/* Set maximum filename length. */
-	t->current_filesystem->name_max = sfs.f_namemax;
+	t->current_filesystem->name_max = svfs.f_namemax;
 	return (ARCHIVE_OK);
 }
 
@@ -1840,7 +1840,7 @@
 #if defined(HAVE_STATVFS)
 	if (svfs.f_flag & ST_NOATIME)
 #else
-	if (sfs.f_flag & ST_NOATIME)
+	if (sfs.f_flags & ST_NOATIME)
 #endif
 		t->current_filesystem->noatime = 1;
 	else
@@ -1864,7 +1864,7 @@
 setup_current_filesystem(struct archive_read_disk *a)
 {
 	struct tree *t = a->tree;
-	struct statvfs sfs;
+	struct statvfs svfs;
 	int r, xr = 0;
 
 	t->current_filesystem->synthetic = -1;/* Not supported */
@@ -1883,7 +1883,7 @@
 			    "openat failed");
 			return (ARCHIVE_FAILED);
 		}
-		r = fstatvfs(fd, &sfs);
+		r = fstatvfs(fd, &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, fd, NULL);
 		close(fd);
@@ -1892,13 +1892,13 @@
 			archive_set_error(&a->archive, errno, "fchdir failed");
 			return (ARCHIVE_FAILED);
 		}
-		r = statvfs(tree_current_access_path(t), &sfs);
+		r = statvfs(tree_current_access_path(t), &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, -1, tree_current_access_path(t));
 #endif
 	} else {
 #ifdef HAVE_FSTATVFS
-		r = fstatvfs(tree_current_dir_fd(t), &sfs);
+		r = fstatvfs(tree_current_dir_fd(t), &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
 #else
@@ -1906,7 +1906,7 @@
 			archive_set_error(&a->archive, errno, "fchdir failed");
 			return (ARCHIVE_FAILED);
 		}
-		r = statvfs(".", &sfs);
+		r = statvfs(".", &svfs);
 		if (r == 0)
 			xr = get_xfer_size(t, -1, ".");
 #endif
@@ -1918,14 +1918,14 @@
 		return (ARCHIVE_FAILED);
 	} else if (xr == 1) {
 		/* pathconf(_PC_REX_*) operations are not supported. */
-		t->current_filesystem->xfer_align = sfs.f_frsize;
+		t->current_filesystem->xfer_align = svfs.f_frsize;
 		t->current_filesystem->max_xfer_size = -1;
-		t->current_filesystem->min_xfer_size = sfs.f_bsize;
-		t->current_filesystem->incr_xfer_size = sfs.f_bsize;
+		t->current_filesystem->min_xfer_size = svfs.f_bsize;
+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;
 	}
 
 #if defined(ST_NOATIME)
-	if (sfs.f_flag & ST_NOATIME)
+	if (svfs.f_flag & ST_NOATIME)
 		t->current_filesystem->noatime = 1;
 	else
 #endif
@@ -1933,7 +1933,7 @@
 
 #if defined(USE_READDIR_R)
 	/* Set maximum filename length. */
-	t->current_filesystem->name_max = sfs.f_namemax;
+	t->current_filesystem->name_max = svfs.f_namemax;
 #endif
 	return (ARCHIVE_OK);
 }
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_filter.3 b/Utilities/cmlibarchive/libarchive/archive_read_filter.3
index 1ba5fcb..4f5c351 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_filter.3
+++ b/Utilities/cmlibarchive/libarchive/archive_read_filter.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd August 14, 2014
+.Dd June 9, 2020
 .Dt ARCHIVE_READ_FILTER 3
 .Os
 .Sh NAME
@@ -50,6 +50,8 @@
 .Ft int
 .Fn archive_read_support_filter_all "struct archive *"
 .Ft int
+.Fn archive_read_support_filter_by_code "struct archive *" "int"
+.Ft int
 .Fn archive_read_support_filter_bzip2 "struct archive *"
 .Ft int
 .Fn archive_read_support_filter_compress "struct archive *"
@@ -116,6 +118,14 @@
 is always enabled by default.
 .It Fn archive_read_support_filter_all
 Enables all available decompression filters.
+.It Fn archive_read_support_filter_by_code
+Enables a single filter specified by the filter code.
+This function does not work with
+.Cm ARCHIVE_FILTER_PROGRAM .
+Note: In statically-linked executables, this will cause
+your program to include support for every filter.
+If executable size is a concern, you may wish to avoid
+using this function.
 .It Fn archive_read_support_filter_program
 Data is fed through the specified external program before being dearchived.
 Note that this disables automatic detection of the compression format,
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c
index 86635e2..561289b 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_open_filename.c
@@ -221,7 +221,9 @@
 	struct read_file_data *mine = (struct read_file_data *)client_data;
 	void *buffer;
 	const char *filename = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
 	const wchar_t *wfilename = NULL;
+#endif
 	int fd = -1;
 	int is_disk_like = 0;
 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -281,10 +283,12 @@
 #endif
 	}
 	if (fstat(fd, &st) != 0) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
 		if (mine->filename_type == FNT_WCS)
 			archive_set_error(a, errno, "Can't stat '%S'",
 			    wfilename);
 		else
+#endif
 			archive_set_error(a, errno, "Can't stat '%s'",
 			    filename);
 		goto fail;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_set_format.c b/Utilities/cmlibarchive/libarchive/archive_read_set_format.c
index 1d3e49d..796dcdc 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_set_format.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_set_format.c
@@ -61,6 +61,9 @@
     case ARCHIVE_FORMAT_CPIO:
       strcpy(str, "cpio");
       break;
+    case ARCHIVE_FORMAT_EMPTY:
+      strcpy(str, "empty");
+      break;
     case ARCHIVE_FORMAT_ISO9660:
       strcpy(str, "iso9660");
       break;
@@ -76,9 +79,15 @@
     case ARCHIVE_FORMAT_RAR_V5:
       strcpy(str, "rar5");
       break;
+    case ARCHIVE_FORMAT_RAW:
+      strcpy(str, "raw");
+      break;
     case ARCHIVE_FORMAT_TAR:
       strcpy(str, "tar");
       break;
+    case ARCHIVE_FORMAT_WARC:
+      strcpy(str, "warc");
+      break;
     case ARCHIVE_FORMAT_XAR:
       strcpy(str, "xar");
       break;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c
new file mode 100644
index 0000000..94c4af6
--- /dev/null
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_by_code.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2020 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_filter_by_code(struct archive *a, int filter_code)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_by_code");
+
+	switch (filter_code) {
+	case ARCHIVE_FILTER_NONE:
+		return archive_read_support_filter_none(a);
+		break;
+	case ARCHIVE_FILTER_GZIP:
+		return archive_read_support_filter_gzip(a);
+		break;
+	case ARCHIVE_FILTER_BZIP2:
+		return archive_read_support_filter_bzip2(a);
+		break;
+	case ARCHIVE_FILTER_COMPRESS:
+		return archive_read_support_filter_compress(a);
+		break;
+	case ARCHIVE_FILTER_LZMA:
+		return archive_read_support_filter_lzma(a);
+		break;
+	case ARCHIVE_FILTER_XZ:
+		return archive_read_support_filter_xz(a);
+		break;
+	case ARCHIVE_FILTER_UU:
+		return archive_read_support_filter_uu(a);
+		break;
+	case ARCHIVE_FILTER_RPM:
+		return archive_read_support_filter_rpm(a);
+		break;
+	case ARCHIVE_FILTER_LZIP:
+		return archive_read_support_filter_lzip(a);
+		break;
+	case ARCHIVE_FILTER_LRZIP:
+		return archive_read_support_filter_lrzip(a);
+		break;
+	case ARCHIVE_FILTER_LZOP:
+		return archive_read_support_filter_lzop(a);
+		break;
+	case ARCHIVE_FILTER_GRZIP:
+		return archive_read_support_filter_grzip(a);
+		break;
+	case ARCHIVE_FILTER_LZ4:
+		return archive_read_support_filter_lz4(a);
+		break;
+	case ARCHIVE_FILTER_ZSTD:
+		return archive_read_support_filter_zstd(a);
+		break;
+	}
+	return (ARCHIVE_FATAL);
+}
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c
index b8bf128..bf5b6f2 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_program.c
@@ -400,7 +400,7 @@
 	static const size_t out_buf_len = 65536;
 	char *out_buf;
 	const char *prefix = "Program: ";
-	pid_t child;
+	int ret;
 	size_t l;
 
 	l = strlen(prefix) + strlen(cmd) + 1;
@@ -426,9 +426,9 @@
 	state->out_buf = out_buf;
 	state->out_buf_len = out_buf_len;
 
-	child = __archive_create_child(cmd, &state->child_stdin,
-	    &state->child_stdout);
-	if (child == -1) {
+	ret = __archive_create_child(cmd, &state->child_stdin,
+	    &state->child_stdout, &state->child);
+	if (ret != ARCHIVE_OK) {
 		free(state->out_buf);
 		archive_string_free(&state->description);
 		free(state);
@@ -437,21 +437,6 @@
 		    cmd);
 		return (ARCHIVE_FATAL);
 	}
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	state->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child);
-	if (state->child == NULL) {
-		child_stop(self, state);
-		free(state->out_buf);
-		archive_string_free(&state->description);
-		free(state);
-		archive_set_error(&self->archive->archive, EINVAL,
-		    "Can't initialize filter; unable to run program \"%s\"",
-		    cmd);
-		return (ARCHIVE_FATAL);
-	}
-#else
-	state->child = child;
-#endif
 
 	self->data = state;
 	self->read = program_filter_read;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
index e1ec60e..c4e8ec7 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_filter_zstd.c
@@ -119,6 +119,8 @@
 
 	/* Zstd frame magic values */
 	const unsigned zstd_magic = 0xFD2FB528U;
+	const unsigned zstd_magic_skippable_start = 0x184D2A50U;
+	const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
 
 	(void) self; /* UNUSED */
 
@@ -129,6 +131,8 @@
 	prefix = archive_le32dec(buffer);
 	if (prefix == zstd_magic)
 		return (32);
+	if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)
+		return (32);
 
 	return (0);
 }
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c
index 034353d..89e96f1 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_by_code.c
@@ -26,6 +26,10 @@
 #include "archive_platform.h"
 __FBSDID("$FreeBSD$");
 
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
 #include "archive.h"
 #include "archive_private.h"
 
@@ -48,6 +52,9 @@
 	case ARCHIVE_FORMAT_CPIO:
 		return archive_read_support_format_cpio(a);
 		break;
+	case ARCHIVE_FORMAT_EMPTY:
+		return archive_read_support_format_empty(a);
+		break;
 	case ARCHIVE_FORMAT_ISO9660:
 		return archive_read_support_format_iso9660(a);
 		break;
@@ -63,9 +70,15 @@
 	case ARCHIVE_FORMAT_RAR_V5:
 		return archive_read_support_format_rar5(a);
 		break;
+	case ARCHIVE_FORMAT_RAW:
+		return archive_read_support_format_raw(a);
+		break;
 	case ARCHIVE_FORMAT_TAR:
 		return archive_read_support_format_tar(a);
 		break;
+	case ARCHIVE_FORMAT_WARC:
+		return archive_read_support_format_warc(a);
+		break;
 	case ARCHIVE_FORMAT_XAR:
 		return archive_read_support_format_xar(a);
 		break;
@@ -73,5 +86,7 @@
 		return archive_read_support_format_zip(a);
 		break;
 	}
+	archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+	    "Invalid format code specified");
 	return (ARCHIVE_FATAL);
 }
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
index 58644ba..57547d4 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_cab.c
@@ -1172,7 +1172,7 @@
 	    cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
 	if (cfdata->sum_calculated != cfdata->sum) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Checksum error CFDATA[%d] %x:%x in %d bytes",
+		    "Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes",
 		    cab->entry_cffolder->cfdata_index -1,
 		    cfdata->sum, cfdata->sum_calculated,
 		    cfdata->compressed_size);
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c
index c641eb9..53fb6cc 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_empty.c
@@ -47,7 +47,7 @@
 
 	r = __archive_read_register_format(a,
 	    NULL,
-	    NULL,
+	    "empty",
 	    archive_read_format_empty_bid,
 	    NULL,
 	    archive_read_format_empty_read_header,
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
index 332944a..93ba295 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_mtree.c
@@ -51,6 +51,7 @@
 
 #include "archive.h"
 #include "archive_entry.h"
+#include "archive_entry_private.h"
 #include "archive_private.h"
 #include "archive_rb.h"
 #include "archive_read_private.h"
@@ -135,6 +136,9 @@
 static int	read_header(struct archive_read *,
 		    struct archive_entry *);
 static int64_t	mtree_atol(char **, int base);
+#ifndef HAVE_STRNLEN
+static size_t	mtree_strnlen(const char *, size_t);
+#endif
 
 /*
  * There's no standard for TIME_T_MAX/TIME_T_MIN.  So we compute them
@@ -186,6 +190,24 @@
 #endif
 }
 
+#ifdef HAVE_STRNLEN
+#define mtree_strnlen(a,b) strnlen(a,b)
+#else
+static size_t
+mtree_strnlen(const char *p, size_t maxlen)
+{
+	size_t i;
+
+	for (i = 0; i <= maxlen; i++) {
+		if (p[i] == 0)
+			break;
+	}
+	if (i > maxlen)
+		return (-1);/* invalid */
+	return (i);
+}
+#endif
+
 static int
 archive_read_format_mtree_options(struct archive_read *a,
     const char *key, const char *val)
@@ -1482,6 +1504,84 @@
 #undef MAX_PACK_ARGS
 }
 
+static int
+parse_hex_nibble(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return 10 + c - 'a';
+#if 0
+	/* XXX: Is uppercase something we should support? */
+	if (c >= 'A' && c <= 'F')
+		return 10 + c - 'A';
+#endif
+
+	return -1;
+}
+
+static int
+parse_digest(struct archive_read *a, struct archive_entry *entry,
+    const char *digest, int type)
+{
+	unsigned char digest_buf[64];
+	int high, low;
+	size_t i, j, len;
+
+	switch (type) {
+	case ARCHIVE_ENTRY_DIGEST_MD5:
+		len = sizeof(entry->digest.md5);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_RMD160:
+		len = sizeof(entry->digest.rmd160);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA1:
+		len = sizeof(entry->digest.sha1);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA256:
+		len = sizeof(entry->digest.sha256);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA384:
+		len = sizeof(entry->digest.sha384);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA512:
+		len = sizeof(entry->digest.sha512);
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+			"Internal error: Unknown digest type");
+		return ARCHIVE_FATAL;
+	}
+
+	if (len > sizeof(digest_buf)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+			"Internal error: Digest storage too large");
+		return ARCHIVE_FATAL;
+	}
+
+	len *= 2;
+
+	if (mtree_strnlen(digest, len+1) != len) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				  "incorrect digest length, ignoring");
+		return ARCHIVE_WARN;
+	}
+
+	for (i = 0, j = 0; i < len; i += 2, j++) {
+		high = parse_hex_nibble(digest[i]);
+		low = parse_hex_nibble(digest[i+1]);
+		if (high == -1 || low == -1) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					  "invalid digest data, ignoring");
+			return ARCHIVE_WARN;
+		}
+
+		digest_buf[j] = high << 4 | low;
+	}
+
+	return archive_entry_set_digest(entry, type, digest_buf);
+}
+
 /*
  * Parse a single keyword and its value.
  */
@@ -1580,8 +1680,10 @@
 		}
 		__LA_FALLTHROUGH;
 	case 'm':
-		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)
-			break;
+		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_MD5);
+		}
 		if (strcmp(key, "mode") == 0) {
 			if (val[0] >= '0' && val[0] <= '7') {
 				*parsed_kws |= MTREE_HAS_PERM;
@@ -1617,21 +1719,32 @@
 			return r;
 		}
 		if (strcmp(key, "rmd160") == 0 ||
-		    strcmp(key, "rmd160digest") == 0)
-			break;
+		    strcmp(key, "rmd160digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_RMD160);
+		}
 		__LA_FALLTHROUGH;
 	case 's':
-		if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0)
-			break;
+		if (strcmp(key, "sha1") == 0 ||
+		    strcmp(key, "sha1digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA1);
+		}
 		if (strcmp(key, "sha256") == 0 ||
-		    strcmp(key, "sha256digest") == 0)
-			break;
+		    strcmp(key, "sha256digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA256);
+		}
 		if (strcmp(key, "sha384") == 0 ||
-		    strcmp(key, "sha384digest") == 0)
-			break;
+		    strcmp(key, "sha384digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA384);
+		}
 		if (strcmp(key, "sha512") == 0 ||
-		    strcmp(key, "sha512digest") == 0)
-			break;
+		    strcmp(key, "sha512digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA512);
+		}
 		if (strcmp(key, "size") == 0) {
 			archive_entry_set_size(entry, mtree_atol(&val, 10));
 			break;
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
index 61f2330..6dca350 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar.c
@@ -151,6 +151,9 @@
 #undef minimum
 #define minimum(a, b)	((a)<(b)?(a):(b))
 
+/* Stack overflow check */
+#define MAX_COMPRESS_DEPTH 1024
+
 /* Fields common to all headers */
 struct rar_header
 {
@@ -340,7 +343,7 @@
 static int read_data_stored(struct archive_read *, const void **, size_t *,
                             int64_t *);
 static int read_data_compressed(struct archive_read *, const void **, size_t *,
-                          int64_t *);
+                          int64_t *, size_t);
 static int rar_br_preparation(struct archive_read *, struct rar_br *);
 static int parse_codes(struct archive_read *);
 static void free_codes(struct archive_read *);
@@ -1026,7 +1029,7 @@
   case COMPRESS_METHOD_NORMAL:
   case COMPRESS_METHOD_GOOD:
   case COMPRESS_METHOD_BEST:
-    ret = read_data_compressed(a, buff, size, offset);
+    ret = read_data_compressed(a, buff, size, offset, 0);
     if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {
       __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
       rar->start_new_table = 1;
@@ -1883,8 +1886,11 @@
 
 static int
 read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
-               int64_t *offset)
+               int64_t *offset, size_t looper)
 {
+  if (looper++ > MAX_COMPRESS_DEPTH)
+    return (ARCHIVE_FATAL);
+
   struct rar *rar;
   int64_t start, end, actualend;
   size_t bs;
@@ -1982,7 +1988,7 @@
         {
           case 0:
             rar->start_new_table = 1;
-            return read_data_compressed(a, buff, size, offset);
+            return read_data_compressed(a, buff, size, offset, looper);
 
           case 2:
             rar->ppmd_eod = 1;/* End Of ppmd Data. */
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
index d3a1c1b..3131955 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_rar5.c
@@ -3084,12 +3084,6 @@
 
 			continue;
 		}
-
-		/* The program counter shouldn't reach here. */
-		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Unsupported block code: 0x%x", num);
-
-		return ARCHIVE_FATAL;
 	}
 
 	return ARCHIVE_OK;
@@ -3837,7 +3831,7 @@
 
 				DEBUG_CODE {
 					printf("Checksum error: CRC32 "
-					    "(was: %08x, expected: %08x)\n",
+					    "(was: %08" PRIx32 ", expected: %08" PRIx32 ")\n",
 					    rar->file.calculated_crc32,
 					    rar->file.stored_crc32);
 				}
@@ -3851,7 +3845,7 @@
 			} else {
 				DEBUG_CODE {
 					printf("Checksum OK: CRC32 "
-					    "(%08x/%08x)\n",
+					    "(%08" PRIx32 "/%08" PRIx32 ")\n",
 					    rar->file.stored_crc32,
 					    rar->file.calculated_crc32);
 				}
@@ -3912,6 +3906,9 @@
 	int ret;
 	struct rar5* rar = get_context(a);
 
+	if (size)
+		*size = 0;
+
 	if(rar->file.dir > 0) {
 		/* Don't process any data if this file entry was declared
 		 * as a directory. This is needed, because entries marked as
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c
index c63d46f..96d8101 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_tar.c
@@ -1797,6 +1797,16 @@
 }
 
 static int
+pax_attribute_rht_security_selinux(struct archive_entry *entry,
+	const char *value, size_t value_length)
+{
+	archive_entry_xattr_add_entry(entry, "security.selinux",
+            value, value_length);
+
+	return 0;
+}
+
+static int
 pax_attribute_acl(struct archive_read *a, struct tar *tar,
     struct archive_entry *entry, const char *value, int type)
 {
@@ -1966,6 +1976,14 @@
 		if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
 			pax_attribute_xattr(entry, key, value);
 		break;
+	case 'R':
+		/* GNU tar uses RHT.security header to store SELinux xattrs
+		 * SCHILY.xattr.security.selinux == RHT.security.selinux */
+		if (strcmp(key, "RHT.security.selinux") == 0) {
+			pax_attribute_rht_security_selinux(entry, value,
+			    value_length);
+			}
+		break;
 	case 'S':
 		/* We support some keys used by the "star" archiver */
 		if (strcmp(key, "SCHILY.acl.access") == 0) {
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c
index 72977b8..2732996 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_warc.c
@@ -127,7 +127,7 @@
 static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);
 
 /* private routines */
-static unsigned int _warc_rdver(const char buf[10], size_t bsz);
+static unsigned int _warc_rdver(const char *buf, size_t bsz);
 static unsigned int _warc_rdtyp(const char *buf, size_t bsz);
 static warc_string_t _warc_rduri(const char *buf, size_t bsz);
 static ssize_t _warc_rdlen(const char *buf, size_t bsz);
@@ -337,6 +337,14 @@
 			mtime = rtime;
 		}
 		break;
+	case WT_NONE:
+	case WT_INFO:
+	case WT_META:
+	case WT_REQ:
+	case WT_RVIS:
+	case WT_CONV:
+	case WT_CONT:
+	case LAST_WT:
 	default:
 		fnam.len = 0U;
 		fnam.str = NULL;
@@ -361,6 +369,14 @@
 			break;
 		}
 		/* FALLTHROUGH */
+	case WT_NONE:
+	case WT_INFO:
+	case WT_META:
+	case WT_REQ:
+	case WT_RVIS:
+	case WT_CONV:
+	case WT_CONT:
+	case LAST_WT:
 	default:
 		/* consume the content and start over */
 		_warc_skip(a);
@@ -427,7 +443,7 @@
 static void*
 deconst(const void *c)
 {
-	return (char *)0x1 + (((const char *)c) - (const char *)0x1);
+	return (void *)(uintptr_t)c;
 }
 
 static char*
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
index 9489e51..2e60cf7 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_xar.c
@@ -36,7 +36,7 @@
 #elif HAVE_BSDXML_H
 #include <bsdxml.h>
 #elif HAVE_EXPAT_H
-#include <expat.h>
+#include <cm3p/expat.h>
 #endif
 #ifdef HAVE_BZLIB_H
 #include <cm3p/bzlib.h>
@@ -458,6 +458,11 @@
 		return (ARCHIVE_FATAL);
 	}
 
+	/* initialize xar->file_queue */
+	xar->file_queue.allocated = 0;
+	xar->file_queue.used = 0;
+	xar->file_queue.files = NULL;
+
 	r = __archive_read_register_format(a,
 	    xar,
 	    "xar",
@@ -1221,10 +1226,12 @@
 	/* Expand our pending files list as necessary. */
 	if (heap->used >= heap->allocated) {
 		struct xar_file **new_pending_files;
-		int new_size = heap->allocated * 2;
+		int new_size;
 
 		if (heap->allocated < 1024)
 			new_size = 1024;
+		else
+			new_size = heap->allocated * 2;
 		/* Overflow might keep us from growing the list. */
 		if (new_size <= heap->allocated) {
 			archive_set_error(&a->archive,
@@ -1238,9 +1245,11 @@
 			    ENOMEM, "Out of memory");
 			return (ARCHIVE_FATAL);
 		}
-		memcpy(new_pending_files, heap->files,
-		    heap->allocated * sizeof(new_pending_files[0]));
-		free(heap->files);
+		if (heap->allocated) {
+			memcpy(new_pending_files, heap->files,
+			    heap->allocated * sizeof(new_pending_files[0]));
+			free(heap->files);
+		}
 		heap->files = new_pending_files;
 		heap->allocated = new_size;
 	}
diff --git a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
index 4d71f98..6314c68 100644
--- a/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_read_support_format_zip.c
@@ -53,10 +53,10 @@
 #include <cm3p/zlib.h>
 #endif
 #ifdef HAVE_BZLIB_H
-#include <bzlib.h>
+#include <cm3p/bzlib.h>
 #endif
 #ifdef HAVE_LZMA_H
-#include <lzma.h>
+#include <cm3p/lzma.h>
 #endif
 
 #include "archive.h"
@@ -899,6 +899,81 @@
 	return ARCHIVE_OK;
 }
 
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+/*
+ * Auxiliary function to uncompress data chunk from zipx archive
+ * (zip with lzma compression).
+ */
+static int
+zipx_lzma_uncompress_buffer(const char *compressed_buffer,
+	size_t compressed_buffer_size,
+	char *uncompressed_buffer,
+	size_t uncompressed_buffer_size)
+{
+	int status = ARCHIVE_FATAL;
+	// length of 'lzma properties data' in lzma compressed
+	// data segment (stream) inside zip archive
+	const size_t lzma_params_length = 5;
+	// offset of 'lzma properties data' from the beginning of lzma stream
+	const size_t lzma_params_offset = 4;
+	// end position of 'lzma properties data' in lzma stream
+	const size_t lzma_params_end = lzma_params_offset + lzma_params_length;
+	if (compressed_buffer == NULL ||
+			compressed_buffer_size < lzma_params_end ||
+			uncompressed_buffer == NULL)
+		return status;
+
+	// prepare header for lzma_alone_decoder to replace zipx header
+	// (see comments in 'zipx_lzma_alone_init' for justification)
+#pragma pack(push)
+#pragma pack(1)
+	struct _alone_header
+	{
+		uint8_t bytes[5]; // lzma_params_length
+		uint64_t uncompressed_size;
+	} alone_header;
+#pragma pack(pop)
+	// copy 'lzma properties data' blob
+	memcpy(&alone_header.bytes[0], compressed_buffer + lzma_params_offset,
+		lzma_params_length);
+	alone_header.uncompressed_size = UINT64_MAX;
+
+	// prepare new compressed buffer, see 'zipx_lzma_alone_init' for details
+	const size_t lzma_alone_buffer_size =
+		compressed_buffer_size - lzma_params_end + sizeof(alone_header);
+	unsigned char *lzma_alone_compressed_buffer =
+		(unsigned char*) malloc(lzma_alone_buffer_size);
+	if (lzma_alone_compressed_buffer == NULL)
+		return status;
+	// copy lzma_alone header into new buffer
+	memcpy(lzma_alone_compressed_buffer, (void*) &alone_header,
+		sizeof(alone_header));
+	// copy compressed data into new buffer
+	memcpy(lzma_alone_compressed_buffer + sizeof(alone_header),
+		compressed_buffer + lzma_params_end,
+		compressed_buffer_size - lzma_params_end);
+
+	// create and fill in lzma_alone_decoder stream
+	lzma_stream stream = LZMA_STREAM_INIT;
+	lzma_ret ret = lzma_alone_decoder(&stream, UINT64_MAX);
+	if (ret == LZMA_OK)
+	{
+		stream.next_in = lzma_alone_compressed_buffer;
+		stream.avail_in = lzma_alone_buffer_size;
+		stream.total_in = 0;
+		stream.next_out = (unsigned char*)uncompressed_buffer;
+		stream.avail_out = uncompressed_buffer_size;
+		stream.total_out = 0;
+		ret = lzma_code(&stream, LZMA_RUN);
+		if (ret == LZMA_OK || ret == LZMA_STREAM_END)
+			status = ARCHIVE_OK;
+	}
+	lzma_end(&stream);
+	free(lzma_alone_compressed_buffer);
+	return status;
+}
+#endif
+
 /*
  * Assumes file pointer is at beginning of local file header.
  */
@@ -1173,18 +1248,64 @@
 			    "Truncated Zip file");
 			return ARCHIVE_FATAL;
 		}
+		// take into account link compression if any
+		size_t linkname_full_length = linkname_length;
+		if (zip->entry->compression != 0)
+		{
+			// symlink target string appeared to be compressed
+			int status = ARCHIVE_FATAL;
+			char *uncompressed_buffer =
+				(char*) malloc(zip_entry->uncompressed_size);
+			if (uncompressed_buffer == NULL)
+			{
+				archive_set_error(&a->archive, ENOMEM,
+					"No memory for lzma decompression");
+				return status;
+			}
+
+			switch (zip->entry->compression)
+			{
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+				case 14: /* ZIPx LZMA compression. */
+					/*(see zip file format specification, section 4.4.5)*/
+					status = zipx_lzma_uncompress_buffer(p,
+						linkname_length,
+						uncompressed_buffer,
+						(size_t)zip_entry->uncompressed_size);
+					break;
+#endif
+				default: /* Unsupported compression. */
+					break;
+			}
+			if (status == ARCHIVE_OK)
+			{
+				p = uncompressed_buffer;
+				linkname_full_length =
+					(size_t)zip_entry->uncompressed_size;
+			}
+			else
+			{
+				archive_set_error(&a->archive,
+					ARCHIVE_ERRNO_FILE_FORMAT,
+					"Unsupported ZIP compression method "
+					"during decompression of link entry (%d: %s)",
+					zip->entry->compression,
+					compression_name(zip->entry->compression));
+				return ARCHIVE_FAILED;
+			}
+		}
 
 		sconv = zip->sconv;
 		if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME))
 			sconv = zip->sconv_utf8;
 		if (sconv == NULL)
 			sconv = zip->sconv_default;
-		if (archive_entry_copy_symlink_l(entry, p, linkname_length,
+		if (archive_entry_copy_symlink_l(entry, p, linkname_full_length,
 		    sconv) != 0) {
 			if (errno != ENOMEM && sconv == zip->sconv_utf8 &&
 			    (zip->entry->zip_flags & ZIP_UTF8_NAME))
 			    archive_entry_copy_symlink_l(entry, p,
-				linkname_length, NULL);
+				linkname_full_length, NULL);
 			if (errno == ENOMEM) {
 				archive_set_error(&a->archive, ENOMEM,
 				    "Can't allocate memory for Symlink");
@@ -1901,15 +2022,15 @@
 
 	if(order < 2 || restore_method > 2) {
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-		    "Invalid parameter set in PPMd8 stream (order=%d, "
-		    "restore=%d)", order, restore_method);
+		    "Invalid parameter set in PPMd8 stream (order=%" PRId32 ", "
+		    "restore=%" PRId32 ")", order, restore_method);
 		return (ARCHIVE_FAILED);
 	}
 
 	/* Allocate the memory needed to properly decompress the file. */
 	if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) {
 		archive_set_error(&a->archive, ENOMEM,
-		    "Unable to allocate memory for PPMd8 stream: %d bytes",
+		    "Unable to allocate memory for PPMd8 stream: %" PRId32 " bytes",
 		    mem << 20);
 		return (ARCHIVE_FATAL);
 	}
diff --git a/Utilities/cmlibarchive/libarchive/archive_string.c b/Utilities/cmlibarchive/libarchive/archive_string.c
index c77dcf5..7460ded 100644
--- a/Utilities/cmlibarchive/libarchive/archive_string.c
+++ b/Utilities/cmlibarchive/libarchive/archive_string.c
@@ -3881,6 +3881,11 @@
 	}
 
 	*p = NULL;
+	/* Try converting WCS to MBS first if MBS does not exist yet. */
+	if ((aes->aes_set & AES_SET_MBS) == 0) {
+		const char *pm; /* unused */
+		archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+	}
 	if (aes->aes_set & AES_SET_MBS) {
 		sc = archive_string_conversion_to_charset(a, "UTF-8", 1);
 		if (sc == NULL)
@@ -3903,9 +3908,9 @@
 archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,
     const char **p)
 {
+	struct archive_string_conv *sc;
 	int r, ret = 0;
 
-	(void)a; /* UNUSED */
 	/* If we already have an MBS form, return that immediately. */
 	if (aes->aes_set & AES_SET_MBS) {
 		*p = aes->aes_mbs.s;
@@ -3926,10 +3931,23 @@
 			ret = -1;
 	}
 
-	/*
-	 * Only a UTF-8 form cannot avail because its conversion already
-	 * failed at archive_mstring_update_utf8().
-	 */
+	/* If there's a UTF-8 form, try converting with the native locale. */
+	if (aes->aes_set & AES_SET_UTF8) {
+		archive_string_empty(&(aes->aes_mbs));
+		sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+		if (sc == NULL)
+			return (-1);/* Couldn't allocate memory for sc. */
+		r = archive_strncpy_l(&(aes->aes_mbs),
+			aes->aes_utf8.s, aes->aes_utf8.length, sc);
+		if (a == NULL)
+			free_sconv_object(sc);
+		*p = aes->aes_mbs.s;
+		if (r == 0) {
+			aes->aes_set |= AES_SET_MBS;
+			ret = 0;/* success; overwrite previous error. */
+		} else
+			ret = -1;/* failure. */
+	}
 	return (ret);
 }
 
@@ -3947,6 +3965,11 @@
 	}
 
 	*wp = NULL;
+	/* Try converting UTF8 to MBS first if MBS does not exist yet. */
+	if ((aes->aes_set & AES_SET_MBS) == 0) {
+		const char *p; /* unused */
+		archive_mstring_get_mbs(a, aes, &p); /* ignore errors, we'll handle it later */
+	}
 	/* Try converting MBS to WCS using native locale. */
 	if (aes->aes_set & AES_SET_MBS) {
 		archive_wstring_empty(&(aes->aes_wcs));
@@ -3962,11 +3985,12 @@
 }
 
 int
-archive_mstring_get_mbs_l(struct archive_mstring *aes,
+archive_mstring_get_mbs_l(struct archive *a, struct archive_mstring *aes,
     const char **p, size_t *length, struct archive_string_conv *sc)
 {
 	int r, ret = 0;
 
+	(void)r; /* UNUSED */
 #if defined(_WIN32) && !defined(__CYGWIN__)
 	/*
 	 * Internationalization programming on Windows must use Wide
@@ -3989,20 +4013,12 @@
 	}
 #endif
 
-	/* If there is not an MBS form but is a WCS form, try converting
+	/* If there is not an MBS form but there is a WCS or UTF8 form, try converting
 	 * with the native locale to be used for translating it to specified
 	 * character-set. */
-	if ((aes->aes_set & AES_SET_MBS) == 0 &&
-	    (aes->aes_set & AES_SET_WCS) != 0) {
-		archive_string_empty(&(aes->aes_mbs));
-		r = archive_string_append_from_wcs(&(aes->aes_mbs),
-		    aes->aes_wcs.s, aes->aes_wcs.length);
-		if (r == 0)
-			aes->aes_set |= AES_SET_MBS;
-		else if (errno == ENOMEM)
-			return (-1);
-		else
-			ret = -1;
+	if ((aes->aes_set & AES_SET_MBS) == 0) {
+		const char *pm; /* unused */
+		archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
 	}
 	/* If we already have an MBS form, use it to be translated to
 	 * specified character-set. */
diff --git a/Utilities/cmlibarchive/libarchive/archive_string.h b/Utilities/cmlibarchive/libarchive/archive_string.h
index 27e1ad6..49d7d30 100644
--- a/Utilities/cmlibarchive/libarchive/archive_string.h
+++ b/Utilities/cmlibarchive/libarchive/archive_string.h
@@ -226,7 +226,7 @@
 int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **);
 int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **);
 int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **);
-int	archive_mstring_get_mbs_l(struct archive_mstring *, const char **,
+int	archive_mstring_get_mbs_l(struct archive *, struct archive_mstring *, const char **,
 	    size_t *, struct archive_string_conv *);
 int	archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);
 int	archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs,
diff --git a/Utilities/cmlibarchive/libarchive/archive_util.c b/Utilities/cmlibarchive/libarchive/archive_util.c
index 10dca73..83586b5 100644
--- a/Utilities/cmlibarchive/libarchive/archive_util.c
+++ b/Utilities/cmlibarchive/libarchive/archive_util.c
@@ -365,6 +365,7 @@
 		}
 		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
 		if (fd == -1) {
+			la_dosmaperr(GetLastError());
 			CloseHandle(h);
 			goto exit_tmpfile;
 		} else
@@ -432,6 +433,11 @@
 		if (temp_name.s[temp_name.length-1] != '/')
 			archive_strappend_char(&temp_name, '/');
 	}
+#ifdef O_TMPFILE
+	fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600); 
+	if(fd >= 0)
+		goto exit_tmpfile;
+#endif
 	archive_strcat(&temp_name, "libarchive_XXXXXX");
 	fd = mkstemp(temp_name.s);
 	if (fd < 0)
diff --git a/Utilities/cmlibarchive/libarchive/archive_write.c b/Utilities/cmlibarchive/libarchive/archive_write.c
index 98a55fb..8d70f51 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write.c
@@ -456,6 +456,25 @@
 }
 
 static int
+archive_write_client_free(struct archive_write_filter *f)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+
+	if (a->client_freer)
+		(*a->client_freer)(&a->archive, a->client_data);
+	a->client_data = NULL;
+
+	/* Clear passphrase. */
+	if (a->passphrase != NULL) {
+		memset(a->passphrase, 0, strlen(a->passphrase));
+		free(a->passphrase);
+		a->passphrase = NULL;
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
 archive_write_client_close(struct archive_write_filter *f)
 {
 	struct archive_write *a = (struct archive_write *)f->archive;
@@ -493,13 +512,7 @@
 		(*a->client_closer)(&a->archive, a->client_data);
 	free(state->buffer);
 	free(state);
-	a->client_data = NULL;
-	/* Clear passphrase. */
-	if (a->passphrase != NULL) {
-		memset(a->passphrase, 0, strlen(a->passphrase));
-		free(a->passphrase);
-		a->passphrase = NULL;
-	}
+
 	/* Clear the close handler myself not to be called again. */
 	f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
 	return (ret);
@@ -509,9 +522,9 @@
  * Open the archive using the current settings.
  */
 int
-archive_write_open(struct archive *_a, void *client_data,
+archive_write_open2(struct archive *_a, void *client_data,
     archive_open_callback *opener, archive_write_callback *writer,
-    archive_close_callback *closer)
+    archive_close_callback *closer, archive_free_callback *freer)
 {
 	struct archive_write *a = (struct archive_write *)_a;
 	struct archive_write_filter *client_filter;
@@ -524,12 +537,14 @@
 	a->client_writer = writer;
 	a->client_opener = opener;
 	a->client_closer = closer;
+	a->client_freer = freer;
 	a->client_data = client_data;
 
 	client_filter = __archive_write_allocate_filter(_a);
 	client_filter->open = archive_write_client_open;
 	client_filter->write = archive_write_client_write;
 	client_filter->close = archive_write_client_close;
+	client_filter->free = archive_write_client_free;
 
 	ret = __archive_write_filters_open(a);
 	if (ret < ARCHIVE_WARN) {
@@ -544,6 +559,15 @@
 	return (ret);
 }
 
+int
+archive_write_open(struct archive *_a, void *client_data,
+    archive_open_callback *opener, archive_write_callback *writer,
+    archive_close_callback *closer)
+{
+	return archive_write_open2(_a, client_data, opener, writer,
+	    closer, NULL);
+}
+
 /*
  * Close out the archive.
  */
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c
index a4bc1d9..c096e72 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_program.c
@@ -196,10 +196,6 @@
 {
 
 	if (data) {
-#if defined(_WIN32) && !defined(__CYGWIN__)
-		if (data->child)
-			CloseHandle(data->child);
-#endif
 		free(data->program_name);
 		free(data->child_buf);
 		free(data);
@@ -211,7 +207,7 @@
 __archive_write_program_open(struct archive_write_filter *f,
     struct archive_write_program_data *data, const char *cmd)
 {
-	pid_t child;
+	int ret;
 
 	if (data->child_buf == NULL) {
 		data->child_buf_len = 65536;
@@ -225,27 +221,13 @@
 		}
 	}
 
-	child = __archive_create_child(cmd, &data->child_stdin,
-		    &data->child_stdout);
-	if (child == -1) {
+	ret = __archive_create_child(cmd, &data->child_stdin,
+		    &data->child_stdout, &data->child);
+	if (ret != ARCHIVE_OK) {
 		archive_set_error(f->archive, EINVAL,
 		    "Can't launch external program: %s", cmd);
 		return (ARCHIVE_FATAL);
 	}
-#if defined(_WIN32) && !defined(__CYGWIN__)
-	data->child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, child);
-	if (data->child == NULL) {
-		close(data->child_stdin);
-		data->child_stdin = -1;
-		close(data->child_stdout);
-		data->child_stdout = -1;
-		archive_set_error(f->archive, EINVAL,
-		    "Can't launch external program: %s", cmd);
-		return (ARCHIVE_FATAL);
-	}
-#else
-	data->child = child;
-#endif
 	return (ARCHIVE_OK);
 }
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c
index d567ac9..00df8da 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_xz.c
@@ -382,8 +382,12 @@
 		    value[1] != '\0')
 			return (ARCHIVE_WARN);
 		data->compression_level = value[0] - '0';
+		if (data->compression_level > 9)
+			data->compression_level = 9;
+#ifdef _AIX
 		if (data->compression_level > 6)
 			data->compression_level = 6;
+#endif
 		return (ARCHIVE_OK);
 	} else if (strcmp(key, "threads") == 0) {
 		char *endptr;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
index f67b025..6d71628 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_add_filter_zstd.c
@@ -59,6 +59,16 @@
 #endif
 };
 
+/* If we don't have the library use default range values (zstdcli.c v1.4.0) */
+#define CLEVEL_MIN -99
+#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */
+#define CLEVEL_DEFAULT 3
+#define CLEVEL_STD_MAX 19 /* without using --ultra */
+#define CLEVEL_MAX 22
+
+#define MINVER_NEGCLEVEL 10304
+#define MINVER_MINCLEVEL 10306
+
 static int archive_compressor_zstd_options(struct archive_write_filter *,
 		    const char *, const char *);
 static int archive_compressor_zstd_open(struct archive_write_filter *);
@@ -96,7 +106,7 @@
 	f->free = &archive_compressor_zstd_free;
 	f->code = ARCHIVE_FILTER_ZSTD;
 	f->name = "zstd";
-	data->compression_level = 3; /* Default level used by the zstd CLI */
+	data->compression_level = CLEVEL_DEFAULT;
 #if HAVE_ZSTD_H && HAVE_LIBZSTD
 	data->cstream = ZSTD_createCStream();
 	if (data->cstream == NULL) {
@@ -135,6 +145,31 @@
 	return (ARCHIVE_OK);
 }
 
+static int string_is_numeric (const char* value)
+{
+       size_t len = strlen(value);
+       size_t i;
+
+       if (len == 0) {
+               return (ARCHIVE_WARN);
+       }
+       else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) {
+               return (ARCHIVE_WARN);
+       }
+       else if (!(value[0] >= '0' && value[0] <= '9') &&
+                value[0] != '-' && value[0] != '+') {
+               return (ARCHIVE_WARN);
+       }
+
+       for (i = 1; i < len; i++) {
+               if (!(value[i] >= '0' && value[i] <= '9')) {
+                       return (ARCHIVE_WARN);
+               }
+       }
+
+       return (ARCHIVE_OK);
+}
+
 /*
  * Set write options.
  */
@@ -146,12 +181,25 @@
 
 	if (strcmp(key, "compression-level") == 0) {
 		int level = atoi(value);
-#if HAVE_ZSTD_H && HAVE_LIBZSTD
-		if (level < 1 || level > ZSTD_maxCLevel()) {
-#else
 		/* If we don't have the library, hard-code the max level */
-		if (level < 1 || level > 22) {
+		int minimum = CLEVEL_MIN;
+		int maximum = CLEVEL_MAX;
+		if (string_is_numeric(value) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+		maximum = ZSTD_maxCLevel();
+#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
+		if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) {
+			minimum = ZSTD_minCLevel();
+		}
+		else
 #endif
+		if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) {
+			minimum = CLEVEL_STD_MIN;
+		}
+#endif
+		if (level < minimum || level > maximum) {
 			return (ARCHIVE_WARN);
 		}
 		data->compression_level = level;
@@ -297,7 +345,26 @@
 	int r;
 
 	archive_string_init(&as);
-	archive_string_sprintf(&as, "zstd -%d", data->compression_level);
+	/* --no-check matches library default */
+	archive_strcpy(&as, "zstd --no-check");
+
+	if (data->compression_level < CLEVEL_STD_MIN) {
+		struct archive_string as2;
+		archive_string_init(&as2);
+		archive_string_sprintf(&as2, " --fast=%d", -data->compression_level);
+		archive_string_concat(&as, &as2);
+		archive_string_free(&as2);
+	} else {
+		struct archive_string as2;
+		archive_string_init(&as2);
+		archive_string_sprintf(&as2, " -%d", data->compression_level);
+		archive_string_concat(&as, &as2);
+		archive_string_free(&as2);
+	}
+
+	if (data->compression_level > CLEVEL_STD_MAX) {
+		archive_strcat(&as, " --ultra");
+	}
 
 	f->write = archive_compressor_zstd_write;
 	r = __archive_write_program_open(f, data->pdata, as.s);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
index c4be9b0..2551ebe 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_posix.c
@@ -546,6 +546,7 @@
 {
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
 	struct fixup_entry *fe;
+	const char *linkname;
 	int ret, r;
 
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
@@ -591,6 +592,17 @@
 		return (ret);
 
 	/*
+	 * Check if we have a hardlink that points to itself.
+	 */
+	linkname = archive_entry_hardlink(a->entry);
+	if (linkname != NULL && strcmp(a->name, linkname) == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Skipping hardlink pointing to itself: %s",
+		    a->name);
+		return (ARCHIVE_WARN);
+	}
+
+	/*
 	 * Query the umask so we get predictable mode settings.
 	 * This gets done on every call to _write_header in case the
 	 * user edits their umask during the extraction for some
@@ -1856,8 +1868,9 @@
 		if (a->tmpname) {
 			if (rename(a->tmpname, a->name) == -1) {
 				archive_set_error(&a->archive, errno,
-				    "rename failed");
-				ret = ARCHIVE_FATAL;
+				    "Failed to rename temporary file");
+				ret = ARCHIVE_FAILED;
+				unlink(a->tmpname);
 			}
 			a->tmpname = NULL;
 		}
@@ -2144,8 +2157,11 @@
 			if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
 			    S_ISREG(a->st.st_mode)) {
 				/* Use a temporary file to extract */
-				if ((a->fd = la_mktemp(a)) == -1)
+				if ((a->fd = la_mktemp(a)) == -1) {
+					archive_set_error(&a->archive, errno,
+					    "Can't create temporary file");
 					return ARCHIVE_FAILED;
+				}
 				a->pst = NULL;
 				en = 0;
 			} else {
@@ -4407,10 +4423,19 @@
 			int e;
 			int namespace;
 
+			namespace = EXTATTR_NAMESPACE_USER;
+
 			if (strncmp(name, "user.", 5) == 0) {
 				/* "user." attributes go to user namespace */
 				name += 5;
 				namespace = EXTATTR_NAMESPACE_USER;
+			} else if (strncmp(name, "system.", 7) == 0) {
+				name += 7;
+				namespace = EXTATTR_NAMESPACE_SYSTEM;
+				if (!strcmp(name, "nfs4.acl") ||
+				    !strcmp(name, "posix1e.acl_access") ||
+				    !strcmp(name, "posix1e.acl_default"))
+					continue;
 			} else {
 				/* Other namespaces are unsupported */
 				archive_strcat(&errlist, name);
@@ -4421,8 +4446,29 @@
 			}
 
 			if (a->fd >= 0) {
+				/*
+				 * On FreeBSD, extattr_set_fd does not
+				 * return the same as
+				 * extattr_set_file. It returns zero
+				 * on success, non-zero on failure.
+				 *
+				 * We can detect the failure by
+				 * manually setting errno prior to the
+				 * call and checking after.
+				 *
+				 * If errno remains zero, fake the
+				 * return value by setting e to size.
+				 *
+				 * This is a hack for now until I
+				 * (Shawn Webb) get FreeBSD to fix the
+				 * issue, if that's even possible.
+				 */
+				errno = 0;
 				e = extattr_set_fd(a->fd, namespace, name,
 				    value, size);
+				if (e == 0 && errno == 0) {
+					e = size;
+				}
 			} else {
 				e = extattr_set_link(
 				    archive_entry_pathname(entry), namespace,
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c
index 77e36c4..0c60017 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_disk_windows.c
@@ -549,6 +549,8 @@
 	a->tmpname = a->_tmpname_data.s;
 
 	fd = __archive_mkstemp(a->tmpname);
+	if (fd == -1)
+		return -1;
 
 	mode = a->mode & 0777 & ~a->user_umask;
 	if (la_chmod(a->tmpname, mode) == -1) {
@@ -1281,9 +1283,11 @@
 			/* Windows does not support atomic rename */
 			disk_unlink(a->name);
 			if (_wrename(a->tmpname, a->name) != 0) {
+				la_dosmaperr(GetLastError());
 				archive_set_error(&a->archive, errno,
-				    "rename failed");
-				ret = ARCHIVE_FATAL;
+				    "Failed to rename temporary file");
+				ret = ARCHIVE_FAILED;
+				disk_unlink(a->tmpname);
 			}
 			a->tmpname = NULL;
 		}
@@ -1573,12 +1577,17 @@
 				S_ISREG(st_mode)) {
 				int fd = la_mktemp(a);
 
-				if (fd == -1)
+				if (fd == -1) {
+					la_dosmaperr(GetLastError());
+					archive_set_error(&a->archive, errno,
+					    "Can't create temporary file");
 					return (ARCHIVE_FAILED);
+				}
 				a->fh = (HANDLE)_get_osfhandle(fd);
-				if (a->fh == INVALID_HANDLE_VALUE)
+				if (a->fh == INVALID_HANDLE_VALUE) {
+					la_dosmaperr(GetLastError());
 					return (ARCHIVE_FAILED);
-
+				}
 				a->pst = NULL;
 				en = 0;
 			} else {
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open.3 b/Utilities/cmlibarchive/libarchive/archive_write_open.3
index 0129d10..29bffe4 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_open.3
+++ b/Utilities/cmlibarchive/libarchive/archive_write_open.3
@@ -24,11 +24,12 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd February 2, 2012
+.Dd November 12, 2020
 .Dt ARCHIVE_WRITE_OPEN 3
 .Os
 .Sh NAME
 .Nm archive_write_open ,
+.Nm archive_write_open2 ,
 .Nm archive_write_open_fd ,
 .Nm archive_write_open_FILE ,
 .Nm archive_write_open_filename ,
@@ -47,6 +48,15 @@
 .Fa "archive_close_callback *"
 .Fc
 .Ft int
+.Fo archive_write_open2
+.Fa "struct archive *"
+.Fa "void *client_data"
+.Fa "archive_open_callback *"
+.Fa "archive_write_callback *"
+.Fa "archive_close_callback *"
+.Fa "archive_free_callback *"
+.Fc
+.Ft int
 .Fn archive_write_open_fd "struct archive *" "int fd"
 .Ft int
 .Fn archive_write_open_FILE "struct archive *" "FILE *file"
@@ -67,6 +77,11 @@
 pointers to three callback functions which will be invoked by
 the compression layer to write the constructed archive.
 This does not alter the default archive padding.
+.It Fn archive_write_open2
+Same as
+.Fn archive_write_open
+with an additional fourth free callback. This function should be preferred to
+.Fn archive_write_open .
 .It Fn archive_write_open_fd
 A convenience form of
 .Fn archive_write_open
@@ -106,14 +121,14 @@
 You can override this by manually invoking
 .Fn archive_write_set_bytes_in_last_block
 before calling
-.Fn archive_write_open .
+.Fn archive_write_open2 .
 The
 .Fn archive_write_open_filename
 function is safe for use with tape drives or other
 block-oriented devices.
 .It Fn archive_write_open_memory
 A convenience form of
-.Fn archive_write_open
+.Fn archive_write_open2
 that accepts a pointer to a block of memory that will receive
 the archive.
 The final
@@ -145,7 +160,7 @@
 callback functions that will be invoked to write data to the
 resulting archive.
 These functions are registered by calling
-.Fn archive_write_open :
+.Fn archive_write_open2 :
 .Bl -item -offset indent
 .It
 .Ft typedef int
@@ -162,6 +177,8 @@
 .Fn archive_set_error
 to register an error code and message and return
 .Cm ARCHIVE_FATAL .
+Please note that if open fails, close is not called and resources must be
+freed inside the open callback or with the free callback.
 .Bl -item -offset indent
 .It
 .Ft typedef la_ssize_t
@@ -192,7 +209,8 @@
 .El
 .Pp
 The close callback is invoked by archive_close when
-the archive processing is complete.
+the archive processing is complete. If the open callback fails, the close
+callback is not invoked.
 The callback should return
 .Cm ARCHIVE_OK
 on success.
@@ -200,7 +218,14 @@
 .Fn archive_set_error
 to register an error code and message and
 return
-.Cm ARCHIVE_FATAL .
+.Bl -item -offset indent
+.It
+.Ft typedef int
+.Fn archive_free_callback "struct archive *" "void *client_data"
+.El
+.Pp
+The free callback is always invoked on archive_free.
+The return code of this callback is not processed.
 .Pp
 Note that if the client-provided write callback function
 returns a non-zero value, that error will be propagated back to the caller
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c b/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c
index d5c426c..b8d491f 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_open_fd.c
@@ -54,7 +54,7 @@
 	int		fd;
 };
 
-static int	file_close(struct archive *, void *);
+static int	file_free(struct archive *, void *);
 static int	file_open(struct archive *, void *);
 static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
 
@@ -72,8 +72,8 @@
 #if defined(__CYGWIN__) || defined(_WIN32)
 	setmode(mine->fd, O_BINARY);
 #endif
-	return (archive_write_open(a, mine,
-		    file_open, file_write, file_close));
+	return (archive_write_open2(a, mine,
+		    file_open, file_write, NULL, file_free));
 }
 
 static int
@@ -134,11 +134,13 @@
 }
 
 static int
-file_close(struct archive *a, void *client_data)
+file_free(struct archive *a, void *client_data)
 {
 	struct write_fd_data	*mine = (struct write_fd_data *)client_data;
 
 	(void)a; /* UNUSED */
+	if (mine == NULL)
+		return (ARCHIVE_OK);
 	free(mine);
 	return (ARCHIVE_OK);
 }
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_file.c b/Utilities/cmlibarchive/libarchive/archive_write_open_file.c
index f6b1412..bf5b55a 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_open_file.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_open_file.c
@@ -51,7 +51,7 @@
 	FILE		*f;
 };
 
-static int	file_close(struct archive *, void *);
+static int	file_free(struct archive *, void *);
 static int	file_open(struct archive *, void *);
 static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
 
@@ -66,8 +66,8 @@
 		return (ARCHIVE_FATAL);
 	}
 	mine->f = f;
-	return (archive_write_open(a, mine,
-		    file_open, file_write, file_close));
+	return (archive_write_open2(a, mine, file_open, file_write,
+	    NULL, file_free));
 }
 
 static int
@@ -99,11 +99,13 @@
 }
 
 static int
-file_close(struct archive *a, void *client_data)
+file_free(struct archive *a, void *client_data)
 {
 	struct write_FILE_data	*mine = client_data;
 
 	(void)a; /* UNUSED */
+	if (mine == NULL)
+		return (ARCHIVE_OK);
 	free(mine);
 	return (ARCHIVE_OK);
 }
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c b/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c
index 66e0dfe..9ceefb1 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_open_filename.c
@@ -62,6 +62,7 @@
 };
 
 static int	file_close(struct archive *, void *);
+static int	file_free(struct archive *, void *);
 static int	file_open(struct archive *, void *);
 static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
 static int	open_filename(struct archive *, int, const void *);
@@ -123,8 +124,8 @@
 		return (ARCHIVE_FAILED);
 	}
 	mine->fd = -1;
-	return (archive_write_open(a, mine,
-		file_open, file_write, file_close));
+	return (archive_write_open2(a, mine,
+		    file_open, file_write, file_close, file_free));
 }
 
 static int
@@ -244,9 +245,25 @@
 
 	(void)a; /* UNUSED */
 
+	if (mine == NULL)
+		return (ARCHIVE_FATAL);
+
 	if (mine->fd >= 0)
 		close(mine->fd);
 
+	return (ARCHIVE_OK);
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+	struct write_file_data	*mine = (struct write_file_data *)client_data;
+
+	(void)a; /* UNUSED */
+
+	if (mine == NULL)
+		return (ARCHIVE_OK);
+
 	archive_mstring_clean(&mine->filename);
 	free(mine);
 	return (ARCHIVE_OK);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c b/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c
index ea6ae0a..a8a0b81 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_open_memory.c
@@ -39,7 +39,7 @@
 	unsigned char * buff;
 };
 
-static int	memory_write_close(struct archive *, void *);
+static int	memory_write_free(struct archive *, void *);
 static int	memory_write_open(struct archive *, void *);
 static ssize_t	memory_write(struct archive *, void *, const void *buff, size_t);
 
@@ -61,8 +61,8 @@
 	mine->buff = buff;
 	mine->size = buffSize;
 	mine->client_size = used;
-	return (archive_write_open(a, mine,
-		    memory_write_open, memory_write, memory_write_close));
+	return (archive_write_open2(a, mine,
+		    memory_write_open, memory_write, NULL, memory_write_free));
 }
 
 static int
@@ -103,11 +103,13 @@
 }
 
 static int
-memory_write_close(struct archive *a, void *client_data)
+memory_write_free(struct archive *a, void *client_data)
 {
 	struct write_memory_data *mine;
 	(void)a; /* UNUSED */
 	mine = client_data;
+	if (mine == NULL)
+		return (ARCHIVE_OK);
 	free(mine);
 	return (ARCHIVE_OK);
 }
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_private.h b/Utilities/cmlibarchive/libarchive/archive_write_private.h
index 27cba03..155fdd7 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_private.h
+++ b/Utilities/cmlibarchive/libarchive/archive_write_private.h
@@ -89,6 +89,7 @@
 	archive_open_callback	*client_opener;
 	archive_write_callback	*client_writer;
 	archive_close_callback	*client_closer;
+	archive_free_callback	*client_freer;
 	void			*client_data;
 
 	/*
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format.c
index 12de080..7dbe7b9 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format.c
@@ -82,7 +82,7 @@
 __archive_write_entry_filetype_unsupported(struct archive *a,
     struct archive_entry *entry, const char *format)
 {
-	char *name = NULL;
+	const char *name = NULL;
 
 	switch (archive_entry_filetype(entry)) {
 	/*
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c
index 69af814..7f2e6ac 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_7zip.c
@@ -1927,8 +1927,8 @@
 		return (ARCHIVE_FATAL);
 	}
 	lzmafilters = (lzma_filter *)(strm+1);
-	if (level > 6)
-		level = 6;
+	if (level > 9)
+		level = 9;
 	if (lzma_lzma_preset(&lzma_opt, level)) {
 		free(strm);
 		lastrm->real_stream = NULL;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c
index 729f9c7..e066733 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio.c
@@ -250,7 +250,7 @@
 	const char *path;
 	size_t len;
 
-	if (archive_entry_filetype(entry) == 0) {
+	if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
 		archive_set_error(&a->archive, -1, "Filetype required");
 		return (ARCHIVE_FAILED);
 	}
@@ -348,7 +348,7 @@
 	format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
 	if (archive_entry_filetype(entry) == AE_IFBLK
 	    || archive_entry_filetype(entry) == AE_IFCHR)
-	    format_octal(archive_entry_dev(entry), h + c_rdev_offset, c_rdev_size);
+	    format_octal(archive_entry_rdev(entry), h + c_rdev_offset, c_rdev_size);
 	else
 	    format_octal(0, h + c_rdev_offset, c_rdev_size);
 	format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c
index 172fda6..f0f3980 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_cpio_newc.c
@@ -190,7 +190,7 @@
 	const char *path;
 	size_t len;
 
-	if (archive_entry_filetype(entry) == 0) {
+	if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
 		archive_set_error(&a->archive, -1, "Filetype required");
 		return (ARCHIVE_FAILED);
 	}
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
index 5db414f..68e3fe3 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_iso9660.c
@@ -2178,7 +2178,8 @@
 	strncpy(system_id, "Windows", size-1);
 	system_id[size-1] = '\0';
 #else
-#error no way to get the system identifier on your platform.
+	strncpy(system_id, "Unknown", size-1);
+	system_id[size-1] = '\0';
 #endif
 }
 
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c
index aa41e9a..619b771 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_mtree.c
@@ -37,6 +37,7 @@
 #include "archive.h"
 #include "archive_digest_private.h"
 #include "archive_entry.h"
+#include "archive_entry_private.h"
 #include "archive_private.h"
 #include "archive_rb.h"
 #include "archive_string.h"
@@ -82,24 +83,7 @@
 struct reg_info {
 	int compute_sum;
 	uint32_t crc;
-#ifdef ARCHIVE_HAS_MD5
-	unsigned char buf_md5[16];
-#endif
-#ifdef ARCHIVE_HAS_RMD160
-	unsigned char buf_rmd160[20];
-#endif
-#ifdef ARCHIVE_HAS_SHA1
-	unsigned char buf_sha1[20];
-#endif
-#ifdef ARCHIVE_HAS_SHA256
-	unsigned char buf_sha256[32];
-#endif
-#ifdef ARCHIVE_HAS_SHA384
-	unsigned char buf_sha384[48];
-#endif
-#ifdef ARCHIVE_HAS_SHA512
-	unsigned char buf_sha512[64];
-#endif
+	struct ae_digest digest;
 };
 
 struct mtree_entry {
@@ -1571,27 +1555,27 @@
 	}
 #ifdef ARCHIVE_HAS_MD5
 	if (mtree->compute_sum & F_MD5)
-		archive_md5_final(&mtree->md5ctx, reg->buf_md5);
+		archive_md5_final(&mtree->md5ctx, reg->digest.md5);
 #endif
 #ifdef ARCHIVE_HAS_RMD160
 	if (mtree->compute_sum & F_RMD160)
-		archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160);
+		archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160);
 #endif
 #ifdef ARCHIVE_HAS_SHA1
 	if (mtree->compute_sum & F_SHA1)
-		archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1);
+		archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1);
 #endif
 #ifdef ARCHIVE_HAS_SHA256
 	if (mtree->compute_sum & F_SHA256)
-		archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256);
+		archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256);
 #endif
 #ifdef ARCHIVE_HAS_SHA384
 	if (mtree->compute_sum & F_SHA384)
-		archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384);
+		archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384);
 #endif
 #ifdef ARCHIVE_HAS_SHA512
 	if (mtree->compute_sum & F_SHA512)
-		archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512);
+		archive_sha512_final(&mtree->sha512ctx, reg->digest.sha512);
 #endif
 	/* Save what types of sum are computed. */
 	reg->compute_sum = mtree->compute_sum;
@@ -1621,42 +1605,47 @@
 		archive_string_sprintf(str, " cksum=%ju",
 		    (uintmax_t)reg->crc);
 	}
+
+#define append_digest(_s, _r, _t) \
+	strappend_bin(_s, _r->digest._t, sizeof(_r->digest._t))
+
 #ifdef ARCHIVE_HAS_MD5
 	if (reg->compute_sum & F_MD5) {
 		archive_strcat(str, " md5digest=");
-		strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5));
+		append_digest(str, reg, md5);
 	}
 #endif
 #ifdef ARCHIVE_HAS_RMD160
 	if (reg->compute_sum & F_RMD160) {
 		archive_strcat(str, " rmd160digest=");
-		strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160));
+		append_digest(str, reg, rmd160);
 	}
 #endif
 #ifdef ARCHIVE_HAS_SHA1
 	if (reg->compute_sum & F_SHA1) {
 		archive_strcat(str, " sha1digest=");
-		strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1));
+		append_digest(str, reg, sha1);
 	}
 #endif
 #ifdef ARCHIVE_HAS_SHA256
 	if (reg->compute_sum & F_SHA256) {
 		archive_strcat(str, " sha256digest=");
-		strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256));
+		append_digest(str, reg, sha256);
 	}
 #endif
 #ifdef ARCHIVE_HAS_SHA384
 	if (reg->compute_sum & F_SHA384) {
 		archive_strcat(str, " sha384digest=");
-		strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384));
+		append_digest(str, reg, sha384);
 	}
 #endif
 #ifdef ARCHIVE_HAS_SHA512
 	if (reg->compute_sum & F_SHA512) {
 		archive_strcat(str, " sha512digest=");
-		strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512));
+		append_digest(str, reg, sha512);
 	}
 #endif
+#undef append_digest
 }
 
 static int
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
index 3b0ffb3..1e35375 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_xar.c
@@ -681,7 +681,8 @@
 {
 	struct xar *xar;
 	enum la_zaction run;
-	size_t size, rsize;
+	size_t size = 0;
+	size_t rsize;
 	int r;
 
 	xar = (struct xar *)a->format_data;
@@ -2930,8 +2931,8 @@
 		return (ARCHIVE_FATAL);
 	}
 	lzmafilters = (lzma_filter *)(strm+1);
-	if (level > 6)
-		level = 6;
+	if (level > 9)
+		level = 9;
 	if (lzma_lzma_preset(&lzma_opt, level)) {
 		free(strm);
 		lastrm->real_stream = NULL;
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
index b0cd215..66a1f84 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_format_zip.c
@@ -584,6 +584,7 @@
 			zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;
 			zip->entry_encryption = zip->encryption_type;
 			break;
+		case ENCRYPTION_NONE:
 		default:
 			break;
 		}
@@ -710,6 +711,7 @@
 				    + AUTH_CODE_SIZE;
 				version_needed = 20;
 				break;
+			case ENCRYPTION_NONE:
 			default:
 				break;
 			}
@@ -762,6 +764,7 @@
 				if (version_needed < 20)
 					version_needed = 20;
 				break;
+			case ENCRYPTION_NONE:
 			default:
 				break;
 			}
@@ -1029,6 +1032,7 @@
 				zip->cctx_valid = zip->hctx_valid = 1;
 			}
 			break;
+		case ENCRYPTION_NONE:
 		default:
 			break;
 		}
@@ -1117,6 +1121,7 @@
 		break;
 #endif
 
+	case COMPRESSION_UNSPECIFIED:
 	default:
 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
 		    "Invalid ZIP compression type");
diff --git a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3 b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3
index cffe571..d4a52e3 100644
--- a/Utilities/cmlibarchive/libarchive/archive_write_set_options.3
+++ b/Utilities/cmlibarchive/libarchive/archive_write_set_options.3
@@ -255,7 +255,8 @@
 .Bl -tag -compact -width indent
 .It Cm compression-level
 The value is interpreted as a decimal integer specifying the
-compression level. Supported values are from 1 to 22.
+compression level. Supported values depend on the library version,
+common values are from 1 to 22.
 .El
 .It Format 7zip
 .Bl -tag -compact -width indent
diff --git a/Utilities/cmlibarchive/libarchive/config_freebsd.h b/Utilities/cmlibarchive/libarchive/config_freebsd.h
index f16fd34..a484618 100644
--- a/Utilities/cmlibarchive/libarchive/config_freebsd.h
+++ b/Utilities/cmlibarchive/libarchive/config_freebsd.h
@@ -24,6 +24,7 @@
  *
  * $FreeBSD$
  */
+#define __LIBARCHIVE_CONFIG_H_INCLUDED 1
 
 #include <osreldate.h>
 
@@ -183,6 +184,7 @@
 #define HAVE_STRFTIME 1
 #define HAVE_STRINGS_H 1
 #define HAVE_STRING_H 1
+#define HAVE_STRNLEN 1
 #define HAVE_STRRCHR 1
 #define HAVE_STRUCT_STATFS_F_NAMEMAX 1
 #define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
diff --git a/Utilities/cmlibarchive/libarchive/cpio.5 b/Utilities/cmlibarchive/libarchive/cpio.5
index 1a2886f..a91f0c5 100644
--- a/Utilities/cmlibarchive/libarchive/cpio.5
+++ b/Utilities/cmlibarchive/libarchive/cpio.5
@@ -244,7 +244,7 @@
 older ASCII format, which supports 8 gigabyte files).
 .Pp
 In this format, hardlinked files are handled by setting the
-filesize to zero for each entry except the last one that
+filesize to zero for each entry except the first one that
 appears in the archive.
 .Ss New CRC Format
 The CRC format is identical to the new ASCII format described
diff --git a/Utilities/cmlibarchive/libarchive/filter_fork.h b/Utilities/cmlibarchive/libarchive/filter_fork.h
index 908e7cd..2bf290c 100644
--- a/Utilities/cmlibarchive/libarchive/filter_fork.h
+++ b/Utilities/cmlibarchive/libarchive/filter_fork.h
@@ -32,8 +32,13 @@
 #error This header is only to be used internally to libarchive.
 #endif
 
-pid_t
-__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout);
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	HANDLE *out_child);
+#else
+	pid_t *out_child);
+#endif
 
 void
 __archive_check_child(int in, int out);
diff --git a/Utilities/cmlibarchive/libarchive/filter_fork_posix.c b/Utilities/cmlibarchive/libarchive/filter_fork_posix.c
index 02dbd4b..ac255c4 100644
--- a/Utilities/cmlibarchive/libarchive/filter_fork_posix.c
+++ b/Utilities/cmlibarchive/libarchive/filter_fork_posix.c
@@ -72,8 +72,9 @@
 
 #include "filter_fork.h"
 
-pid_t
-__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout)
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+		pid_t *out_child)
 {
 	pid_t child;
 	int stdin_pipe[2], stdout_pipe[2], tmp;
@@ -177,7 +178,8 @@
 	fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
 	__archive_cmdline_free(cmdline);
 
-	return child;
+	*out_child = child;
+	return ARCHIVE_OK;
 
 #if HAVE_POSIX_SPAWNP
 actions_inited:
@@ -192,7 +194,7 @@
 	close(stdin_pipe[1]);
 state_allocated:
 	__archive_cmdline_free(cmdline);
-	return -1;
+	return ARCHIVE_FAILED;
 }
 
 void
diff --git a/Utilities/cmlibarchive/libarchive/filter_fork_windows.c b/Utilities/cmlibarchive/libarchive/filter_fork_windows.c
index ad271fe..8d11179 100644
--- a/Utilities/cmlibarchive/libarchive/filter_fork_windows.c
+++ b/Utilities/cmlibarchive/libarchive/filter_fork_windows.c
@@ -31,8 +31,9 @@
 
 #include "filter_fork.h"
 
-pid_t
-__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout)
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+		HANDLE *out_child)
 {
 	HANDLE childStdout[2], childStdin[2],childStderr;
 	SECURITY_ATTRIBUTES secAtts;
@@ -44,6 +45,7 @@
 	char *arg0, *ext;
 	int i, l;
 	DWORD fl, fl_old;
+	HANDLE child;
 
 	childStdout[0] = childStdout[1] = INVALID_HANDLE_VALUE;
 	childStdin[0] = childStdin[1] = INVALID_HANDLE_VALUE;
@@ -154,13 +156,20 @@
 	*child_stdout = _open_osfhandle((intptr_t)childStdout[0], _O_RDONLY);
 	*child_stdin = _open_osfhandle((intptr_t)childStdin[1], _O_WRONLY);
 	
+	child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+		childInfo.dwProcessId);
+	if (child == NULL) // INVALID_HANDLE_VALUE ?
+		goto fail;
+
+	*out_child = child;
+
 	CloseHandle(childStdout[1]);
 	CloseHandle(childStdin[0]);
 
 	archive_string_free(&cmdline);
 	archive_string_free(&fullpath);
 	__archive_cmdline_free(acmd);
-	return (childInfo.dwProcessId);
+	return ARCHIVE_OK;
 
 fail:
 	if (childStdout[0] != INVALID_HANDLE_VALUE)
@@ -176,7 +185,7 @@
 	archive_string_free(&cmdline);
 	archive_string_free(&fullpath);
 	__archive_cmdline_free(acmd);
-	return (-1);
+	return ARCHIVE_FAILED;
 }
 
 void
diff --git a/Utilities/cmlibuv/include/uv/win.h b/Utilities/cmlibuv/include/uv/win.h
index f86357b..9b5d5dc 100644
--- a/Utilities/cmlibuv/include/uv/win.h
+++ b/Utilities/cmlibuv/include/uv/win.h
@@ -45,7 +45,14 @@
 #endif
 
 #include <mswsock.h>
+// Disable the typedef in mstcpip.h of MinGW.
+#define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__AVOID
+#define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__AVOID
+#define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__AVOID
 #include <ws2tcpip.h>
+#undef _TCP_INITIAL_RTO_PARAMETERS
+#undef TCP_INITIAL_RTO_PARAMETERS
+#undef PTCP_INITIAL_RTO_PARAMETERS
 #include <windows.h>
 
 #include <process.h>
diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c
index e6d61ee..4245e02 100644
--- a/Utilities/cmlibuv/src/unix/core.c
+++ b/Utilities/cmlibuv/src/unix/core.c
@@ -72,8 +72,6 @@
 # include <sys/sysctl.h>
 # include <sys/filio.h>
 # include <sys/wait.h>
-# include <sys/param.h>
-# include <sys/cpuset.h>
 # if defined(__FreeBSD__)
 #  define uv__accept4 accept4
 # endif
@@ -82,6 +80,11 @@
 # endif
 #endif
 
+#if defined(__FreeBSD__)
+# include <sys/param.h>
+# include <sys/cpuset.h>
+#endif
+
 #if defined(__MVS__)
 #include <sys/ioctl.h>
 #endif
diff --git a/Utilities/cmlibuv/src/win/process.c b/Utilities/cmlibuv/src/win/process.c
index 04718db..aada889 100644
--- a/Utilities/cmlibuv/src/win/process.c
+++ b/Utilities/cmlibuv/src/win/process.c
@@ -169,7 +169,10 @@
                                     size_t cwd_len) {
   WCHAR *result, *result_pos;
   DWORD attrs;
-  if (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') {
+  if (
+    (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') ||
+    (dir_len > 2 && dir[0] == L'/' && dir[1] == L'/') 
+   ) {
     /* It's a UNC path so ignore cwd */
     cwd_len = 0;
   } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) {
diff --git a/Utilities/cmzstd/README.md b/Utilities/cmzstd/README.md
index 5c300fd..dcca766 100644
--- a/Utilities/cmzstd/README.md
+++ b/Utilities/cmzstd/README.md
@@ -176,6 +176,12 @@
 You can build the zstd binary via buck by executing: `buck build programs:zstd` from the root of the repo.
 The output binary will be in `buck-out/gen/programs/`.
 
+## Testing
+
+You can run quick local smoke tests by executing the `playTest.sh` script from the `src/tests` directory.
+Two env variables `$ZSTD_BIN` and `$DATAGEN_BIN` are needed for the test script to locate the zstd and datagen binary.
+For information on CI testing, please refer to TESTING.md
+
 ## Status
 
 Zstandard is currently deployed within Facebook. It is used continuously to compress large amounts of data in multiple formats and use cases.
@@ -187,7 +193,7 @@
 
 ## Contributing
 
-The "dev" branch is the one where all contributions are merged before reaching "master".
-If you plan to propose a patch, please commit into the "dev" branch, or its own feature branch.
-Direct commit to "master" are not permitted.
+The `dev` branch is the one where all contributions are merged before reaching `release`.
+If you plan to propose a patch, please commit into the `dev` branch, or its own feature branch.
+Direct commit to `release` are not permitted.
 For more information, please read [CONTRIBUTING](CONTRIBUTING.md).
diff --git a/Utilities/cmzstd/lib/common/bitstream.h b/Utilities/cmzstd/lib/common/bitstream.h
index 37b99c0..2e5a933 100644
--- a/Utilities/cmzstd/lib/common/bitstream.h
+++ b/Utilities/cmzstd/lib/common/bitstream.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * bitstream
  * Part of FSE library
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -17,7 +17,6 @@
 #if defined (__cplusplus)
 extern "C" {
 #endif
-
 /*
 *  This API consists of small unitary functions, which must be inlined for best performance.
 *  Since link-time-optimization is not available for all compilers,
@@ -36,10 +35,12 @@
 /*=========================================
 *  Target specific
 =========================================*/
-#if defined(__BMI__) && defined(__GNUC__)
-#  include <immintrin.h>   /* support for bextr (experimental) */
-#elif defined(__ICCARM__)
-#  include <intrinsics.h>
+#ifndef ZSTD_NO_INTRINSICS
+#  if defined(__BMI__) && defined(__GNUC__)
+#    include <immintrin.h>   /* support for bextr (experimental) */
+#  elif defined(__ICCARM__)
+#    include <intrinsics.h>
+#  endif
 #endif
 
 #define STREAM_ACCUMULATOR_MIN_32  25
@@ -141,8 +142,12 @@
     assert(val != 0);
     {
 #   if defined(_MSC_VER)   /* Visual */
-        unsigned long r=0;
-        return _BitScanReverse ( &r, val ) ? (unsigned)r : 0;
+#       if STATIC_BMI2 == 1
+		return _lzcnt_u32(val) ^ 31;
+#       else
+		unsigned long r = 0;
+		return _BitScanReverse(&r, val) ? (unsigned)r : 0;
+#       endif
 #   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* Use GCC Intrinsic */
         return __builtin_clz (val) ^ 31;
 #   elif defined(__ICCARM__)    /* IAR Intrinsic */
@@ -198,7 +203,7 @@
 MEM_STATIC void BIT_addBits(BIT_CStream_t* bitC,
                             size_t value, unsigned nbBits)
 {
-    MEM_STATIC_ASSERT(BIT_MASK_SIZE == 32);
+    DEBUG_STATIC_ASSERT(BIT_MASK_SIZE == 32);
     assert(nbBits < BIT_MASK_SIZE);
     assert(nbBits + bitC->bitPos < sizeof(bitC->bitContainer) * 8);
     bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos;
@@ -271,7 +276,7 @@
  */
 MEM_STATIC size_t BIT_initDStream(BIT_DStream_t* bitD, const void* srcBuffer, size_t srcSize)
 {
-    if (srcSize < 1) { memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
+    if (srcSize < 1) { ZSTD_memset(bitD, 0, sizeof(*bitD)); return ERROR(srcSize_wrong); }
 
     bitD->start = (const char*)srcBuffer;
     bitD->limitPtr = bitD->start + sizeof(bitD->bitContainer);
@@ -317,12 +322,12 @@
     return srcSize;
 }
 
-MEM_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start)
+MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getUpperBits(size_t bitContainer, U32 const start)
 {
     return bitContainer >> start;
 }
 
-MEM_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits)
+MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits)
 {
     U32 const regMask = sizeof(bitContainer)*8 - 1;
     /* if start > regMask, bitstream is corrupted, and result is undefined */
@@ -330,10 +335,14 @@
     return (bitContainer >> (start & regMask)) & BIT_mask[nbBits];
 }
 
-MEM_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
+MEM_STATIC FORCE_INLINE_ATTR size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits)
 {
+#if defined(STATIC_BMI2) && STATIC_BMI2 == 1
+	return  _bzhi_u64(bitContainer, nbBits);
+#else
     assert(nbBits < BIT_MASK_SIZE);
     return bitContainer & BIT_mask[nbBits];
+#endif
 }
 
 /*! BIT_lookBits() :
@@ -342,7 +351,7 @@
  *  On 32-bits, maxNbBits==24.
  *  On 64-bits, maxNbBits==56.
  * @return : value extracted */
-MEM_STATIC size_t BIT_lookBits(const BIT_DStream_t* bitD, U32 nbBits)
+MEM_STATIC  FORCE_INLINE_ATTR size_t BIT_lookBits(const BIT_DStream_t*  bitD, U32 nbBits)
 {
     /* arbitrate between double-shift and shift+mask */
 #if 1
@@ -365,7 +374,7 @@
     return (bitD->bitContainer << (bitD->bitsConsumed & regMask)) >> (((regMask+1)-nbBits) & regMask);
 }
 
-MEM_STATIC void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
+MEM_STATIC FORCE_INLINE_ATTR void BIT_skipBits(BIT_DStream_t* bitD, U32 nbBits)
 {
     bitD->bitsConsumed += nbBits;
 }
@@ -374,7 +383,7 @@
  *  Read (consume) next n bits from local register and update.
  *  Pay attention to not read more than nbBits contained into local register.
  * @return : extracted value. */
-MEM_STATIC size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits)
+MEM_STATIC FORCE_INLINE_ATTR size_t BIT_readBits(BIT_DStream_t* bitD, unsigned nbBits)
 {
     size_t const value = BIT_lookBits(bitD, nbBits);
     BIT_skipBits(bitD, nbBits);
diff --git a/Utilities/cmzstd/lib/common/compiler.h b/Utilities/cmzstd/lib/common/compiler.h
index 95e9483..f717855 100644
--- a/Utilities/cmzstd/lib/common/compiler.h
+++ b/Utilities/cmzstd/lib/common/compiler.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -39,6 +39,17 @@
 #endif
 
 /**
+  On MSVC qsort requires that functions passed into it use the __cdecl calling conversion(CC).
+  This explictly marks such functions as __cdecl so that the code will still compile
+  if a CC other than __cdecl has been made the default.
+*/
+#if  defined(_MSC_VER)
+#  define WIN_CDECL __cdecl
+#else
+#  define WIN_CDECL
+#endif
+
+/**
  * FORCE_INLINE_TEMPLATE is used to define C "templates", which take constant
  * parameters. They must be inlined for the compiler to eliminate the constant
  * branches.
@@ -79,6 +90,7 @@
 #  endif
 #endif
 
+
 /* target attribute */
 #ifndef __has_attribute
   #define __has_attribute(x) 0  /* Compatibility with non-clang compilers. */
@@ -114,12 +126,12 @@
 #    include <mmintrin.h>   /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */
 #    define PREFETCH_L1(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
 #    define PREFETCH_L2(ptr)  _mm_prefetch((const char*)(ptr), _MM_HINT_T1)
-#    elif defined(__aarch64__)
-#     define PREFETCH_L1(ptr)  __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr)))
-#     define PREFETCH_L2(ptr)  __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr)))
 #  elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) )
 #    define PREFETCH_L1(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */)
 #    define PREFETCH_L2(ptr)  __builtin_prefetch((ptr), 0 /* rw==read */, 2 /* locality */)
+#  elif defined(__aarch64__)
+#    define PREFETCH_L1(ptr)  __asm__ __volatile__("prfm pldl1keep, %0" ::"Q"(*(ptr)))
+#    define PREFETCH_L2(ptr)  __asm__ __volatile__("prfm pldl2keep, %0" ::"Q"(*(ptr)))
 #  else
 #    define PREFETCH_L1(ptr) (void)(ptr)  /* disabled */
 #    define PREFETCH_L2(ptr) (void)(ptr)  /* disabled */
@@ -139,7 +151,7 @@
 
 /* vectorization
  * older GCC (pre gcc-4.3 picked as the cutoff) uses a different syntax */
-#if !defined(__INTEL_COMPILER) && !defined(__clang__) && defined(__GNUC__)
+#if !defined(__INTEL_COMPILER) && !defined(__clang__) && !defined(__LCC__) && defined(__GNUC__)
 #  if (__GNUC__ == 4 && __GNUC_MINOR__ > 3) || (__GNUC__ >= 5)
 #    define DONT_VECTORIZE __attribute__((optimize("no-tree-vectorize")))
 #  else
@@ -172,4 +184,106 @@
 #  pragma warning(disable : 4324)        /* disable: C4324: padded structure */
 #endif
 
+/*Like DYNAMIC_BMI2 but for compile time determination of BMI2 support*/
+#ifndef STATIC_BMI2
+#  if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_I86))
+#    ifdef __AVX2__  //MSVC does not have a BMI2 specific flag, but every CPU that supports AVX2 also supports BMI2
+#       define STATIC_BMI2 1
+#    endif
+#  endif
+#endif
+
+#ifndef STATIC_BMI2
+    #define STATIC_BMI2 0
+#endif
+
+/* compat. with non-clang compilers */
+#ifndef __has_builtin
+#  define __has_builtin(x) 0
+#endif
+
+/* compat. with non-clang compilers */
+#ifndef __has_feature
+#  define __has_feature(x) 0
+#endif
+
+/* detects whether we are being compiled under msan */
+#ifndef ZSTD_MEMORY_SANITIZER
+#  if __has_feature(memory_sanitizer)
+#    define ZSTD_MEMORY_SANITIZER 1
+#  else
+#    define ZSTD_MEMORY_SANITIZER 0
+#  endif
+#endif
+
+#if ZSTD_MEMORY_SANITIZER
+/* Not all platforms that support msan provide sanitizers/msan_interface.h.
+ * We therefore declare the functions we need ourselves, rather than trying to
+ * include the header file... */
+#include <stddef.h>  /* size_t */
+#define ZSTD_DEPS_NEED_STDINT
+#include "zstd_deps.h"  /* intptr_t */
+
+/* Make memory region fully initialized (without changing its contents). */
+void __msan_unpoison(const volatile void *a, size_t size);
+
+/* Make memory region fully uninitialized (without changing its contents).
+   This is a legacy interface that does not update origin information. Use
+   __msan_allocated_memory() instead. */
+void __msan_poison(const volatile void *a, size_t size);
+
+/* Returns the offset of the first (at least partially) poisoned byte in the
+   memory range, or -1 if the whole range is good. */
+intptr_t __msan_test_shadow(const volatile void *x, size_t size);
+#endif
+
+/* detects whether we are being compiled under asan */
+#ifndef ZSTD_ADDRESS_SANITIZER
+#  if __has_feature(address_sanitizer)
+#    define ZSTD_ADDRESS_SANITIZER 1
+#  elif defined(__SANITIZE_ADDRESS__)
+#    define ZSTD_ADDRESS_SANITIZER 1
+#  else
+#    define ZSTD_ADDRESS_SANITIZER 0
+#  endif
+#endif
+
+#if ZSTD_ADDRESS_SANITIZER
+/* Not all platforms that support asan provide sanitizers/asan_interface.h.
+ * We therefore declare the functions we need ourselves, rather than trying to
+ * include the header file... */
+#include <stddef.h>  /* size_t */
+
+/**
+ * Marks a memory region (<c>[addr, addr+size)</c>) as unaddressable.
+ *
+ * This memory must be previously allocated by your program. Instrumented
+ * code is forbidden from accessing addresses in this region until it is
+ * unpoisoned. This function is not guaranteed to poison the entire region -
+ * it could poison only a subregion of <c>[addr, addr+size)</c> due to ASan
+ * alignment restrictions.
+ *
+ * \note This function is not thread-safe because no two threads can poison or
+ * unpoison memory in the same memory region simultaneously.
+ *
+ * \param addr Start of memory region.
+ * \param size Size of memory region. */
+void __asan_poison_memory_region(void const volatile *addr, size_t size);
+
+/**
+ * Marks a memory region (<c>[addr, addr+size)</c>) as addressable.
+ *
+ * This memory must be previously allocated by your program. Accessing
+ * addresses in this region is allowed until this region is poisoned again.
+ * This function could unpoison a super-region of <c>[addr, addr+size)</c> due
+ * to ASan alignment restrictions.
+ *
+ * \note This function is not thread-safe because no two threads can
+ * poison or unpoison memory in the same memory region simultaneously.
+ *
+ * \param addr Start of memory region.
+ * \param size Size of memory region. */
+void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+#endif
+
 #endif /* ZSTD_COMPILER_H */
diff --git a/Utilities/cmzstd/lib/common/cpu.h b/Utilities/cmzstd/lib/common/cpu.h
index 6e8a974..8acd33b 100644
--- a/Utilities/cmzstd/lib/common/cpu.h
+++ b/Utilities/cmzstd/lib/common/cpu.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -16,8 +16,6 @@
  * https://github.com/facebook/folly/blob/master/folly/CpuId.h
  */
 
-#include <string.h>
-
 #include "mem.h"
 
 #ifdef _MSC_VER
diff --git a/Utilities/cmzstd/lib/common/debug.c b/Utilities/cmzstd/lib/common/debug.c
index f303f4a..bb863c9 100644
--- a/Utilities/cmzstd/lib/common/debug.c
+++ b/Utilities/cmzstd/lib/common/debug.c
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * debug
  * Part of FSE library
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
diff --git a/Utilities/cmzstd/lib/common/debug.h b/Utilities/cmzstd/lib/common/debug.h
index ac62248..3b2a320 100644
--- a/Utilities/cmzstd/lib/common/debug.h
+++ b/Utilities/cmzstd/lib/common/debug.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * debug
  * Part of FSE library
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -51,15 +51,6 @@
 #endif
 
 
-/* DEBUGFILE can be defined externally,
- * typically through compiler command line.
- * note : currently useless.
- * Value must be stderr or stdout */
-#ifndef DEBUGFILE
-#  define DEBUGFILE stderr
-#endif
-
-
 /* recommended values for DEBUGLEVEL :
  * 0 : release mode, no debug, all run-time checks disabled
  * 1 : enables assert() only, no display
@@ -76,7 +67,8 @@
  */
 
 #if (DEBUGLEVEL>=1)
-#  include <assert.h>
+#  define ZSTD_DEPS_NEED_ASSERT
+#  include "zstd_deps.h"
 #else
 #  ifndef assert   /* assert may be already defined, due to prior #include <assert.h> */
 #    define assert(condition) ((void)0)   /* disable assert (default) */
@@ -84,7 +76,8 @@
 #endif
 
 #if (DEBUGLEVEL>=2)
-#  include <stdio.h>
+#  define ZSTD_DEPS_NEED_IO
+#  include "zstd_deps.h"
 extern int g_debuglevel; /* the variable is only declared,
                             it actually lives in debug.c,
                             and is shared by the whole process.
@@ -92,14 +85,14 @@
                             It's useful when enabling very verbose levels
                             on selective conditions (such as position in src) */
 
-#  define RAWLOG(l, ...) {                                      \
-                if (l<=g_debuglevel) {                          \
-                    fprintf(stderr, __VA_ARGS__);               \
+#  define RAWLOG(l, ...) {                                       \
+                if (l<=g_debuglevel) {                           \
+                    ZSTD_DEBUG_PRINT(__VA_ARGS__);               \
             }   }
-#  define DEBUGLOG(l, ...) {                                    \
-                if (l<=g_debuglevel) {                          \
-                    fprintf(stderr, __FILE__ ": " __VA_ARGS__); \
-                    fprintf(stderr, " \n");                     \
+#  define DEBUGLOG(l, ...) {                                     \
+                if (l<=g_debuglevel) {                           \
+                    ZSTD_DEBUG_PRINT(__FILE__ ": " __VA_ARGS__); \
+                    ZSTD_DEBUG_PRINT(" \n");                     \
             }   }
 #else
 #  define RAWLOG(l, ...)      {}    /* disabled */
diff --git a/Utilities/cmzstd/lib/common/entropy_common.c b/Utilities/cmzstd/lib/common/entropy_common.c
index 9d3e4e8..41cd695 100644
--- a/Utilities/cmzstd/lib/common/entropy_common.c
+++ b/Utilities/cmzstd/lib/common/entropy_common.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * Common functions of New Generation Entropy library
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -38,8 +38,31 @@
 /*-**************************************************************
 *  FSE NCount encoding-decoding
 ****************************************************************/
-size_t FSE_readNCount (short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
-                 const void* headerBuffer, size_t hbSize)
+static U32 FSE_ctz(U32 val)
+{
+    assert(val != 0);
+    {
+#   if defined(_MSC_VER)   /* Visual */
+        unsigned long r=0;
+        return _BitScanForward(&r, val) ? (unsigned)r : 0;
+#   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* GCC Intrinsic */
+        return __builtin_ctz(val);
+#   elif defined(__ICCARM__)    /* IAR Intrinsic */
+        return __CTZ(val);
+#   else   /* Software version */
+        U32 count = 0;
+        while ((val & 1) == 0) {
+            val >>= 1;
+            ++count;
+        }
+        return count;
+#   endif
+    }
+}
+
+FORCE_INLINE_TEMPLATE
+size_t FSE_readNCount_body(short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+                           const void* headerBuffer, size_t hbSize)
 {
     const BYTE* const istart = (const BYTE*) headerBuffer;
     const BYTE* const iend = istart + hbSize;
@@ -50,23 +73,23 @@
     U32 bitStream;
     int bitCount;
     unsigned charnum = 0;
+    unsigned const maxSV1 = *maxSVPtr + 1;
     int previous0 = 0;
 
-    if (hbSize < 4) {
-        /* This function only works when hbSize >= 4 */
-        char buffer[4];
-        memset(buffer, 0, sizeof(buffer));
-        memcpy(buffer, headerBuffer, hbSize);
+    if (hbSize < 8) {
+        /* This function only works when hbSize >= 8 */
+        char buffer[8] = {0};
+        ZSTD_memcpy(buffer, headerBuffer, hbSize);
         {   size_t const countSize = FSE_readNCount(normalizedCounter, maxSVPtr, tableLogPtr,
                                                     buffer, sizeof(buffer));
             if (FSE_isError(countSize)) return countSize;
             if (countSize > hbSize) return ERROR(corruption_detected);
             return countSize;
     }   }
-    assert(hbSize >= 4);
+    assert(hbSize >= 8);
 
     /* init */
-    memset(normalizedCounter, 0, (*maxSVPtr+1) * sizeof(normalizedCounter[0]));   /* all symbols not present in NCount have a frequency of 0 */
+    ZSTD_memset(normalizedCounter, 0, (*maxSVPtr+1) * sizeof(normalizedCounter[0]));   /* all symbols not present in NCount have a frequency of 0 */
     bitStream = MEM_readLE32(ip);
     nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG;   /* extract tableLog */
     if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) return ERROR(tableLog_tooLarge);
@@ -77,36 +100,58 @@
     threshold = 1<<nbBits;
     nbBits++;
 
-    while ((remaining>1) & (charnum<=*maxSVPtr)) {
+    for (;;) {
         if (previous0) {
-            unsigned n0 = charnum;
-            while ((bitStream & 0xFFFF) == 0xFFFF) {
-                n0 += 24;
-                if (ip < iend-5) {
-                    ip += 2;
-                    bitStream = MEM_readLE32(ip) >> bitCount;
+            /* Count the number of repeats. Each time the
+             * 2-bit repeat code is 0b11 there is another
+             * repeat.
+             * Avoid UB by setting the high bit to 1.
+             */
+            int repeats = FSE_ctz(~bitStream | 0x80000000) >> 1;
+            while (repeats >= 12) {
+                charnum += 3 * 12;
+                if (LIKELY(ip <= iend-7)) {
+                    ip += 3;
                 } else {
-                    bitStream >>= 16;
-                    bitCount   += 16;
-            }   }
-            while ((bitStream & 3) == 3) {
-                n0 += 3;
-                bitStream >>= 2;
-                bitCount += 2;
+                    bitCount -= (int)(8 * (iend - 7 - ip));
+                    bitCount &= 31;
+                    ip = iend - 4;
+                }
+                bitStream = MEM_readLE32(ip) >> bitCount;
+                repeats = FSE_ctz(~bitStream | 0x80000000) >> 1;
             }
-            n0 += bitStream & 3;
+            charnum += 3 * repeats;
+            bitStream >>= 2 * repeats;
+            bitCount += 2 * repeats;
+
+            /* Add the final repeat which isn't 0b11. */
+            assert((bitStream & 3) < 3);
+            charnum += bitStream & 3;
             bitCount += 2;
-            if (n0 > *maxSVPtr) return ERROR(maxSymbolValue_tooSmall);
-            while (charnum < n0) normalizedCounter[charnum++] = 0;
-            if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
+
+            /* This is an error, but break and return an error
+             * at the end, because returning out of a loop makes
+             * it harder for the compiler to optimize.
+             */
+            if (charnum >= maxSV1) break;
+
+            /* We don't need to set the normalized count to 0
+             * because we already memset the whole buffer to 0.
+             */
+
+            if (LIKELY(ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
                 assert((bitCount >> 3) <= 3); /* For first condition to work */
                 ip += bitCount>>3;
                 bitCount &= 7;
-                bitStream = MEM_readLE32(ip) >> bitCount;
             } else {
-                bitStream >>= 2;
-        }   }
-        {   int const max = (2*threshold-1) - remaining;
+                bitCount -= (int)(8 * (iend - 4 - ip));
+                bitCount &= 31;
+                ip = iend - 4;
+            }
+            bitStream = MEM_readLE32(ip) >> bitCount;
+        }
+        {
+            int const max = (2*threshold-1) - remaining;
             int count;
 
             if ((bitStream & (threshold-1)) < (U32)max) {
@@ -119,24 +164,43 @@
             }
 
             count--;   /* extra accuracy */
-            remaining -= count < 0 ? -count : count;   /* -1 means +1 */
+            /* When it matters (small blocks), this is a
+             * predictable branch, because we don't use -1.
+             */
+            if (count >= 0) {
+                remaining -= count;
+            } else {
+                assert(count == -1);
+                remaining += count;
+            }
             normalizedCounter[charnum++] = (short)count;
             previous0 = !count;
-            while (remaining < threshold) {
-                nbBits--;
-                threshold >>= 1;
-            }
 
-            if ((ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
+            assert(threshold > 1);
+            if (remaining < threshold) {
+                /* This branch can be folded into the
+                 * threshold update condition because we
+                 * know that threshold > 1.
+                 */
+                if (remaining <= 1) break;
+                nbBits = BIT_highbit32(remaining) + 1;
+                threshold = 1 << (nbBits - 1);
+            }
+            if (charnum >= maxSV1) break;
+
+            if (LIKELY(ip <= iend-7) || (ip + (bitCount>>3) <= iend-4)) {
                 ip += bitCount>>3;
                 bitCount &= 7;
             } else {
                 bitCount -= (int)(8 * (iend - 4 - ip));
+                bitCount &= 31;
                 ip = iend - 4;
             }
-            bitStream = MEM_readLE32(ip) >> (bitCount & 31);
-    }   }   /* while ((remaining>1) & (charnum<=*maxSVPtr)) */
+            bitStream = MEM_readLE32(ip) >> bitCount;
+    }   }
     if (remaining != 1) return ERROR(corruption_detected);
+    /* Only possible when there are too many zeros. */
+    if (charnum > maxSV1) return ERROR(maxSymbolValue_tooSmall);
     if (bitCount > 32) return ERROR(corruption_detected);
     *maxSVPtr = charnum-1;
 
@@ -144,6 +208,43 @@
     return ip-istart;
 }
 
+/* Avoids the FORCE_INLINE of the _body() function. */
+static size_t FSE_readNCount_body_default(
+        short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+        const void* headerBuffer, size_t hbSize)
+{
+    return FSE_readNCount_body(normalizedCounter, maxSVPtr, tableLogPtr, headerBuffer, hbSize);
+}
+
+#if DYNAMIC_BMI2
+TARGET_ATTRIBUTE("bmi2") static size_t FSE_readNCount_body_bmi2(
+        short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+        const void* headerBuffer, size_t hbSize)
+{
+    return FSE_readNCount_body(normalizedCounter, maxSVPtr, tableLogPtr, headerBuffer, hbSize);
+}
+#endif
+
+size_t FSE_readNCount_bmi2(
+        short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+        const void* headerBuffer, size_t hbSize, int bmi2)
+{
+#if DYNAMIC_BMI2
+    if (bmi2) {
+        return FSE_readNCount_body_bmi2(normalizedCounter, maxSVPtr, tableLogPtr, headerBuffer, hbSize);
+    }
+#endif
+    (void)bmi2;
+    return FSE_readNCount_body_default(normalizedCounter, maxSVPtr, tableLogPtr, headerBuffer, hbSize);
+}
+
+size_t FSE_readNCount(
+        short* normalizedCounter, unsigned* maxSVPtr, unsigned* tableLogPtr,
+        const void* headerBuffer, size_t hbSize)
+{
+    return FSE_readNCount_bmi2(normalizedCounter, maxSVPtr, tableLogPtr, headerBuffer, hbSize, /* bmi2 */ 0);
+}
+
 
 /*! HUF_readStats() :
     Read compact Huffman tree, saved by HUF_writeCTable().
@@ -156,6 +257,17 @@
                      U32* nbSymbolsPtr, U32* tableLogPtr,
                      const void* src, size_t srcSize)
 {
+    U32 wksp[HUF_READ_STATS_WORKSPACE_SIZE_U32];
+    return HUF_readStats_wksp(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, wksp, sizeof(wksp), /* bmi2 */ 0);
+}
+
+FORCE_INLINE_TEMPLATE size_t
+HUF_readStats_body(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+                   U32* nbSymbolsPtr, U32* tableLogPtr,
+                   const void* src, size_t srcSize,
+                   void* workSpace, size_t wkspSize,
+                   int bmi2)
+{
     U32 weightTotal;
     const BYTE* ip = (const BYTE*) src;
     size_t iSize;
@@ -163,7 +275,7 @@
 
     if (!srcSize) return ERROR(srcSize_wrong);
     iSize = ip[0];
-    /* memset(huffWeight, 0, hwSize);   *//* is not necessary, even though some analyzer complain ... */
+    /* ZSTD_memset(huffWeight, 0, hwSize);   *//* is not necessary, even though some analyzer complain ... */
 
     if (iSize >= 128) {  /* special header */
         oSize = iSize - 127;
@@ -177,14 +289,14 @@
                 huffWeight[n+1] = ip[n/2] & 15;
     }   }   }
     else  {   /* header compressed with FSE (normal case) */
-        FSE_DTable fseWorkspace[FSE_DTABLE_SIZE_U32(6)];  /* 6 is max possible tableLog for HUF header (maybe even 5, to be tested) */
         if (iSize+1 > srcSize) return ERROR(srcSize_wrong);
-        oSize = FSE_decompress_wksp(huffWeight, hwSize-1, ip+1, iSize, fseWorkspace, 6);   /* max (hwSize-1) values decoded, as last one is implied */
+        /* max (hwSize-1) values decoded, as last one is implied */
+        oSize = FSE_decompress_wksp_bmi2(huffWeight, hwSize-1, ip+1, iSize, 6, workSpace, wkspSize, bmi2);
         if (FSE_isError(oSize)) return oSize;
     }
 
     /* collect weight stats */
-    memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32));
+    ZSTD_memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32));
     weightTotal = 0;
     {   U32 n; for (n=0; n<oSize; n++) {
             if (huffWeight[n] >= HUF_TABLELOG_MAX) return ERROR(corruption_detected);
@@ -214,3 +326,37 @@
     *nbSymbolsPtr = (U32)(oSize+1);
     return iSize+1;
 }
+
+/* Avoids the FORCE_INLINE of the _body() function. */
+static size_t HUF_readStats_body_default(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+                     U32* nbSymbolsPtr, U32* tableLogPtr,
+                     const void* src, size_t srcSize,
+                     void* workSpace, size_t wkspSize)
+{
+    return HUF_readStats_body(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize, 0);
+}
+
+#if DYNAMIC_BMI2
+static TARGET_ATTRIBUTE("bmi2") size_t HUF_readStats_body_bmi2(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+                     U32* nbSymbolsPtr, U32* tableLogPtr,
+                     const void* src, size_t srcSize,
+                     void* workSpace, size_t wkspSize)
+{
+    return HUF_readStats_body(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize, 1);
+}
+#endif
+
+size_t HUF_readStats_wksp(BYTE* huffWeight, size_t hwSize, U32* rankStats,
+                     U32* nbSymbolsPtr, U32* tableLogPtr,
+                     const void* src, size_t srcSize,
+                     void* workSpace, size_t wkspSize,
+                     int bmi2)
+{
+#if DYNAMIC_BMI2
+    if (bmi2) {
+        return HUF_readStats_body_bmi2(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize);
+    }
+#endif
+    (void)bmi2;
+    return HUF_readStats_body_default(huffWeight, hwSize, rankStats, nbSymbolsPtr, tableLogPtr, src, srcSize, workSpace, wkspSize);
+}
diff --git a/Utilities/cmzstd/lib/common/error_private.c b/Utilities/cmzstd/lib/common/error_private.c
index cd43752..6d1135f 100644
--- a/Utilities/cmzstd/lib/common/error_private.c
+++ b/Utilities/cmzstd/lib/common/error_private.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -48,6 +48,7 @@
     case PREFIX(frameIndex_tooLarge): return "Frame index is too large";
     case PREFIX(seekableIO): return "An I/O error occurred when reading/seeking";
     case PREFIX(dstBuffer_wrong): return "Destination buffer is wrong";
+    case PREFIX(srcBuffer_wrong): return "Source buffer is wrong";
     case PREFIX(maxCode):
     default: return notErrorCode;
     }
diff --git a/Utilities/cmzstd/lib/common/error_private.h b/Utilities/cmzstd/lib/common/error_private.h
index 982cf8e..6d8b9f7 100644
--- a/Utilities/cmzstd/lib/common/error_private.h
+++ b/Utilities/cmzstd/lib/common/error_private.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -21,8 +21,8 @@
 /* ****************************************
 *  Dependencies
 ******************************************/
-#include <stddef.h>        /* size_t */
-#include "zstd_errors.h"  /* enum list */
+#include "../zstd_errors.h"  /* enum list */
+#include "zstd_deps.h"       /* size_t */
 
 
 /* ****************************************
diff --git a/Utilities/cmzstd/lib/common/fse.h b/Utilities/cmzstd/lib/common/fse.h
index ff54e70..19dd4fe 100644
--- a/Utilities/cmzstd/lib/common/fse.h
+++ b/Utilities/cmzstd/lib/common/fse.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * FSE : Finite State Entropy codec
  * Public Prototypes declaration
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -23,7 +23,7 @@
 /*-*****************************************
 *  Dependencies
 ******************************************/
-#include <stddef.h>    /* size_t, ptrdiff_t */
+#include "zstd_deps.h"    /* size_t, ptrdiff_t */
 
 
 /*-*****************************************
@@ -137,10 +137,16 @@
 /*! FSE_normalizeCount():
     normalize counts so that sum(count[]) == Power_of_2 (2^tableLog)
     'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1).
+    useLowProbCount is a boolean parameter which trades off compressed size for
+    faster header decoding. When it is set to 1, the compressed data will be slightly
+    smaller. And when it is set to 0, FSE_readNCount() and FSE_buildDTable() will be
+    faster. If you are compressing a small amount of data (< 2 KB) then useLowProbCount=0
+    is a good default, since header deserialization makes a big speed difference.
+    Otherwise, useLowProbCount=1 is a good default, since the speed difference is small.
     @return : tableLog,
               or an errorCode, which can be tested using FSE_isError() */
 FSE_PUBLIC_API size_t FSE_normalizeCount(short* normalizedCounter, unsigned tableLog,
-                    const unsigned* count, size_t srcSize, unsigned maxSymbolValue);
+                    const unsigned* count, size_t srcSize, unsigned maxSymbolValue, unsigned useLowProbCount);
 
 /*! FSE_NCountWriteBound():
     Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'.
@@ -228,6 +234,13 @@
                            unsigned* maxSymbolValuePtr, unsigned* tableLogPtr,
                            const void* rBuffer, size_t rBuffSize);
 
+/*! FSE_readNCount_bmi2():
+ * Same as FSE_readNCount() but pass bmi2=1 when your CPU supports BMI2 and 0 otherwise.
+ */
+FSE_PUBLIC_API size_t FSE_readNCount_bmi2(short* normalizedCounter,
+                           unsigned* maxSymbolValuePtr, unsigned* tableLogPtr,
+                           const void* rBuffer, size_t rBuffSize, int bmi2);
+
 /*! Constructor and Destructor of FSE_DTable.
     Note that its size depends on 'tableLog' */
 typedef unsigned FSE_DTable;   /* don't allocate that. It's just a way to be more restrictive than void* */
@@ -288,12 +301,12 @@
 *******************************************/
 /* FSE buffer bounds */
 #define FSE_NCOUNTBOUND 512
-#define FSE_BLOCKBOUND(size) (size + (size>>7) + 4 /* fse states */ + sizeof(size_t) /* bitContainer */)
+#define FSE_BLOCKBOUND(size) ((size) + ((size)>>7) + 4 /* fse states */ + sizeof(size_t) /* bitContainer */)
 #define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size))   /* Macro version, useful for static allocation */
 
 /* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */
-#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue)   (1 + (1<<(maxTableLog-1)) + ((maxSymbolValue+1)*2))
-#define FSE_DTABLE_SIZE_U32(maxTableLog)                   (1 + (1<<maxTableLog))
+#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue)   (1 + (1<<((maxTableLog)-1)) + (((maxSymbolValue)+1)*2))
+#define FSE_DTABLE_SIZE_U32(maxTableLog)                   (1 + (1<<(maxTableLog)))
 
 /* or use the size to malloc() space directly. Pay attention to alignment restrictions though */
 #define FSE_CTABLE_SIZE(maxTableLog, maxSymbolValue)   (FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(FSE_CTable))
@@ -309,9 +322,9 @@
 
 /* FSE_compress_wksp() :
  * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
- * FSE_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable.
+ * FSE_COMPRESS_WKSP_SIZE_U32() provides the minimum size required for `workSpace` as a table of FSE_CTable.
  */
-#define FSE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue)   ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) )
+#define FSE_COMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue)   ( FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) + ((maxTableLog > 12) ? (1 << (maxTableLog - 2)) : 1024) )
 size_t FSE_compress_wksp (void* dst, size_t dstSize, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
 
 size_t FSE_buildCTable_raw (FSE_CTable* ct, unsigned nbBits);
@@ -322,18 +335,30 @@
 
 /* FSE_buildCTable_wksp() :
  * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`).
- * `wkspSize` must be >= `(1<<tableLog)`.
+ * `wkspSize` must be >= `FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog)` of `unsigned`.
  */
+#define FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog) (maxSymbolValue + 2 + (1ull << (tableLog - 2)))
+#define FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog) (sizeof(unsigned) * FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(maxSymbolValue, tableLog))
 size_t FSE_buildCTable_wksp(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
 
+#define FSE_BUILD_DTABLE_WKSP_SIZE(maxTableLog, maxSymbolValue) (sizeof(short) * (maxSymbolValue + 1) + (1ULL << maxTableLog) + 8)
+#define FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) ((FSE_BUILD_DTABLE_WKSP_SIZE(maxTableLog, maxSymbolValue) + sizeof(unsigned) - 1) / sizeof(unsigned))
+FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize);
+/**< Same as FSE_buildDTable(), using an externally allocated `workspace` produced with `FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxSymbolValue)` */
+
 size_t FSE_buildDTable_raw (FSE_DTable* dt, unsigned nbBits);
 /**< build a fake FSE_DTable, designed to read a flat distribution where each symbol uses nbBits */
 
 size_t FSE_buildDTable_rle (FSE_DTable* dt, unsigned char symbolValue);
 /**< build a fake FSE_DTable, designed to always generate the same symbolValue */
 
-size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog);
-/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DTABLE_SIZE_U32(maxLog)` */
+#define FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) (FSE_DTABLE_SIZE_U32(maxTableLog) + FSE_BUILD_DTABLE_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) + (FSE_MAX_SYMBOL_VALUE + 1) / 2 + 1)
+#define FSE_DECOMPRESS_WKSP_SIZE(maxTableLog, maxSymbolValue) (FSE_DECOMPRESS_WKSP_SIZE_U32(maxTableLog, maxSymbolValue) * sizeof(unsigned))
+size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize);
+/**< same as FSE_decompress(), using an externally allocated `workSpace` produced with `FSE_DECOMPRESS_WKSP_SIZE_U32(maxLog, maxSymbolValue)` */
+
+size_t FSE_decompress_wksp_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize, int bmi2);
+/**< Same as FSE_decompress_wksp() but with dynamic BMI2 support. Pass 1 if your CPU supports BMI2 or 0 if it doesn't. */
 
 typedef enum {
    FSE_repeat_none,  /**< Cannot use the previous table */
@@ -644,6 +669,9 @@
 #ifndef FSE_DEFAULT_MEMORY_USAGE
 #  define FSE_DEFAULT_MEMORY_USAGE 13
 #endif
+#if (FSE_DEFAULT_MEMORY_USAGE > FSE_MAX_MEMORY_USAGE)
+#  error "FSE_DEFAULT_MEMORY_USAGE must be <= FSE_MAX_MEMORY_USAGE"
+#endif
 
 /*!FSE_MAX_SYMBOL_VALUE :
 *  Maximum symbol value authorized.
@@ -677,7 +705,7 @@
 #  error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported"
 #endif
 
-#define FSE_TABLESTEP(tableSize) ((tableSize>>1) + (tableSize>>3) + 3)
+#define FSE_TABLESTEP(tableSize) (((tableSize)>>1) + ((tableSize)>>3) + 3)
 
 
 #endif /* FSE_STATIC_LINKING_ONLY */
diff --git a/Utilities/cmzstd/lib/common/fse_decompress.c b/Utilities/cmzstd/lib/common/fse_decompress.c
index bcc2223..f4ff58f 100644
--- a/Utilities/cmzstd/lib/common/fse_decompress.c
+++ b/Utilities/cmzstd/lib/common/fse_decompress.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * FSE : Finite State Entropy decoder
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -16,13 +16,14 @@
 /* **************************************************************
 *  Includes
 ****************************************************************/
-#include <stdlib.h>     /* malloc, free, qsort */
-#include <string.h>     /* memcpy, memset */
+#include "debug.h"      /* assert */
 #include "bitstream.h"
 #include "compiler.h"
 #define FSE_STATIC_LINKING_ONLY
 #include "fse.h"
 #include "error_private.h"
+#define ZSTD_DEPS_NEED_MALLOC
+#include "zstd_deps.h"
 
 
 /* **************************************************************
@@ -59,25 +60,27 @@
 FSE_DTable* FSE_createDTable (unsigned tableLog)
 {
     if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
-    return (FSE_DTable*)malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) );
+    return (FSE_DTable*)ZSTD_malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) );
 }
 
 void FSE_freeDTable (FSE_DTable* dt)
 {
-    free(dt);
+    ZSTD_free(dt);
 }
 
-size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
+static size_t FSE_buildDTable_internal(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize)
 {
     void* const tdPtr = dt+1;   /* because *dt is unsigned, 32-bits aligned on 32-bits */
     FSE_DECODE_TYPE* const tableDecode = (FSE_DECODE_TYPE*) (tdPtr);
-    U16 symbolNext[FSE_MAX_SYMBOL_VALUE+1];
+    U16* symbolNext = (U16*)workSpace;
+    BYTE* spread = (BYTE*)(symbolNext + maxSymbolValue + 1);
 
     U32 const maxSV1 = maxSymbolValue + 1;
     U32 const tableSize = 1 << tableLog;
     U32 highThreshold = tableSize-1;
 
     /* Sanity Checks */
+    if (FSE_BUILD_DTABLE_WKSP_SIZE(tableLog, maxSymbolValue) > wkspSize) return ERROR(maxSymbolValue_tooLarge);
     if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) return ERROR(maxSymbolValue_tooLarge);
     if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);
 
@@ -95,11 +98,57 @@
                     if (normalizedCounter[s] >= largeLimit) DTableH.fastMode=0;
                     symbolNext[s] = normalizedCounter[s];
         }   }   }
-        memcpy(dt, &DTableH, sizeof(DTableH));
+        ZSTD_memcpy(dt, &DTableH, sizeof(DTableH));
     }
 
     /* Spread symbols */
-    {   U32 const tableMask = tableSize-1;
+    if (highThreshold == tableSize - 1) {
+        size_t const tableMask = tableSize-1;
+        size_t const step = FSE_TABLESTEP(tableSize);
+        /* First lay down the symbols in order.
+         * We use a uint64_t to lay down 8 bytes at a time. This reduces branch
+         * misses since small blocks generally have small table logs, so nearly
+         * all symbols have counts <= 8. We ensure we have 8 bytes at the end of
+         * our buffer to handle the over-write.
+         */
+        {
+            U64 const add = 0x0101010101010101ull;
+            size_t pos = 0;
+            U64 sv = 0;
+            U32 s;
+            for (s=0; s<maxSV1; ++s, sv += add) {
+                int i;
+                int const n = normalizedCounter[s];
+                MEM_write64(spread + pos, sv);
+                for (i = 8; i < n; i += 8) {
+                    MEM_write64(spread + pos + i, sv);
+                }
+                pos += n;
+            }
+        }
+        /* Now we spread those positions across the table.
+         * The benefit of doing it in two stages is that we avoid the the
+         * variable size inner loop, which caused lots of branch misses.
+         * Now we can run through all the positions without any branch misses.
+         * We unroll the loop twice, since that is what emperically worked best.
+         */
+        {
+            size_t position = 0;
+            size_t s;
+            size_t const unroll = 2;
+            assert(tableSize % unroll == 0); /* FSE_MIN_TABLELOG is 5 */
+            for (s = 0; s < (size_t)tableSize; s += unroll) {
+                size_t u;
+                for (u = 0; u < unroll; ++u) {
+                    size_t const uPosition = (position + (u * step)) & tableMask;
+                    tableDecode[uPosition].symbol = spread[s + u];
+                }
+                position = (position + (unroll * step)) & tableMask;
+            }
+            assert(position == 0);
+        }
+    } else {
+        U32 const tableMask = tableSize-1;
         U32 const step = FSE_TABLESTEP(tableSize);
         U32 s, position = 0;
         for (s=0; s<maxSV1; s++) {
@@ -124,6 +173,11 @@
     return 0;
 }
 
+size_t FSE_buildDTable_wksp(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void* workSpace, size_t wkspSize)
+{
+    return FSE_buildDTable_internal(dt, normalizedCounter, maxSymbolValue, tableLog, workSpace, wkspSize);
+}
+
 
 #ifndef FSE_COMMONDEFS_ONLY
 
@@ -251,36 +305,99 @@
 }
 
 
-size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, FSE_DTable* workSpace, unsigned maxLog)
+size_t FSE_decompress_wksp(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize)
+{
+    return FSE_decompress_wksp_bmi2(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, /* bmi2 */ 0);
+}
+
+typedef struct {
+    short ncount[FSE_MAX_SYMBOL_VALUE + 1];
+    FSE_DTable dtable[1]; /* Dynamically sized */
+} FSE_DecompressWksp;
+
+
+FORCE_INLINE_TEMPLATE size_t FSE_decompress_wksp_body(
+        void* dst, size_t dstCapacity,
+        const void* cSrc, size_t cSrcSize,
+        unsigned maxLog, void* workSpace, size_t wkspSize,
+        int bmi2)
 {
     const BYTE* const istart = (const BYTE*)cSrc;
     const BYTE* ip = istart;
-    short counting[FSE_MAX_SYMBOL_VALUE+1];
     unsigned tableLog;
     unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE;
+    FSE_DecompressWksp* const wksp = (FSE_DecompressWksp*)workSpace;
+
+    DEBUG_STATIC_ASSERT((FSE_MAX_SYMBOL_VALUE + 1) % 2 == 0);
+    if (wkspSize < sizeof(*wksp)) return ERROR(GENERIC);
 
     /* normal FSE decoding mode */
-    size_t const NCountLength = FSE_readNCount (counting, &maxSymbolValue, &tableLog, istart, cSrcSize);
-    if (FSE_isError(NCountLength)) return NCountLength;
-    /* if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); */  /* too small input size; supposed to be already checked in NCountLength, only remaining case : NCountLength==cSrcSize */
-    if (tableLog > maxLog) return ERROR(tableLog_tooLarge);
-    ip += NCountLength;
-    cSrcSize -= NCountLength;
+    {
+        size_t const NCountLength = FSE_readNCount_bmi2(wksp->ncount, &maxSymbolValue, &tableLog, istart, cSrcSize, bmi2);
+        if (FSE_isError(NCountLength)) return NCountLength;
+        if (tableLog > maxLog) return ERROR(tableLog_tooLarge);
+        assert(NCountLength <= cSrcSize);
+        ip += NCountLength;
+        cSrcSize -= NCountLength;
+    }
 
-    CHECK_F( FSE_buildDTable (workSpace, counting, maxSymbolValue, tableLog) );
+    if (FSE_DECOMPRESS_WKSP_SIZE(tableLog, maxSymbolValue) > wkspSize) return ERROR(tableLog_tooLarge);
+    workSpace = wksp->dtable + FSE_DTABLE_SIZE_U32(tableLog);
+    wkspSize -= sizeof(*wksp) + FSE_DTABLE_SIZE(tableLog);
 
-    return FSE_decompress_usingDTable (dst, dstCapacity, ip, cSrcSize, workSpace);   /* always return, even if it is an error code */
+    CHECK_F( FSE_buildDTable_internal(wksp->dtable, wksp->ncount, maxSymbolValue, tableLog, workSpace, wkspSize) );
+
+    {
+        const void* ptr = wksp->dtable;
+        const FSE_DTableHeader* DTableH = (const FSE_DTableHeader*)ptr;
+        const U32 fastMode = DTableH->fastMode;
+
+        /* select fast mode (static) */
+        if (fastMode) return FSE_decompress_usingDTable_generic(dst, dstCapacity, ip, cSrcSize, wksp->dtable, 1);
+        return FSE_decompress_usingDTable_generic(dst, dstCapacity, ip, cSrcSize, wksp->dtable, 0);
+    }
+}
+
+/* Avoids the FORCE_INLINE of the _body() function. */
+static size_t FSE_decompress_wksp_body_default(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize)
+{
+    return FSE_decompress_wksp_body(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, 0);
+}
+
+#if DYNAMIC_BMI2
+TARGET_ATTRIBUTE("bmi2") static size_t FSE_decompress_wksp_body_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize)
+{
+    return FSE_decompress_wksp_body(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize, 1);
+}
+#endif
+
+size_t FSE_decompress_wksp_bmi2(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize, unsigned maxLog, void* workSpace, size_t wkspSize, int bmi2)
+{
+#if DYNAMIC_BMI2
+    if (bmi2) {
+        return FSE_decompress_wksp_body_bmi2(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize);
+    }
+#endif
+    (void)bmi2;
+    return FSE_decompress_wksp_body_default(dst, dstCapacity, cSrc, cSrcSize, maxLog, workSpace, wkspSize);
 }
 
 
 typedef FSE_DTable DTable_max_t[FSE_DTABLE_SIZE_U32(FSE_MAX_TABLELOG)];
 
-size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize)
-{
-    DTable_max_t dt;   /* Static analyzer seems unable to understand this table will be properly initialized later */
-    return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, dt, FSE_MAX_TABLELOG);
+#ifndef ZSTD_NO_UNUSED_FUNCTIONS
+size_t FSE_buildDTable(FSE_DTable* dt, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog) {
+    U32 wksp[FSE_BUILD_DTABLE_WKSP_SIZE_U32(FSE_TABLELOG_ABSOLUTE_MAX, FSE_MAX_SYMBOL_VALUE)];
+    return FSE_buildDTable_wksp(dt, normalizedCounter, maxSymbolValue, tableLog, wksp, sizeof(wksp));
 }
 
+size_t FSE_decompress(void* dst, size_t dstCapacity, const void* cSrc, size_t cSrcSize)
+{
+    /* Static analyzer seems unable to understand this table will be properly initialized later */
+    U32 wksp[FSE_DECOMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)];
+    return FSE_decompress_wksp(dst, dstCapacity, cSrc, cSrcSize, FSE_MAX_TABLELOG, wksp, sizeof(wksp));
+}
+#endif
 
 
 #endif   /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/common/huf.h b/Utilities/cmzstd/lib/common/huf.h
index ef43268..3d47ced 100644
--- a/Utilities/cmzstd/lib/common/huf.h
+++ b/Utilities/cmzstd/lib/common/huf.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * huff0 huffman codec,
  * part of Finite State Entropy library
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  * You can contact the author at :
  * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -20,7 +20,7 @@
 #define HUF_H_298734234
 
 /* *** Dependencies *** */
-#include <stddef.h>    /* size_t */
+#include "zstd_deps.h"    /* size_t */
 
 
 /* *** library symbols visibility *** */
@@ -111,6 +111,8 @@
 
 /* *** Dependencies *** */
 #include "mem.h"   /* U32 */
+#define FSE_STATIC_LINKING_ONLY
+#include "fse.h"
 
 
 /* *** Constants *** */
@@ -133,12 +135,16 @@
 #define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size))   /* Macro version, useful for static allocation */
 
 /* static allocation of HUF's Compression Table */
+/* this is a private definition, just exposed for allocation and strict aliasing purpose. never EVER access its members directly */
+struct HUF_CElt_s {
+  U16  val;
+  BYTE nbBits;
+};   /* typedef'd to HUF_CElt */
+typedef struct HUF_CElt_s HUF_CElt;   /* consider it an incomplete type */
 #define HUF_CTABLE_SIZE_U32(maxSymbolValue)   ((maxSymbolValue)+1)   /* Use tables of U32, for proper alignment */
 #define HUF_CTABLE_SIZE(maxSymbolValue)       (HUF_CTABLE_SIZE_U32(maxSymbolValue) * sizeof(U32))
 #define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \
-    U32 name##hb[HUF_CTABLE_SIZE_U32(maxSymbolValue)]; \
-    void* name##hv = &(name##hb); \
-    HUF_CElt* name = (HUF_CElt*)(name##hv)   /* no final ; */
+    HUF_CElt name[HUF_CTABLE_SIZE_U32(maxSymbolValue)] /* no final ; */
 
 /* static allocation of HUF's DTable */
 typedef U32 HUF_DTable;
@@ -184,9 +190,9 @@
  *  or to save and regenerate 'CTable' using external methods.
  */
 unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue);
-typedef struct HUF_CElt_s HUF_CElt;   /* incomplete type */
 size_t HUF_buildCTable (HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits);   /* @return : maxNbBits; CTable and count can overlap. In which case, CTable will overwrite count content */
 size_t HUF_writeCTable (void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog);
+size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize, const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog, void* workspace, size_t workspaceSize);
 size_t HUF_compress4X_usingCTable(void* dst, size_t dstSize, const void* src, size_t srcSize, const HUF_CElt* CTable);
 size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue);
 int HUF_validateCTable(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue);
@@ -226,6 +232,19 @@
                      U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
                      const void* src, size_t srcSize);
 
+/*! HUF_readStats_wksp() :
+ * Same as HUF_readStats() but takes an external workspace which must be
+ * 4-byte aligned and its size must be >= HUF_READ_STATS_WORKSPACE_SIZE.
+ * If the CPU has BMI2 support, pass bmi2=1, otherwise pass bmi2=0.
+ */
+#define HUF_READ_STATS_WORKSPACE_SIZE_U32 FSE_DECOMPRESS_WKSP_SIZE_U32(6, HUF_TABLELOG_MAX-1)
+#define HUF_READ_STATS_WORKSPACE_SIZE (HUF_READ_STATS_WORKSPACE_SIZE_U32 * sizeof(unsigned))
+size_t HUF_readStats_wksp(BYTE* huffWeight, size_t hwSize,
+                          U32* rankStats, U32* nbSymbolsPtr, U32* tableLogPtr,
+                          const void* src, size_t srcSize,
+                          void* workspace, size_t wkspSize,
+                          int bmi2);
+
 /** HUF_readCTable() :
  *  Loading a CTable saved with HUF_writeCTable() */
 size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned *hasZeroWeights);
@@ -260,7 +279,7 @@
  *  a required workspace size greater than that specified in the following
  *  macro.
  */
-#define HUF_DECOMPRESS_WORKSPACE_SIZE (2 << 10)
+#define HUF_DECOMPRESS_WORKSPACE_SIZE ((2 << 10) + (1 << 9))
 #define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32))
 
 #ifndef HUF_FORCE_DECOMPRESS_X2
@@ -332,6 +351,9 @@
 #endif
 size_t HUF_decompress4X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2);
 size_t HUF_decompress4X_hufOnly_wksp_bmi2(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize, void* workSpace, size_t wkspSize, int bmi2);
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2);
+#endif
 
 #endif /* HUF_STATIC_LINKING_ONLY */
 
diff --git a/Utilities/cmzstd/lib/common/mem.h b/Utilities/cmzstd/lib/common/mem.h
index c3f98fc..9f3b81a 100644
--- a/Utilities/cmzstd/lib/common/mem.h
+++ b/Utilities/cmzstd/lib/common/mem.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -18,8 +18,10 @@
 /*-****************************************
 *  Dependencies
 ******************************************/
-#include <stddef.h>     /* size_t, ptrdiff_t */
-#include <string.h>     /* memcpy */
+#include <stddef.h>  /* size_t, ptrdiff_t */
+#include "compiler.h"  /* __has_builtin */
+#include "debug.h"  /* DEBUG_STATIC_ASSERT */
+#include "zstd_deps.h"  /* ZSTD_memcpy */
 
 
 /*-****************************************
@@ -39,93 +41,15 @@
 #  define MEM_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
 #endif
 
-#ifndef __has_builtin
-#  define __has_builtin(x) 0  /* compat. with non-clang compilers */
-#endif
-
-/* code only tested on 32 and 64 bits systems */
-#define MEM_STATIC_ASSERT(c)   { enum { MEM_static_assert = 1/(int)(!!(c)) }; }
-MEM_STATIC void MEM_check(void) { MEM_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); }
-
-/* detects whether we are being compiled under msan */
-#if defined (__has_feature)
-#  if __has_feature(memory_sanitizer)
-#    define MEMORY_SANITIZER 1
-#  endif
-#endif
-
-#if defined (MEMORY_SANITIZER)
-/* Not all platforms that support msan provide sanitizers/msan_interface.h.
- * We therefore declare the functions we need ourselves, rather than trying to
- * include the header file... */
-
-#include <stdint.h> /* intptr_t */
-
-/* Make memory region fully initialized (without changing its contents). */
-void __msan_unpoison(const volatile void *a, size_t size);
-
-/* Make memory region fully uninitialized (without changing its contents).
-   This is a legacy interface that does not update origin information. Use
-   __msan_allocated_memory() instead. */
-void __msan_poison(const volatile void *a, size_t size);
-
-/* Returns the offset of the first (at least partially) poisoned byte in the
-   memory range, or -1 if the whole range is good. */
-intptr_t __msan_test_shadow(const volatile void *x, size_t size);
-#endif
-
-/* detects whether we are being compiled under asan */
-#if defined (__has_feature)
-#  if __has_feature(address_sanitizer)
-#    define ADDRESS_SANITIZER 1
-#  endif
-#elif defined(__SANITIZE_ADDRESS__)
-#  define ADDRESS_SANITIZER 1
-#endif
-
-#if defined (ADDRESS_SANITIZER)
-/* Not all platforms that support asan provide sanitizers/asan_interface.h.
- * We therefore declare the functions we need ourselves, rather than trying to
- * include the header file... */
-
-/**
- * Marks a memory region (<c>[addr, addr+size)</c>) as unaddressable.
- *
- * This memory must be previously allocated by your program. Instrumented
- * code is forbidden from accessing addresses in this region until it is
- * unpoisoned. This function is not guaranteed to poison the entire region -
- * it could poison only a subregion of <c>[addr, addr+size)</c> due to ASan
- * alignment restrictions.
- *
- * \note This function is not thread-safe because no two threads can poison or
- * unpoison memory in the same memory region simultaneously.
- *
- * \param addr Start of memory region.
- * \param size Size of memory region. */
-void __asan_poison_memory_region(void const volatile *addr, size_t size);
-
-/**
- * Marks a memory region (<c>[addr, addr+size)</c>) as addressable.
- *
- * This memory must be previously allocated by your program. Accessing
- * addresses in this region is allowed until this region is poisoned again.
- * This function could unpoison a super-region of <c>[addr, addr+size)</c> due
- * to ASan alignment restrictions.
- *
- * \note This function is not thread-safe because no two threads can
- * poison or unpoison memory in the same memory region simultaneously.
- *
- * \param addr Start of memory region.
- * \param size Size of memory region. */
-void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
-#endif
-
-
 /*-**************************************************************
 *  Basic Types
 *****************************************************************/
 #if  !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
-# include <stdint.h>
+#  if defined(_AIX)
+#    include <inttypes.h>
+#  else
+#    include <stdint.h> /* intptr_t */
+#  endif
   typedef   uint8_t BYTE;
   typedef  uint16_t U16;
   typedef   int16_t S16;
@@ -157,7 +81,53 @@
 
 
 /*-**************************************************************
-*  Memory I/O
+*  Memory I/O API
+*****************************************************************/
+/*=== Static platform detection ===*/
+MEM_STATIC unsigned MEM_32bits(void);
+MEM_STATIC unsigned MEM_64bits(void);
+MEM_STATIC unsigned MEM_isLittleEndian(void);
+
+/*=== Native unaligned read/write ===*/
+MEM_STATIC U16 MEM_read16(const void* memPtr);
+MEM_STATIC U32 MEM_read32(const void* memPtr);
+MEM_STATIC U64 MEM_read64(const void* memPtr);
+MEM_STATIC size_t MEM_readST(const void* memPtr);
+
+MEM_STATIC void MEM_write16(void* memPtr, U16 value);
+MEM_STATIC void MEM_write32(void* memPtr, U32 value);
+MEM_STATIC void MEM_write64(void* memPtr, U64 value);
+
+/*=== Little endian unaligned read/write ===*/
+MEM_STATIC U16 MEM_readLE16(const void* memPtr);
+MEM_STATIC U32 MEM_readLE24(const void* memPtr);
+MEM_STATIC U32 MEM_readLE32(const void* memPtr);
+MEM_STATIC U64 MEM_readLE64(const void* memPtr);
+MEM_STATIC size_t MEM_readLEST(const void* memPtr);
+
+MEM_STATIC void MEM_writeLE16(void* memPtr, U16 val);
+MEM_STATIC void MEM_writeLE24(void* memPtr, U32 val);
+MEM_STATIC void MEM_writeLE32(void* memPtr, U32 val32);
+MEM_STATIC void MEM_writeLE64(void* memPtr, U64 val64);
+MEM_STATIC void MEM_writeLEST(void* memPtr, size_t val);
+
+/*=== Big endian unaligned read/write ===*/
+MEM_STATIC U32 MEM_readBE32(const void* memPtr);
+MEM_STATIC U64 MEM_readBE64(const void* memPtr);
+MEM_STATIC size_t MEM_readBEST(const void* memPtr);
+
+MEM_STATIC void MEM_writeBE32(void* memPtr, U32 val32);
+MEM_STATIC void MEM_writeBE64(void* memPtr, U64 val64);
+MEM_STATIC void MEM_writeBEST(void* memPtr, size_t val);
+
+/*=== Byteswap ===*/
+MEM_STATIC U32 MEM_swap32(U32 in);
+MEM_STATIC U64 MEM_swap64(U64 in);
+MEM_STATIC size_t MEM_swapST(size_t in);
+
+
+/*-**************************************************************
+*  Memory I/O Implementation
 *****************************************************************/
 /* MEM_FORCE_MEMORY_ACCESS :
  * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
@@ -234,37 +204,37 @@
 
 MEM_STATIC U16 MEM_read16(const void* memPtr)
 {
-    U16 val; memcpy(&val, memPtr, sizeof(val)); return val;
+    U16 val; ZSTD_memcpy(&val, memPtr, sizeof(val)); return val;
 }
 
 MEM_STATIC U32 MEM_read32(const void* memPtr)
 {
-    U32 val; memcpy(&val, memPtr, sizeof(val)); return val;
+    U32 val; ZSTD_memcpy(&val, memPtr, sizeof(val)); return val;
 }
 
 MEM_STATIC U64 MEM_read64(const void* memPtr)
 {
-    U64 val; memcpy(&val, memPtr, sizeof(val)); return val;
+    U64 val; ZSTD_memcpy(&val, memPtr, sizeof(val)); return val;
 }
 
 MEM_STATIC size_t MEM_readST(const void* memPtr)
 {
-    size_t val; memcpy(&val, memPtr, sizeof(val)); return val;
+    size_t val; ZSTD_memcpy(&val, memPtr, sizeof(val)); return val;
 }
 
 MEM_STATIC void MEM_write16(void* memPtr, U16 value)
 {
-    memcpy(memPtr, &value, sizeof(value));
+    ZSTD_memcpy(memPtr, &value, sizeof(value));
 }
 
 MEM_STATIC void MEM_write32(void* memPtr, U32 value)
 {
-    memcpy(memPtr, &value, sizeof(value));
+    ZSTD_memcpy(memPtr, &value, sizeof(value));
 }
 
 MEM_STATIC void MEM_write64(void* memPtr, U64 value)
 {
-    memcpy(memPtr, &value, sizeof(value));
+    ZSTD_memcpy(memPtr, &value, sizeof(value));
 }
 
 #endif /* MEM_FORCE_MEMORY_ACCESS */
@@ -336,7 +306,7 @@
 
 MEM_STATIC U32 MEM_readLE24(const void* memPtr)
 {
-    return MEM_readLE16(memPtr) + (((const BYTE*)memPtr)[2] << 16);
+    return (U32)MEM_readLE16(memPtr) + ((U32)(((const BYTE*)memPtr)[2]) << 16);
 }
 
 MEM_STATIC void MEM_writeLE24(void* memPtr, U32 val)
@@ -443,6 +413,9 @@
         MEM_writeBE64(memPtr, (U64)val);
 }
 
+/* code only tested on 32 and 64 bits systems */
+MEM_STATIC void MEM_check(void) { DEBUG_STATIC_ASSERT((sizeof(size_t)==4) || (sizeof(size_t)==8)); }
+
 
 #if defined (__cplusplus)
 }
diff --git a/Utilities/cmzstd/lib/common/pool.c b/Utilities/cmzstd/lib/common/pool.c
index aa4b4de..ea70b8b 100644
--- a/Utilities/cmzstd/lib/common/pool.c
+++ b/Utilities/cmzstd/lib/common/pool.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -10,9 +10,9 @@
 
 
 /* ======   Dependencies   ======= */
-#include <stddef.h>    /* size_t */
+#include "zstd_deps.h" /* size_t */
 #include "debug.h"     /* assert */
-#include "zstd_internal.h"  /* ZSTD_malloc, ZSTD_free */
+#include "zstd_internal.h"  /* ZSTD_customMalloc, ZSTD_customFree */
 #include "pool.h"
 
 /* ======   Compiler specifics   ====== */
@@ -105,6 +105,10 @@
     assert(0);  /* Unreachable */
 }
 
+POOL_ctx* ZSTD_createThreadPool(size_t numThreads) {
+    return POOL_create (numThreads, 0);
+}
+
 POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {
     return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
 }
@@ -115,14 +119,14 @@
     /* Check parameters */
     if (!numThreads) { return NULL; }
     /* Allocate the context and zero initialize */
-    ctx = (POOL_ctx*)ZSTD_calloc(sizeof(POOL_ctx), customMem);
+    ctx = (POOL_ctx*)ZSTD_customCalloc(sizeof(POOL_ctx), customMem);
     if (!ctx) { return NULL; }
     /* Initialize the job queue.
      * It needs one extra space since one space is wasted to differentiate
      * empty and full queues.
      */
     ctx->queueSize = queueSize + 1;
-    ctx->queue = (POOL_job*)ZSTD_malloc(ctx->queueSize * sizeof(POOL_job), customMem);
+    ctx->queue = (POOL_job*)ZSTD_customMalloc(ctx->queueSize * sizeof(POOL_job), customMem);
     ctx->queueHead = 0;
     ctx->queueTail = 0;
     ctx->numThreadsBusy = 0;
@@ -136,7 +140,7 @@
     }
     ctx->shutdown = 0;
     /* Allocate space for the thread handles */
-    ctx->threads = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), customMem);
+    ctx->threads = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), customMem);
     ctx->threadCapacity = 0;
     ctx->customMem = customMem;
     /* Check for errors */
@@ -179,12 +183,14 @@
     ZSTD_pthread_mutex_destroy(&ctx->queueMutex);
     ZSTD_pthread_cond_destroy(&ctx->queuePushCond);
     ZSTD_pthread_cond_destroy(&ctx->queuePopCond);
-    ZSTD_free(ctx->queue, ctx->customMem);
-    ZSTD_free(ctx->threads, ctx->customMem);
-    ZSTD_free(ctx, ctx->customMem);
+    ZSTD_customFree(ctx->queue, ctx->customMem);
+    ZSTD_customFree(ctx->threads, ctx->customMem);
+    ZSTD_customFree(ctx, ctx->customMem);
 }
 
-
+void ZSTD_freeThreadPool (ZSTD_threadPool* pool) {
+  POOL_free (pool);
+}
 
 size_t POOL_sizeof(POOL_ctx *ctx) {
     if (ctx==NULL) return 0;  /* supports sizeof NULL */
@@ -203,11 +209,11 @@
         return 0;
     }
     /* numThreads > threadCapacity */
-    {   ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_malloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem);
+    {   ZSTD_pthread_t* const threadPool = (ZSTD_pthread_t*)ZSTD_customMalloc(numThreads * sizeof(ZSTD_pthread_t), ctx->customMem);
         if (!threadPool) return 1;
         /* replace existing thread pool */
-        memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool));
-        ZSTD_free(ctx->threads, ctx->customMem);
+        ZSTD_memcpy(threadPool, ctx->threads, ctx->threadCapacity * sizeof(*threadPool));
+        ZSTD_customFree(ctx->threads, ctx->customMem);
         ctx->threads = threadPool;
         /* Initialize additional threads */
         {   size_t threadId;
@@ -301,7 +307,7 @@
 struct POOL_ctx_s {
     int dummy;
 };
-static POOL_ctx g_ctx;
+static POOL_ctx g_poolCtx;
 
 POOL_ctx* POOL_create(size_t numThreads, size_t queueSize) {
     return POOL_create_advanced(numThreads, queueSize, ZSTD_defaultCMem);
@@ -311,11 +317,11 @@
     (void)numThreads;
     (void)queueSize;
     (void)customMem;
-    return &g_ctx;
+    return &g_poolCtx;
 }
 
 void POOL_free(POOL_ctx* ctx) {
-    assert(!ctx || ctx == &g_ctx);
+    assert(!ctx || ctx == &g_poolCtx);
     (void)ctx;
 }
 
@@ -337,7 +343,7 @@
 
 size_t POOL_sizeof(POOL_ctx* ctx) {
     if (ctx==NULL) return 0;  /* supports sizeof NULL */
-    assert(ctx == &g_ctx);
+    assert(ctx == &g_poolCtx);
     return sizeof(*ctx);
 }
 
diff --git a/Utilities/cmzstd/lib/common/pool.h b/Utilities/cmzstd/lib/common/pool.h
index 259bafc..e18aa07 100644
--- a/Utilities/cmzstd/lib/common/pool.h
+++ b/Utilities/cmzstd/lib/common/pool.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -16,7 +16,7 @@
 #endif
 
 
-#include <stddef.h>   /* size_t */
+#include "zstd_deps.h"
 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_customMem */
 #include "../zstd.h"
 
diff --git a/Utilities/cmzstd/lib/common/threading.c b/Utilities/cmzstd/lib/common/threading.c
index e2edb31..92cf57c 100644
--- a/Utilities/cmzstd/lib/common/threading.c
+++ b/Utilities/cmzstd/lib/common/threading.c
@@ -78,11 +78,12 @@
 
 #if defined(ZSTD_MULTITHREAD) && DEBUGLEVEL >= 1 && !defined(_WIN32)
 
-#include <stdlib.h>
+#define ZSTD_DEPS_NEED_MALLOC
+#include "zstd_deps.h"
 
 int ZSTD_pthread_mutex_init(ZSTD_pthread_mutex_t* mutex, pthread_mutexattr_t const* attr)
 {
-    *mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
+    *mutex = (pthread_mutex_t*)ZSTD_malloc(sizeof(pthread_mutex_t));
     if (!*mutex)
         return 1;
     return pthread_mutex_init(*mutex, attr);
@@ -94,14 +95,14 @@
         return 0;
     {
         int const ret = pthread_mutex_destroy(*mutex);
-        free(*mutex);
+        ZSTD_free(*mutex);
         return ret;
     }
 }
 
 int ZSTD_pthread_cond_init(ZSTD_pthread_cond_t* cond, pthread_condattr_t const* attr)
 {
-    *cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
+    *cond = (pthread_cond_t*)ZSTD_malloc(sizeof(pthread_cond_t));
     if (!*cond)
         return 1;
     return pthread_cond_init(*cond, attr);
@@ -113,7 +114,7 @@
         return 0;
     {
         int const ret = pthread_cond_destroy(*cond);
-        free(*cond);
+        ZSTD_free(*cond);
         return ret;
     }
 }
diff --git a/Utilities/cmzstd/lib/common/xxhash.c b/Utilities/cmzstd/lib/common/xxhash.c
index 3d7655e..926b336 100644
--- a/Utilities/cmzstd/lib/common/xxhash.c
+++ b/Utilities/cmzstd/lib/common/xxhash.c
@@ -1,6 +1,6 @@
 /*
  *  xxHash - Fast Hash algorithm
- *  Copyright (c) 2012-2020, Yann Collet, Facebook, Inc.
+ *  Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - xxHash homepage: http://www.xxhash.com
@@ -75,14 +75,12 @@
 *  Includes & Memory related functions
 ***************************************/
 /* Modify the local functions below should you wish to use some other memory routines */
-/* for malloc(), free() */
-#include <stdlib.h>
-#include <stddef.h>     /* size_t */
-static void* XXH_malloc(size_t s) { return malloc(s); }
-static void  XXH_free  (void* p)  { free(p); }
-/* for memcpy() */
-#include <string.h>
-static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
+/* for ZSTD_malloc(), ZSTD_free() */
+#define ZSTD_DEPS_NEED_MALLOC
+#include "zstd_deps.h"  /* size_t, ZSTD_malloc, ZSTD_free, ZSTD_memcpy */
+static void* XXH_malloc(size_t s) { return ZSTD_malloc(s); }
+static void  XXH_free  (void* p)  { ZSTD_free(p); }
+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return ZSTD_memcpy(dest,src,size); }
 
 #ifndef XXH_STATIC_LINKING_ONLY
 #  define XXH_STATIC_LINKING_ONLY
@@ -93,49 +91,13 @@
 /* *************************************
 *  Compiler Specific Options
 ***************************************/
-#if (defined(__GNUC__) && !defined(__STRICT_ANSI__)) || defined(__cplusplus) || defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
-#  define INLINE_KEYWORD inline
-#else
-#  define INLINE_KEYWORD
-#endif
-
-#if defined(__GNUC__) || defined(__ICCARM__)
-#  define FORCE_INLINE_ATTR __attribute__((always_inline))
-#elif defined(_MSC_VER)
-#  define FORCE_INLINE_ATTR __forceinline
-#else
-#  define FORCE_INLINE_ATTR
-#endif
-
-#define FORCE_INLINE_TEMPLATE static INLINE_KEYWORD FORCE_INLINE_ATTR
-
-
-#ifdef _MSC_VER
-#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */
-#endif
+#include "compiler.h"
 
 
 /* *************************************
 *  Basic Types
 ***************************************/
-#ifndef MEM_MODULE
-# define MEM_MODULE
-# if !defined (__VMS) && (defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
-#   include <stdint.h>
-    typedef uint8_t  BYTE;
-    typedef uint16_t U16;
-    typedef uint32_t U32;
-    typedef  int32_t S32;
-    typedef uint64_t U64;
-#  else
-    typedef unsigned char      BYTE;
-    typedef unsigned short     U16;
-    typedef unsigned int       U32;
-    typedef   signed int       S32;
-    typedef unsigned long long U64;   /* if your compiler doesn't support unsigned long long, replace by another 64-bit type here. Note that xxhash.h will also need to be updated. */
-#  endif
-#endif
-
+#include "mem.h"  /* BYTE, U32, U64, size_t */
 
 #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
 
@@ -161,14 +123,14 @@
 static U32 XXH_read32(const void* memPtr)
 {
     U32 val;
-    memcpy(&val, memPtr, sizeof(val));
+    ZSTD_memcpy(&val, memPtr, sizeof(val));
     return val;
 }
 
 static U64 XXH_read64(const void* memPtr)
 {
     U64 val;
-    memcpy(&val, memPtr, sizeof(val));
+    ZSTD_memcpy(&val, memPtr, sizeof(val));
     return val;
 }
 
@@ -305,12 +267,12 @@
 ****************************/
 XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* restrict dstState, const XXH32_state_t* restrict srcState)
 {
-    memcpy(dstState, srcState, sizeof(*dstState));
+    ZSTD_memcpy(dstState, srcState, sizeof(*dstState));
 }
 
 XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* restrict dstState, const XXH64_state_t* restrict srcState)
 {
-    memcpy(dstState, srcState, sizeof(*dstState));
+    ZSTD_memcpy(dstState, srcState, sizeof(*dstState));
 }
 
 
@@ -552,12 +514,12 @@
 XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
 {
     XXH32_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
-    memset(&state, 0, sizeof(state)-4);   /* do not write into reserved, for future removal */
+    ZSTD_memset(&state, 0, sizeof(state)-4);   /* do not write into reserved, for future removal */
     state.v1 = seed + PRIME32_1 + PRIME32_2;
     state.v2 = seed + PRIME32_2;
     state.v3 = seed + 0;
     state.v4 = seed - PRIME32_1;
-    memcpy(statePtr, &state, sizeof(state));
+    ZSTD_memcpy(statePtr, &state, sizeof(state));
     return XXH_OK;
 }
 
@@ -565,12 +527,12 @@
 XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
 {
     XXH64_state_t state;   /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
-    memset(&state, 0, sizeof(state)-8);   /* do not write into reserved, for future removal */
+    ZSTD_memset(&state, 0, sizeof(state)-8);   /* do not write into reserved, for future removal */
     state.v1 = seed + PRIME64_1 + PRIME64_2;
     state.v2 = seed + PRIME64_2;
     state.v3 = seed + 0;
     state.v4 = seed - PRIME64_1;
-    memcpy(statePtr, &state, sizeof(state));
+    ZSTD_memcpy(statePtr, &state, sizeof(state));
     return XXH_OK;
 }
 
@@ -841,14 +803,14 @@
 {
     XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
     if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
-    memcpy(dst, &hash, sizeof(*dst));
+    ZSTD_memcpy(dst, &hash, sizeof(*dst));
 }
 
 XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
 {
     XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
     if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
-    memcpy(dst, &hash, sizeof(*dst));
+    ZSTD_memcpy(dst, &hash, sizeof(*dst));
 }
 
 XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
diff --git a/Utilities/cmzstd/lib/common/xxhash.h b/Utilities/cmzstd/lib/common/xxhash.h
index 4207eba..16c1f16 100644
--- a/Utilities/cmzstd/lib/common/xxhash.h
+++ b/Utilities/cmzstd/lib/common/xxhash.h
@@ -1,7 +1,7 @@
 /*
  * xxHash - Extremely Fast Hash algorithm
  * Header File
- * Copyright (c) 2012-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  * You can contact the author at :
  * - xxHash source repository : https://github.com/Cyan4973/xxHash
@@ -55,7 +55,7 @@
 /* ****************************
 *  Definitions
 ******************************/
-#include <stddef.h>   /* size_t */
+#include "zstd_deps.h"
 typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
 
 
diff --git a/Utilities/cmzstd/lib/common/zstd_common.c b/Utilities/cmzstd/lib/common/zstd_common.c
index 91fe332..3d7e35b 100644
--- a/Utilities/cmzstd/lib/common/zstd_common.c
+++ b/Utilities/cmzstd/lib/common/zstd_common.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -13,8 +13,8 @@
 /*-*************************************
 *  Dependencies
 ***************************************/
-#include <stdlib.h>      /* malloc, calloc, free */
-#include <string.h>      /* memset */
+#define ZSTD_DEPS_NEED_MALLOC
+#include "zstd_deps.h"   /* ZSTD_malloc, ZSTD_calloc, ZSTD_free, ZSTD_memset */
 #include "error_private.h"
 #include "zstd_internal.h"
 
@@ -53,31 +53,31 @@
 /*=**************************************************************
 *  Custom allocator
 ****************************************************************/
-void* ZSTD_malloc(size_t size, ZSTD_customMem customMem)
+void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem)
 {
     if (customMem.customAlloc)
         return customMem.customAlloc(customMem.opaque, size);
-    return malloc(size);
+    return ZSTD_malloc(size);
 }
 
-void* ZSTD_calloc(size_t size, ZSTD_customMem customMem)
+void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem)
 {
     if (customMem.customAlloc) {
         /* calloc implemented as malloc+memset;
          * not as efficient as calloc, but next best guess for custom malloc */
         void* const ptr = customMem.customAlloc(customMem.opaque, size);
-        memset(ptr, 0, size);
+        ZSTD_memset(ptr, 0, size);
         return ptr;
     }
-    return calloc(1, size);
+    return ZSTD_calloc(1, size);
 }
 
-void ZSTD_free(void* ptr, ZSTD_customMem customMem)
+void ZSTD_customFree(void* ptr, ZSTD_customMem customMem)
 {
     if (ptr!=NULL) {
         if (customMem.customFree)
             customMem.customFree(customMem.opaque, ptr);
         else
-            free(ptr);
+            ZSTD_free(ptr);
     }
 }
diff --git a/Utilities/cmzstd/lib/common/zstd_deps.h b/Utilities/cmzstd/lib/common/zstd_deps.h
new file mode 100644
index 0000000..1421134
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/zstd_deps.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+/* This file provides common libc dependencies that zstd requires.
+ * The purpose is to allow replacing this file with a custom implementation
+ * to compile zstd without libc support.
+ */
+
+/* Need:
+ * NULL
+ * INT_MAX
+ * UINT_MAX
+ * ZSTD_memcpy()
+ * ZSTD_memset()
+ * ZSTD_memmove()
+ */
+#ifndef ZSTD_DEPS_COMMON
+#define ZSTD_DEPS_COMMON
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#if defined(__GNUC__) && __GNUC__ >= 4
+# define ZSTD_memcpy(d,s,l) __builtin_memcpy((d),(s),(l))
+# define ZSTD_memmove(d,s,l) __builtin_memmove((d),(s),(l))
+# define ZSTD_memset(p,v,l) __builtin_memset((p),(v),(l))
+#else
+# define ZSTD_memcpy(d,s,l) memcpy((d),(s),(l))
+# define ZSTD_memmove(d,s,l) memmove((d),(s),(l))
+# define ZSTD_memset(p,v,l) memset((p),(v),(l))
+#endif
+
+#endif /* ZSTD_DEPS_COMMON */
+
+/* Need:
+ * ZSTD_malloc()
+ * ZSTD_free()
+ * ZSTD_calloc()
+ */
+#ifdef ZSTD_DEPS_NEED_MALLOC
+#ifndef ZSTD_DEPS_MALLOC
+#define ZSTD_DEPS_MALLOC
+
+#include <stdlib.h>
+
+#define ZSTD_malloc(s) malloc(s)
+#define ZSTD_calloc(n,s) calloc((n), (s))
+#define ZSTD_free(p) free((p))
+
+#endif /* ZSTD_DEPS_MALLOC */
+#endif /* ZSTD_DEPS_NEED_MALLOC */
+
+/*
+ * Provides 64-bit math support.
+ * Need:
+ * U64 ZSTD_div64(U64 dividend, U32 divisor)
+ */
+#ifdef ZSTD_DEPS_NEED_MATH64
+#ifndef ZSTD_DEPS_MATH64
+#define ZSTD_DEPS_MATH64
+
+#define ZSTD_div64(dividend, divisor) ((dividend) / (divisor))
+
+#endif /* ZSTD_DEPS_MATH64 */
+#endif /* ZSTD_DEPS_NEED_MATH64 */
+
+/* Need:
+ * assert()
+ */
+#ifdef ZSTD_DEPS_NEED_ASSERT
+#ifndef ZSTD_DEPS_ASSERT
+#define ZSTD_DEPS_ASSERT
+
+#include <assert.h>
+
+#endif /* ZSTD_DEPS_ASSERT */
+#endif /* ZSTD_DEPS_NEED_ASSERT */
+
+/* Need:
+ * ZSTD_DEBUG_PRINT()
+ */
+#ifdef ZSTD_DEPS_NEED_IO
+#ifndef ZSTD_DEPS_IO
+#define ZSTD_DEPS_IO
+
+#include <stdio.h>
+#define ZSTD_DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__)
+
+#endif /* ZSTD_DEPS_IO */
+#endif /* ZSTD_DEPS_NEED_IO */
+
+/* Only requested when <stdint.h> is known to be present.
+ * Need:
+ * intptr_t
+ */
+#ifdef ZSTD_DEPS_NEED_STDINT
+#ifndef ZSTD_DEPS_STDINT
+#define ZSTD_DEPS_STDINT
+
+#include <stdint.h>
+
+#endif /* ZSTD_DEPS_STDINT */
+#endif /* ZSTD_DEPS_NEED_STDINT */
diff --git a/Utilities/cmzstd/lib/common/zstd_internal.h b/Utilities/cmzstd/lib/common/zstd_internal.h
index 3bc7e55..68252e9 100644
--- a/Utilities/cmzstd/lib/common/zstd_internal.h
+++ b/Utilities/cmzstd/lib/common/zstd_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -19,7 +19,7 @@
 /*-*************************************
 *  Dependencies
 ***************************************/
-#ifdef __aarch64__
+#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON)
 #include <arm_neon.h>
 #endif
 #include "compiler.h"
@@ -36,6 +36,11 @@
 #  define XXH_STATIC_LINKING_ONLY  /* XXH64_state_t */
 #endif
 #include "xxhash.h"                /* XXH_reset, update, digest */
+#ifndef ZSTD_NO_TRACE
+#  include "zstd_trace.h"
+#else
+#  define ZSTD_TRACE 0
+#endif
 
 #if defined (__cplusplus)
 extern "C" {
@@ -139,7 +144,7 @@
 
 #define ZSTD_REP_NUM      3                 /* number of repcodes */
 #define ZSTD_REP_MOVE     (ZSTD_REP_NUM-1)
-static const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 };
+static UNUSED_ATTR const U32 repStartValue[ZSTD_REP_NUM] = { 1, 4, 8 };
 
 #define KB *(1 <<10)
 #define MB *(1 <<20)
@@ -153,13 +158,13 @@
 #define BIT0   1
 
 #define ZSTD_WINDOWLOG_ABSOLUTEMIN 10
-static const size_t ZSTD_fcs_fieldSize[4] = { 0, 2, 4, 8 };
-static const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 };
+static UNUSED_ATTR const size_t ZSTD_fcs_fieldSize[4] = { 0, 2, 4, 8 };
+static UNUSED_ATTR const size_t ZSTD_did_fieldSize[4] = { 0, 1, 2, 4 };
 
 #define ZSTD_FRAMEIDSIZE 4   /* magic number size */
 
 #define ZSTD_BLOCKHEADERSIZE 3   /* C standard doesn't allow `static const` variable to be init using another `static const` variable */
-static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE;
+static UNUSED_ATTR const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE;
 typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e;
 
 #define ZSTD_FRAMECHECKSUMSIZE 4
@@ -186,61 +191,75 @@
 #define OffFSELog   8
 #define MaxFSELog  MAX(MAX(MLFSELog, LLFSELog), OffFSELog)
 
-static const U32 LL_bits[MaxLL+1] = { 0, 0, 0, 0, 0, 0, 0, 0,
-                                      0, 0, 0, 0, 0, 0, 0, 0,
-                                      1, 1, 1, 1, 2, 2, 3, 3,
-                                      4, 6, 7, 8, 9,10,11,12,
-                                     13,14,15,16 };
-static const S16 LL_defaultNorm[MaxLL+1] = { 4, 3, 2, 2, 2, 2, 2, 2,
-                                             2, 2, 2, 2, 2, 1, 1, 1,
-                                             2, 2, 2, 2, 2, 2, 2, 2,
-                                             2, 3, 2, 1, 1, 1, 1, 1,
-                                            -1,-1,-1,-1 };
+#define ZSTD_MAX_HUF_HEADER_SIZE 128 /* header + <= 127 byte tree description */
+/* Each table cannot take more than #symbols * FSELog bits */
+#define ZSTD_MAX_FSE_HEADERS_SIZE (((MaxML + 1) * MLFSELog + (MaxLL + 1) * LLFSELog + (MaxOff + 1) * OffFSELog + 7) / 8)
+
+static UNUSED_ATTR const U32 LL_bits[MaxLL+1] = {
+     0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0,
+     1, 1, 1, 1, 2, 2, 3, 3,
+     4, 6, 7, 8, 9,10,11,12,
+    13,14,15,16
+};
+static UNUSED_ATTR const S16 LL_defaultNorm[MaxLL+1] = {
+     4, 3, 2, 2, 2, 2, 2, 2,
+     2, 2, 2, 2, 2, 1, 1, 1,
+     2, 2, 2, 2, 2, 2, 2, 2,
+     2, 3, 2, 1, 1, 1, 1, 1,
+    -1,-1,-1,-1
+};
 #define LL_DEFAULTNORMLOG 6  /* for static allocation */
-static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG;
+static UNUSED_ATTR const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG;
 
-static const U32 ML_bits[MaxML+1] = { 0, 0, 0, 0, 0, 0, 0, 0,
-                                      0, 0, 0, 0, 0, 0, 0, 0,
-                                      0, 0, 0, 0, 0, 0, 0, 0,
-                                      0, 0, 0, 0, 0, 0, 0, 0,
-                                      1, 1, 1, 1, 2, 2, 3, 3,
-                                      4, 4, 5, 7, 8, 9,10,11,
-                                     12,13,14,15,16 };
-static const S16 ML_defaultNorm[MaxML+1] = { 1, 4, 3, 2, 2, 2, 2, 2,
-                                             2, 1, 1, 1, 1, 1, 1, 1,
-                                             1, 1, 1, 1, 1, 1, 1, 1,
-                                             1, 1, 1, 1, 1, 1, 1, 1,
-                                             1, 1, 1, 1, 1, 1, 1, 1,
-                                             1, 1, 1, 1, 1, 1,-1,-1,
-                                            -1,-1,-1,-1,-1 };
+static UNUSED_ATTR const U32 ML_bits[MaxML+1] = {
+     0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0,
+     0, 0, 0, 0, 0, 0, 0, 0,
+     1, 1, 1, 1, 2, 2, 3, 3,
+     4, 4, 5, 7, 8, 9,10,11,
+    12,13,14,15,16
+};
+static UNUSED_ATTR const S16 ML_defaultNorm[MaxML+1] = {
+     1, 4, 3, 2, 2, 2, 2, 2,
+     2, 1, 1, 1, 1, 1, 1, 1,
+     1, 1, 1, 1, 1, 1, 1, 1,
+     1, 1, 1, 1, 1, 1, 1, 1,
+     1, 1, 1, 1, 1, 1, 1, 1,
+     1, 1, 1, 1, 1, 1,-1,-1,
+    -1,-1,-1,-1,-1
+};
 #define ML_DEFAULTNORMLOG 6  /* for static allocation */
-static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG;
+static UNUSED_ATTR const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG;
 
-static const S16 OF_defaultNorm[DefaultMaxOff+1] = { 1, 1, 1, 1, 1, 1, 2, 2,
-                                                     2, 1, 1, 1, 1, 1, 1, 1,
-                                                     1, 1, 1, 1, 1, 1, 1, 1,
-                                                    -1,-1,-1,-1,-1 };
+static UNUSED_ATTR const S16 OF_defaultNorm[DefaultMaxOff+1] = {
+     1, 1, 1, 1, 1, 1, 2, 2,
+     2, 1, 1, 1, 1, 1, 1, 1,
+     1, 1, 1, 1, 1, 1, 1, 1,
+    -1,-1,-1,-1,-1
+};
 #define OF_DEFAULTNORMLOG 5  /* for static allocation */
-static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
+static UNUSED_ATTR const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
 
 
 /*-*******************************************
 *  Shared functions to include for inlining
 *********************************************/
 static void ZSTD_copy8(void* dst, const void* src) {
-#ifdef __aarch64__
+#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON)
     vst1_u8((uint8_t*)dst, vld1_u8((const uint8_t*)src));
 #else
-    memcpy(dst, src, 8);
+    ZSTD_memcpy(dst, src, 8);
 #endif
 }
 
 #define COPY8(d,s) { ZSTD_copy8(d,s); d+=8; s+=8; }
 static void ZSTD_copy16(void* dst, const void* src) {
-#ifdef __aarch64__
+#if !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON)
     vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
 #else
-    memcpy(dst, src, 16);
+    ZSTD_memcpy(dst, src, 16);
 #endif
 }
 #define COPY16(d,s) { ZSTD_copy16(d,s); d+=16; s+=16; }
@@ -255,13 +274,13 @@
 } ZSTD_overlap_e;
 
 /*! ZSTD_wildcopy() :
- *  Custom version of memcpy(), can over read/write up to WILDCOPY_OVERLENGTH bytes (if length==0)
+ *  Custom version of ZSTD_memcpy(), can over read/write up to WILDCOPY_OVERLENGTH bytes (if length==0)
  *  @param ovtype controls the overlap detection
  *         - ZSTD_no_overlap: The source and destination are guaranteed to be at least WILDCOPY_VECLEN bytes apart.
  *         - ZSTD_overlap_src_before_dst: The src and dst may overlap, but they MUST be at least 8 bytes apart.
  *           The src buffer must be before the dst buffer.
  */
-MEM_STATIC FORCE_INLINE_ATTR 
+MEM_STATIC FORCE_INLINE_ATTR
 void ZSTD_wildcopy(void* dst, const void* src, ptrdiff_t length, ZSTD_overlap_e const ovtype)
 {
     ptrdiff_t diff = (BYTE*)dst - (const BYTE*)src;
@@ -284,14 +303,16 @@
          * one COPY16() in the first call. Then, do two calls per loop since
          * at that point it is more likely to have a high trip count.
          */
-#ifndef __aarch64__
+#ifdef __aarch64__
         do {
             COPY16(op, ip);
         }
         while (op < oend);
 #else
-        COPY16(op, ip);
-        if (op >= oend) return;
+        ZSTD_copy16(op, ip);
+        if (16 >= length) return;
+        op += 16;
+        ip += 16;
         do {
             COPY16(op, ip);
             COPY16(op, ip);
@@ -305,7 +326,7 @@
 {
     size_t const length = MIN(dstCapacity, srcSize);
     if (length > 0) {
-        memcpy(dst, src, length);
+        ZSTD_memcpy(dst, src, length);
     }
     return length;
 }
@@ -320,28 +341,46 @@
  * In which case, resize it down to free some memory */
 #define ZSTD_WORKSPACETOOLARGE_MAXDURATION 128
 
+/* Controls whether the input/output buffer is buffered or stable. */
+typedef enum {
+    ZSTD_bm_buffered = 0,  /* Buffer the input/output */
+    ZSTD_bm_stable = 1     /* ZSTD_inBuffer/ZSTD_outBuffer is stable */
+} ZSTD_bufferMode_e;
+
 
 /*-*******************************************
 *  Private declarations
 *********************************************/
 typedef struct seqDef_s {
-    U32 offset;
+    U32 offset;         /* offset == rawOffset + ZSTD_REP_NUM, or equivalently, offCode + 1 */
     U16 litLength;
     U16 matchLength;
 } seqDef;
 
+/* Controls whether seqStore has a single "long" litLength or matchLength. See seqStore_t. */
+typedef enum {
+    ZSTD_llt_none = 0,             /* no longLengthType */
+    ZSTD_llt_literalLength = 1,    /* represents a long literal */
+    ZSTD_llt_matchLength = 2       /* represents a long match */
+} ZSTD_longLengthType_e;
+
 typedef struct {
     seqDef* sequencesStart;
-    seqDef* sequences;
+    seqDef* sequences;      /* ptr to end of sequences */
     BYTE* litStart;
-    BYTE* lit;
+    BYTE* lit;              /* ptr to end of literals */
     BYTE* llCode;
     BYTE* mlCode;
     BYTE* ofCode;
     size_t maxNbSeq;
     size_t maxNbLit;
-    U32   longLengthID;   /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */
-    U32   longLengthPos;
+
+    /* longLengthPos and longLengthType to allow us to represent either a single litLength or matchLength
+     * in the seqStore that has a value larger than U16 (if it exists). To do so, we increment
+     * the existing value of the litLength or matchLength by 0x10000.
+     */
+    ZSTD_longLengthType_e   longLengthType;
+    U32                     longLengthPos;  /* Index of the sequence to apply long length modification to */
 } seqStore_t;
 
 typedef struct {
@@ -351,7 +390,7 @@
 
 /**
  * Returns the ZSTD_sequenceLength for the given sequences. It handles the decoding of long sequences
- * indicated by longLengthPos and longLengthID, and adds MINMATCH back to matchLength.
+ * indicated by longLengthPos and longLengthType, and adds MINMATCH back to matchLength.
  */
 MEM_STATIC ZSTD_sequenceLength ZSTD_getSequenceLength(seqStore_t const* seqStore, seqDef const* seq)
 {
@@ -359,10 +398,10 @@
     seqLen.litLength = seq->litLength;
     seqLen.matchLength = seq->matchLength + MINMATCH;
     if (seqStore->longLengthPos == (U32)(seq - seqStore->sequencesStart)) {
-        if (seqStore->longLengthID == 1) {
+        if (seqStore->longLengthType == ZSTD_llt_literalLength) {
             seqLen.litLength += 0xFFFF;
         }
-        if (seqStore->longLengthID == 2) {
+        if (seqStore->longLengthType == ZSTD_llt_matchLength) {
             seqLen.matchLength += 0xFFFF;
         }
     }
@@ -384,9 +423,9 @@
 void ZSTD_seqToCodes(const seqStore_t* seqStorePtr);   /* compress, dictBuilder, decodeCorpus (shouldn't get its definition from here) */
 
 /* custom memory allocation functions */
-void* ZSTD_malloc(size_t size, ZSTD_customMem customMem);
-void* ZSTD_calloc(size_t size, ZSTD_customMem customMem);
-void ZSTD_free(void* ptr, ZSTD_customMem customMem);
+void* ZSTD_customMalloc(size_t size, ZSTD_customMem customMem);
+void* ZSTD_customCalloc(size_t size, ZSTD_customMem customMem);
+void ZSTD_customFree(void* ptr, ZSTD_customMem customMem);
 
 
 MEM_STATIC U32 ZSTD_highbit32(U32 val)   /* compress, dictBuilder, decodeCorpus */
@@ -394,8 +433,12 @@
     assert(val != 0);
     {
 #   if defined(_MSC_VER)   /* Visual */
-        unsigned long r=0;
-        return _BitScanReverse(&r, val) ? (unsigned)r : 0;
+#       if STATIC_BMI2 == 1
+            return _lzcnt_u32(val)^31;
+#       else
+            unsigned long r=0;
+            return _BitScanReverse(&r, val) ? (unsigned)r : 0;
+#       endif
 #   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* GCC Intrinsic */
         return __builtin_clz (val) ^ 31;
 #   elif defined(__ICCARM__)    /* IAR Intrinsic */
diff --git a/Utilities/cmzstd/lib/common/zstd_trace.h b/Utilities/cmzstd/lib/common/zstd_trace.h
new file mode 100644
index 0000000..485cadf
--- /dev/null
+++ b/Utilities/cmzstd/lib/common/zstd_trace.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_TRACE_H
+#define ZSTD_TRACE_H
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+/* weak symbol support */
+#if !defined(ZSTD_HAVE_WEAK_SYMBOLS) && defined(__GNUC__) && \
+    !defined(__APPLE__) && !defined(_WIN32) && !defined(__MINGW32__) && \
+    !defined(__CYGWIN__) && !defined(_AIX)
+#  define ZSTD_HAVE_WEAK_SYMBOLS 1
+#else
+#  define ZSTD_HAVE_WEAK_SYMBOLS 0
+#endif
+#if ZSTD_HAVE_WEAK_SYMBOLS
+#  define ZSTD_WEAK_ATTR __attribute__((__weak__))
+#else
+#  define ZSTD_WEAK_ATTR
+#endif
+
+/* Only enable tracing when weak symbols are available. */
+#ifndef ZSTD_TRACE
+#  define ZSTD_TRACE ZSTD_HAVE_WEAK_SYMBOLS
+#endif
+
+#if ZSTD_TRACE
+
+struct ZSTD_CCtx_s;
+struct ZSTD_DCtx_s;
+struct ZSTD_CCtx_params_s;
+
+typedef struct {
+    /**
+     * ZSTD_VERSION_NUMBER
+     *
+     * This is guaranteed to be the first member of ZSTD_trace.
+     * Otherwise, this struct is not stable between versions. If
+     * the version number does not match your expectation, you
+     * should not interpret the rest of the struct.
+     */
+    unsigned version;
+    /**
+     * Non-zero if streaming (de)compression is used.
+     */
+    unsigned streaming;
+    /**
+     * The dictionary ID.
+     */
+    unsigned dictionaryID;
+    /**
+     * Is the dictionary cold?
+     * Only set on decompression.
+     */
+    unsigned dictionaryIsCold;
+    /**
+     * The dictionary size or zero if no dictionary.
+     */
+    size_t dictionarySize;
+    /**
+     * The uncompressed size of the data.
+     */
+    size_t uncompressedSize;
+    /**
+     * The compressed size of the data.
+     */
+    size_t compressedSize;
+    /**
+     * The fully resolved CCtx parameters (NULL on decompression).
+     */
+    struct ZSTD_CCtx_params_s const* params;
+    /**
+     * The ZSTD_CCtx pointer (NULL on decompression).
+     */
+    struct ZSTD_CCtx_s const* cctx;
+    /**
+     * The ZSTD_DCtx pointer (NULL on compression).
+     */
+    struct ZSTD_DCtx_s const* dctx;
+} ZSTD_Trace;
+
+/**
+ * A tracing context. It must be 0 when tracing is disabled.
+ * Otherwise, any non-zero value returned by a tracing begin()
+ * function is presented to any subsequent calls to end().
+ *
+ * Any non-zero value is treated as tracing is enabled and not
+ * interpreted by the library.
+ *
+ * Two possible uses are:
+ * * A timestamp for when the begin() function was called.
+ * * A unique key identifying the (de)compression, like the
+ *   address of the [dc]ctx pointer if you need to track
+ *   more information than just a timestamp.
+ */
+typedef unsigned long long ZSTD_TraceCtx;
+
+/**
+ * Trace the beginning of a compression call.
+ * @param cctx The dctx pointer for the compression.
+ *             It can be used as a key to map begin() to end().
+ * @returns Non-zero if tracing is enabled. The return value is
+ *          passed to ZSTD_trace_compress_end().
+ */
+ZSTD_WEAK_ATTR ZSTD_TraceCtx ZSTD_trace_compress_begin(
+    struct ZSTD_CCtx_s const* cctx);
+
+/**
+ * Trace the end of a compression call.
+ * @param ctx The return value of ZSTD_trace_compress_begin().
+ * @param trace The zstd tracing info.
+ */
+ZSTD_WEAK_ATTR void ZSTD_trace_compress_end(
+    ZSTD_TraceCtx ctx,
+    ZSTD_Trace const* trace);
+
+/**
+ * Trace the beginning of a decompression call.
+ * @param dctx The dctx pointer for the decompression.
+ *             It can be used as a key to map begin() to end().
+ * @returns Non-zero if tracing is enabled. The return value is
+ *          passed to ZSTD_trace_compress_end().
+ */
+ZSTD_WEAK_ATTR ZSTD_TraceCtx ZSTD_trace_decompress_begin(
+    struct ZSTD_DCtx_s const* dctx);
+
+/**
+ * Trace the end of a decompression call.
+ * @param ctx The return value of ZSTD_trace_decompress_begin().
+ * @param trace The zstd tracing info.
+ */
+ZSTD_WEAK_ATTR void ZSTD_trace_decompress_end(
+    ZSTD_TraceCtx ctx,
+    ZSTD_Trace const* trace);
+
+#endif /* ZSTD_TRACE */
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* ZSTD_TRACE_H */
diff --git a/Utilities/cmzstd/lib/compress/fse_compress.c b/Utilities/cmzstd/lib/compress/fse_compress.c
index a427598..b4297ec 100644
--- a/Utilities/cmzstd/lib/compress/fse_compress.c
+++ b/Utilities/cmzstd/lib/compress/fse_compress.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * FSE : Finite State Entropy encoder
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -15,8 +15,6 @@
 /* **************************************************************
 *  Includes
 ****************************************************************/
-#include <stdlib.h>     /* malloc, free, qsort */
-#include <string.h>     /* memcpy, memset */
 #include "../common/compiler.h"
 #include "../common/mem.h"        /* U32, U16, etc. */
 #include "../common/debug.h"      /* assert, DEBUGLOG */
@@ -25,6 +23,9 @@
 #define FSE_STATIC_LINKING_ONLY
 #include "../common/fse.h"
 #include "../common/error_private.h"
+#define ZSTD_DEPS_NEED_MALLOC
+#define ZSTD_DEPS_NEED_MATH64
+#include "../common/zstd_deps.h"  /* ZSTD_malloc, ZSTD_free, ZSTD_memcpy, ZSTD_memset */
 
 
 /* **************************************************************
@@ -74,13 +75,15 @@
     void* const FSCT = ((U32*)ptr) + 1 /* header */ + (tableLog ? tableSize>>1 : 1) ;
     FSE_symbolCompressionTransform* const symbolTT = (FSE_symbolCompressionTransform*) (FSCT);
     U32 const step = FSE_TABLESTEP(tableSize);
-    U32 cumul[FSE_MAX_SYMBOL_VALUE+2];
 
-    FSE_FUNCTION_TYPE* const tableSymbol = (FSE_FUNCTION_TYPE*)workSpace;
+    U32* cumul = (U32*)workSpace;
+    FSE_FUNCTION_TYPE* tableSymbol = (FSE_FUNCTION_TYPE*)(cumul + (maxSymbolValue + 2));
+
     U32 highThreshold = tableSize-1;
 
+    if ((size_t)workSpace & 3) return ERROR(GENERIC); /* Must be 4 byte aligned */
+    if (FSE_BUILD_CTABLE_WORKSPACE_SIZE(maxSymbolValue, tableLog) > wkspSize) return ERROR(tableLog_tooLarge);
     /* CTable header */
-    if (((size_t)1 << tableLog) * sizeof(FSE_FUNCTION_TYPE) > wkspSize) return ERROR(tableLog_tooLarge);
     tableU16[-2] = (U16) tableLog;
     tableU16[-1] = (U16) maxSymbolValue;
     assert(tableLog < 16);   /* required for threshold strategy to work */
@@ -89,7 +92,7 @@
      * http://fastcompression.blogspot.fr/2014/02/fse-distributing-symbol-values.html */
 
      #ifdef __clang_analyzer__
-     memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize);   /* useless initialization, just to keep scan-build happy */
+     ZSTD_memset(tableSymbol, 0, sizeof(*tableSymbol) * tableSize);   /* useless initialization, just to keep scan-build happy */
      #endif
 
     /* symbol start positions */
@@ -168,12 +171,13 @@
     return 0;
 }
 
-
+#ifndef ZSTD_NO_UNUSED_FUNCTIONS
 size_t FSE_buildCTable(FSE_CTable* ct, const short* normalizedCounter, unsigned maxSymbolValue, unsigned tableLog)
 {
     FSE_FUNCTION_TYPE tableSymbol[FSE_MAX_TABLESIZE];   /* memset() is not necessary, even if static analyzer complain about it */
     return FSE_buildCTable_wksp(ct, normalizedCounter, maxSymbolValue, tableLog, tableSymbol, sizeof(tableSymbol));
 }
+#endif
 
 
 
@@ -307,10 +311,10 @@
     size_t size;
     if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
     size = FSE_CTABLE_SIZE_U32 (tableLog, maxSymbolValue) * sizeof(U32);
-    return (FSE_CTable*)malloc(size);
+    return (FSE_CTable*)ZSTD_malloc(size);
 }
 
-void FSE_freeCTable (FSE_CTable* ct) { free(ct); }
+void FSE_freeCTable (FSE_CTable* ct) { ZSTD_free(ct); }
 
 /* provides the minimum logSize to safely represent a distribution */
 static unsigned FSE_minTableLog(size_t srcSize, unsigned maxSymbolValue)
@@ -341,11 +345,10 @@
     return FSE_optimalTableLog_internal(maxTableLog, srcSize, maxSymbolValue, 2);
 }
 
-
 /* Secondary normalization method.
    To be used when primary method fails. */
 
-static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, size_t total, U32 maxSymbolValue)
+static size_t FSE_normalizeM2(short* norm, U32 tableLog, const unsigned* count, size_t total, U32 maxSymbolValue, short lowProbCount)
 {
     short const NOT_YET_ASSIGNED = -2;
     U32 s;
@@ -362,7 +365,7 @@
             continue;
         }
         if (count[s] <= lowThreshold) {
-            norm[s] = -1;
+            norm[s] = lowProbCount;
             distributed++;
             total -= count[s];
             continue;
@@ -414,7 +417,7 @@
 
     {   U64 const vStepLog = 62 - tableLog;
         U64 const mid = (1ULL << (vStepLog-1)) - 1;
-        U64 const rStep = ((((U64)1<<vStepLog) * ToDistribute) + mid) / total;   /* scale on remaining */
+        U64 const rStep = ZSTD_div64((((U64)1<<vStepLog) * ToDistribute) + mid, (U32)total);   /* scale on remaining */
         U64 tmpTotal = mid;
         for (s=0; s<=maxSymbolValue; s++) {
             if (norm[s]==NOT_YET_ASSIGNED) {
@@ -431,10 +434,9 @@
     return 0;
 }
 
-
 size_t FSE_normalizeCount (short* normalizedCounter, unsigned tableLog,
                            const unsigned* count, size_t total,
-                           unsigned maxSymbolValue)
+                           unsigned maxSymbolValue, unsigned useLowProbCount)
 {
     /* Sanity checks */
     if (tableLog==0) tableLog = FSE_DEFAULT_TABLELOG;
@@ -443,8 +445,9 @@
     if (tableLog < FSE_minTableLog(total, maxSymbolValue)) return ERROR(GENERIC);   /* Too small tableLog, compression potentially impossible */
 
     {   static U32 const rtbTable[] = {     0, 473195, 504333, 520860, 550000, 700000, 750000, 830000 };
+        short const lowProbCount = useLowProbCount ? -1 : 1;
         U64 const scale = 62 - tableLog;
-        U64 const step = ((U64)1<<62) / total;   /* <== here, one division ! */
+        U64 const step = ZSTD_div64((U64)1<<62, (U32)total);   /* <== here, one division ! */
         U64 const vStep = 1ULL<<(scale-20);
         int stillToDistribute = 1<<tableLog;
         unsigned s;
@@ -456,7 +459,7 @@
             if (count[s] == total) return 0;   /* rle special case */
             if (count[s] == 0) { normalizedCounter[s]=0; continue; }
             if (count[s] <= lowThreshold) {
-                normalizedCounter[s] = -1;
+                normalizedCounter[s] = lowProbCount;
                 stillToDistribute--;
             } else {
                 short proba = (short)((count[s]*step) >> scale);
@@ -470,7 +473,7 @@
         }   }
         if (-stillToDistribute >= (normalizedCounter[largest] >> 1)) {
             /* corner case, need another normalization method */
-            size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue);
+            size_t const errorCode = FSE_normalizeM2(normalizedCounter, tableLog, count, total, maxSymbolValue, lowProbCount);
             if (FSE_isError(errorCode)) return errorCode;
         }
         else normalizedCounter[largest] += (short)stillToDistribute;
@@ -625,6 +628,7 @@
 
 size_t FSE_compressBound(size_t size) { return FSE_COMPRESSBOUND(size); }
 
+#ifndef ZSTD_NO_UNUSED_FUNCTIONS
 /* FSE_compress_wksp() :
  * Same as FSE_compress2(), but using an externally allocated scratch buffer (`workSpace`).
  * `wkspSize` size must be `(1<<tableLog)`.
@@ -643,7 +647,7 @@
     size_t const scratchBufferSize = wkspSize - (CTableSize * sizeof(FSE_CTable));
 
     /* init conditions */
-    if (wkspSize < FSE_WKSP_SIZE_U32(tableLog, maxSymbolValue)) return ERROR(tableLog_tooLarge);
+    if (wkspSize < FSE_COMPRESS_WKSP_SIZE_U32(tableLog, maxSymbolValue)) return ERROR(tableLog_tooLarge);
     if (srcSize <= 1) return 0;  /* Not compressible */
     if (!maxSymbolValue) maxSymbolValue = FSE_MAX_SYMBOL_VALUE;
     if (!tableLog) tableLog = FSE_DEFAULT_TABLELOG;
@@ -656,7 +660,7 @@
     }
 
     tableLog = FSE_optimalTableLog(tableLog, srcSize, maxSymbolValue);
-    CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue) );
+    CHECK_F( FSE_normalizeCount(norm, tableLog, count, srcSize, maxSymbolValue, /* useLowProbCount */ srcSize >= 2048) );
 
     /* Write table description header */
     {   CHECK_V_F(nc_err, FSE_writeNCount(op, oend-op, norm, maxSymbolValue, tableLog) );
@@ -678,13 +682,16 @@
 
 typedef struct {
     FSE_CTable CTable_max[FSE_CTABLE_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE)];
-    BYTE scratchBuffer[1 << FSE_MAX_TABLELOG];
+    union {
+      U32 hist_wksp[HIST_WKSP_SIZE_U32];
+      BYTE scratchBuffer[1 << FSE_MAX_TABLELOG];
+    } workspace;
 } fseWkspMax_t;
 
 size_t FSE_compress2 (void* dst, size_t dstCapacity, const void* src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog)
 {
     fseWkspMax_t scratchBuffer;
-    DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE));   /* compilation failures here means scratchBuffer is not large enough */
+    DEBUG_STATIC_ASSERT(sizeof(scratchBuffer) >= FSE_COMPRESS_WKSP_SIZE_U32(FSE_MAX_TABLELOG, FSE_MAX_SYMBOL_VALUE));   /* compilation failures here means scratchBuffer is not large enough */
     if (tableLog > FSE_MAX_TABLELOG) return ERROR(tableLog_tooLarge);
     return FSE_compress_wksp(dst, dstCapacity, src, srcSize, maxSymbolValue, tableLog, &scratchBuffer, sizeof(scratchBuffer));
 }
@@ -693,6 +700,6 @@
 {
     return FSE_compress2(dst, dstCapacity, src, srcSize, FSE_MAX_SYMBOL_VALUE, FSE_DEFAULT_TABLELOG);
 }
-
+#endif
 
 #endif   /* FSE_COMMONDEFS_ONLY */
diff --git a/Utilities/cmzstd/lib/compress/hist.c b/Utilities/cmzstd/lib/compress/hist.c
index 61e08c7..073c57e 100644
--- a/Utilities/cmzstd/lib/compress/hist.c
+++ b/Utilities/cmzstd/lib/compress/hist.c
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * hist : Histogram functions
  * part of Finite State Entropy project
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -34,7 +34,7 @@
     unsigned maxSymbolValue = *maxSymbolValuePtr;
     unsigned largestCount=0;
 
-    memset(count, 0, (maxSymbolValue+1) * sizeof(*count));
+    ZSTD_memset(count, 0, (maxSymbolValue+1) * sizeof(*count));
     if (srcSize==0) { *maxSymbolValuePtr = 0; return 0; }
 
     while (ip<end) {
@@ -60,9 +60,9 @@
  * this design makes better use of OoO cpus,
  * and is noticeably faster when some values are heavily repeated.
  * But it needs some additional workspace for intermediate tables.
- * `workSpace` size must be a table of size >= HIST_WKSP_SIZE_U32.
+ * `workSpace` must be a U32 table of size >= HIST_WKSP_SIZE_U32.
  * @return : largest histogram frequency,
- *           or an error code (notably when histogram would be larger than *maxSymbolValuePtr). */
+ *           or an error code (notably when histogram's alphabet is larger than *maxSymbolValuePtr) */
 static size_t HIST_count_parallel_wksp(
                                 unsigned* count, unsigned* maxSymbolValuePtr,
                                 const void* source, size_t sourceSize,
@@ -71,22 +71,21 @@
 {
     const BYTE* ip = (const BYTE*)source;
     const BYTE* const iend = ip+sourceSize;
-    unsigned maxSymbolValue = *maxSymbolValuePtr;
+    size_t const countSize = (*maxSymbolValuePtr + 1) * sizeof(*count);
     unsigned max=0;
     U32* const Counting1 = workSpace;
     U32* const Counting2 = Counting1 + 256;
     U32* const Counting3 = Counting2 + 256;
     U32* const Counting4 = Counting3 + 256;
 
-    memset(workSpace, 0, 4*256*sizeof(unsigned));
-
     /* safety checks */
+    assert(*maxSymbolValuePtr <= 255);
     if (!sourceSize) {
-        memset(count, 0, maxSymbolValue + 1);
+        ZSTD_memset(count, 0, countSize);
         *maxSymbolValuePtr = 0;
         return 0;
     }
-    if (!maxSymbolValue) maxSymbolValue = 255;            /* 0 == default */
+    ZSTD_memset(workSpace, 0, 4*256*sizeof(unsigned));
 
     /* by stripes of 16 bytes */
     {   U32 cached = MEM_read32(ip); ip += 4;
@@ -118,21 +117,18 @@
     /* finish last symbols */
     while (ip<iend) Counting1[*ip++]++;
 
-    if (check) {   /* verify stats will fit into destination table */
-        U32 s; for (s=255; s>maxSymbolValue; s--) {
-            Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s];
-            if (Counting1[s]) return ERROR(maxSymbolValue_tooSmall);
-    }   }
-
     {   U32 s;
-        if (maxSymbolValue > 255) maxSymbolValue = 255;
-        for (s=0; s<=maxSymbolValue; s++) {
-            count[s] = Counting1[s] + Counting2[s] + Counting3[s] + Counting4[s];
-            if (count[s] > max) max = count[s];
+        for (s=0; s<256; s++) {
+            Counting1[s] += Counting2[s] + Counting3[s] + Counting4[s];
+            if (Counting1[s] > max) max = Counting1[s];
     }   }
 
-    while (!count[maxSymbolValue]) maxSymbolValue--;
-    *maxSymbolValuePtr = maxSymbolValue;
+    {   unsigned maxSymbolValue = 255;
+        while (!Counting1[maxSymbolValue]) maxSymbolValue--;
+        if (check && maxSymbolValue > *maxSymbolValuePtr) return ERROR(maxSymbolValue_tooSmall);
+        *maxSymbolValuePtr = maxSymbolValue;
+        ZSTD_memmove(count, Counting1, countSize);   /* in case count & Counting1 are overlapping */
+    }
     return (size_t)max;
 }
 
@@ -152,14 +148,6 @@
     return HIST_count_parallel_wksp(count, maxSymbolValuePtr, source, sourceSize, trustInput, (U32*)workSpace);
 }
 
-/* fast variant (unsafe : won't check if src contains values beyond count[] limit) */
-size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr,
-                     const void* source, size_t sourceSize)
-{
-    unsigned tmpCounters[HIST_WKSP_SIZE_U32];
-    return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters, sizeof(tmpCounters));
-}
-
 /* HIST_count_wksp() :
  * Same as HIST_count(), but using an externally provided scratch buffer.
  * `workSpace` size must be table of >= HIST_WKSP_SIZE_U32 unsigned */
@@ -175,9 +163,19 @@
     return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, workSpace, workSpaceSize);
 }
 
+#ifndef ZSTD_NO_UNUSED_FUNCTIONS
+/* fast variant (unsafe : won't check if src contains values beyond count[] limit) */
+size_t HIST_countFast(unsigned* count, unsigned* maxSymbolValuePtr,
+                     const void* source, size_t sourceSize)
+{
+    unsigned tmpCounters[HIST_WKSP_SIZE_U32];
+    return HIST_countFast_wksp(count, maxSymbolValuePtr, source, sourceSize, tmpCounters, sizeof(tmpCounters));
+}
+
 size_t HIST_count(unsigned* count, unsigned* maxSymbolValuePtr,
                  const void* src, size_t srcSize)
 {
     unsigned tmpCounters[HIST_WKSP_SIZE_U32];
     return HIST_count_wksp(count, maxSymbolValuePtr, src, srcSize, tmpCounters, sizeof(tmpCounters));
 }
+#endif
diff --git a/Utilities/cmzstd/lib/compress/hist.h b/Utilities/cmzstd/lib/compress/hist.h
index 77e3ec4..228ed48 100644
--- a/Utilities/cmzstd/lib/compress/hist.h
+++ b/Utilities/cmzstd/lib/compress/hist.h
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * hist : Histogram functions
  * part of Finite State Entropy project
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -14,7 +14,7 @@
 ****************************************************************** */
 
 /* --- dependencies --- */
-#include <stddef.h>   /* size_t */
+#include "../common/zstd_deps.h"   /* size_t */
 
 
 /* --- simple histogram functions --- */
diff --git a/Utilities/cmzstd/lib/compress/huf_compress.c b/Utilities/cmzstd/lib/compress/huf_compress.c
index 5468798..485906e 100644
--- a/Utilities/cmzstd/lib/compress/huf_compress.c
+++ b/Utilities/cmzstd/lib/compress/huf_compress.c
@@ -1,6 +1,6 @@
 /* ******************************************************************
  * Huffman encoder, part of New Generation Entropy library
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -23,8 +23,7 @@
 /* **************************************************************
 *  Includes
 ****************************************************************/
-#include <string.h>     /* memcpy, memset */
-#include <stdio.h>      /* printf (debug) */
+#include "../common/zstd_deps.h"     /* ZSTD_memcpy, ZSTD_memset */
 #include "../common/compiler.h"
 #include "../common/bitstream.h"
 #include "hist.h"
@@ -60,7 +59,15 @@
  * Note : all elements within weightTable are supposed to be <= HUF_TABLELOG_MAX.
  */
 #define MAX_FSE_TABLELOG_FOR_HUFF_HEADER 6
-static size_t HUF_compressWeights (void* dst, size_t dstSize, const void* weightTable, size_t wtSize)
+
+typedef struct {
+    FSE_CTable CTable[FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX)];
+    U32 scratchBuffer[FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(HUF_TABLELOG_MAX, MAX_FSE_TABLELOG_FOR_HUFF_HEADER)];
+    unsigned count[HUF_TABLELOG_MAX+1];
+    S16 norm[HUF_TABLELOG_MAX+1];
+} HUF_CompressWeightsWksp;
+
+static size_t HUF_compressWeights(void* dst, size_t dstSize, const void* weightTable, size_t wtSize, void* workspace, size_t workspaceSize)
 {
     BYTE* const ostart = (BYTE*) dst;
     BYTE* op = ostart;
@@ -68,33 +75,30 @@
 
     unsigned maxSymbolValue = HUF_TABLELOG_MAX;
     U32 tableLog = MAX_FSE_TABLELOG_FOR_HUFF_HEADER;
+    HUF_CompressWeightsWksp* wksp = (HUF_CompressWeightsWksp*)workspace;
 
-    FSE_CTable CTable[FSE_CTABLE_SIZE_U32(MAX_FSE_TABLELOG_FOR_HUFF_HEADER, HUF_TABLELOG_MAX)];
-    BYTE scratchBuffer[1<<MAX_FSE_TABLELOG_FOR_HUFF_HEADER];
-
-    unsigned count[HUF_TABLELOG_MAX+1];
-    S16 norm[HUF_TABLELOG_MAX+1];
+    if (workspaceSize < sizeof(HUF_CompressWeightsWksp)) return ERROR(GENERIC);
 
     /* init conditions */
     if (wtSize <= 1) return 0;  /* Not compressible */
 
     /* Scan input and build symbol stats */
-    {   unsigned const maxCount = HIST_count_simple(count, &maxSymbolValue, weightTable, wtSize);   /* never fails */
+    {   unsigned const maxCount = HIST_count_simple(wksp->count, &maxSymbolValue, weightTable, wtSize);   /* never fails */
         if (maxCount == wtSize) return 1;   /* only a single symbol in src : rle */
         if (maxCount == 1) return 0;        /* each symbol present maximum once => not compressible */
     }
 
     tableLog = FSE_optimalTableLog(tableLog, wtSize, maxSymbolValue);
-    CHECK_F( FSE_normalizeCount(norm, tableLog, count, wtSize, maxSymbolValue) );
+    CHECK_F( FSE_normalizeCount(wksp->norm, tableLog, wksp->count, wtSize, maxSymbolValue, /* useLowProbCount */ 0) );
 
     /* Write table description header */
-    {   CHECK_V_F(hSize, FSE_writeNCount(op, (size_t)(oend-op), norm, maxSymbolValue, tableLog) );
+    {   CHECK_V_F(hSize, FSE_writeNCount(op, (size_t)(oend-op), wksp->norm, maxSymbolValue, tableLog) );
         op += hSize;
     }
 
     /* Compress */
-    CHECK_F( FSE_buildCTable_wksp(CTable, norm, maxSymbolValue, tableLog, scratchBuffer, sizeof(scratchBuffer)) );
-    {   CHECK_V_F(cSize, FSE_compress_usingCTable(op, (size_t)(oend - op), weightTable, wtSize, CTable) );
+    CHECK_F( FSE_buildCTable_wksp(wksp->CTable, wksp->norm, maxSymbolValue, tableLog, wksp->scratchBuffer, sizeof(wksp->scratchBuffer)) );
+    {   CHECK_V_F(cSize, FSE_compress_usingCTable(op, (size_t)(oend - op), weightTable, wtSize, wksp->CTable) );
         if (cSize == 0) return 0;   /* not enough space for compressed data */
         op += cSize;
     }
@@ -103,34 +107,33 @@
 }
 
 
-struct HUF_CElt_s {
-  U16  val;
-  BYTE nbBits;
-};   /* typedef'd to HUF_CElt within "huf.h" */
-
-/*! HUF_writeCTable() :
-    `CTable` : Huffman tree to save, using huf representation.
-    @return : size of saved CTable */
-size_t HUF_writeCTable (void* dst, size_t maxDstSize,
-                        const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog)
-{
+typedef struct {
+    HUF_CompressWeightsWksp wksp;
     BYTE bitsToWeight[HUF_TABLELOG_MAX + 1];   /* precomputed conversion table */
     BYTE huffWeight[HUF_SYMBOLVALUE_MAX];
+} HUF_WriteCTableWksp;
+
+size_t HUF_writeCTable_wksp(void* dst, size_t maxDstSize,
+                            const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog,
+                            void* workspace, size_t workspaceSize)
+{
     BYTE* op = (BYTE*)dst;
     U32 n;
+    HUF_WriteCTableWksp* wksp = (HUF_WriteCTableWksp*)workspace;
 
-     /* check conditions */
+    /* check conditions */
+    if (workspaceSize < sizeof(HUF_WriteCTableWksp)) return ERROR(GENERIC);
     if (maxSymbolValue > HUF_SYMBOLVALUE_MAX) return ERROR(maxSymbolValue_tooLarge);
 
     /* convert to weight */
-    bitsToWeight[0] = 0;
+    wksp->bitsToWeight[0] = 0;
     for (n=1; n<huffLog+1; n++)
-        bitsToWeight[n] = (BYTE)(huffLog + 1 - n);
+        wksp->bitsToWeight[n] = (BYTE)(huffLog + 1 - n);
     for (n=0; n<maxSymbolValue; n++)
-        huffWeight[n] = bitsToWeight[CTable[n].nbBits];
+        wksp->huffWeight[n] = wksp->bitsToWeight[CTable[n].nbBits];
 
     /* attempt weights compression by FSE */
-    {   CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, huffWeight, maxSymbolValue) );
+    {   CHECK_V_F(hSize, HUF_compressWeights(op+1, maxDstSize-1, wksp->huffWeight, maxSymbolValue, &wksp->wksp, sizeof(wksp->wksp)) );
         if ((hSize>1) & (hSize < maxSymbolValue/2)) {   /* FSE compressed */
             op[0] = (BYTE)hSize;
             return hSize+1;
@@ -140,12 +143,22 @@
     if (maxSymbolValue > (256-128)) return ERROR(GENERIC);   /* should not happen : likely means source cannot be compressed */
     if (((maxSymbolValue+1)/2) + 1 > maxDstSize) return ERROR(dstSize_tooSmall);   /* not enough space within dst buffer */
     op[0] = (BYTE)(128 /*special case*/ + (maxSymbolValue-1));
-    huffWeight[maxSymbolValue] = 0;   /* to be sure it doesn't cause msan issue in final combination */
+    wksp->huffWeight[maxSymbolValue] = 0;   /* to be sure it doesn't cause msan issue in final combination */
     for (n=0; n<maxSymbolValue; n+=2)
-        op[(n/2)+1] = (BYTE)((huffWeight[n] << 4) + huffWeight[n+1]);
+        op[(n/2)+1] = (BYTE)((wksp->huffWeight[n] << 4) + wksp->huffWeight[n+1]);
     return ((maxSymbolValue+1)/2) + 1;
 }
 
+/*! HUF_writeCTable() :
+    `CTable` : Huffman tree to save, using huf representation.
+    @return : size of saved CTable */
+size_t HUF_writeCTable (void* dst, size_t maxDstSize,
+                        const HUF_CElt* CTable, unsigned maxSymbolValue, unsigned huffLog)
+{
+    HUF_WriteCTableWksp wksp;
+    return HUF_writeCTable_wksp(dst, maxDstSize, CTable, maxSymbolValue, huffLog, &wksp, sizeof(wksp));
+}
+
 
 size_t HUF_readCTable (HUF_CElt* CTable, unsigned* maxSymbolValuePtr, const void* src, size_t srcSize, unsigned* hasZeroWeights)
 {
@@ -156,6 +169,7 @@
 
     /* get symbol weights */
     CHECK_V_F(readSize, HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX+1, rankVal, &nbSymbols, &tableLog, src, srcSize));
+    *hasZeroWeights = (rankVal[0] > 0);
 
     /* check result */
     if (tableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
@@ -164,16 +178,14 @@
     /* Prepare base value per rank */
     {   U32 n, nextRankStart = 0;
         for (n=1; n<=tableLog; n++) {
-            U32 current = nextRankStart;
+            U32 curr = nextRankStart;
             nextRankStart += (rankVal[n] << (n-1));
-            rankVal[n] = current;
+            rankVal[n] = curr;
     }   }
 
     /* fill nbBits */
-    *hasZeroWeights = 0;
     {   U32 n; for (n=0; n<nbSymbols; n++) {
             const U32 w = huffWeight[n];
-            *hasZeroWeights |= (w == 0);
             CTable[n].nbBits = (BYTE)(tableLog + 1 - w) & -(w != 0);
     }   }
 
@@ -212,32 +224,63 @@
     BYTE nbBits;
 } nodeElt;
 
+/**
+ * HUF_setMaxHeight():
+ * Enforces maxNbBits on the Huffman tree described in huffNode.
+ *
+ * It sets all nodes with nbBits > maxNbBits to be maxNbBits. Then it adjusts
+ * the tree to so that it is a valid canonical Huffman tree.
+ *
+ * @pre               The sum of the ranks of each symbol == 2^largestBits,
+ *                    where largestBits == huffNode[lastNonNull].nbBits.
+ * @post              The sum of the ranks of each symbol == 2^largestBits,
+ *                    where largestBits is the return value <= maxNbBits.
+ *
+ * @param huffNode    The Huffman tree modified in place to enforce maxNbBits.
+ * @param lastNonNull The symbol with the lowest count in the Huffman tree.
+ * @param maxNbBits   The maximum allowed number of bits, which the Huffman tree
+ *                    may not respect. After this function the Huffman tree will
+ *                    respect maxNbBits.
+ * @return            The maximum number of bits of the Huffman tree after adjustment,
+ *                    necessarily no more than maxNbBits.
+ */
 static U32 HUF_setMaxHeight(nodeElt* huffNode, U32 lastNonNull, U32 maxNbBits)
 {
     const U32 largestBits = huffNode[lastNonNull].nbBits;
-    if (largestBits <= maxNbBits) return largestBits;   /* early exit : no elt > maxNbBits */
+    /* early exit : no elt > maxNbBits, so the tree is already valid. */
+    if (largestBits <= maxNbBits) return largestBits;
 
     /* there are several too large elements (at least >= 2) */
     {   int totalCost = 0;
         const U32 baseCost = 1 << (largestBits - maxNbBits);
         int n = (int)lastNonNull;
 
+        /* Adjust any ranks > maxNbBits to maxNbBits.
+         * Compute totalCost, which is how far the sum of the ranks is
+         * we are over 2^largestBits after adjust the offending ranks.
+         */
         while (huffNode[n].nbBits > maxNbBits) {
             totalCost += baseCost - (1 << (largestBits - huffNode[n].nbBits));
             huffNode[n].nbBits = (BYTE)maxNbBits;
-            n --;
-        }  /* n stops at huffNode[n].nbBits <= maxNbBits */
-        while (huffNode[n].nbBits == maxNbBits) n--;   /* n end at index of smallest symbol using < maxNbBits */
+            n--;
+        }
+        /* n stops at huffNode[n].nbBits <= maxNbBits */
+        assert(huffNode[n].nbBits <= maxNbBits);
+        /* n end at index of smallest symbol using < maxNbBits */
+        while (huffNode[n].nbBits == maxNbBits) --n;
 
-        /* renorm totalCost */
-        totalCost >>= (largestBits - maxNbBits);  /* note : totalCost is necessarily a multiple of baseCost */
+        /* renorm totalCost from 2^largestBits to 2^maxNbBits
+         * note : totalCost is necessarily a multiple of baseCost */
+        assert((totalCost & (baseCost - 1)) == 0);
+        totalCost >>= (largestBits - maxNbBits);
+        assert(totalCost > 0);
 
         /* repay normalized cost */
         {   U32 const noSymbol = 0xF0F0F0F0;
             U32 rankLast[HUF_TABLELOG_MAX+2];
 
-            /* Get pos of last (smallest) symbol per rank */
-            memset(rankLast, 0xF0, sizeof(rankLast));
+            /* Get pos of last (smallest = lowest cum. count) symbol per rank */
+            ZSTD_memset(rankLast, 0xF0, sizeof(rankLast));
             {   U32 currentNbBits = maxNbBits;
                 int pos;
                 for (pos=n ; pos >= 0; pos--) {
@@ -247,34 +290,65 @@
             }   }
 
             while (totalCost > 0) {
+                /* Try to reduce the next power of 2 above totalCost because we
+                 * gain back half the rank.
+                 */
                 U32 nBitsToDecrease = BIT_highbit32((U32)totalCost) + 1;
                 for ( ; nBitsToDecrease > 1; nBitsToDecrease--) {
                     U32 const highPos = rankLast[nBitsToDecrease];
                     U32 const lowPos = rankLast[nBitsToDecrease-1];
                     if (highPos == noSymbol) continue;
+                    /* Decrease highPos if no symbols of lowPos or if it is
+                     * not cheaper to remove 2 lowPos than highPos.
+                     */
                     if (lowPos == noSymbol) break;
                     {   U32 const highTotal = huffNode[highPos].count;
                         U32 const lowTotal = 2 * huffNode[lowPos].count;
                         if (highTotal <= lowTotal) break;
                 }   }
                 /* only triggered when no more rank 1 symbol left => find closest one (note : there is necessarily at least one !) */
+                assert(rankLast[nBitsToDecrease] != noSymbol || nBitsToDecrease == 1);
                 /* HUF_MAX_TABLELOG test just to please gcc 5+; but it should not be necessary */
                 while ((nBitsToDecrease<=HUF_TABLELOG_MAX) && (rankLast[nBitsToDecrease] == noSymbol))
-                    nBitsToDecrease ++;
+                    nBitsToDecrease++;
+                assert(rankLast[nBitsToDecrease] != noSymbol);
+                /* Increase the number of bits to gain back half the rank cost. */
                 totalCost -= 1 << (nBitsToDecrease-1);
+                huffNode[rankLast[nBitsToDecrease]].nbBits++;
+
+                /* Fix up the new rank.
+                 * If the new rank was empty, this symbol is now its smallest.
+                 * Otherwise, this symbol will be the largest in the new rank so no adjustment.
+                 */
                 if (rankLast[nBitsToDecrease-1] == noSymbol)
-                    rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease];   /* this rank is no longer empty */
-                huffNode[rankLast[nBitsToDecrease]].nbBits ++;
+                    rankLast[nBitsToDecrease-1] = rankLast[nBitsToDecrease];
+                /* Fix up the old rank.
+                 * If the symbol was at position 0, meaning it was the highest weight symbol in the tree,
+                 * it must be the only symbol in its rank, so the old rank now has no symbols.
+                 * Otherwise, since the Huffman nodes are sorted by count, the previous position is now
+                 * the smallest node in the rank. If the previous position belongs to a different rank,
+                 * then the rank is now empty.
+                 */
                 if (rankLast[nBitsToDecrease] == 0)    /* special case, reached largest symbol */
                     rankLast[nBitsToDecrease] = noSymbol;
                 else {
                     rankLast[nBitsToDecrease]--;
                     if (huffNode[rankLast[nBitsToDecrease]].nbBits != maxNbBits-nBitsToDecrease)
                         rankLast[nBitsToDecrease] = noSymbol;   /* this rank is now empty */
-            }   }   /* while (totalCost > 0) */
+                }
+            }   /* while (totalCost > 0) */
 
+            /* If we've removed too much weight, then we have to add it back.
+             * To avoid overshooting again, we only adjust the smallest rank.
+             * We take the largest nodes from the lowest rank 0 and move them
+             * to rank 1. There's guaranteed to be enough rank 0 symbols because
+             * TODO.
+             */
             while (totalCost < 0) {  /* Sometimes, cost correction overshoot */
-                if (rankLast[1] == noSymbol) {  /* special case : no rank 1 symbol (using maxNbBits-1); let's create one from largest rank 0 (using maxNbBits) */
+                /* special case : no rank 1 symbol (using maxNbBits-1);
+                 * let's create one from largest rank 0 (using maxNbBits).
+                 */
+                if (rankLast[1] == noSymbol) {
                     while (huffNode[n].nbBits == maxNbBits) n--;
                     huffNode[n+1].nbBits--;
                     assert(n >= 0);
@@ -285,14 +359,16 @@
                 huffNode[ rankLast[1] + 1 ].nbBits--;
                 rankLast[1]++;
                 totalCost ++;
-    }   }   }   /* there are several too large elements (at least >= 2) */
+            }
+        }   /* repay normalized cost */
+    }   /* there are several too large elements (at least >= 2) */
 
     return maxNbBits;
 }
 
 typedef struct {
     U32 base;
-    U32 current;
+    U32 curr;
 } rankPos;
 
 typedef nodeElt huffNodeTable[HUF_CTABLE_WORKSPACE_SIZE_U32];
@@ -304,21 +380,45 @@
   rankPos rankPosition[RANK_POSITION_TABLE_SIZE];
 } HUF_buildCTable_wksp_tables;
 
+/**
+ * HUF_sort():
+ * Sorts the symbols [0, maxSymbolValue] by count[symbol] in decreasing order.
+ *
+ * @param[out] huffNode       Sorted symbols by decreasing count. Only members `.count` and `.byte` are filled.
+ *                            Must have (maxSymbolValue + 1) entries.
+ * @param[in]  count          Histogram of the symbols.
+ * @param[in]  maxSymbolValue Maximum symbol value.
+ * @param      rankPosition   This is a scratch workspace. Must have RANK_POSITION_TABLE_SIZE entries.
+ */
 static void HUF_sort(nodeElt* huffNode, const unsigned* count, U32 maxSymbolValue, rankPos* rankPosition)
 {
-    U32 n;
+    int n;
+    int const maxSymbolValue1 = (int)maxSymbolValue + 1;
 
-    memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE);
-    for (n=0; n<=maxSymbolValue; n++) {
-        U32 r = BIT_highbit32(count[n] + 1);
-        rankPosition[r].base ++;
+    /* Compute base and set curr to base.
+     * For symbol s let lowerRank = BIT_highbit32(count[n]+1) and rank = lowerRank + 1.
+     * Then 2^lowerRank <= count[n]+1 <= 2^rank.
+     * We attribute each symbol to lowerRank's base value, because we want to know where
+     * each rank begins in the output, so for rank R we want to count ranks R+1 and above.
+     */
+    ZSTD_memset(rankPosition, 0, sizeof(*rankPosition) * RANK_POSITION_TABLE_SIZE);
+    for (n = 0; n < maxSymbolValue1; ++n) {
+        U32 lowerRank = BIT_highbit32(count[n] + 1);
+        rankPosition[lowerRank].base++;
     }
-    for (n=30; n>0; n--) rankPosition[n-1].base += rankPosition[n].base;
-    for (n=0; n<32; n++) rankPosition[n].current = rankPosition[n].base;
-    for (n=0; n<=maxSymbolValue; n++) {
+    assert(rankPosition[RANK_POSITION_TABLE_SIZE - 1].base == 0);
+    for (n = RANK_POSITION_TABLE_SIZE - 1; n > 0; --n) {
+        rankPosition[n-1].base += rankPosition[n].base;
+        rankPosition[n-1].curr = rankPosition[n-1].base;
+    }
+    /* Sort */
+    for (n = 0; n < maxSymbolValue1; ++n) {
         U32 const c = count[n];
         U32 const r = BIT_highbit32(c+1) + 1;
-        U32 pos = rankPosition[r].current++;
+        U32 pos = rankPosition[r].curr++;
+        /* Insert into the correct position in the rank.
+         * We have at most 256 symbols, so this insertion should be fine.
+         */
         while ((pos > rankPosition[r].base) && (c > huffNode[pos-1].count)) {
             huffNode[pos] = huffNode[pos-1];
             pos--;
@@ -335,28 +435,20 @@
  */
 #define STARTNODE (HUF_SYMBOLVALUE_MAX+1)
 
-size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize)
+/* HUF_buildTree():
+ * Takes the huffNode array sorted by HUF_sort() and builds an unlimited-depth Huffman tree.
+ *
+ * @param huffNode        The array sorted by HUF_sort(). Builds the Huffman tree in this array.
+ * @param maxSymbolValue  The maximum symbol value.
+ * @return                The smallest node in the Huffman tree (by count).
+ */
+static int HUF_buildTree(nodeElt* huffNode, U32 maxSymbolValue)
 {
-    HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace;
-    nodeElt* const huffNode0 = wksp_tables->huffNodeTbl;
-    nodeElt* const huffNode = huffNode0+1;
+    nodeElt* const huffNode0 = huffNode - 1;
     int nonNullRank;
     int lowS, lowN;
     int nodeNb = STARTNODE;
     int n, nodeRoot;
-
-    /* safety checks */
-    if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC);  /* must be aligned on 4-bytes boundaries */
-    if (wkspSize < sizeof(HUF_buildCTable_wksp_tables))
-      return ERROR(workSpace_tooSmall);
-    if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
-    if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
-      return ERROR(maxSymbolValue_tooLarge);
-    memset(huffNode0, 0, sizeof(huffNodeTable));
-
-    /* sort, decreasing order */
-    HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition);
-
     /* init for parents */
     nonNullRank = (int)maxSymbolValue;
     while(huffNode[nonNullRank].count == 0) nonNullRank--;
@@ -383,40 +475,70 @@
     for (n=0; n<=nonNullRank; n++)
         huffNode[n].nbBits = huffNode[ huffNode[n].parent ].nbBits + 1;
 
-    /* enforce maxTableLog */
-    maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
-
-    /* fill result into tree (val, nbBits) */
-    {   U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0};
-        U16 valPerRank[HUF_TABLELOG_MAX+1] = {0};
-        int const alphabetSize = (int)(maxSymbolValue + 1);
-        if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC);   /* check fit into table */
-        for (n=0; n<=nonNullRank; n++)
-            nbPerRank[huffNode[n].nbBits]++;
-        /* determine stating value per rank */
-        {   U16 min = 0;
-            for (n=(int)maxNbBits; n>0; n--) {
-                valPerRank[n] = min;      /* get starting value within each rank */
-                min += nbPerRank[n];
-                min >>= 1;
-        }   }
-        for (n=0; n<alphabetSize; n++)
-            tree[huffNode[n].byte].nbBits = huffNode[n].nbBits;   /* push nbBits per symbol, symbol order */
-        for (n=0; n<alphabetSize; n++)
-            tree[n].val = valPerRank[tree[n].nbBits]++;   /* assign value within rank, symbol order */
-    }
-
-    return maxNbBits;
+    return nonNullRank;
 }
 
-/** HUF_buildCTable() :
- * @return : maxNbBits
- *  Note : count is used before tree is written, so they can safely overlap
+/**
+ * HUF_buildCTableFromTree():
+ * Build the CTable given the Huffman tree in huffNode.
+ *
+ * @param[out] CTable         The output Huffman CTable.
+ * @param      huffNode       The Huffman tree.
+ * @param      nonNullRank    The last and smallest node in the Huffman tree.
+ * @param      maxSymbolValue The maximum symbol value.
+ * @param      maxNbBits      The exact maximum number of bits used in the Huffman tree.
  */
-size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits)
+static void HUF_buildCTableFromTree(HUF_CElt* CTable, nodeElt const* huffNode, int nonNullRank, U32 maxSymbolValue, U32 maxNbBits)
 {
-    HUF_buildCTable_wksp_tables workspace;
-    return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace));
+    /* fill result into ctable (val, nbBits) */
+    int n;
+    U16 nbPerRank[HUF_TABLELOG_MAX+1] = {0};
+    U16 valPerRank[HUF_TABLELOG_MAX+1] = {0};
+    int const alphabetSize = (int)(maxSymbolValue + 1);
+    for (n=0; n<=nonNullRank; n++)
+        nbPerRank[huffNode[n].nbBits]++;
+    /* determine starting value per rank */
+    {   U16 min = 0;
+        for (n=(int)maxNbBits; n>0; n--) {
+            valPerRank[n] = min;      /* get starting value within each rank */
+            min += nbPerRank[n];
+            min >>= 1;
+    }   }
+    for (n=0; n<alphabetSize; n++)
+        CTable[huffNode[n].byte].nbBits = huffNode[n].nbBits;   /* push nbBits per symbol, symbol order */
+    for (n=0; n<alphabetSize; n++)
+        CTable[n].val = valPerRank[CTable[n].nbBits]++;   /* assign value within rank, symbol order */
+}
+
+size_t HUF_buildCTable_wksp (HUF_CElt* tree, const unsigned* count, U32 maxSymbolValue, U32 maxNbBits, void* workSpace, size_t wkspSize)
+{
+    HUF_buildCTable_wksp_tables* const wksp_tables = (HUF_buildCTable_wksp_tables*)workSpace;
+    nodeElt* const huffNode0 = wksp_tables->huffNodeTbl;
+    nodeElt* const huffNode = huffNode0+1;
+    int nonNullRank;
+
+    /* safety checks */
+    if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC);  /* must be aligned on 4-bytes boundaries */
+    if (wkspSize < sizeof(HUF_buildCTable_wksp_tables))
+      return ERROR(workSpace_tooSmall);
+    if (maxNbBits == 0) maxNbBits = HUF_TABLELOG_DEFAULT;
+    if (maxSymbolValue > HUF_SYMBOLVALUE_MAX)
+      return ERROR(maxSymbolValue_tooLarge);
+    ZSTD_memset(huffNode0, 0, sizeof(huffNodeTable));
+
+    /* sort, decreasing order */
+    HUF_sort(huffNode, count, maxSymbolValue, wksp_tables->rankPosition);
+
+    /* build tree */
+    nonNullRank = HUF_buildTree(huffNode, maxSymbolValue);
+
+    /* enforce maxTableLog */
+    maxNbBits = HUF_setMaxHeight(huffNode, (U32)nonNullRank, maxNbBits);
+    if (maxNbBits > HUF_TABLELOG_MAX) return ERROR(GENERIC);   /* check fit into table */
+
+    HUF_buildCTableFromTree(tree, huffNode, nonNullRank, maxSymbolValue, maxNbBits);
+
+    return maxNbBits;
 }
 
 size_t HUF_estimateCompressedSize(const HUF_CElt* CTable, const unsigned* count, unsigned maxSymbolValue)
@@ -629,29 +751,33 @@
 typedef struct {
     unsigned count[HUF_SYMBOLVALUE_MAX + 1];
     HUF_CElt CTable[HUF_SYMBOLVALUE_MAX + 1];
-    HUF_buildCTable_wksp_tables buildCTable_wksp;
+    union {
+        HUF_buildCTable_wksp_tables buildCTable_wksp;
+        HUF_WriteCTableWksp writeCTable_wksp;
+    } wksps;
 } HUF_compress_tables_t;
 
 /* HUF_compress_internal() :
- * `workSpace` must a table of at least HUF_WORKSPACE_SIZE_U32 unsigned */
+ * `workSpace_align4` must be aligned on 4-bytes boundaries,
+ * and occupies the same space as a table of HUF_WORKSPACE_SIZE_U32 unsigned */
 static size_t
 HUF_compress_internal (void* dst, size_t dstSize,
                  const void* src, size_t srcSize,
                        unsigned maxSymbolValue, unsigned huffLog,
                        HUF_nbStreams_e nbStreams,
-                       void* workSpace, size_t wkspSize,
+                       void* workSpace_align4, size_t wkspSize,
                        HUF_CElt* oldHufTable, HUF_repeat* repeat, int preferRepeat,
                  const int bmi2)
 {
-    HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace;
+    HUF_compress_tables_t* const table = (HUF_compress_tables_t*)workSpace_align4;
     BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = ostart + dstSize;
     BYTE* op = ostart;
 
     HUF_STATIC_ASSERT(sizeof(*table) <= HUF_WORKSPACE_SIZE);
+    assert(((size_t)workSpace_align4 & 3) == 0);   /* must be aligned on 4-bytes boundaries */
 
     /* checks & inits */
-    if (((size_t)workSpace & 3) != 0) return ERROR(GENERIC);  /* must be aligned on 4-bytes boundaries */
     if (wkspSize < HUF_WORKSPACE_SIZE) return ERROR(workSpace_tooSmall);
     if (!srcSize) return 0;  /* Uncompressed */
     if (!dstSize) return 0;  /* cannot fit anything within dst budget */
@@ -669,7 +795,7 @@
     }
 
     /* Scan input and build symbol stats */
-    {   CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace, wkspSize) );
+    {   CHECK_V_F(largest, HIST_count_wksp (table->count, &maxSymbolValue, (const BYTE*)src, srcSize, workSpace_align4, wkspSize) );
         if (largest == srcSize) { *ostart = ((const BYTE*)src)[0]; return 1; }   /* single symbol, rle */
         if (largest <= (srcSize >> 7)+4) return 0;   /* heuristic : probably not compressible enough */
     }
@@ -691,16 +817,17 @@
     huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
     {   size_t const maxBits = HUF_buildCTable_wksp(table->CTable, table->count,
                                             maxSymbolValue, huffLog,
-                                            &table->buildCTable_wksp, sizeof(table->buildCTable_wksp));
+                                            &table->wksps.buildCTable_wksp, sizeof(table->wksps.buildCTable_wksp));
         CHECK_F(maxBits);
         huffLog = (U32)maxBits;
         /* Zero unused symbols in CTable, so we can check it for validity */
-        memset(table->CTable + (maxSymbolValue + 1), 0,
+        ZSTD_memset(table->CTable + (maxSymbolValue + 1), 0,
                sizeof(table->CTable) - ((maxSymbolValue + 1) * sizeof(HUF_CElt)));
     }
 
     /* Write table description header */
-    {   CHECK_V_F(hSize, HUF_writeCTable (op, dstSize, table->CTable, maxSymbolValue, huffLog) );
+    {   CHECK_V_F(hSize, HUF_writeCTable_wksp(op, dstSize, table->CTable, maxSymbolValue, huffLog,
+                                              &table->wksps.writeCTable_wksp, sizeof(table->wksps.writeCTable_wksp)) );
         /* Check if using previous huffman table is beneficial */
         if (repeat && *repeat != HUF_repeat_none) {
             size_t const oldSize = HUF_estimateCompressedSize(oldHufTable, table->count, maxSymbolValue);
@@ -716,7 +843,7 @@
         op += hSize;
         if (repeat) { *repeat = HUF_repeat_none; }
         if (oldHufTable)
-            memcpy(oldHufTable, table->CTable, sizeof(table->CTable));  /* Save new table */
+            ZSTD_memcpy(oldHufTable, table->CTable, sizeof(table->CTable));  /* Save new table */
     }
     return HUF_compressCTable_internal(ostart, op, oend,
                                        src, srcSize,
@@ -747,14 +874,6 @@
                                  repeat, preferRepeat, bmi2);
 }
 
-size_t HUF_compress1X (void* dst, size_t dstSize,
-                 const void* src, size_t srcSize,
-                 unsigned maxSymbolValue, unsigned huffLog)
-{
-    unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
-    return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
-}
-
 /* HUF_compress4X_repeat():
  * compress input using 4 streams.
  * provide workspace to generate compression tables */
@@ -784,6 +903,25 @@
                                  hufTable, repeat, preferRepeat, bmi2);
 }
 
+#ifndef ZSTD_NO_UNUSED_FUNCTIONS
+/** HUF_buildCTable() :
+ * @return : maxNbBits
+ *  Note : count is used before tree is written, so they can safely overlap
+ */
+size_t HUF_buildCTable (HUF_CElt* tree, const unsigned* count, unsigned maxSymbolValue, unsigned maxNbBits)
+{
+    HUF_buildCTable_wksp_tables workspace;
+    return HUF_buildCTable_wksp(tree, count, maxSymbolValue, maxNbBits, &workspace, sizeof(workspace));
+}
+
+size_t HUF_compress1X (void* dst, size_t dstSize,
+                 const void* src, size_t srcSize,
+                 unsigned maxSymbolValue, unsigned huffLog)
+{
+    unsigned workSpace[HUF_WORKSPACE_SIZE_U32];
+    return HUF_compress1X_wksp(dst, dstSize, src, srcSize, maxSymbolValue, huffLog, workSpace, sizeof(workSpace));
+}
+
 size_t HUF_compress2 (void* dst, size_t dstSize,
                 const void* src, size_t srcSize,
                 unsigned maxSymbolValue, unsigned huffLog)
@@ -796,3 +934,4 @@
 {
     return HUF_compress2(dst, maxDstSize, src, srcSize, 255, HUF_TABLELOG_DEFAULT);
 }
+#endif
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress.c b/Utilities/cmzstd/lib/compress/zstd_compress.c
index 3f963b1..b7ee298 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,8 +11,7 @@
 /*-*************************************
 *  Dependencies
 ***************************************/
-#include <limits.h>         /* INT_MAX */
-#include <string.h>         /* memset */
+#include "../common/zstd_deps.h"  /* INT_MAX, ZSTD_memset, ZSTD_memcpy */
 #include "../common/cpu.h"
 #include "../common/mem.h"
 #include "hist.h"           /* HIST_countFast_wksp */
@@ -30,6 +29,19 @@
 #include "zstd_ldm.h"
 #include "zstd_compress_superblock.h"
 
+/* ***************************************************************
+*  Tuning parameters
+*****************************************************************/
+/*!
+ * COMPRESS_HEAPMODE :
+ * Select how default decompression function ZSTD_compress() allocates its context,
+ * on stack (0, default), or into heap (1).
+ * Note that functions with explicit context such as ZSTD_compressCCtx() are unaffected.
+ */
+#ifndef ZSTD_COMPRESS_HEAPMODE
+#  define ZSTD_COMPRESS_HEAPMODE 0
+#endif
+
 
 /*-*************************************
 *  Helper functions
@@ -52,6 +64,7 @@
 struct ZSTD_CDict_s {
     const void* dictContent;
     size_t dictContentSize;
+    ZSTD_dictContentType_e dictContentType; /* The dictContentType the CDict was created with */
     U32* entropyWorkspace; /* entropy workspace of HUF_WORKSPACE_SIZE bytes */
     ZSTD_cwksp workspace;
     ZSTD_matchState_t matchState;
@@ -59,6 +72,10 @@
     ZSTD_customMem customMem;
     U32 dictID;
     int compressionLevel; /* 0 indicates that advanced API was used to select CDict params */
+    ZSTD_useRowMatchFinderMode_e useRowMatchFinder; /* Indicates whether the CDict was created with params that would use
+                                                     * row-based matchfinder. Unless the cdict is reloaded, we will use
+                                                     * the same greedy/lazy matchfinder at compression time.
+                                                     */
 };  /* typedef'd to ZSTD_CDict within "zstd.h" */
 
 ZSTD_CCtx* ZSTD_createCCtx(void)
@@ -69,7 +86,7 @@
 static void ZSTD_initCCtx(ZSTD_CCtx* cctx, ZSTD_customMem memManager)
 {
     assert(cctx != NULL);
-    memset(cctx, 0, sizeof(*cctx));
+    ZSTD_memset(cctx, 0, sizeof(*cctx));
     cctx->customMem = memManager;
     cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
     {   size_t const err = ZSTD_CCtx_reset(cctx, ZSTD_reset_parameters);
@@ -82,8 +99,8 @@
 {
     ZSTD_STATIC_ASSERT(zcss_init==0);
     ZSTD_STATIC_ASSERT(ZSTD_CONTENTSIZE_UNKNOWN==(0ULL - 1));
-    if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
-    {   ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_malloc(sizeof(ZSTD_CCtx), customMem);
+    if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
+    {   ZSTD_CCtx* const cctx = (ZSTD_CCtx*)ZSTD_customMalloc(sizeof(ZSTD_CCtx), customMem);
         if (!cctx) return NULL;
         ZSTD_initCCtx(cctx, customMem);
         return cctx;
@@ -96,20 +113,20 @@
     ZSTD_CCtx* cctx;
     if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL;  /* minimum size */
     if ((size_t)workspace & 7) return NULL;  /* must be 8-aligned */
-    ZSTD_cwksp_init(&ws, workspace, workspaceSize);
+    ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_static_alloc);
 
     cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx));
     if (cctx == NULL) return NULL;
 
-    memset(cctx, 0, sizeof(ZSTD_CCtx));
+    ZSTD_memset(cctx, 0, sizeof(ZSTD_CCtx));
     ZSTD_cwksp_move(&cctx->workspace, &ws);
     cctx->staticSize = workspaceSize;
 
     /* statically sized space. entropyWorkspace never moves (but prev/next block swap places) */
-    if (!ZSTD_cwksp_check_available(&cctx->workspace, HUF_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t))) return NULL;
+    if (!ZSTD_cwksp_check_available(&cctx->workspace, ENTROPY_WORKSPACE_SIZE + 2 * sizeof(ZSTD_compressedBlockState_t))) return NULL;
     cctx->blockState.prevCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t));
     cctx->blockState.nextCBlock = (ZSTD_compressedBlockState_t*)ZSTD_cwksp_reserve_object(&cctx->workspace, sizeof(ZSTD_compressedBlockState_t));
-    cctx->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cctx->workspace, HUF_WORKSPACE_SIZE);
+    cctx->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cctx->workspace, ENTROPY_WORKSPACE_SIZE);
     cctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
     return cctx;
 }
@@ -119,10 +136,10 @@
  */
 static void ZSTD_clearAllDicts(ZSTD_CCtx* cctx)
 {
-    ZSTD_free(cctx->localDict.dictBuffer, cctx->customMem);
+    ZSTD_customFree(cctx->localDict.dictBuffer, cctx->customMem);
     ZSTD_freeCDict(cctx->localDict.cdict);
-    memset(&cctx->localDict, 0, sizeof(cctx->localDict));
-    memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));
+    ZSTD_memset(&cctx->localDict, 0, sizeof(cctx->localDict));
+    ZSTD_memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));
     cctx->cdict = NULL;
 }
 
@@ -153,7 +170,7 @@
         int cctxInWorkspace = ZSTD_cwksp_owns_buffer(&cctx->workspace, cctx);
         ZSTD_freeCCtxContent(cctx);
         if (!cctxInWorkspace) {
-            ZSTD_free(cctx, cctx->customMem);
+            ZSTD_customFree(cctx, cctx->customMem);
         }
     }
     return 0;
@@ -189,15 +206,90 @@
 /* private API call, for dictBuilder only */
 const seqStore_t* ZSTD_getSeqStore(const ZSTD_CCtx* ctx) { return &(ctx->seqStore); }
 
+/* Returns true if the strategy supports using a row based matchfinder */
+static int ZSTD_rowMatchFinderSupported(const ZSTD_strategy strategy) {
+    return (strategy >= ZSTD_greedy && strategy <= ZSTD_lazy2);
+}
+
+/* Returns true if the strategy and useRowMatchFinder mode indicate that we will use the row based matchfinder
+ * for this compression.
+ */
+static int ZSTD_rowMatchFinderUsed(const ZSTD_strategy strategy, const ZSTD_useRowMatchFinderMode_e mode) {
+    assert(mode != ZSTD_urm_auto);
+    return ZSTD_rowMatchFinderSupported(strategy) && (mode == ZSTD_urm_enableRowMatchFinder);
+}
+
+/* Returns row matchfinder usage enum given an initial mode and cParams */
+static ZSTD_useRowMatchFinderMode_e ZSTD_resolveRowMatchFinderMode(ZSTD_useRowMatchFinderMode_e mode,
+                                                                   const ZSTD_compressionParameters* const cParams) {
+#if !defined(ZSTD_NO_INTRINSICS) && (defined(__SSE2__) || defined(__ARM_NEON))
+    int const kHasSIMD128 = 1;
+#else
+    int const kHasSIMD128 = 0;
+#endif
+    if (mode != ZSTD_urm_auto) return mode; /* if requested enabled, but no SIMD, we still will use row matchfinder */
+    mode = ZSTD_urm_disableRowMatchFinder;
+    if (!ZSTD_rowMatchFinderSupported(cParams->strategy)) return mode;
+    if (kHasSIMD128) {
+        if (cParams->windowLog > 14) mode = ZSTD_urm_enableRowMatchFinder;
+    } else {
+        if (cParams->windowLog > 17) mode = ZSTD_urm_enableRowMatchFinder;
+    }
+    return mode;
+}
+
+/* Returns 1 if the arguments indicate that we should allocate a chainTable, 0 otherwise */
+static int ZSTD_allocateChainTable(const ZSTD_strategy strategy,
+                                   const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                                   const U32 forDDSDict) {
+    assert(useRowMatchFinder != ZSTD_urm_auto);
+    /* We always should allocate a chaintable if we are allocating a matchstate for a DDS dictionary matchstate.
+     * We do not allocate a chaintable if we are using ZSTD_fast, or are using the row-based matchfinder.
+     */
+    return forDDSDict || ((strategy != ZSTD_fast) && !ZSTD_rowMatchFinderUsed(strategy, useRowMatchFinder));
+}
+
+/* Returns 1 if compression parameters are such that we should
+ * enable long distance matching (wlog >= 27, strategy >= btopt).
+ * Returns 0 otherwise.
+ */
+static U32 ZSTD_CParams_shouldEnableLdm(const ZSTD_compressionParameters* const cParams) {
+    return cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 27;
+}
+
+/* Returns 1 if compression parameters are such that we should
+ * enable blockSplitter (wlog >= 17, strategy >= btopt).
+ * Returns 0 otherwise.
+ */
+static U32 ZSTD_CParams_useBlockSplitter(const ZSTD_compressionParameters* const cParams) {
+    return cParams->strategy >= ZSTD_btopt && cParams->windowLog >= 17;
+}
+
 static ZSTD_CCtx_params ZSTD_makeCCtxParamsFromCParams(
         ZSTD_compressionParameters cParams)
 {
     ZSTD_CCtx_params cctxParams;
-    memset(&cctxParams, 0, sizeof(cctxParams));
+    /* should not matter, as all cParams are presumed properly defined */
+    ZSTD_CCtxParams_init(&cctxParams, ZSTD_CLEVEL_DEFAULT);
     cctxParams.cParams = cParams;
-    cctxParams.compressionLevel = ZSTD_CLEVEL_DEFAULT;  /* should not matter, as all cParams are presumed properly defined */
+
+    /* Adjust advanced params according to cParams */
+    if (ZSTD_CParams_shouldEnableLdm(&cParams)) {
+        DEBUGLOG(4, "ZSTD_makeCCtxParamsFromCParams(): Including LDM into cctx params");
+        cctxParams.ldmParams.enableLdm = 1;
+        /* LDM is enabled by default for optimal parser and window size >= 128MB */
+        ZSTD_ldm_adjustParameters(&cctxParams.ldmParams, &cParams);
+        assert(cctxParams.ldmParams.hashLog >= cctxParams.ldmParams.bucketSizeLog);
+        assert(cctxParams.ldmParams.hashRateLog < 32);
+    }
+
+    if (ZSTD_CParams_useBlockSplitter(&cParams)) {
+        DEBUGLOG(4, "ZSTD_makeCCtxParamsFromCParams(): Including block splitting into cctx params");
+        cctxParams.splitBlocks = 1;
+    }
+
+    cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams.useRowMatchFinder, &cParams);
     assert(!ZSTD_checkCParams(cParams));
-    cctxParams.fParams.contentSizeFlag = 1;
     return cctxParams;
 }
 
@@ -205,13 +297,12 @@
         ZSTD_customMem customMem)
 {
     ZSTD_CCtx_params* params;
-    if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
-    params = (ZSTD_CCtx_params*)ZSTD_calloc(
+    if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
+    params = (ZSTD_CCtx_params*)ZSTD_customCalloc(
             sizeof(ZSTD_CCtx_params), customMem);
     if (!params) { return NULL; }
+    ZSTD_CCtxParams_init(params, ZSTD_CLEVEL_DEFAULT);
     params->customMem = customMem;
-    params->compressionLevel = ZSTD_CLEVEL_DEFAULT;
-    params->fParams.contentSizeFlag = 1;
     return params;
 }
 
@@ -223,7 +314,7 @@
 size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params)
 {
     if (params == NULL) { return 0; }
-    ZSTD_free(params, params->customMem);
+    ZSTD_customFree(params, params->customMem);
     return 0;
 }
 
@@ -234,35 +325,54 @@
 
 size_t ZSTD_CCtxParams_init(ZSTD_CCtx_params* cctxParams, int compressionLevel) {
     RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!");
-    memset(cctxParams, 0, sizeof(*cctxParams));
+    ZSTD_memset(cctxParams, 0, sizeof(*cctxParams));
     cctxParams->compressionLevel = compressionLevel;
     cctxParams->fParams.contentSizeFlag = 1;
     return 0;
 }
 
+#define ZSTD_NO_CLEVEL 0
+
+/**
+ * Initializes the cctxParams from params and compressionLevel.
+ * @param compressionLevel If params are derived from a compression level then that compression level, otherwise ZSTD_NO_CLEVEL.
+ */
+static void ZSTD_CCtxParams_init_internal(ZSTD_CCtx_params* cctxParams, ZSTD_parameters const* params, int compressionLevel)
+{
+    assert(!ZSTD_checkCParams(params->cParams));
+    ZSTD_memset(cctxParams, 0, sizeof(*cctxParams));
+    cctxParams->cParams = params->cParams;
+    cctxParams->fParams = params->fParams;
+    /* Should not matter, as all cParams are presumed properly defined.
+     * But, set it for tracing anyway.
+     */
+    cctxParams->compressionLevel = compressionLevel;
+    cctxParams->useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams->useRowMatchFinder, &params->cParams);
+    DEBUGLOG(4, "ZSTD_CCtxParams_init_internal: useRowMatchFinder=%d", cctxParams->useRowMatchFinder);
+}
+
 size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params)
 {
     RETURN_ERROR_IF(!cctxParams, GENERIC, "NULL pointer!");
     FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) , "");
-    memset(cctxParams, 0, sizeof(*cctxParams));
-    assert(!ZSTD_checkCParams(params.cParams));
-    cctxParams->cParams = params.cParams;
-    cctxParams->fParams = params.fParams;
-    cctxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT;   /* should not matter, as all cParams are presumed properly defined */
+    ZSTD_CCtxParams_init_internal(cctxParams, &params, ZSTD_NO_CLEVEL);
     return 0;
 }
 
-/* ZSTD_assignParamsToCCtxParams() :
- * params is presumed valid at this stage */
-static ZSTD_CCtx_params ZSTD_assignParamsToCCtxParams(
-        const ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params)
+/**
+ * Sets cctxParams' cParams and fParams from params, but otherwise leaves them alone.
+ * @param param Validated zstd parameters.
+ */
+static void ZSTD_CCtxParams_setZstdParams(
+        ZSTD_CCtx_params* cctxParams, const ZSTD_parameters* params)
 {
-    ZSTD_CCtx_params ret = *cctxParams;
     assert(!ZSTD_checkCParams(params->cParams));
-    ret.cParams = params->cParams;
-    ret.fParams = params->fParams;
-    ret.compressionLevel = ZSTD_CLEVEL_DEFAULT;   /* should not matter, as all cParams are presumed properly defined */
-    return ret;
+    cctxParams->cParams = params->cParams;
+    cctxParams->fParams = params->fParams;
+    /* Should not matter, as all cParams are presumed properly defined.
+     * But, set it for tracing anyway.
+     */
+    cctxParams->compressionLevel = ZSTD_NO_CLEVEL;
 }
 
 ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)
@@ -354,6 +464,11 @@
 #endif
         return bounds;
 
+    case ZSTD_c_enableDedicatedDictSearch:
+        bounds.lowerBound = 0;
+        bounds.upperBound = 1;
+        return bounds;
+
     case ZSTD_c_enableLongDistanceMatching:
         bounds.lowerBound = 0;
         bounds.upperBound = 1;
@@ -397,7 +512,7 @@
         return bounds;
 
     case ZSTD_c_forceAttachDict:
-        ZSTD_STATIC_ASSERT(ZSTD_dictDefaultAttach < ZSTD_dictForceCopy);
+        ZSTD_STATIC_ASSERT(ZSTD_dictDefaultAttach < ZSTD_dictForceLoad);
         bounds.lowerBound = ZSTD_dictDefaultAttach;
         bounds.upperBound = ZSTD_dictForceLoad;       /* note : how to ensure at compile time that this is the highest value enum ? */
         return bounds;
@@ -418,6 +533,37 @@
         bounds.upperBound = ZSTD_SRCSIZEHINT_MAX;
         return bounds;
 
+    case ZSTD_c_stableInBuffer:
+    case ZSTD_c_stableOutBuffer:
+        bounds.lowerBound = (int)ZSTD_bm_buffered;
+        bounds.upperBound = (int)ZSTD_bm_stable;
+        return bounds;
+
+    case ZSTD_c_blockDelimiters:
+        bounds.lowerBound = (int)ZSTD_sf_noBlockDelimiters;
+        bounds.upperBound = (int)ZSTD_sf_explicitBlockDelimiters;
+        return bounds;
+
+    case ZSTD_c_validateSequences:
+        bounds.lowerBound = 0;
+        bounds.upperBound = 1;
+        return bounds;
+
+    case ZSTD_c_splitBlocks:
+        bounds.lowerBound = 0;
+        bounds.upperBound = 1;
+        return bounds;
+
+    case ZSTD_c_useRowMatchFinder:
+        bounds.lowerBound = (int)ZSTD_urm_auto;
+        bounds.upperBound = (int)ZSTD_urm_enableRowMatchFinder;
+        return bounds;
+
+    case ZSTD_c_deterministicRefPrefix:
+        bounds.lowerBound = 0;
+        bounds.upperBound = 1;
+        return bounds;
+
     default:
         bounds.error = ERROR(parameter_unsupported);
         return bounds;
@@ -465,6 +611,7 @@
     case ZSTD_c_jobSize:
     case ZSTD_c_overlapLog:
     case ZSTD_c_rsyncable:
+    case ZSTD_c_enableDedicatedDictSearch:
     case ZSTD_c_enableLongDistanceMatching:
     case ZSTD_c_ldmHashLog:
     case ZSTD_c_ldmMinMatch:
@@ -474,6 +621,13 @@
     case ZSTD_c_literalCompressionMode:
     case ZSTD_c_targetCBlockSize:
     case ZSTD_c_srcSizeHint:
+    case ZSTD_c_stableInBuffer:
+    case ZSTD_c_stableOutBuffer:
+    case ZSTD_c_blockDelimiters:
+    case ZSTD_c_validateSequences:
+    case ZSTD_c_splitBlocks:
+    case ZSTD_c_useRowMatchFinder:
+    case ZSTD_c_deterministicRefPrefix:
     default:
         return 0;
     }
@@ -515,12 +669,20 @@
     case ZSTD_c_jobSize:
     case ZSTD_c_overlapLog:
     case ZSTD_c_rsyncable:
+    case ZSTD_c_enableDedicatedDictSearch:
     case ZSTD_c_enableLongDistanceMatching:
     case ZSTD_c_ldmHashLog:
     case ZSTD_c_ldmMinMatch:
     case ZSTD_c_ldmBucketSizeLog:
     case ZSTD_c_targetCBlockSize:
     case ZSTD_c_srcSizeHint:
+    case ZSTD_c_stableInBuffer:
+    case ZSTD_c_stableOutBuffer:
+    case ZSTD_c_blockDelimiters:
+    case ZSTD_c_validateSequences:
+    case ZSTD_c_splitBlocks:
+    case ZSTD_c_useRowMatchFinder:
+    case ZSTD_c_deterministicRefPrefix:
         break;
 
     default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
@@ -541,9 +703,10 @@
 
     case ZSTD_c_compressionLevel : {
         FORWARD_IF_ERROR(ZSTD_cParam_clampBounds(param, &value), "");
-        if (value) {  /* 0 : does not change current level */
+        if (value == 0)
+            CCtxParams->compressionLevel = ZSTD_CLEVEL_DEFAULT; /* 0 == default */
+        else
             CCtxParams->compressionLevel = value;
-        }
         if (CCtxParams->compressionLevel >= 0) return (size_t)CCtxParams->compressionLevel;
         return 0;  /* return type (size_t) cannot represent negative values */
     }
@@ -667,6 +830,10 @@
         return CCtxParams->rsyncable;
 #endif
 
+    case ZSTD_c_enableDedicatedDictSearch :
+        CCtxParams->enableDedicatedDictSearch = (value!=0);
+        return CCtxParams->enableDedicatedDictSearch;
+
     case ZSTD_c_enableLongDistanceMatching :
         CCtxParams->ldmParams.enableLdm = (value!=0);
         return CCtxParams->ldmParams.enableLdm;
@@ -707,17 +874,52 @@
         CCtxParams->srcSizeHint = value;
         return CCtxParams->srcSizeHint;
 
+    case ZSTD_c_stableInBuffer:
+        BOUNDCHECK(ZSTD_c_stableInBuffer, value);
+        CCtxParams->inBufferMode = (ZSTD_bufferMode_e)value;
+        return CCtxParams->inBufferMode;
+
+    case ZSTD_c_stableOutBuffer:
+        BOUNDCHECK(ZSTD_c_stableOutBuffer, value);
+        CCtxParams->outBufferMode = (ZSTD_bufferMode_e)value;
+        return CCtxParams->outBufferMode;
+
+    case ZSTD_c_blockDelimiters:
+        BOUNDCHECK(ZSTD_c_blockDelimiters, value);
+        CCtxParams->blockDelimiters = (ZSTD_sequenceFormat_e)value;
+        return CCtxParams->blockDelimiters;
+
+    case ZSTD_c_validateSequences:
+        BOUNDCHECK(ZSTD_c_validateSequences, value);
+        CCtxParams->validateSequences = value;
+        return CCtxParams->validateSequences;
+
+    case ZSTD_c_splitBlocks:
+        BOUNDCHECK(ZSTD_c_splitBlocks, value);
+        CCtxParams->splitBlocks = value;
+        return CCtxParams->splitBlocks;
+
+    case ZSTD_c_useRowMatchFinder:
+        BOUNDCHECK(ZSTD_c_useRowMatchFinder, value);
+        CCtxParams->useRowMatchFinder = (ZSTD_useRowMatchFinderMode_e)value;
+        return CCtxParams->useRowMatchFinder;
+
+    case ZSTD_c_deterministicRefPrefix:
+        BOUNDCHECK(ZSTD_c_deterministicRefPrefix, value);
+        CCtxParams->deterministicRefPrefix = !!value;
+        return CCtxParams->deterministicRefPrefix;
+
     default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
     }
 }
 
-size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value)
+size_t ZSTD_CCtx_getParameter(ZSTD_CCtx const* cctx, ZSTD_cParameter param, int* value)
 {
     return ZSTD_CCtxParams_getParameter(&cctx->requestedParams, param, value);
 }
 
 size_t ZSTD_CCtxParams_getParameter(
-        ZSTD_CCtx_params* CCtxParams, ZSTD_cParameter param, int* value)
+        ZSTD_CCtx_params const* CCtxParams, ZSTD_cParameter param, int* value)
 {
     switch(param)
     {
@@ -794,6 +996,9 @@
         *value = CCtxParams->rsyncable;
         break;
 #endif
+    case ZSTD_c_enableDedicatedDictSearch :
+        *value = CCtxParams->enableDedicatedDictSearch;
+        break;
     case ZSTD_c_enableLongDistanceMatching :
         *value = CCtxParams->ldmParams.enableLdm;
         break;
@@ -815,6 +1020,27 @@
     case ZSTD_c_srcSizeHint :
         *value = (int)CCtxParams->srcSizeHint;
         break;
+    case ZSTD_c_stableInBuffer :
+        *value = (int)CCtxParams->inBufferMode;
+        break;
+    case ZSTD_c_stableOutBuffer :
+        *value = (int)CCtxParams->outBufferMode;
+        break;
+    case ZSTD_c_blockDelimiters :
+        *value = (int)CCtxParams->blockDelimiters;
+        break;
+    case ZSTD_c_validateSequences :
+        *value = (int)CCtxParams->validateSequences;
+        break;
+    case ZSTD_c_splitBlocks :
+        *value = (int)CCtxParams->splitBlocks;
+        break;
+    case ZSTD_c_useRowMatchFinder :
+        *value = (int)CCtxParams->useRowMatchFinder;
+        break;
+    case ZSTD_c_deterministicRefPrefix:
+        *value = (int)CCtxParams->deterministicRefPrefix;
+        break;
     default: RETURN_ERROR(parameter_unsupported, "unknown parameter");
     }
     return 0;
@@ -850,6 +1076,14 @@
     return 0;
 }
 
+static ZSTD_compressionParameters ZSTD_dedicatedDictSearch_getCParams(
+        int const compressionLevel,
+        size_t const dictSize);
+static int ZSTD_dedicatedDictSearch_isSupported(
+        const ZSTD_compressionParameters* cParams);
+static void ZSTD_dedicatedDictSearch_revertCParams(
+        ZSTD_compressionParameters* cParams);
+
 /**
  * Initializes the local dict using the requested parameters.
  * NOTE: This does not use the pledged src size, because it may be used for more
@@ -858,8 +1092,6 @@
 static size_t ZSTD_initLocalDict(ZSTD_CCtx* cctx)
 {
     ZSTD_localDict* const dl = &cctx->localDict;
-    ZSTD_compressionParameters const cParams = ZSTD_getCParamsFromCCtxParams(
-            &cctx->requestedParams, ZSTD_CONTENTSIZE_UNKNOWN, dl->dictSize);
     if (dl->dict == NULL) {
         /* No local dictionary. */
         assert(dl->dictBuffer == NULL);
@@ -876,12 +1108,12 @@
     assert(cctx->cdict == NULL);
     assert(cctx->prefixDict.dict == NULL);
 
-    dl->cdict = ZSTD_createCDict_advanced(
+    dl->cdict = ZSTD_createCDict_advanced2(
             dl->dict,
             dl->dictSize,
             ZSTD_dlm_byRef,
             dl->dictContentType,
-            cParams,
+            &cctx->requestedParams,
             cctx->customMem);
     RETURN_ERROR_IF(!dl->cdict, memory_allocation, "ZSTD_createCDict_advanced failed");
     cctx->cdict = dl->cdict;
@@ -894,8 +1126,6 @@
 {
     RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
                     "Can't load a dictionary when ctx is not in init stage.");
-    RETURN_ERROR_IF(cctx->staticSize, memory_allocation,
-                    "no malloc for static CCtx");
     DEBUGLOG(4, "ZSTD_CCtx_loadDictionary_advanced (size: %u)", (U32)dictSize);
     ZSTD_clearAllDicts(cctx);  /* in case one already exists */
     if (dict == NULL || dictSize == 0)  /* no dictionary mode */
@@ -903,9 +1133,12 @@
     if (dictLoadMethod == ZSTD_dlm_byRef) {
         cctx->localDict.dict = dict;
     } else {
-        void* dictBuffer = ZSTD_malloc(dictSize, cctx->customMem);
+        void* dictBuffer;
+        RETURN_ERROR_IF(cctx->staticSize, memory_allocation,
+                        "no malloc for static CCtx");
+        dictBuffer = ZSTD_customMalloc(dictSize, cctx->customMem);
         RETURN_ERROR_IF(!dictBuffer, memory_allocation, "NULL pointer!");
-        memcpy(dictBuffer, dict, dictSize);
+        ZSTD_memcpy(dictBuffer, dict, dictSize);
         cctx->localDict.dictBuffer = dictBuffer;
         cctx->localDict.dict = dictBuffer;
     }
@@ -938,6 +1171,14 @@
     return 0;
 }
 
+size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool)
+{
+    RETURN_ERROR_IF(cctx->streamStage != zcss_init, stage_wrong,
+                    "Can't ref a pool when ctx not in init stage.");
+    cctx->pool = pool;
+    return 0;
+}
+
 size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx, const void* prefix, size_t prefixSize)
 {
     return ZSTD_CCtx_refPrefix_advanced(cctx, prefix, prefixSize, ZSTD_dct_rawContent);
@@ -1022,23 +1263,83 @@
     return hashLog - btScale;
 }
 
+/** ZSTD_dictAndWindowLog() :
+ * Returns an adjusted window log that is large enough to fit the source and the dictionary.
+ * The zstd format says that the entire dictionary is valid if one byte of the dictionary
+ * is within the window. So the hashLog and chainLog should be large enough to reference both
+ * the dictionary and the window. So we must use this adjusted dictAndWindowLog when downsizing
+ * the hashLog and windowLog.
+ * NOTE: srcSize must not be ZSTD_CONTENTSIZE_UNKNOWN.
+ */
+static U32 ZSTD_dictAndWindowLog(U32 windowLog, U64 srcSize, U64 dictSize)
+{
+    const U64 maxWindowSize = 1ULL << ZSTD_WINDOWLOG_MAX;
+    /* No dictionary ==> No change */
+    if (dictSize == 0) {
+        return windowLog;
+    }
+    assert(windowLog <= ZSTD_WINDOWLOG_MAX);
+    assert(srcSize != ZSTD_CONTENTSIZE_UNKNOWN); /* Handled in ZSTD_adjustCParams_internal() */
+    {
+        U64 const windowSize = 1ULL << windowLog;
+        U64 const dictAndWindowSize = dictSize + windowSize;
+        /* If the window size is already large enough to fit both the source and the dictionary
+         * then just use the window size. Otherwise adjust so that it fits the dictionary and
+         * the window.
+         */
+        if (windowSize >= dictSize + srcSize) {
+            return windowLog; /* Window size large enough already */
+        } else if (dictAndWindowSize >= maxWindowSize) {
+            return ZSTD_WINDOWLOG_MAX; /* Larger than max window log */
+        } else  {
+            return ZSTD_highbit32((U32)dictAndWindowSize - 1) + 1;
+        }
+    }
+}
+
 /** ZSTD_adjustCParams_internal() :
  *  optimize `cPar` for a specified input (`srcSize` and `dictSize`).
  *  mostly downsize to reduce memory consumption and initialization latency.
  * `srcSize` can be ZSTD_CONTENTSIZE_UNKNOWN when not known.
+ * `mode` is the mode for parameter adjustment. See docs for `ZSTD_cParamMode_e`.
  *  note : `srcSize==0` means 0!
  *  condition : cPar is presumed validated (can be checked using ZSTD_checkCParams()). */
 static ZSTD_compressionParameters
 ZSTD_adjustCParams_internal(ZSTD_compressionParameters cPar,
                             unsigned long long srcSize,
-                            size_t dictSize)
+                            size_t dictSize,
+                            ZSTD_cParamMode_e mode)
 {
-    static const U64 minSrcSize = 513; /* (1<<9) + 1 */
-    static const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);
+    const U64 minSrcSize = 513; /* (1<<9) + 1 */
+    const U64 maxWindowResize = 1ULL << (ZSTD_WINDOWLOG_MAX-1);
     assert(ZSTD_checkCParams(cPar)==0);
 
-    if (dictSize && srcSize == ZSTD_CONTENTSIZE_UNKNOWN)
-        srcSize = minSrcSize;
+    switch (mode) {
+    case ZSTD_cpm_unknown:
+    case ZSTD_cpm_noAttachDict:
+        /* If we don't know the source size, don't make any
+         * assumptions about it. We will already have selected
+         * smaller parameters if a dictionary is in use.
+         */
+        break;
+    case ZSTD_cpm_createCDict:
+        /* Assume a small source size when creating a dictionary
+         * with an unkown source size.
+         */
+        if (dictSize && srcSize == ZSTD_CONTENTSIZE_UNKNOWN)
+            srcSize = minSrcSize;
+        break;
+    case ZSTD_cpm_attachDict:
+        /* Dictionary has its own dedicated parameters which have
+         * already been selected. We are selecting parameters
+         * for only the source.
+         */
+        dictSize = 0;
+        break;
+    default:
+        assert(0);
+        break;
+    }
 
     /* resize windowLog if input is small enough, to use less memory */
     if ( (srcSize < maxWindowResize)
@@ -1049,10 +1350,12 @@
                             ZSTD_highbit32(tSize-1) + 1;
         if (cPar.windowLog > srcLog) cPar.windowLog = srcLog;
     }
-    if (cPar.hashLog > cPar.windowLog+1) cPar.hashLog = cPar.windowLog+1;
-    {   U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy);
-        if (cycleLog > cPar.windowLog)
-            cPar.chainLog -= (cycleLog - cPar.windowLog);
+    if (srcSize != ZSTD_CONTENTSIZE_UNKNOWN) {
+        U32 const dictAndWindowLog = ZSTD_dictAndWindowLog(cPar.windowLog, (U64)srcSize, (U64)dictSize);
+        U32 const cycleLog = ZSTD_cycleLog(cPar.chainLog, cPar.strategy);
+        if (cPar.hashLog > dictAndWindowLog+1) cPar.hashLog = dictAndWindowLog+1;
+        if (cycleLog > dictAndWindowLog)
+            cPar.chainLog -= (cycleLog - dictAndWindowLog);
     }
 
     if (cPar.windowLog < ZSTD_WINDOWLOG_ABSOLUTEMIN)
@@ -1068,38 +1371,50 @@
 {
     cPar = ZSTD_clampCParams(cPar);   /* resulting cPar is necessarily valid (all parameters within range) */
     if (srcSize == 0) srcSize = ZSTD_CONTENTSIZE_UNKNOWN;
-    return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize);
+    return ZSTD_adjustCParams_internal(cPar, srcSize, dictSize, ZSTD_cpm_unknown);
 }
 
-static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize);
-static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize);
+static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode);
+static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode);
+
+static void ZSTD_overrideCParams(
+              ZSTD_compressionParameters* cParams,
+        const ZSTD_compressionParameters* overrides)
+{
+    if (overrides->windowLog)    cParams->windowLog    = overrides->windowLog;
+    if (overrides->hashLog)      cParams->hashLog      = overrides->hashLog;
+    if (overrides->chainLog)     cParams->chainLog     = overrides->chainLog;
+    if (overrides->searchLog)    cParams->searchLog    = overrides->searchLog;
+    if (overrides->minMatch)     cParams->minMatch     = overrides->minMatch;
+    if (overrides->targetLength) cParams->targetLength = overrides->targetLength;
+    if (overrides->strategy)     cParams->strategy     = overrides->strategy;
+}
 
 ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
-        const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize)
+        const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode)
 {
     ZSTD_compressionParameters cParams;
     if (srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN && CCtxParams->srcSizeHint > 0) {
       srcSizeHint = CCtxParams->srcSizeHint;
     }
-    cParams = ZSTD_getCParams_internal(CCtxParams->compressionLevel, srcSizeHint, dictSize);
+    cParams = ZSTD_getCParams_internal(CCtxParams->compressionLevel, srcSizeHint, dictSize, mode);
     if (CCtxParams->ldmParams.enableLdm) cParams.windowLog = ZSTD_LDM_DEFAULT_WINDOW_LOG;
-    if (CCtxParams->cParams.windowLog) cParams.windowLog = CCtxParams->cParams.windowLog;
-    if (CCtxParams->cParams.hashLog) cParams.hashLog = CCtxParams->cParams.hashLog;
-    if (CCtxParams->cParams.chainLog) cParams.chainLog = CCtxParams->cParams.chainLog;
-    if (CCtxParams->cParams.searchLog) cParams.searchLog = CCtxParams->cParams.searchLog;
-    if (CCtxParams->cParams.minMatch) cParams.minMatch = CCtxParams->cParams.minMatch;
-    if (CCtxParams->cParams.targetLength) cParams.targetLength = CCtxParams->cParams.targetLength;
-    if (CCtxParams->cParams.strategy) cParams.strategy = CCtxParams->cParams.strategy;
+    ZSTD_overrideCParams(&cParams, &CCtxParams->cParams);
     assert(!ZSTD_checkCParams(cParams));
     /* srcSizeHint == 0 means 0 */
-    return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize);
+    return ZSTD_adjustCParams_internal(cParams, srcSizeHint, dictSize, mode);
 }
 
 static size_t
 ZSTD_sizeof_matchState(const ZSTD_compressionParameters* const cParams,
+                       const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                       const U32 enableDedicatedDictSearch,
                        const U32 forCCtx)
 {
-    size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
+    /* chain table size should be 0 for fast or row-hash strategies */
+    size_t const chainSize = ZSTD_allocateChainTable(cParams->strategy, useRowMatchFinder, enableDedicatedDictSearch && !forCCtx)
+                                ? ((size_t)1 << cParams->chainLog)
+                                : 0;
     size_t const hSize = ((size_t)1) << cParams->hashLog;
     U32    const hashLog3 = (forCCtx && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
     size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0;
@@ -1109,71 +1424,117 @@
                             + hSize * sizeof(U32)
                             + h3Size * sizeof(U32);
     size_t const optPotentialSpace =
-        ZSTD_cwksp_alloc_size((MaxML+1) * sizeof(U32))
-      + ZSTD_cwksp_alloc_size((MaxLL+1) * sizeof(U32))
-      + ZSTD_cwksp_alloc_size((MaxOff+1) * sizeof(U32))
-      + ZSTD_cwksp_alloc_size((1<<Litbits) * sizeof(U32))
-      + ZSTD_cwksp_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t))
-      + ZSTD_cwksp_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));
+        ZSTD_cwksp_aligned_alloc_size((MaxML+1) * sizeof(U32))
+      + ZSTD_cwksp_aligned_alloc_size((MaxLL+1) * sizeof(U32))
+      + ZSTD_cwksp_aligned_alloc_size((MaxOff+1) * sizeof(U32))
+      + ZSTD_cwksp_aligned_alloc_size((1<<Litbits) * sizeof(U32))
+      + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_match_t))
+      + ZSTD_cwksp_aligned_alloc_size((ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));
+    size_t const lazyAdditionalSpace = ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)
+                                            ? ZSTD_cwksp_aligned_alloc_size(hSize*sizeof(U16))
+                                            : 0;
     size_t const optSpace = (forCCtx && (cParams->strategy >= ZSTD_btopt))
                                 ? optPotentialSpace
                                 : 0;
+    size_t const slackSpace = ZSTD_cwksp_slack_space_required();
+
+    /* tables are guaranteed to be sized in multiples of 64 bytes (or 16 uint32_t) */
+    ZSTD_STATIC_ASSERT(ZSTD_HASHLOG_MIN >= 4 && ZSTD_WINDOWLOG_MIN >= 4 && ZSTD_CHAINLOG_MIN >= 4);
+    assert(useRowMatchFinder != ZSTD_urm_auto);
+
     DEBUGLOG(4, "chainSize: %u - hSize: %u - h3Size: %u",
                 (U32)chainSize, (U32)hSize, (U32)h3Size);
-    return tableSpace + optSpace;
+    return tableSpace + optSpace + slackSpace + lazyAdditionalSpace;
+}
+
+static size_t ZSTD_estimateCCtxSize_usingCCtxParams_internal(
+        const ZSTD_compressionParameters* cParams,
+        const ldmParams_t* ldmParams,
+        const int isStatic,
+        const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+        const size_t buffInSize,
+        const size_t buffOutSize,
+        const U64 pledgedSrcSize)
+{
+    size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << cParams->windowLog), pledgedSrcSize));
+    size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
+    U32    const divider = (cParams->minMatch==3) ? 3 : 4;
+    size_t const maxNbSeq = blockSize / divider;
+    size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize)
+                            + ZSTD_cwksp_aligned_alloc_size(maxNbSeq * sizeof(seqDef))
+                            + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE));
+    size_t const entropySpace = ZSTD_cwksp_alloc_size(ENTROPY_WORKSPACE_SIZE);
+    size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t));
+    size_t const matchStateSize = ZSTD_sizeof_matchState(cParams, useRowMatchFinder, /* enableDedicatedDictSearch */ 0, /* forCCtx */ 1);
+
+    size_t const ldmSpace = ZSTD_ldm_getTableSize(*ldmParams);
+    size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(*ldmParams, blockSize);
+    size_t const ldmSeqSpace = ldmParams->enableLdm ?
+        ZSTD_cwksp_aligned_alloc_size(maxNbLdmSeq * sizeof(rawSeq)) : 0;
+
+
+    size_t const bufferSpace = ZSTD_cwksp_alloc_size(buffInSize)
+                             + ZSTD_cwksp_alloc_size(buffOutSize);
+
+    size_t const cctxSpace = isStatic ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0;
+
+    size_t const neededSpace =
+        cctxSpace +
+        entropySpace +
+        blockStateSpace +
+        ldmSpace +
+        ldmSeqSpace +
+        matchStateSize +
+        tokenSpace +
+        bufferSpace;
+
+    DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace);
+    return neededSpace;
 }
 
 size_t ZSTD_estimateCCtxSize_usingCCtxParams(const ZSTD_CCtx_params* params)
 {
+    ZSTD_compressionParameters const cParams =
+                ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict);
+    ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder,
+                                                                                         &cParams);
+
     RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only.");
-    {   ZSTD_compressionParameters const cParams =
-                ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0);
-        size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);
-        U32    const divider = (cParams.minMatch==3) ? 3 : 4;
-        size_t const maxNbSeq = blockSize / divider;
-        size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize)
-                                + ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(seqDef))
-                                + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE));
-        size_t const entropySpace = ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE);
-        size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t));
-        size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 1);
-
-        size_t const ldmSpace = ZSTD_ldm_getTableSize(params->ldmParams);
-        size_t const ldmSeqSpace = ZSTD_cwksp_alloc_size(ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize) * sizeof(rawSeq));
-
-        /* estimateCCtxSize is for one-shot compression. So no buffers should
-         * be needed. However, we still allocate two 0-sized buffers, which can
-         * take space under ASAN. */
-        size_t const bufferSpace = ZSTD_cwksp_alloc_size(0)
-                                 + ZSTD_cwksp_alloc_size(0);
-
-        size_t const cctxSpace = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx));
-
-        size_t const neededSpace =
-            cctxSpace +
-            entropySpace +
-            blockStateSpace +
-            ldmSpace +
-            ldmSeqSpace +
-            matchStateSize +
-            tokenSpace +
-            bufferSpace;
-
-        DEBUGLOG(5, "estimate workspace : %u", (U32)neededSpace);
-        return neededSpace;
-    }
+    /* estimateCCtxSize is for one-shot compression. So no buffers should
+     * be needed. However, we still allocate two 0-sized buffers, which can
+     * take space under ASAN. */
+    return ZSTD_estimateCCtxSize_usingCCtxParams_internal(
+        &cParams, &params->ldmParams, 1, useRowMatchFinder, 0, 0, ZSTD_CONTENTSIZE_UNKNOWN);
 }
 
 size_t ZSTD_estimateCCtxSize_usingCParams(ZSTD_compressionParameters cParams)
 {
-    ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);
-    return ZSTD_estimateCCtxSize_usingCCtxParams(&params);
+    ZSTD_CCtx_params initialParams = ZSTD_makeCCtxParamsFromCParams(cParams);
+    if (ZSTD_rowMatchFinderSupported(cParams.strategy)) {
+        /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */
+        size_t noRowCCtxSize;
+        size_t rowCCtxSize;
+        initialParams.useRowMatchFinder = ZSTD_urm_disableRowMatchFinder;
+        noRowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams);
+        initialParams.useRowMatchFinder = ZSTD_urm_enableRowMatchFinder;
+        rowCCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams);
+        return MAX(noRowCCtxSize, rowCCtxSize);
+    } else {
+        return ZSTD_estimateCCtxSize_usingCCtxParams(&initialParams);
+    }
 }
 
 static size_t ZSTD_estimateCCtxSize_internal(int compressionLevel)
 {
-    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);
-    return ZSTD_estimateCCtxSize_usingCParams(cParams);
+    int tier = 0;
+    size_t largestSize = 0;
+    static const unsigned long long srcSizeTiers[4] = {16 KB, 128 KB, 256 KB, ZSTD_CONTENTSIZE_UNKNOWN};
+    for (; tier < 4; ++tier) {
+        /* Choose the set of cParams for a given level across all srcSizes that give the largest cctxSize */
+        ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, srcSizeTiers[tier], 0, ZSTD_cpm_noAttachDict);
+        largestSize = MAX(ZSTD_estimateCCtxSize_usingCParams(cParams), largestSize);
+    }
+    return largestSize;
 }
 
 size_t ZSTD_estimateCCtxSize(int compressionLevel)
@@ -1181,6 +1542,7 @@
     int level;
     size_t memBudget = 0;
     for (level=MIN(compressionLevel, 1); level<=compressionLevel; level++) {
+        /* Ensure monotonically increasing memory usage as compression level increases */
         size_t const newMB = ZSTD_estimateCCtxSize_internal(level);
         if (newMB > memBudget) memBudget = newMB;
     }
@@ -1191,27 +1553,42 @@
 {
     RETURN_ERROR_IF(params->nbWorkers > 0, GENERIC, "Estimate CCtx size is supported for single-threaded compression only.");
     {   ZSTD_compressionParameters const cParams =
-                ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0);
-        size_t const CCtxSize = ZSTD_estimateCCtxSize_usingCCtxParams(params);
+                ZSTD_getCParamsFromCCtxParams(params, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict);
         size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, (size_t)1 << cParams.windowLog);
-        size_t const inBuffSize = ((size_t)1 << cParams.windowLog) + blockSize;
-        size_t const outBuffSize = ZSTD_compressBound(blockSize) + 1;
-        size_t const streamingSize = ZSTD_cwksp_alloc_size(inBuffSize)
-                                   + ZSTD_cwksp_alloc_size(outBuffSize);
+        size_t const inBuffSize = (params->inBufferMode == ZSTD_bm_buffered)
+                ? ((size_t)1 << cParams.windowLog) + blockSize
+                : 0;
+        size_t const outBuffSize = (params->outBufferMode == ZSTD_bm_buffered)
+                ? ZSTD_compressBound(blockSize) + 1
+                : 0;
+        ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params->useRowMatchFinder, &params->cParams);
 
-        return CCtxSize + streamingSize;
+        return ZSTD_estimateCCtxSize_usingCCtxParams_internal(
+            &cParams, &params->ldmParams, 1, useRowMatchFinder, inBuffSize, outBuffSize,
+            ZSTD_CONTENTSIZE_UNKNOWN);
     }
 }
 
 size_t ZSTD_estimateCStreamSize_usingCParams(ZSTD_compressionParameters cParams)
 {
-    ZSTD_CCtx_params const params = ZSTD_makeCCtxParamsFromCParams(cParams);
-    return ZSTD_estimateCStreamSize_usingCCtxParams(&params);
+    ZSTD_CCtx_params initialParams = ZSTD_makeCCtxParamsFromCParams(cParams);
+    if (ZSTD_rowMatchFinderSupported(cParams.strategy)) {
+        /* Pick bigger of not using and using row-based matchfinder for greedy and lazy strategies */
+        size_t noRowCCtxSize;
+        size_t rowCCtxSize;
+        initialParams.useRowMatchFinder = ZSTD_urm_disableRowMatchFinder;
+        noRowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams);
+        initialParams.useRowMatchFinder = ZSTD_urm_enableRowMatchFinder;
+        rowCCtxSize = ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams);
+        return MAX(noRowCCtxSize, rowCCtxSize);
+    } else {
+        return ZSTD_estimateCStreamSize_usingCCtxParams(&initialParams);
+    }
 }
 
 static size_t ZSTD_estimateCStreamSize_internal(int compressionLevel)
 {
-    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);
+    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict);
     return ZSTD_estimateCStreamSize_usingCParams(cParams);
 }
 
@@ -1305,16 +1682,6 @@
 }
 
 /**
- * Indicates whether this compression proceeds directly from user-provided
- * source buffer to user-provided destination buffer (ZSTDb_not_buffered), or
- * whether the context needs to buffer the input/output (ZSTDb_buffered).
- */
-typedef enum {
-    ZSTDb_not_buffered,
-    ZSTDb_buffered
-} ZSTD_buffered_policy_e;
-
-/**
  * Controls, for this matchState reset, whether the tables need to be cleared /
  * prepared for the coming compression (ZSTDcrp_makeClean), or whether the
  * tables can be left unclean (ZSTDcrp_leaveDirty), because we know that a
@@ -1341,20 +1708,27 @@
     ZSTD_resetTarget_CCtx
 } ZSTD_resetTarget_e;
 
+
 static size_t
 ZSTD_reset_matchState(ZSTD_matchState_t* ms,
                       ZSTD_cwksp* ws,
                 const ZSTD_compressionParameters* cParams,
+                const ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
                 const ZSTD_compResetPolicy_e crp,
                 const ZSTD_indexResetPolicy_e forceResetIndex,
                 const ZSTD_resetTarget_e forWho)
 {
-    size_t const chainSize = (cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cParams->chainLog);
+    /* disable chain table allocation for fast or row-based strategies */
+    size_t const chainSize = ZSTD_allocateChainTable(cParams->strategy, useRowMatchFinder,
+                                                     ms->dedicatedDictSearch && (forWho == ZSTD_resetTarget_CDict))
+                                ? ((size_t)1 << cParams->chainLog)
+                                : 0;
     size_t const hSize = ((size_t)1) << cParams->hashLog;
     U32    const hashLog3 = ((forWho == ZSTD_resetTarget_CCtx) && cParams->minMatch==3) ? MIN(ZSTD_HASHLOG3_MAX, cParams->windowLog) : 0;
     size_t const h3Size = hashLog3 ? ((size_t)1) << hashLog3 : 0;
 
     DEBUGLOG(4, "reset indices : %u", forceResetIndex == ZSTDirp_reset);
+    assert(useRowMatchFinder != ZSTD_urm_auto);
     if (forceResetIndex == ZSTDirp_reset) {
         ZSTD_window_init(&ms->window);
         ZSTD_cwksp_mark_tables_dirty(ws);
@@ -1393,11 +1767,23 @@
         ms->opt.priceTable = (ZSTD_optimal_t*)ZSTD_cwksp_reserve_aligned(ws, (ZSTD_OPT_NUM+1) * sizeof(ZSTD_optimal_t));
     }
 
+    if (ZSTD_rowMatchFinderUsed(cParams->strategy, useRowMatchFinder)) {
+        {   /* Row match finder needs an additional table of hashes ("tags") */
+            size_t const tagTableSize = hSize*sizeof(U16);
+            ms->tagTable = (U16*)ZSTD_cwksp_reserve_aligned(ws, tagTableSize);
+            if (ms->tagTable) ZSTD_memset(ms->tagTable, 0, tagTableSize);
+        }
+        {   /* Switch to 32-entry rows if searchLog is 5 (or more) */
+            U32 const rowLog = cParams->searchLog < 5 ? 4 : 5;
+            assert(cParams->hashLog > rowLog);
+            ms->rowHashLog = cParams->hashLog - rowLog;
+        }
+    }
+
     ms->cParams = *cParams;
 
     RETURN_ERROR_IF(ZSTD_cwksp_reserve_failed(ws), memory_allocation,
                     "failed a workspace allocation in ZSTD_reset_matchState");
-
     return 0;
 }
 
@@ -1414,75 +1800,85 @@
     return (size_t)(w.nextSrc - w.base) > (ZSTD_CURRENT_MAX - ZSTD_INDEXOVERFLOW_MARGIN);
 }
 
+/** ZSTD_dictTooBig():
+ * When dictionaries are larger than ZSTD_CHUNKSIZE_MAX they can't be loaded in
+ * one go generically. So we ensure that in that case we reset the tables to zero,
+ * so that we can load as much of the dictionary as possible.
+ */
+static int ZSTD_dictTooBig(size_t const loadedDictSize)
+{
+    return loadedDictSize > ZSTD_CHUNKSIZE_MAX;
+}
+
 /*! ZSTD_resetCCtx_internal() :
-    note : `params` are assumed fully validated at this stage */
+ * @param loadedDictSize The size of the dictionary to be loaded
+ * into the context, if any. If no dictionary is used, or the
+ * dictionary is being attached / copied, then pass 0.
+ * note : `params` are assumed fully validated at this stage.
+ */
 static size_t ZSTD_resetCCtx_internal(ZSTD_CCtx* zc,
-                                      ZSTD_CCtx_params params,
+                                      ZSTD_CCtx_params const* params,
                                       U64 const pledgedSrcSize,
+                                      size_t const loadedDictSize,
                                       ZSTD_compResetPolicy_e const crp,
                                       ZSTD_buffered_policy_e const zbuff)
 {
     ZSTD_cwksp* const ws = &zc->workspace;
-    DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u",
-                (U32)pledgedSrcSize, params.cParams.windowLog);
-    assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+    DEBUGLOG(4, "ZSTD_resetCCtx_internal: pledgedSrcSize=%u, wlog=%u, useRowMatchFinder=%d",
+                (U32)pledgedSrcSize, params->cParams.windowLog, (int)params->useRowMatchFinder);
+    assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams)));
 
     zc->isFirstBlock = 1;
 
-    if (params.ldmParams.enableLdm) {
+    /* Set applied params early so we can modify them for LDM,
+     * and point params at the applied params.
+     */
+    zc->appliedParams = *params;
+    params = &zc->appliedParams;
+
+    assert(params->useRowMatchFinder != ZSTD_urm_auto);
+    if (params->ldmParams.enableLdm) {
         /* Adjust long distance matching parameters */
-        ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
-        assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
-        assert(params.ldmParams.hashRateLog < 32);
-        zc->ldmState.hashPower = ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength);
+        ZSTD_ldm_adjustParameters(&zc->appliedParams.ldmParams, &params->cParams);
+        assert(params->ldmParams.hashLog >= params->ldmParams.bucketSizeLog);
+        assert(params->ldmParams.hashRateLog < 32);
     }
 
-    {   size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params.cParams.windowLog), pledgedSrcSize));
+    {   size_t const windowSize = MAX(1, (size_t)MIN(((U64)1 << params->cParams.windowLog), pledgedSrcSize));
         size_t const blockSize = MIN(ZSTD_BLOCKSIZE_MAX, windowSize);
-        U32    const divider = (params.cParams.minMatch==3) ? 3 : 4;
+        U32    const divider = (params->cParams.minMatch==3) ? 3 : 4;
         size_t const maxNbSeq = blockSize / divider;
-        size_t const tokenSpace = ZSTD_cwksp_alloc_size(WILDCOPY_OVERLENGTH + blockSize)
-                                + ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(seqDef))
-                                + 3 * ZSTD_cwksp_alloc_size(maxNbSeq * sizeof(BYTE));
-        size_t const buffOutSize = (zbuff==ZSTDb_buffered) ? ZSTD_compressBound(blockSize)+1 : 0;
-        size_t const buffInSize = (zbuff==ZSTDb_buffered) ? windowSize + blockSize : 0;
-        size_t const matchStateSize = ZSTD_sizeof_matchState(&params.cParams, /* forCCtx */ 1);
-        size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params.ldmParams, blockSize);
+        size_t const buffOutSize = (zbuff == ZSTDb_buffered && params->outBufferMode == ZSTD_bm_buffered)
+                ? ZSTD_compressBound(blockSize) + 1
+                : 0;
+        size_t const buffInSize = (zbuff == ZSTDb_buffered && params->inBufferMode == ZSTD_bm_buffered)
+                ? windowSize + blockSize
+                : 0;
+        size_t const maxNbLdmSeq = ZSTD_ldm_getMaxNbSeq(params->ldmParams, blockSize);
 
-        ZSTD_indexResetPolicy_e needsIndexReset = zc->initialized ? ZSTDirp_continue : ZSTDirp_reset;
+        int const indexTooClose = ZSTD_indexTooCloseToMax(zc->blockState.matchState.window);
+        int const dictTooBig = ZSTD_dictTooBig(loadedDictSize);
+        ZSTD_indexResetPolicy_e needsIndexReset =
+            (indexTooClose || dictTooBig || !zc->initialized) ? ZSTDirp_reset : ZSTDirp_continue;
 
-        if (ZSTD_indexTooCloseToMax(zc->blockState.matchState.window)) {
-            needsIndexReset = ZSTDirp_reset;
-        }
+        size_t const neededSpace =
+            ZSTD_estimateCCtxSize_usingCCtxParams_internal(
+                &params->cParams, &params->ldmParams, zc->staticSize != 0, params->useRowMatchFinder,
+                buffInSize, buffOutSize, pledgedSrcSize);
+        int resizeWorkspace;
+
+        FORWARD_IF_ERROR(neededSpace, "cctx size estimate failed!");
 
         if (!zc->staticSize) ZSTD_cwksp_bump_oversized_duration(ws, 0);
 
-        /* Check if workspace is large enough, alloc a new one if needed */
-        {   size_t const cctxSpace = zc->staticSize ? ZSTD_cwksp_alloc_size(sizeof(ZSTD_CCtx)) : 0;
-            size_t const entropySpace = ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE);
-            size_t const blockStateSpace = 2 * ZSTD_cwksp_alloc_size(sizeof(ZSTD_compressedBlockState_t));
-            size_t const bufferSpace = ZSTD_cwksp_alloc_size(buffInSize) + ZSTD_cwksp_alloc_size(buffOutSize);
-            size_t const ldmSpace = ZSTD_ldm_getTableSize(params.ldmParams);
-            size_t const ldmSeqSpace = ZSTD_cwksp_alloc_size(maxNbLdmSeq * sizeof(rawSeq));
-
-            size_t const neededSpace =
-                cctxSpace +
-                entropySpace +
-                blockStateSpace +
-                ldmSpace +
-                ldmSeqSpace +
-                matchStateSize +
-                tokenSpace +
-                bufferSpace;
-
+        {   /* Check if workspace is large enough, alloc a new one if needed */
             int const workspaceTooSmall = ZSTD_cwksp_sizeof(ws) < neededSpace;
             int const workspaceWasteful = ZSTD_cwksp_check_wasteful(ws, neededSpace);
-
-            DEBUGLOG(4, "Need %zuKB workspace, including %zuKB for match state, and %zuKB for buffers",
-                        neededSpace>>10, matchStateSize>>10, bufferSpace>>10);
+            resizeWorkspace = workspaceTooSmall || workspaceWasteful;
+            DEBUGLOG(4, "Need %zu B workspace", neededSpace);
             DEBUGLOG(4, "windowSize: %zu - blockSize: %zu", windowSize, blockSize);
 
-            if (workspaceTooSmall || workspaceWasteful) {
+            if (resizeWorkspace) {
                 DEBUGLOG(4, "Resize workspaceSize from %zuKB to %zuKB",
                             ZSTD_cwksp_sizeof(ws) >> 10,
                             neededSpace >> 10);
@@ -1503,15 +1899,14 @@
                 RETURN_ERROR_IF(zc->blockState.prevCBlock == NULL, memory_allocation, "couldn't allocate prevCBlock");
                 zc->blockState.nextCBlock = (ZSTD_compressedBlockState_t*) ZSTD_cwksp_reserve_object(ws, sizeof(ZSTD_compressedBlockState_t));
                 RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate nextCBlock");
-                zc->entropyWorkspace = (U32*) ZSTD_cwksp_reserve_object(ws, HUF_WORKSPACE_SIZE);
+                zc->entropyWorkspace = (U32*) ZSTD_cwksp_reserve_object(ws, ENTROPY_WORKSPACE_SIZE);
                 RETURN_ERROR_IF(zc->blockState.nextCBlock == NULL, memory_allocation, "couldn't allocate entropyWorkspace");
         }   }
 
         ZSTD_cwksp_clear(ws);
 
         /* init params */
-        zc->appliedParams = params;
-        zc->blockState.matchState.cParams = params.cParams;
+        zc->blockState.matchState.cParams = params->cParams;
         zc->pledgedSrcSizePlusOne = pledgedSrcSize+1;
         zc->consumedSrcSize = 0;
         zc->producedCSize = 0;
@@ -1524,6 +1919,7 @@
         XXH64_reset(&zc->xxhState, 0);
         zc->stage = ZSTDcs_init;
         zc->dictID = 0;
+        zc->dictContentSize = 0;
 
         ZSTD_reset_compressedBlockState(zc->blockState.prevCBlock);
 
@@ -1534,19 +1930,20 @@
         zc->seqStore.maxNbLit = blockSize;
 
         /* buffers */
+        zc->bufferedPolicy = zbuff;
         zc->inBuffSize = buffInSize;
         zc->inBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffInSize);
         zc->outBuffSize = buffOutSize;
         zc->outBuff = (char*)ZSTD_cwksp_reserve_buffer(ws, buffOutSize);
 
         /* ldm bucketOffsets table */
-        if (params.ldmParams.enableLdm) {
+        if (params->ldmParams.enableLdm) {
             /* TODO: avoid memset? */
-            size_t const ldmBucketSize =
-                  ((size_t)1) << (params.ldmParams.hashLog -
-                                  params.ldmParams.bucketSizeLog);
-            zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, ldmBucketSize);
-            memset(zc->ldmState.bucketOffsets, 0, ldmBucketSize);
+            size_t const numBuckets =
+                  ((size_t)1) << (params->ldmParams.hashLog -
+                                  params->ldmParams.bucketSizeLog);
+            zc->ldmState.bucketOffsets = ZSTD_cwksp_reserve_buffer(ws, numBuckets);
+            ZSTD_memset(zc->ldmState.bucketOffsets, 0, numBuckets);
         }
 
         /* sequences storage */
@@ -1560,26 +1957,28 @@
         FORWARD_IF_ERROR(ZSTD_reset_matchState(
             &zc->blockState.matchState,
             ws,
-            &params.cParams,
+            &params->cParams,
+            params->useRowMatchFinder,
             crp,
             needsIndexReset,
             ZSTD_resetTarget_CCtx), "");
 
         /* ldm hash table */
-        if (params.ldmParams.enableLdm) {
+        if (params->ldmParams.enableLdm) {
             /* TODO: avoid memset? */
-            size_t const ldmHSize = ((size_t)1) << params.ldmParams.hashLog;
+            size_t const ldmHSize = ((size_t)1) << params->ldmParams.hashLog;
             zc->ldmState.hashTable = (ldmEntry_t*)ZSTD_cwksp_reserve_aligned(ws, ldmHSize * sizeof(ldmEntry_t));
-            memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t));
+            ZSTD_memset(zc->ldmState.hashTable, 0, ldmHSize * sizeof(ldmEntry_t));
             zc->ldmSequences = (rawSeq*)ZSTD_cwksp_reserve_aligned(ws, maxNbLdmSeq * sizeof(rawSeq));
             zc->maxNbLdmSequences = maxNbLdmSeq;
 
             ZSTD_window_init(&zc->ldmState.window);
-            ZSTD_window_clear(&zc->ldmState.window);
             zc->ldmState.loadedDictEnd = 0;
         }
 
+        assert(ZSTD_cwksp_estimated_space_within_bounds(ws, neededSpace, resizeWorkspace));
         DEBUGLOG(3, "wksp: finished allocating, %zd bytes remain available", ZSTD_cwksp_available_space(ws));
+
         zc->initialized = 1;
 
         return 0;
@@ -1618,12 +2017,14 @@
                                  U64 pledgedSrcSize)
 {
     size_t cutoff = attachDictSizeCutoffs[cdict->matchState.cParams.strategy];
-    return ( pledgedSrcSize <= cutoff
-          || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN
-          || params->attachDictPref == ZSTD_dictForceAttach )
-        && params->attachDictPref != ZSTD_dictForceCopy
-        && !params->forceWindow; /* dictMatchState isn't correctly
-                                 * handled in _enforceMaxDist */
+    int const dedicatedDictSearch = cdict->matchState.dedicatedDictSearch;
+    return dedicatedDictSearch
+        || ( ( pledgedSrcSize <= cutoff
+            || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN
+            || params->attachDictPref == ZSTD_dictForceAttach )
+          && params->attachDictPref != ZSTD_dictForceCopy
+          && !params->forceWindow ); /* dictMatchState isn't correctly
+                                      * handled in _enforceMaxDist */
 }
 
 static size_t
@@ -1633,17 +2034,28 @@
                         U64 pledgedSrcSize,
                         ZSTD_buffered_policy_e zbuff)
 {
-    {   const ZSTD_compressionParameters* const cdict_cParams = &cdict->matchState.cParams;
+    DEBUGLOG(4, "ZSTD_resetCCtx_byAttachingCDict() pledgedSrcSize=%llu",
+                (unsigned long long)pledgedSrcSize);
+    {
+        ZSTD_compressionParameters adjusted_cdict_cParams = cdict->matchState.cParams;
         unsigned const windowLog = params.cParams.windowLog;
         assert(windowLog != 0);
         /* Resize working context table params for input only, since the dict
          * has its own tables. */
-        /* pledgeSrcSize == 0 means 0! */
-        params.cParams = ZSTD_adjustCParams_internal(*cdict_cParams, pledgedSrcSize, 0);
+        /* pledgedSrcSize == 0 means 0! */
+
+        if (cdict->matchState.dedicatedDictSearch) {
+            ZSTD_dedicatedDictSearch_revertCParams(&adjusted_cdict_cParams);
+        }
+
+        params.cParams = ZSTD_adjustCParams_internal(adjusted_cdict_cParams, pledgedSrcSize,
+                                                     cdict->dictContentSize, ZSTD_cpm_attachDict);
         params.cParams.windowLog = windowLog;
-        FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+        params.useRowMatchFinder = cdict->useRowMatchFinder;    /* cdict overrides */
+        FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, &params, pledgedSrcSize,
+                                                 /* loadedDictSize */ 0,
                                                  ZSTDcrp_makeClean, zbuff), "");
-        assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);
+        assert(cctx->appliedParams.cParams.strategy == adjusted_cdict_cParams.strategy);
     }
 
     {   const U32 cdictEnd = (U32)( cdict->matchState.window.nextSrc
@@ -1668,9 +2080,10 @@
     }   }
 
     cctx->dictID = cdict->dictID;
+    cctx->dictContentSize = cdict->dictContentSize;
 
     /* copy block state */
-    memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
+    ZSTD_memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
 
     return 0;
 }
@@ -1683,14 +2096,18 @@
 {
     const ZSTD_compressionParameters *cdict_cParams = &cdict->matchState.cParams;
 
-    DEBUGLOG(4, "copying dictionary into context");
+    assert(!cdict->matchState.dedicatedDictSearch);
+    DEBUGLOG(4, "ZSTD_resetCCtx_byCopyingCDict() pledgedSrcSize=%llu",
+                (unsigned long long)pledgedSrcSize);
 
     {   unsigned const windowLog = params.cParams.windowLog;
         assert(windowLog != 0);
         /* Copy only compression parameters related to tables. */
         params.cParams = *cdict_cParams;
         params.cParams.windowLog = windowLog;
-        FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+        params.useRowMatchFinder = cdict->useRowMatchFinder;
+        FORWARD_IF_ERROR(ZSTD_resetCCtx_internal(cctx, &params, pledgedSrcSize,
+                                                 /* loadedDictSize */ 0,
                                                  ZSTDcrp_leaveDirty, zbuff), "");
         assert(cctx->appliedParams.cParams.strategy == cdict_cParams->strategy);
         assert(cctx->appliedParams.cParams.hashLog == cdict_cParams->hashLog);
@@ -1698,24 +2115,37 @@
     }
 
     ZSTD_cwksp_mark_tables_dirty(&cctx->workspace);
+    assert(params.useRowMatchFinder != ZSTD_urm_auto);
 
     /* copy tables */
-    {   size_t const chainSize = (cdict_cParams->strategy == ZSTD_fast) ? 0 : ((size_t)1 << cdict_cParams->chainLog);
+    {   size_t const chainSize = ZSTD_allocateChainTable(cdict_cParams->strategy, cdict->useRowMatchFinder, 0 /* DDS guaranteed disabled */)
+                                                            ? ((size_t)1 << cdict_cParams->chainLog)
+                                                            : 0;
         size_t const hSize =  (size_t)1 << cdict_cParams->hashLog;
 
-        memcpy(cctx->blockState.matchState.hashTable,
+        ZSTD_memcpy(cctx->blockState.matchState.hashTable,
                cdict->matchState.hashTable,
                hSize * sizeof(U32));
-        memcpy(cctx->blockState.matchState.chainTable,
+        /* Do not copy cdict's chainTable if cctx has parameters such that it would not use chainTable */
+        if (ZSTD_allocateChainTable(cctx->appliedParams.cParams.strategy, cctx->appliedParams.useRowMatchFinder, 0 /* forDDSDict */)) {
+            ZSTD_memcpy(cctx->blockState.matchState.chainTable,
                cdict->matchState.chainTable,
                chainSize * sizeof(U32));
+        }
+        /* copy tag table */
+        if (ZSTD_rowMatchFinderUsed(cdict_cParams->strategy, cdict->useRowMatchFinder)) {
+            size_t const tagTableSize = hSize*sizeof(U16);
+            ZSTD_memcpy(cctx->blockState.matchState.tagTable,
+                cdict->matchState.tagTable,
+                tagTableSize);
+        }
     }
 
     /* Zero the hashTable3, since the cdict never fills it */
     {   int const h3log = cctx->blockState.matchState.hashLog3;
         size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0;
         assert(cdict->matchState.hashLog3 == 0);
-        memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));
+        ZSTD_memset(cctx->blockState.matchState.hashTable3, 0, h3Size * sizeof(U32));
     }
 
     ZSTD_cwksp_mark_tables_clean(&cctx->workspace);
@@ -1729,9 +2159,10 @@
     }
 
     cctx->dictID = cdict->dictID;
+    cctx->dictContentSize = cdict->dictContentSize;
 
     /* copy block state */
-    memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
+    ZSTD_memcpy(cctx->blockState.prevCBlock, &cdict->cBlockState, sizeof(cdict->cBlockState));
 
     return 0;
 }
@@ -1771,16 +2202,18 @@
                             U64 pledgedSrcSize,
                             ZSTD_buffered_policy_e zbuff)
 {
-    DEBUGLOG(5, "ZSTD_copyCCtx_internal");
     RETURN_ERROR_IF(srcCCtx->stage!=ZSTDcs_init, stage_wrong,
                     "Can't copy a ctx that's not in init stage.");
-
-    memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));
+    DEBUGLOG(5, "ZSTD_copyCCtx_internal");
+    ZSTD_memcpy(&dstCCtx->customMem, &srcCCtx->customMem, sizeof(ZSTD_customMem));
     {   ZSTD_CCtx_params params = dstCCtx->requestedParams;
         /* Copy only compression parameters related to tables. */
         params.cParams = srcCCtx->appliedParams.cParams;
+        assert(srcCCtx->appliedParams.useRowMatchFinder != ZSTD_urm_auto);
+        params.useRowMatchFinder = srcCCtx->appliedParams.useRowMatchFinder;
         params.fParams = fParams;
-        ZSTD_resetCCtx_internal(dstCCtx, params, pledgedSrcSize,
+        ZSTD_resetCCtx_internal(dstCCtx, &params, pledgedSrcSize,
+                                /* loadedDictSize */ 0,
                                 ZSTDcrp_leaveDirty, zbuff);
         assert(dstCCtx->appliedParams.cParams.windowLog == srcCCtx->appliedParams.cParams.windowLog);
         assert(dstCCtx->appliedParams.cParams.strategy == srcCCtx->appliedParams.cParams.strategy);
@@ -1792,18 +2225,22 @@
     ZSTD_cwksp_mark_tables_dirty(&dstCCtx->workspace);
 
     /* copy tables */
-    {   size_t const chainSize = (srcCCtx->appliedParams.cParams.strategy == ZSTD_fast) ? 0 : ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog);
+    {   size_t const chainSize = ZSTD_allocateChainTable(srcCCtx->appliedParams.cParams.strategy,
+                                                         srcCCtx->appliedParams.useRowMatchFinder,
+                                                         0 /* forDDSDict */)
+                                    ? ((size_t)1 << srcCCtx->appliedParams.cParams.chainLog)
+                                    : 0;
         size_t const hSize =  (size_t)1 << srcCCtx->appliedParams.cParams.hashLog;
         int const h3log = srcCCtx->blockState.matchState.hashLog3;
         size_t const h3Size = h3log ? ((size_t)1 << h3log) : 0;
 
-        memcpy(dstCCtx->blockState.matchState.hashTable,
+        ZSTD_memcpy(dstCCtx->blockState.matchState.hashTable,
                srcCCtx->blockState.matchState.hashTable,
                hSize * sizeof(U32));
-        memcpy(dstCCtx->blockState.matchState.chainTable,
+        ZSTD_memcpy(dstCCtx->blockState.matchState.chainTable,
                srcCCtx->blockState.matchState.chainTable,
                chainSize * sizeof(U32));
-        memcpy(dstCCtx->blockState.matchState.hashTable3,
+        ZSTD_memcpy(dstCCtx->blockState.matchState.hashTable3,
                srcCCtx->blockState.matchState.hashTable3,
                h3Size * sizeof(U32));
     }
@@ -1819,9 +2256,10 @@
         dstMatchState->loadedDictEnd= srcMatchState->loadedDictEnd;
     }
     dstCCtx->dictID = srcCCtx->dictID;
+    dstCCtx->dictContentSize = srcCCtx->dictContentSize;
 
     /* copy block state */
-    memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));
+    ZSTD_memcpy(dstCCtx->blockState.prevCBlock, srcCCtx->blockState.prevCBlock, sizeof(*srcCCtx->blockState.prevCBlock));
 
     return 0;
 }
@@ -1834,7 +2272,7 @@
 size_t ZSTD_copyCCtx(ZSTD_CCtx* dstCCtx, const ZSTD_CCtx* srcCCtx, unsigned long long pledgedSrcSize)
 {
     ZSTD_frameParameters fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
-    ZSTD_buffered_policy_e const zbuff = (ZSTD_buffered_policy_e)(srcCCtx->inBuffSize>0);
+    ZSTD_buffered_policy_e const zbuff = srcCCtx->bufferedPolicy;
     ZSTD_STATIC_ASSERT((U32)ZSTDb_buffered==1);
     if (pledgedSrcSize==0) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
     fParams.contentSizeFlag = (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN);
@@ -1861,7 +2299,7 @@
     assert((size & (ZSTD_ROWSIZE-1)) == 0);  /* multiple of ZSTD_ROWSIZE */
     assert(size < (1U<<31));   /* can be casted to int */
 
-#if defined (MEMORY_SANITIZER) && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
+#if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
     /* To validate that the table re-use logic is sound, and that we don't
      * access table space that we haven't cleaned, we re-"poison" the table
      * space every time we mark it dirty.
@@ -1905,7 +2343,7 @@
         ZSTD_reduceTable(ms->hashTable, hSize, reducerValue);
     }
 
-    if (params->cParams.strategy != ZSTD_fast) {
+    if (ZSTD_allocateChainTable(params->cParams.strategy, params->useRowMatchFinder, (U32)ms->dedicatedDictSearch)) {
         U32 const chainSize = (U32)1 << params->cParams.chainLog;
         if (params->cParams.strategy == ZSTD_btlazy2)
             ZSTD_reduceTable_btlazy2(ms->chainTable, chainSize, reducerValue);
@@ -1942,9 +2380,9 @@
         ofCodeTable[u] = (BYTE)ZSTD_highbit32(sequences[u].offset);
         mlCodeTable[u] = (BYTE)ZSTD_MLcode(mlv);
     }
-    if (seqStorePtr->longLengthID==1)
+    if (seqStorePtr->longLengthType==ZSTD_llt_literalLength)
         llCodeTable[seqStorePtr->longLengthPos] = MaxLL;
-    if (seqStorePtr->longLengthID==2)
+    if (seqStorePtr->longLengthType==ZSTD_llt_matchLength)
         mlCodeTable[seqStorePtr->longLengthPos] = MaxML;
 }
 
@@ -1958,10 +2396,158 @@
     return (cctxParams->targetCBlockSize != 0);
 }
 
-/* ZSTD_compressSequences_internal():
- * actually compresses both literals and sequences */
+/* ZSTD_blockSplitterEnabled():
+ * Returns if block splitting param is being used
+ * If used, compression will do best effort to split a block in order to improve compression ratio.
+ * Returns 1 if true, 0 otherwise. */
+static int ZSTD_blockSplitterEnabled(ZSTD_CCtx_params* cctxParams)
+{
+    DEBUGLOG(5, "ZSTD_blockSplitterEnabled(splitBlocks=%d)", cctxParams->splitBlocks);
+    return (cctxParams->splitBlocks != 0);
+}
+
+/* Type returned by ZSTD_buildSequencesStatistics containing finalized symbol encoding types
+ * and size of the sequences statistics
+ */
+typedef struct {
+    U32 LLtype;
+    U32 Offtype;
+    U32 MLtype;
+    size_t size;
+    size_t lastCountSize; /* Accounts for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */
+} ZSTD_symbolEncodingTypeStats_t;
+
+/* ZSTD_buildSequencesStatistics():
+ * Returns a ZSTD_symbolEncodingTypeStats_t, or a zstd error code in the `size` field.
+ * Modifies `nextEntropy` to have the appropriate values as a side effect.
+ * nbSeq must be greater than 0.
+ *
+ * entropyWkspSize must be of size at least ENTROPY_WORKSPACE_SIZE - (MaxSeq + 1)*sizeof(U32)
+ */
+static ZSTD_symbolEncodingTypeStats_t
+ZSTD_buildSequencesStatistics(seqStore_t* seqStorePtr, size_t nbSeq,
+                        const ZSTD_fseCTables_t* prevEntropy, ZSTD_fseCTables_t* nextEntropy,
+                              BYTE* dst, const BYTE* const dstEnd,
+                              ZSTD_strategy strategy, unsigned* countWorkspace,
+                              void* entropyWorkspace, size_t entropyWkspSize) {
+    BYTE* const ostart = dst;
+    const BYTE* const oend = dstEnd;
+    BYTE* op = ostart;
+    FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable;
+    FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable;
+    FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable;
+    const BYTE* const ofCodeTable = seqStorePtr->ofCode;
+    const BYTE* const llCodeTable = seqStorePtr->llCode;
+    const BYTE* const mlCodeTable = seqStorePtr->mlCode;
+    ZSTD_symbolEncodingTypeStats_t stats;
+
+    stats.lastCountSize = 0;
+    /* convert length/distances into codes */
+    ZSTD_seqToCodes(seqStorePtr);
+    assert(op <= oend);
+    assert(nbSeq != 0); /* ZSTD_selectEncodingType() divides by nbSeq */
+    /* build CTable for Literal Lengths */
+    {   unsigned max = MaxLL;
+        size_t const mostFrequent = HIST_countFast_wksp(countWorkspace, &max, llCodeTable, nbSeq, entropyWorkspace, entropyWkspSize);   /* can't fail */
+        DEBUGLOG(5, "Building LL table");
+        nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode;
+        stats.LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode,
+                                        countWorkspace, max, mostFrequent, nbSeq,
+                                        LLFSELog, prevEntropy->litlengthCTable,
+                                        LL_defaultNorm, LL_defaultNormLog,
+                                        ZSTD_defaultAllowed, strategy);
+        assert(set_basic < set_compressed && set_rle < set_compressed);
+        assert(!(stats.LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
+        {   size_t const countSize = ZSTD_buildCTable(
+                op, (size_t)(oend - op),
+                CTable_LitLength, LLFSELog, (symbolEncodingType_e)stats.LLtype,
+                countWorkspace, max, llCodeTable, nbSeq,
+                LL_defaultNorm, LL_defaultNormLog, MaxLL,
+                prevEntropy->litlengthCTable,
+                sizeof(prevEntropy->litlengthCTable),
+                entropyWorkspace, entropyWkspSize);
+            if (ZSTD_isError(countSize)) {
+                DEBUGLOG(3, "ZSTD_buildCTable for LitLens failed");
+                stats.size = countSize;
+                return stats;
+            }
+            if (stats.LLtype == set_compressed)
+                stats.lastCountSize = countSize;
+            op += countSize;
+            assert(op <= oend);
+    }   }
+    /* build CTable for Offsets */
+    {   unsigned max = MaxOff;
+        size_t const mostFrequent = HIST_countFast_wksp(
+            countWorkspace, &max, ofCodeTable, nbSeq, entropyWorkspace, entropyWkspSize);  /* can't fail */
+        /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */
+        ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;
+        DEBUGLOG(5, "Building OF table");
+        nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode;
+        stats.Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode,
+                                        countWorkspace, max, mostFrequent, nbSeq,
+                                        OffFSELog, prevEntropy->offcodeCTable,
+                                        OF_defaultNorm, OF_defaultNormLog,
+                                        defaultPolicy, strategy);
+        assert(!(stats.Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */
+        {   size_t const countSize = ZSTD_buildCTable(
+                op, (size_t)(oend - op),
+                CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)stats.Offtype,
+                countWorkspace, max, ofCodeTable, nbSeq,
+                OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
+                prevEntropy->offcodeCTable,
+                sizeof(prevEntropy->offcodeCTable),
+                entropyWorkspace, entropyWkspSize);
+            if (ZSTD_isError(countSize)) {
+                DEBUGLOG(3, "ZSTD_buildCTable for Offsets failed");
+                stats.size = countSize;
+                return stats;
+            }
+            if (stats.Offtype == set_compressed)
+                stats.lastCountSize = countSize;
+            op += countSize;
+            assert(op <= oend);
+    }   }
+    /* build CTable for MatchLengths */
+    {   unsigned max = MaxML;
+        size_t const mostFrequent = HIST_countFast_wksp(
+            countWorkspace, &max, mlCodeTable, nbSeq, entropyWorkspace, entropyWkspSize);   /* can't fail */
+        DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));
+        nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode;
+        stats.MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode,
+                                        countWorkspace, max, mostFrequent, nbSeq,
+                                        MLFSELog, prevEntropy->matchlengthCTable,
+                                        ML_defaultNorm, ML_defaultNormLog,
+                                        ZSTD_defaultAllowed, strategy);
+        assert(!(stats.MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
+        {   size_t const countSize = ZSTD_buildCTable(
+                op, (size_t)(oend - op),
+                CTable_MatchLength, MLFSELog, (symbolEncodingType_e)stats.MLtype,
+                countWorkspace, max, mlCodeTable, nbSeq,
+                ML_defaultNorm, ML_defaultNormLog, MaxML,
+                prevEntropy->matchlengthCTable,
+                sizeof(prevEntropy->matchlengthCTable),
+                entropyWorkspace, entropyWkspSize);
+            if (ZSTD_isError(countSize)) {
+                DEBUGLOG(3, "ZSTD_buildCTable for MatchLengths failed");
+                stats.size = countSize;
+                return stats;
+            }
+            if (stats.MLtype == set_compressed)
+                stats.lastCountSize = countSize;
+            op += countSize;
+            assert(op <= oend);
+    }   }
+    stats.size = (size_t)(op-ostart);
+    return stats;
+}
+
+/* ZSTD_entropyCompressSeqStore_internal():
+ * compresses both literals and sequences
+ * Returns compressed size of block, or a zstd error.
+ */
 MEM_STATIC size_t
-ZSTD_compressSequences_internal(seqStore_t* seqStorePtr,
+ZSTD_entropyCompressSeqStore_internal(seqStore_t* seqStorePtr,
                           const ZSTD_entropyCTables_t* prevEntropy,
                                 ZSTD_entropyCTables_t* nextEntropy,
                           const ZSTD_CCtx_params* cctxParams,
@@ -1971,24 +2557,26 @@
 {
     const int longOffsets = cctxParams->cParams.windowLog > STREAM_ACCUMULATOR_MIN;
     ZSTD_strategy const strategy = cctxParams->cParams.strategy;
-    unsigned count[MaxSeq+1];
+    unsigned* count = (unsigned*)entropyWorkspace;
     FSE_CTable* CTable_LitLength = nextEntropy->fse.litlengthCTable;
     FSE_CTable* CTable_OffsetBits = nextEntropy->fse.offcodeCTable;
     FSE_CTable* CTable_MatchLength = nextEntropy->fse.matchlengthCTable;
-    U32 LLtype, Offtype, MLtype;   /* compressed, raw or rle */
     const seqDef* const sequences = seqStorePtr->sequencesStart;
+    const size_t nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
     const BYTE* const ofCodeTable = seqStorePtr->ofCode;
     const BYTE* const llCodeTable = seqStorePtr->llCode;
     const BYTE* const mlCodeTable = seqStorePtr->mlCode;
     BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = ostart + dstCapacity;
     BYTE* op = ostart;
-    size_t const nbSeq = (size_t)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
-    BYTE* seqHead;
-    BYTE* lastNCount = NULL;
+    size_t lastCountSize;
 
-    DEBUGLOG(5, "ZSTD_compressSequences_internal (nbSeq=%zu)", nbSeq);
+    entropyWorkspace = count + (MaxSeq + 1);
+    entropyWkspSize -= (MaxSeq + 1) * sizeof(*count);
+
+    DEBUGLOG(4, "ZSTD_entropyCompressSeqStore_internal (nbSeq=%zu)", nbSeq);
     ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
+    assert(entropyWkspSize >= HUF_WORKSPACE_SIZE);
 
     /* Compress literals */
     {   const BYTE* const literals = seqStorePtr->litStart;
@@ -2023,98 +2611,23 @@
     assert(op <= oend);
     if (nbSeq==0) {
         /* Copy the old tables over as if we repeated them */
-        memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
+        ZSTD_memcpy(&nextEntropy->fse, &prevEntropy->fse, sizeof(prevEntropy->fse));
         return (size_t)(op - ostart);
     }
-
-    /* seqHead : flags for FSE encoding type */
-    seqHead = op++;
-    assert(op <= oend);
-
-    /* convert length/distances into codes */
-    ZSTD_seqToCodes(seqStorePtr);
-    /* build CTable for Literal Lengths */
-    {   unsigned max = MaxLL;
-        size_t const mostFrequent = HIST_countFast_wksp(count, &max, llCodeTable, nbSeq, entropyWorkspace, entropyWkspSize);   /* can't fail */
-        DEBUGLOG(5, "Building LL table");
-        nextEntropy->fse.litlength_repeatMode = prevEntropy->fse.litlength_repeatMode;
-        LLtype = ZSTD_selectEncodingType(&nextEntropy->fse.litlength_repeatMode,
-                                        count, max, mostFrequent, nbSeq,
-                                        LLFSELog, prevEntropy->fse.litlengthCTable,
-                                        LL_defaultNorm, LL_defaultNormLog,
-                                        ZSTD_defaultAllowed, strategy);
-        assert(set_basic < set_compressed && set_rle < set_compressed);
-        assert(!(LLtype < set_compressed && nextEntropy->fse.litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
-        {   size_t const countSize = ZSTD_buildCTable(
-                op, (size_t)(oend - op),
-                CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,
-                count, max, llCodeTable, nbSeq,
-                LL_defaultNorm, LL_defaultNormLog, MaxLL,
-                prevEntropy->fse.litlengthCTable,
-                sizeof(prevEntropy->fse.litlengthCTable),
-                entropyWorkspace, entropyWkspSize);
-            FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for LitLens failed");
-            if (LLtype == set_compressed)
-                lastNCount = op;
-            op += countSize;
-            assert(op <= oend);
-    }   }
-    /* build CTable for Offsets */
-    {   unsigned max = MaxOff;
-        size_t const mostFrequent = HIST_countFast_wksp(
-            count, &max, ofCodeTable, nbSeq, entropyWorkspace, entropyWkspSize);  /* can't fail */
-        /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */
-        ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;
-        DEBUGLOG(5, "Building OF table");
-        nextEntropy->fse.offcode_repeatMode = prevEntropy->fse.offcode_repeatMode;
-        Offtype = ZSTD_selectEncodingType(&nextEntropy->fse.offcode_repeatMode,
-                                        count, max, mostFrequent, nbSeq,
-                                        OffFSELog, prevEntropy->fse.offcodeCTable,
-                                        OF_defaultNorm, OF_defaultNormLog,
-                                        defaultPolicy, strategy);
-        assert(!(Offtype < set_compressed && nextEntropy->fse.offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */
-        {   size_t const countSize = ZSTD_buildCTable(
-                op, (size_t)(oend - op),
-                CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,
-                count, max, ofCodeTable, nbSeq,
-                OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
-                prevEntropy->fse.offcodeCTable,
-                sizeof(prevEntropy->fse.offcodeCTable),
-                entropyWorkspace, entropyWkspSize);
-            FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for Offsets failed");
-            if (Offtype == set_compressed)
-                lastNCount = op;
-            op += countSize;
-            assert(op <= oend);
-    }   }
-    /* build CTable for MatchLengths */
-    {   unsigned max = MaxML;
-        size_t const mostFrequent = HIST_countFast_wksp(
-            count, &max, mlCodeTable, nbSeq, entropyWorkspace, entropyWkspSize);   /* can't fail */
-        DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));
-        nextEntropy->fse.matchlength_repeatMode = prevEntropy->fse.matchlength_repeatMode;
-        MLtype = ZSTD_selectEncodingType(&nextEntropy->fse.matchlength_repeatMode,
-                                        count, max, mostFrequent, nbSeq,
-                                        MLFSELog, prevEntropy->fse.matchlengthCTable,
-                                        ML_defaultNorm, ML_defaultNormLog,
-                                        ZSTD_defaultAllowed, strategy);
-        assert(!(MLtype < set_compressed && nextEntropy->fse.matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
-        {   size_t const countSize = ZSTD_buildCTable(
-                op, (size_t)(oend - op),
-                CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,
-                count, max, mlCodeTable, nbSeq,
-                ML_defaultNorm, ML_defaultNormLog, MaxML,
-                prevEntropy->fse.matchlengthCTable,
-                sizeof(prevEntropy->fse.matchlengthCTable),
-                entropyWorkspace, entropyWkspSize);
-            FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for MatchLengths failed");
-            if (MLtype == set_compressed)
-                lastNCount = op;
-            op += countSize;
-            assert(op <= oend);
-    }   }
-
-    *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));
+    {
+        ZSTD_symbolEncodingTypeStats_t stats;
+        BYTE* seqHead = op++;
+        /* build stats for sequences */
+        stats = ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
+                                             &prevEntropy->fse, &nextEntropy->fse,
+                                              op, oend,
+                                              strategy, count,
+                                              entropyWorkspace, entropyWkspSize);
+        FORWARD_IF_ERROR(stats.size, "ZSTD_buildSequencesStatistics failed!");
+        *seqHead = (BYTE)((stats.LLtype<<6) + (stats.Offtype<<4) + (stats.MLtype<<2));
+        lastCountSize = stats.lastCountSize;
+        op += stats.size;
+    }
 
     {   size_t const bitstreamSize = ZSTD_encodeSequences(
                                         op, (size_t)(oend - op),
@@ -2134,9 +2647,9 @@
          * In this exceedingly rare case, we will simply emit an uncompressed
          * block, since it isn't worth optimizing.
          */
-        if (lastNCount && (op - lastNCount) < 4) {
-            /* NCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */
-            assert(op - lastNCount == 3);
+        if (lastCountSize && (lastCountSize + bitstreamSize) < 4) {
+            /* lastCountSize >= 2 && bitstreamSize > 0 ==> lastCountSize == 3 */
+            assert(lastCountSize + bitstreamSize == 3);
             DEBUGLOG(5, "Avoiding bug in zstd decoder in versions <= 1.3.4 by "
                         "emitting an uncompressed block.");
             return 0;
@@ -2148,7 +2661,7 @@
 }
 
 MEM_STATIC size_t
-ZSTD_compressSequences(seqStore_t* seqStorePtr,
+ZSTD_entropyCompressSeqStore(seqStore_t* seqStorePtr,
                        const ZSTD_entropyCTables_t* prevEntropy,
                              ZSTD_entropyCTables_t* nextEntropy,
                        const ZSTD_CCtx_params* cctxParams,
@@ -2157,7 +2670,7 @@
                              void* entropyWorkspace, size_t entropyWkspSize,
                              int bmi2)
 {
-    size_t const cSize = ZSTD_compressSequences_internal(
+    size_t const cSize = ZSTD_entropyCompressSeqStore_internal(
                             seqStorePtr, prevEntropy, nextEntropy, cctxParams,
                             dst, dstCapacity,
                             entropyWorkspace, entropyWkspSize, bmi2);
@@ -2167,22 +2680,22 @@
      */
     if ((cSize == ERROR(dstSize_tooSmall)) & (srcSize <= dstCapacity))
         return 0;  /* block not compressed */
-    FORWARD_IF_ERROR(cSize, "ZSTD_compressSequences_internal failed");
+    FORWARD_IF_ERROR(cSize, "ZSTD_entropyCompressSeqStore_internal failed");
 
     /* Check compressibility */
     {   size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, cctxParams->cParams.strategy);
         if (cSize >= maxCSize) return 0;  /* block not compressed */
     }
-
+    DEBUGLOG(4, "ZSTD_entropyCompressSeqStore() cSize: %zu", cSize);
     return cSize;
 }
 
 /* ZSTD_selectBlockCompressor() :
  * Not static, but internal use only (used by long distance matcher)
  * assumption : strat is a valid strategy */
-ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode)
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_useRowMatchFinderMode_e useRowMatchFinder, ZSTD_dictMode_e dictMode)
 {
-    static const ZSTD_blockCompressor blockCompressor[3][ZSTD_STRATEGY_MAX+1] = {
+    static const ZSTD_blockCompressor blockCompressor[4][ZSTD_STRATEGY_MAX+1] = {
         { ZSTD_compressBlock_fast  /* default for 0 */,
           ZSTD_compressBlock_fast,
           ZSTD_compressBlock_doubleFast,
@@ -2212,13 +2725,44 @@
           ZSTD_compressBlock_btlazy2_dictMatchState,
           ZSTD_compressBlock_btopt_dictMatchState,
           ZSTD_compressBlock_btultra_dictMatchState,
-          ZSTD_compressBlock_btultra_dictMatchState }
+          ZSTD_compressBlock_btultra_dictMatchState },
+        { NULL  /* default for 0 */,
+          NULL,
+          NULL,
+          ZSTD_compressBlock_greedy_dedicatedDictSearch,
+          ZSTD_compressBlock_lazy_dedicatedDictSearch,
+          ZSTD_compressBlock_lazy2_dedicatedDictSearch,
+          NULL,
+          NULL,
+          NULL,
+          NULL }
     };
     ZSTD_blockCompressor selectedCompressor;
     ZSTD_STATIC_ASSERT((unsigned)ZSTD_fast == 1);
 
     assert(ZSTD_cParam_withinBounds(ZSTD_c_strategy, strat));
-    selectedCompressor = blockCompressor[(int)dictMode][(int)strat];
+    DEBUGLOG(4, "Selected block compressor: dictMode=%d strat=%d rowMatchfinder=%d", (int)dictMode, (int)strat, (int)useRowMatchFinder);
+    if (ZSTD_rowMatchFinderUsed(strat, useRowMatchFinder)) {
+        static const ZSTD_blockCompressor rowBasedBlockCompressors[4][3] = {
+            { ZSTD_compressBlock_greedy_row,
+            ZSTD_compressBlock_lazy_row,
+            ZSTD_compressBlock_lazy2_row },
+            { ZSTD_compressBlock_greedy_extDict_row,
+            ZSTD_compressBlock_lazy_extDict_row,
+            ZSTD_compressBlock_lazy2_extDict_row },
+            { ZSTD_compressBlock_greedy_dictMatchState_row,
+            ZSTD_compressBlock_lazy_dictMatchState_row,
+            ZSTD_compressBlock_lazy2_dictMatchState_row },
+            { ZSTD_compressBlock_greedy_dedicatedDictSearch_row,
+            ZSTD_compressBlock_lazy_dedicatedDictSearch_row,
+            ZSTD_compressBlock_lazy2_dedicatedDictSearch_row }
+        };
+        DEBUGLOG(4, "Selecting a row-based matchfinder");
+        assert(useRowMatchFinder != ZSTD_urm_auto);
+        selectedCompressor = rowBasedBlockCompressors[(int)dictMode][(int)strat - (int)ZSTD_greedy];
+    } else {
+        selectedCompressor = blockCompressor[(int)dictMode][(int)strat];
+    }
     assert(selectedCompressor != NULL);
     return selectedCompressor;
 }
@@ -2226,7 +2770,7 @@
 static void ZSTD_storeLastLiterals(seqStore_t* seqStorePtr,
                                    const BYTE* anchor, size_t lastLLSize)
 {
-    memcpy(seqStorePtr->lit, anchor, lastLLSize);
+    ZSTD_memcpy(seqStorePtr->lit, anchor, lastLLSize);
     seqStorePtr->lit += lastLLSize;
 }
 
@@ -2234,7 +2778,7 @@
 {
     ssPtr->lit = ssPtr->litStart;
     ssPtr->sequences = ssPtr->sequencesStart;
-    ssPtr->longLengthID = 0;
+    ssPtr->longLengthType = ZSTD_llt_none;
 }
 
 typedef enum { ZSTDbss_compress, ZSTDbss_noCompress } ZSTD_buildSeqStore_e;
@@ -2247,7 +2791,11 @@
     /* Assert that we have correctly flushed the ctx params into the ms's copy */
     ZSTD_assertEqualCParams(zc->appliedParams.cParams, ms->cParams);
     if (srcSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
-        ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);
+        if (zc->appliedParams.cParams.strategy >= ZSTD_btopt) {
+            ZSTD_ldm_skipRawSeqStoreBytes(&zc->externSeqStore, srcSize);
+        } else {
+            ZSTD_ldm_skipSequences(&zc->externSeqStore, srcSize, zc->appliedParams.cParams.minMatch);
+        }
         return ZSTDbss_noCompress; /* don't even attempt compression below a certain srcSize */
     }
     ZSTD_resetSeqStore(&(zc->seqStore));
@@ -2263,10 +2811,10 @@
     /* limited update after a very long match */
     {   const BYTE* const base = ms->window.base;
         const BYTE* const istart = (const BYTE*)src;
-        const U32 current = (U32)(istart-base);
+        const U32 curr = (U32)(istart-base);
         if (sizeof(ptrdiff_t)==8) assert(istart - base < (ptrdiff_t)(U32)(-1));   /* ensure no overflow */
-        if (current > ms->nextToUpdate + 384)
-            ms->nextToUpdate = current - MIN(192, (U32)(current - ms->nextToUpdate - 384));
+        if (curr > ms->nextToUpdate + 384)
+            ms->nextToUpdate = curr - MIN(192, (U32)(curr - ms->nextToUpdate - 384));
     }
 
     /* select and store sequences */
@@ -2283,10 +2831,11 @@
                 ZSTD_ldm_blockCompress(&zc->externSeqStore,
                                        ms, &zc->seqStore,
                                        zc->blockState.nextCBlock->rep,
+                                       zc->appliedParams.useRowMatchFinder,
                                        src, srcSize);
             assert(zc->externSeqStore.pos <= zc->externSeqStore.size);
         } else if (zc->appliedParams.ldmParams.enableLdm) {
-            rawSeqStore_t ldmSeqStore = {NULL, 0, 0, 0};
+            rawSeqStore_t ldmSeqStore = kNullRawSeqStore;
 
             ldmSeqStore.seq = zc->ldmSequences;
             ldmSeqStore.capacity = zc->maxNbLdmSequences;
@@ -2299,10 +2848,14 @@
                 ZSTD_ldm_blockCompress(&ldmSeqStore,
                                        ms, &zc->seqStore,
                                        zc->blockState.nextCBlock->rep,
+                                       zc->appliedParams.useRowMatchFinder,
                                        src, srcSize);
             assert(ldmSeqStore.pos == ldmSeqStore.size);
         } else {   /* not long range mode */
-            ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy, dictMode);
+            ZSTD_blockCompressor const blockCompressor = ZSTD_selectBlockCompressor(zc->appliedParams.cParams.strategy,
+                                                                                    zc->appliedParams.useRowMatchFinder,
+                                                                                    dictMode);
+            ms->ldmSeqStore = NULL;
             lastLLSize = blockCompressor(ms, &zc->seqStore, zc->blockState.nextCBlock->rep, src, srcSize);
         }
         {   const BYTE* const lastLiterals = (const BYTE*)src + srcSize - lastLLSize;
@@ -2314,59 +2867,72 @@
 static void ZSTD_copyBlockSequences(ZSTD_CCtx* zc)
 {
     const seqStore_t* seqStore = ZSTD_getSeqStore(zc);
-    const seqDef* seqs = seqStore->sequencesStart;
-    size_t seqsSize = seqStore->sequences - seqs;
+    const seqDef* seqStoreSeqs = seqStore->sequencesStart;
+    size_t seqStoreSeqSize = seqStore->sequences - seqStoreSeqs;
+    size_t seqStoreLiteralsSize = (size_t)(seqStore->lit - seqStore->litStart);
+    size_t literalsRead = 0;
+    size_t lastLLSize;
 
     ZSTD_Sequence* outSeqs = &zc->seqCollector.seqStart[zc->seqCollector.seqIndex];
-    size_t i; size_t position; int repIdx;
+    size_t i;
+    repcodes_t updatedRepcodes;
 
     assert(zc->seqCollector.seqIndex + 1 < zc->seqCollector.maxSequences);
-    for (i = 0, position = 0; i < seqsSize; ++i) {
-        outSeqs[i].offset = seqs[i].offset;
-        outSeqs[i].litLength = seqs[i].litLength;
-        outSeqs[i].matchLength = seqs[i].matchLength + MINMATCH;
+    /* Ensure we have enough space for last literals "sequence" */
+    assert(zc->seqCollector.maxSequences >= seqStoreSeqSize + 1);
+    ZSTD_memcpy(updatedRepcodes.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t));
+    for (i = 0; i < seqStoreSeqSize; ++i) {
+        U32 rawOffset = seqStoreSeqs[i].offset - ZSTD_REP_NUM;
+        outSeqs[i].litLength = seqStoreSeqs[i].litLength;
+        outSeqs[i].matchLength = seqStoreSeqs[i].matchLength + MINMATCH;
+        outSeqs[i].rep = 0;
 
         if (i == seqStore->longLengthPos) {
-            if (seqStore->longLengthID == 1) {
+            if (seqStore->longLengthType == ZSTD_llt_literalLength) {
                 outSeqs[i].litLength += 0x10000;
-            } else if (seqStore->longLengthID == 2) {
+            } else if (seqStore->longLengthType == ZSTD_llt_matchLength) {
                 outSeqs[i].matchLength += 0x10000;
             }
         }
 
-        if (outSeqs[i].offset <= ZSTD_REP_NUM) {
-            outSeqs[i].rep = outSeqs[i].offset;
-            repIdx = (unsigned int)i - outSeqs[i].offset;
-
-            if (outSeqs[i].litLength == 0) {
-                if (outSeqs[i].offset < 3) {
-                    --repIdx;
+        if (seqStoreSeqs[i].offset <= ZSTD_REP_NUM) {
+            /* Derive the correct offset corresponding to a repcode */
+            outSeqs[i].rep = seqStoreSeqs[i].offset;
+            if (outSeqs[i].litLength != 0) {
+                rawOffset = updatedRepcodes.rep[outSeqs[i].rep - 1];
+            } else {
+                if (outSeqs[i].rep == 3) {
+                    rawOffset = updatedRepcodes.rep[0] - 1;
                 } else {
-                    repIdx = (unsigned int)i - 1;
+                    rawOffset = updatedRepcodes.rep[outSeqs[i].rep];
                 }
-                ++outSeqs[i].rep;
             }
-            assert(repIdx >= -3);
-            outSeqs[i].offset = repIdx >= 0 ? outSeqs[repIdx].offset : repStartValue[-repIdx - 1];
-            if (outSeqs[i].rep == 4) {
-                --outSeqs[i].offset;
-            }
-        } else {
-            outSeqs[i].offset -= ZSTD_REP_NUM;
         }
-
-        position += outSeqs[i].litLength;
-        outSeqs[i].matchPos = (unsigned int)position;
-        position += outSeqs[i].matchLength;
+        outSeqs[i].offset = rawOffset;
+        /* seqStoreSeqs[i].offset == offCode+1, and ZSTD_updateRep() expects offCode
+           so we provide seqStoreSeqs[i].offset - 1 */
+        updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep,
+                                         seqStoreSeqs[i].offset - 1,
+                                         seqStoreSeqs[i].litLength == 0);
+        literalsRead += outSeqs[i].litLength;
     }
-    zc->seqCollector.seqIndex += seqsSize;
+    /* Insert last literals (if any exist) in the block as a sequence with ml == off == 0.
+     * If there are no last literals, then we'll emit (of: 0, ml: 0, ll: 0), which is a marker
+     * for the block boundary, according to the API.
+     */
+    assert(seqStoreLiteralsSize >= literalsRead);
+    lastLLSize = seqStoreLiteralsSize - literalsRead;
+    outSeqs[i].litLength = (U32)lastLLSize;
+    outSeqs[i].matchLength = outSeqs[i].offset = outSeqs[i].rep = 0;
+    seqStoreSeqSize++;
+    zc->seqCollector.seqIndex += seqStoreSeqSize;
 }
 
-size_t ZSTD_getSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
-    size_t outSeqsSize, const void* src, size_t srcSize)
+size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
+                              size_t outSeqsSize, const void* src, size_t srcSize)
 {
     const size_t dstCapacity = ZSTD_compressBound(srcSize);
-    void* dst = ZSTD_malloc(dstCapacity, ZSTD_defaultCMem);
+    void* dst = ZSTD_customMalloc(dstCapacity, ZSTD_defaultCMem);
     SeqCollector seqCollector;
 
     RETURN_ERROR_IF(dst == NULL, memory_allocation, "NULL pointer!");
@@ -2378,16 +2944,47 @@
     zc->seqCollector = seqCollector;
 
     ZSTD_compress2(zc, dst, dstCapacity, src, srcSize);
-    ZSTD_free(dst, ZSTD_defaultCMem);
+    ZSTD_customFree(dst, ZSTD_defaultCMem);
     return zc->seqCollector.seqIndex;
 }
 
-/* Returns true if the given block is a RLE block */
-static int ZSTD_isRLE(const BYTE *ip, size_t length) {
+size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize) {
+    size_t in = 0;
+    size_t out = 0;
+    for (; in < seqsSize; ++in) {
+        if (sequences[in].offset == 0 && sequences[in].matchLength == 0) {
+            if (in != seqsSize - 1) {
+                sequences[in+1].litLength += sequences[in].litLength;
+            }
+        } else {
+            sequences[out] = sequences[in];
+            ++out;
+        }
+    }
+    return out;
+}
+
+/* Unrolled loop to read four size_ts of input at a time. Returns 1 if is RLE, 0 if not. */
+static int ZSTD_isRLE(const BYTE* src, size_t length) {
+    const BYTE* ip = src;
+    const BYTE value = ip[0];
+    const size_t valueST = (size_t)((U64)value * 0x0101010101010101ULL);
+    const size_t unrollSize = sizeof(size_t) * 4;
+    const size_t unrollMask = unrollSize - 1;
+    const size_t prefixLength = length & unrollMask;
     size_t i;
-    if (length < 2) return 1;
-    for (i = 1; i < length; ++i) {
-        if (ip[0] != ip[i]) return 0;
+    size_t u;
+    if (length == 1) return 1;
+    /* Check if prefix is RLE first before using unrolled loop */
+    if (prefixLength && ZSTD_count(ip+1, ip, ip+prefixLength) != prefixLength-1) {
+        return 0;
+    }
+    for (i = prefixLength; i != length; i += unrollSize) {
+        for (u = 0; u < unrollSize; u += sizeof(size_t)) {
+            if (MEM_readST(ip + i + u) != valueST) {
+                return 0;
+            }
+        }
     }
     return 1;
 }
@@ -2404,11 +3001,713 @@
     return nbSeqs < 4 && nbLits < 10;
 }
 
-static void ZSTD_confirmRepcodesAndEntropyTables(ZSTD_CCtx* zc)
+static void ZSTD_blockState_confirmRepcodesAndEntropyTables(ZSTD_blockState_t* const bs)
 {
-    ZSTD_compressedBlockState_t* const tmp = zc->blockState.prevCBlock;
-    zc->blockState.prevCBlock = zc->blockState.nextCBlock;
-    zc->blockState.nextCBlock = tmp;
+    ZSTD_compressedBlockState_t* const tmp = bs->prevCBlock;
+    bs->prevCBlock = bs->nextCBlock;
+    bs->nextCBlock = tmp;
+}
+
+/* Writes the block header */
+static void writeBlockHeader(void* op, size_t cSize, size_t blockSize, U32 lastBlock) {
+    U32 const cBlockHeader = cSize == 1 ?
+                        lastBlock + (((U32)bt_rle)<<1) + (U32)(blockSize << 3) :
+                        lastBlock + (((U32)bt_compressed)<<1) + (U32)(cSize << 3);
+    MEM_writeLE24(op, cBlockHeader);
+    DEBUGLOG(3, "writeBlockHeader: cSize: %zu blockSize: %zu lastBlock: %u", cSize, blockSize, lastBlock);
+}
+
+/** ZSTD_buildBlockEntropyStats_literals() :
+ *  Builds entropy for the literals.
+ *  Stores literals block type (raw, rle, compressed, repeat) and
+ *  huffman description table to hufMetadata.
+ *  Requires ENTROPY_WORKSPACE_SIZE workspace
+ *  @return : size of huffman description table or error code */
+static size_t ZSTD_buildBlockEntropyStats_literals(void* const src, size_t srcSize,
+                                            const ZSTD_hufCTables_t* prevHuf,
+                                                  ZSTD_hufCTables_t* nextHuf,
+                                                  ZSTD_hufCTablesMetadata_t* hufMetadata,
+                                                  const int disableLiteralsCompression,
+                                                  void* workspace, size_t wkspSize)
+{
+    BYTE* const wkspStart = (BYTE*)workspace;
+    BYTE* const wkspEnd = wkspStart + wkspSize;
+    BYTE* const countWkspStart = wkspStart;
+    unsigned* const countWksp = (unsigned*)workspace;
+    const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned);
+    BYTE* const nodeWksp = countWkspStart + countWkspSize;
+    const size_t nodeWkspSize = wkspEnd-nodeWksp;
+    unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX;
+    unsigned huffLog = HUF_TABLELOG_DEFAULT;
+    HUF_repeat repeat = prevHuf->repeatMode;
+    DEBUGLOG(5, "ZSTD_buildBlockEntropyStats_literals (srcSize=%zu)", srcSize);
+
+    /* Prepare nextEntropy assuming reusing the existing table */
+    ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+
+    if (disableLiteralsCompression) {
+        DEBUGLOG(5, "set_basic - disabled");
+        hufMetadata->hType = set_basic;
+        return 0;
+    }
+
+    /* small ? don't even attempt compression (speed opt) */
+#ifndef COMPRESS_LITERALS_SIZE_MIN
+#define COMPRESS_LITERALS_SIZE_MIN 63
+#endif
+    {   size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
+        if (srcSize <= minLitSize) {
+            DEBUGLOG(5, "set_basic - too small");
+            hufMetadata->hType = set_basic;
+            return 0;
+        }
+    }
+
+    /* Scan input and build symbol stats */
+    {   size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)src, srcSize, workspace, wkspSize);
+        FORWARD_IF_ERROR(largest, "HIST_count_wksp failed");
+        if (largest == srcSize) {
+            DEBUGLOG(5, "set_rle");
+            hufMetadata->hType = set_rle;
+            return 0;
+        }
+        if (largest <= (srcSize >> 7)+4) {
+            DEBUGLOG(5, "set_basic - no gain");
+            hufMetadata->hType = set_basic;
+            return 0;
+        }
+    }
+
+    /* Validate the previous Huffman table */
+    if (repeat == HUF_repeat_check && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) {
+        repeat = HUF_repeat_none;
+    }
+
+    /* Build Huffman Tree */
+    ZSTD_memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable));
+    huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
+    {   size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp,
+                                                    maxSymbolValue, huffLog,
+                                                    nodeWksp, nodeWkspSize);
+        FORWARD_IF_ERROR(maxBits, "HUF_buildCTable_wksp");
+        huffLog = (U32)maxBits;
+        {   /* Build and write the CTable */
+            size_t const newCSize = HUF_estimateCompressedSize(
+                    (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue);
+            size_t const hSize = HUF_writeCTable_wksp(
+                    hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer),
+                    (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog,
+                    nodeWksp, nodeWkspSize);
+            /* Check against repeating the previous CTable */
+            if (repeat != HUF_repeat_none) {
+                size_t const oldCSize = HUF_estimateCompressedSize(
+                        (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue);
+                if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) {
+                    DEBUGLOG(5, "set_repeat - smaller");
+                    ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+                    hufMetadata->hType = set_repeat;
+                    return 0;
+                }
+            }
+            if (newCSize + hSize >= srcSize) {
+                DEBUGLOG(5, "set_basic - no gains");
+                ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+                hufMetadata->hType = set_basic;
+                return 0;
+            }
+            DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize);
+            hufMetadata->hType = set_compressed;
+            nextHuf->repeatMode = HUF_repeat_check;
+            return hSize;
+        }
+    }
+}
+
+
+/* ZSTD_buildDummySequencesStatistics():
+ * Returns a ZSTD_symbolEncodingTypeStats_t with all encoding types as set_basic,
+ * and updates nextEntropy to the appropriate repeatMode.
+ */
+static ZSTD_symbolEncodingTypeStats_t
+ZSTD_buildDummySequencesStatistics(ZSTD_fseCTables_t* nextEntropy) {
+    ZSTD_symbolEncodingTypeStats_t stats = {set_basic, set_basic, set_basic, 0, 0};
+    nextEntropy->litlength_repeatMode = FSE_repeat_none;
+    nextEntropy->offcode_repeatMode = FSE_repeat_none;
+    nextEntropy->matchlength_repeatMode = FSE_repeat_none;
+    return stats;
+}
+
+/** ZSTD_buildBlockEntropyStats_sequences() :
+ *  Builds entropy for the sequences.
+ *  Stores symbol compression modes and fse table to fseMetadata.
+ *  Requires ENTROPY_WORKSPACE_SIZE wksp.
+ *  @return : size of fse tables or error code */
+static size_t ZSTD_buildBlockEntropyStats_sequences(seqStore_t* seqStorePtr,
+                                              const ZSTD_fseCTables_t* prevEntropy,
+                                                    ZSTD_fseCTables_t* nextEntropy,
+                                              const ZSTD_CCtx_params* cctxParams,
+                                                    ZSTD_fseCTablesMetadata_t* fseMetadata,
+                                                    void* workspace, size_t wkspSize)
+{
+    ZSTD_strategy const strategy = cctxParams->cParams.strategy;
+    size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
+    BYTE* const ostart = fseMetadata->fseTablesBuffer;
+    BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer);
+    BYTE* op = ostart;
+    unsigned* countWorkspace = (unsigned*)workspace;
+    unsigned* entropyWorkspace = countWorkspace + (MaxSeq + 1);
+    size_t entropyWorkspaceSize = wkspSize - (MaxSeq + 1) * sizeof(*countWorkspace);
+    ZSTD_symbolEncodingTypeStats_t stats;
+
+    DEBUGLOG(5, "ZSTD_buildBlockEntropyStats_sequences (nbSeq=%zu)", nbSeq);
+    stats = nbSeq != 0 ? ZSTD_buildSequencesStatistics(seqStorePtr, nbSeq,
+                                          prevEntropy, nextEntropy, op, oend,
+                                          strategy, countWorkspace,
+                                          entropyWorkspace, entropyWorkspaceSize)
+                       : ZSTD_buildDummySequencesStatistics(nextEntropy);
+    FORWARD_IF_ERROR(stats.size, "ZSTD_buildSequencesStatistics failed!");
+    fseMetadata->llType = (symbolEncodingType_e) stats.LLtype;
+    fseMetadata->ofType = (symbolEncodingType_e) stats.Offtype;
+    fseMetadata->mlType = (symbolEncodingType_e) stats.MLtype;
+    fseMetadata->lastCountSize = stats.lastCountSize;
+    return stats.size;
+}
+
+
+/** ZSTD_buildBlockEntropyStats() :
+ *  Builds entropy for the block.
+ *  Requires workspace size ENTROPY_WORKSPACE_SIZE
+ *
+ *  @return : 0 on success or error code
+ */
+size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr,
+                             const ZSTD_entropyCTables_t* prevEntropy,
+                                   ZSTD_entropyCTables_t* nextEntropy,
+                             const ZSTD_CCtx_params* cctxParams,
+                                   ZSTD_entropyCTablesMetadata_t* entropyMetadata,
+                                   void* workspace, size_t wkspSize)
+{
+    size_t const litSize = seqStorePtr->lit - seqStorePtr->litStart;
+    entropyMetadata->hufMetadata.hufDesSize =
+        ZSTD_buildBlockEntropyStats_literals(seqStorePtr->litStart, litSize,
+                                            &prevEntropy->huf, &nextEntropy->huf,
+                                            &entropyMetadata->hufMetadata,
+                                            ZSTD_disableLiteralsCompression(cctxParams),
+                                            workspace, wkspSize);
+    FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildBlockEntropyStats_literals failed");
+    entropyMetadata->fseMetadata.fseTablesSize =
+        ZSTD_buildBlockEntropyStats_sequences(seqStorePtr,
+                                              &prevEntropy->fse, &nextEntropy->fse,
+                                              cctxParams,
+                                              &entropyMetadata->fseMetadata,
+                                              workspace, wkspSize);
+    FORWARD_IF_ERROR(entropyMetadata->fseMetadata.fseTablesSize, "ZSTD_buildBlockEntropyStats_sequences failed");
+    return 0;
+}
+
+/* Returns the size estimate for the literals section (header + content) of a block */
+static size_t ZSTD_estimateBlockSize_literal(const BYTE* literals, size_t litSize,
+                                                const ZSTD_hufCTables_t* huf,
+                                                const ZSTD_hufCTablesMetadata_t* hufMetadata,
+                                                void* workspace, size_t wkspSize,
+                                                int writeEntropy)
+{
+    unsigned* const countWksp = (unsigned*)workspace;
+    unsigned maxSymbolValue = HUF_SYMBOLVALUE_MAX;
+    size_t literalSectionHeaderSize = 3 + (litSize >= 1 KB) + (litSize >= 16 KB);
+    U32 singleStream = litSize < 256;
+
+    if (hufMetadata->hType == set_basic) return litSize;
+    else if (hufMetadata->hType == set_rle) return 1;
+    else if (hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat) {
+        size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)literals, litSize, workspace, wkspSize);
+        if (ZSTD_isError(largest)) return litSize;
+        {   size_t cLitSizeEstimate = HUF_estimateCompressedSize((const HUF_CElt*)huf->CTable, countWksp, maxSymbolValue);
+            if (writeEntropy) cLitSizeEstimate += hufMetadata->hufDesSize;
+            if (!singleStream) cLitSizeEstimate += 6; /* multi-stream huffman uses 6-byte jump table */
+            return cLitSizeEstimate + literalSectionHeaderSize;
+    }   }
+    assert(0); /* impossible */
+    return 0;
+}
+
+/* Returns the size estimate for the FSE-compressed symbols (of, ml, ll) of a block */
+static size_t ZSTD_estimateBlockSize_symbolType(symbolEncodingType_e type,
+                        const BYTE* codeTable, size_t nbSeq, unsigned maxCode,
+                        const FSE_CTable* fseCTable,
+                        const U32* additionalBits,
+                        short const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
+                        void* workspace, size_t wkspSize)
+{
+    unsigned* const countWksp = (unsigned*)workspace;
+    const BYTE* ctp = codeTable;
+    const BYTE* const ctStart = ctp;
+    const BYTE* const ctEnd = ctStart + nbSeq;
+    size_t cSymbolTypeSizeEstimateInBits = 0;
+    unsigned max = maxCode;
+
+    HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize);  /* can't fail */
+    if (type == set_basic) {
+        /* We selected this encoding type, so it must be valid. */
+        assert(max <= defaultMax);
+        (void)defaultMax;
+        cSymbolTypeSizeEstimateInBits = ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max);
+    } else if (type == set_rle) {
+        cSymbolTypeSizeEstimateInBits = 0;
+    } else if (type == set_compressed || type == set_repeat) {
+        cSymbolTypeSizeEstimateInBits = ZSTD_fseBitCost(fseCTable, countWksp, max);
+    }
+    if (ZSTD_isError(cSymbolTypeSizeEstimateInBits)) {
+        return nbSeq * 10;
+    }
+    while (ctp < ctEnd) {
+        if (additionalBits) cSymbolTypeSizeEstimateInBits += additionalBits[*ctp];
+        else cSymbolTypeSizeEstimateInBits += *ctp; /* for offset, offset code is also the number of additional bits */
+        ctp++;
+    }
+    return cSymbolTypeSizeEstimateInBits >> 3;
+}
+
+/* Returns the size estimate for the sequences section (header + content) of a block */
+static size_t ZSTD_estimateBlockSize_sequences(const BYTE* ofCodeTable,
+                                                  const BYTE* llCodeTable,
+                                                  const BYTE* mlCodeTable,
+                                                  size_t nbSeq,
+                                                  const ZSTD_fseCTables_t* fseTables,
+                                                  const ZSTD_fseCTablesMetadata_t* fseMetadata,
+                                                  void* workspace, size_t wkspSize,
+                                                  int writeEntropy)
+{
+    size_t sequencesSectionHeaderSize = 1 /* seqHead */ + 1 /* min seqSize size */ + (nbSeq >= 128) + (nbSeq >= LONGNBSEQ);
+    size_t cSeqSizeEstimate = 0;
+    cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, nbSeq, MaxOff,
+                                         fseTables->offcodeCTable, NULL,
+                                         OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
+                                         workspace, wkspSize);
+    cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->llType, llCodeTable, nbSeq, MaxLL,
+                                         fseTables->litlengthCTable, LL_bits,
+                                         LL_defaultNorm, LL_defaultNormLog, MaxLL,
+                                         workspace, wkspSize);
+    cSeqSizeEstimate += ZSTD_estimateBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, nbSeq, MaxML,
+                                         fseTables->matchlengthCTable, ML_bits,
+                                         ML_defaultNorm, ML_defaultNormLog, MaxML,
+                                         workspace, wkspSize);
+    if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize;
+    return cSeqSizeEstimate + sequencesSectionHeaderSize;
+}
+
+/* Returns the size estimate for a given stream of literals, of, ll, ml */
+static size_t ZSTD_estimateBlockSize(const BYTE* literals, size_t litSize,
+                                     const BYTE* ofCodeTable,
+                                     const BYTE* llCodeTable,
+                                     const BYTE* mlCodeTable,
+                                     size_t nbSeq,
+                                     const ZSTD_entropyCTables_t* entropy,
+                                     const ZSTD_entropyCTablesMetadata_t* entropyMetadata,
+                                     void* workspace, size_t wkspSize,
+                                     int writeLitEntropy, int writeSeqEntropy) {
+    size_t const literalsSize = ZSTD_estimateBlockSize_literal(literals, litSize,
+                                                         &entropy->huf, &entropyMetadata->hufMetadata,
+                                                         workspace, wkspSize, writeLitEntropy);
+    size_t const seqSize = ZSTD_estimateBlockSize_sequences(ofCodeTable, llCodeTable, mlCodeTable,
+                                                         nbSeq, &entropy->fse, &entropyMetadata->fseMetadata,
+                                                         workspace, wkspSize, writeSeqEntropy);
+    return seqSize + literalsSize + ZSTD_blockHeaderSize;
+}
+
+/* Builds entropy statistics and uses them for blocksize estimation.
+ *
+ * Returns the estimated compressed size of the seqStore, or a zstd error.
+ */
+static size_t ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(seqStore_t* seqStore, const ZSTD_CCtx* zc) {
+    ZSTD_entropyCTablesMetadata_t entropyMetadata;
+    FORWARD_IF_ERROR(ZSTD_buildBlockEntropyStats(seqStore,
+                    &zc->blockState.prevCBlock->entropy,
+                    &zc->blockState.nextCBlock->entropy,
+                    &zc->appliedParams,
+                    &entropyMetadata,
+                    zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */), "");
+    return ZSTD_estimateBlockSize(seqStore->litStart, (size_t)(seqStore->lit - seqStore->litStart),
+                    seqStore->ofCode, seqStore->llCode, seqStore->mlCode,
+                    (size_t)(seqStore->sequences - seqStore->sequencesStart),
+                    &zc->blockState.nextCBlock->entropy, &entropyMetadata, zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE,
+                    (int)(entropyMetadata.hufMetadata.hType == set_compressed), 1);
+}
+
+/* Returns literals bytes represented in a seqStore */
+static size_t ZSTD_countSeqStoreLiteralsBytes(const seqStore_t* const seqStore) {
+    size_t literalsBytes = 0;
+    size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart;
+    size_t i;
+    for (i = 0; i < nbSeqs; ++i) {
+        seqDef seq = seqStore->sequencesStart[i];
+        literalsBytes += seq.litLength;
+        if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_literalLength) {
+            literalsBytes += 0x10000;
+        }
+    }
+    return literalsBytes;
+}
+
+/* Returns match bytes represented in a seqStore */
+static size_t ZSTD_countSeqStoreMatchBytes(const seqStore_t* const seqStore) {
+    size_t matchBytes = 0;
+    size_t const nbSeqs = seqStore->sequences - seqStore->sequencesStart;
+    size_t i;
+    for (i = 0; i < nbSeqs; ++i) {
+        seqDef seq = seqStore->sequencesStart[i];
+        matchBytes += seq.matchLength + MINMATCH;
+        if (i == seqStore->longLengthPos && seqStore->longLengthType == ZSTD_llt_matchLength) {
+            matchBytes += 0x10000;
+        }
+    }
+    return matchBytes;
+}
+
+/* Derives the seqStore that is a chunk of the originalSeqStore from [startIdx, endIdx).
+ * Stores the result in resultSeqStore.
+ */
+static void ZSTD_deriveSeqStoreChunk(seqStore_t* resultSeqStore,
+                               const seqStore_t* originalSeqStore,
+                                     size_t startIdx, size_t endIdx) {
+    BYTE* const litEnd = originalSeqStore->lit;
+    size_t literalsBytes;
+    size_t literalsBytesPreceding = 0;
+
+    *resultSeqStore = *originalSeqStore;
+    if (startIdx > 0) {
+        resultSeqStore->sequences = originalSeqStore->sequencesStart + startIdx;
+        literalsBytesPreceding = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
+    }
+
+    /* Move longLengthPos into the correct position if necessary */
+    if (originalSeqStore->longLengthType != ZSTD_llt_none) {
+        if (originalSeqStore->longLengthPos < startIdx || originalSeqStore->longLengthPos > endIdx) {
+            resultSeqStore->longLengthType = ZSTD_llt_none;
+        } else {
+            resultSeqStore->longLengthPos -= (U32)startIdx;
+        }
+    }
+    resultSeqStore->sequencesStart = originalSeqStore->sequencesStart + startIdx;
+    resultSeqStore->sequences = originalSeqStore->sequencesStart + endIdx;
+    literalsBytes = ZSTD_countSeqStoreLiteralsBytes(resultSeqStore);
+    resultSeqStore->litStart += literalsBytesPreceding;
+    if (endIdx == (size_t)(originalSeqStore->sequences - originalSeqStore->sequencesStart)) {
+        /* This accounts for possible last literals if the derived chunk reaches the end of the block */
+        resultSeqStore->lit = litEnd;
+    } else {
+        resultSeqStore->lit = resultSeqStore->litStart+literalsBytes;
+    }
+    resultSeqStore->llCode += startIdx;
+    resultSeqStore->mlCode += startIdx;
+    resultSeqStore->ofCode += startIdx;
+}
+
+/**
+ * Returns the raw offset represented by the combination of offCode, ll0, and repcode history.
+ * offCode must be an offCode representing a repcode, therefore in the range of [0, 2].
+ */
+static U32 ZSTD_resolveRepcodeToRawOffset(const U32 rep[ZSTD_REP_NUM], const U32 offCode, const U32 ll0) {
+    U32 const adjustedOffCode = offCode + ll0;
+    assert(offCode < ZSTD_REP_NUM);
+    if (adjustedOffCode == ZSTD_REP_NUM) {
+        /* litlength == 0 and offCode == 2 implies selection of first repcode - 1 */
+        assert(rep[0] > 0);
+        return rep[0] - 1;
+    }
+    return rep[adjustedOffCode];
+}
+
+/**
+ * ZSTD_seqStore_resolveOffCodes() reconciles any possible divergences in offset history that may arise
+ * due to emission of RLE/raw blocks that disturb the offset history, and replaces any repcodes within
+ * the seqStore that may be invalid.
+ *
+ * dRepcodes are updated as would be on the decompression side. cRepcodes are updated exactly in
+ * accordance with the seqStore.
+ */
+static void ZSTD_seqStore_resolveOffCodes(repcodes_t* const dRepcodes, repcodes_t* const cRepcodes,
+                                          seqStore_t* const seqStore, U32 const nbSeq) {
+    U32 idx = 0;
+    for (; idx < nbSeq; ++idx) {
+        seqDef* const seq = seqStore->sequencesStart + idx;
+        U32 const ll0 = (seq->litLength == 0);
+        U32 offCode = seq->offset - 1;
+        assert(seq->offset > 0);
+        if (offCode <= ZSTD_REP_MOVE) {
+            U32 const dRawOffset = ZSTD_resolveRepcodeToRawOffset(dRepcodes->rep, offCode, ll0);
+            U32 const cRawOffset = ZSTD_resolveRepcodeToRawOffset(cRepcodes->rep, offCode, ll0);
+            /* Adjust simulated decompression repcode history if we come across a mismatch. Replace
+             * the repcode with the offset it actually references, determined by the compression
+             * repcode history.
+             */
+            if (dRawOffset != cRawOffset) {
+                seq->offset = cRawOffset + ZSTD_REP_NUM;
+            }
+        }
+        /* Compression repcode history is always updated with values directly from the unmodified seqStore.
+         * Decompression repcode history may use modified seq->offset value taken from compression repcode history.
+         */
+        *dRepcodes = ZSTD_updateRep(dRepcodes->rep, seq->offset - 1, ll0);
+        *cRepcodes = ZSTD_updateRep(cRepcodes->rep, offCode, ll0);
+    }
+}
+
+/* ZSTD_compressSeqStore_singleBlock():
+ * Compresses a seqStore into a block with a block header, into the buffer dst.
+ *
+ * Returns the total size of that block (including header) or a ZSTD error code.
+ */
+static size_t ZSTD_compressSeqStore_singleBlock(ZSTD_CCtx* zc, seqStore_t* const seqStore,
+                                                repcodes_t* const dRep, repcodes_t* const cRep,
+                                                void* dst, size_t dstCapacity,
+                                                const void* src, size_t srcSize,
+                                                U32 lastBlock, U32 isPartition) {
+    const U32 rleMaxLength = 25;
+    BYTE* op = (BYTE*)dst;
+    const BYTE* ip = (const BYTE*)src;
+    size_t cSize;
+    size_t cSeqsSize;
+
+    /* In case of an RLE or raw block, the simulated decompression repcode history must be reset */
+    repcodes_t const dRepOriginal = *dRep;
+    if (isPartition)
+        ZSTD_seqStore_resolveOffCodes(dRep, cRep, seqStore, (U32)(seqStore->sequences - seqStore->sequencesStart));
+
+    cSeqsSize = ZSTD_entropyCompressSeqStore(seqStore,
+                &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,
+                &zc->appliedParams,
+                op + ZSTD_blockHeaderSize, dstCapacity - ZSTD_blockHeaderSize,
+                srcSize,
+                zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
+                zc->bmi2);
+    FORWARD_IF_ERROR(cSeqsSize, "ZSTD_entropyCompressSeqStore failed!");
+
+    if (!zc->isFirstBlock &&
+        cSeqsSize < rleMaxLength &&
+        ZSTD_isRLE((BYTE const*)src, srcSize)) {
+        /* We don't want to emit our first block as a RLE even if it qualifies because
+        * doing so will cause the decoder (cli only) to throw a "should consume all input error."
+        * This is only an issue for zstd <= v1.4.3
+        */
+        cSeqsSize = 1;
+    }
+
+    if (zc->seqCollector.collectSequences) {
+        ZSTD_copyBlockSequences(zc);
+        ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
+        return 0;
+    }
+
+    if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
+        zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
+
+    if (cSeqsSize == 0) {
+        cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock);
+        FORWARD_IF_ERROR(cSize, "Nocompress block failed");
+        DEBUGLOG(4, "Writing out nocompress block, size: %zu", cSize);
+        *dRep = dRepOriginal; /* reset simulated decompression repcode history */
+    } else if (cSeqsSize == 1) {
+        cSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, srcSize, lastBlock);
+        FORWARD_IF_ERROR(cSize, "RLE compress block failed");
+        DEBUGLOG(4, "Writing out RLE block, size: %zu", cSize);
+        *dRep = dRepOriginal; /* reset simulated decompression repcode history */
+    } else {
+        ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
+        writeBlockHeader(op, cSeqsSize, srcSize, lastBlock);
+        cSize = ZSTD_blockHeaderSize + cSeqsSize;
+        DEBUGLOG(4, "Writing out compressed block, size: %zu", cSize);
+    }
+    return cSize;
+}
+
+/* Struct to keep track of where we are in our recursive calls. */
+typedef struct {
+    U32* splitLocations;    /* Array of split indices */
+    size_t idx;             /* The current index within splitLocations being worked on */
+} seqStoreSplits;
+
+#define MIN_SEQUENCES_BLOCK_SPLITTING 300
+#define MAX_NB_SPLITS 196
+
+/* Helper function to perform the recursive search for block splits.
+ * Estimates the cost of seqStore prior to split, and estimates the cost of splitting the sequences in half.
+ * If advantageous to split, then we recurse down the two sub-blocks. If not, or if an error occurred in estimation, then
+ * we do not recurse.
+ *
+ * Note: The recursion depth is capped by a heuristic minimum number of sequences, defined by MIN_SEQUENCES_BLOCK_SPLITTING.
+ * In theory, this means the absolute largest recursion depth is 10 == log2(maxNbSeqInBlock/MIN_SEQUENCES_BLOCK_SPLITTING).
+ * In practice, recursion depth usually doesn't go beyond 4.
+ *
+ * Furthermore, the number of splits is capped by MAX_NB_SPLITS. At MAX_NB_SPLITS == 196 with the current existing blockSize
+ * maximum of 128 KB, this value is actually impossible to reach.
+ */
+static void ZSTD_deriveBlockSplitsHelper(seqStoreSplits* splits, size_t startIdx, size_t endIdx,
+                                         const ZSTD_CCtx* zc, const seqStore_t* origSeqStore) {
+    seqStore_t fullSeqStoreChunk;
+    seqStore_t firstHalfSeqStore;
+    seqStore_t secondHalfSeqStore;
+    size_t estimatedOriginalSize;
+    size_t estimatedFirstHalfSize;
+    size_t estimatedSecondHalfSize;
+    size_t midIdx = (startIdx + endIdx)/2;
+
+    if (endIdx - startIdx < MIN_SEQUENCES_BLOCK_SPLITTING || splits->idx >= MAX_NB_SPLITS) {
+        return;
+    }
+    ZSTD_deriveSeqStoreChunk(&fullSeqStoreChunk, origSeqStore, startIdx, endIdx);
+    ZSTD_deriveSeqStoreChunk(&firstHalfSeqStore, origSeqStore, startIdx, midIdx);
+    ZSTD_deriveSeqStoreChunk(&secondHalfSeqStore, origSeqStore, midIdx, endIdx);
+    estimatedOriginalSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&fullSeqStoreChunk, zc);
+    estimatedFirstHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&firstHalfSeqStore, zc);
+    estimatedSecondHalfSize = ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&secondHalfSeqStore, zc);
+    DEBUGLOG(5, "Estimated original block size: %zu -- First half split: %zu -- Second half split: %zu",
+             estimatedOriginalSize, estimatedFirstHalfSize, estimatedSecondHalfSize);
+    if (ZSTD_isError(estimatedOriginalSize) || ZSTD_isError(estimatedFirstHalfSize) || ZSTD_isError(estimatedSecondHalfSize)) {
+        return;
+    }
+    if (estimatedFirstHalfSize + estimatedSecondHalfSize < estimatedOriginalSize) {
+        ZSTD_deriveBlockSplitsHelper(splits, startIdx, midIdx, zc, origSeqStore);
+        splits->splitLocations[splits->idx] = (U32)midIdx;
+        splits->idx++;
+        ZSTD_deriveBlockSplitsHelper(splits, midIdx, endIdx, zc, origSeqStore);
+    }
+}
+
+/* Base recursive function. Populates a table with intra-block partition indices that can improve compression ratio.
+ *
+ * Returns the number of splits made (which equals the size of the partition table - 1).
+ */
+static size_t ZSTD_deriveBlockSplits(ZSTD_CCtx* zc, U32 partitions[], U32 nbSeq) {
+    seqStoreSplits splits = {partitions, 0};
+    if (nbSeq <= 4) {
+        DEBUGLOG(4, "ZSTD_deriveBlockSplits: Too few sequences to split");
+        /* Refuse to try and split anything with less than 4 sequences */
+        return 0;
+    }
+    ZSTD_deriveBlockSplitsHelper(&splits, 0, nbSeq, zc, &zc->seqStore);
+    splits.splitLocations[splits.idx] = nbSeq;
+    DEBUGLOG(5, "ZSTD_deriveBlockSplits: final nb partitions: %zu", splits.idx+1);
+    return splits.idx;
+}
+
+/* ZSTD_compressBlock_splitBlock():
+ * Attempts to split a given block into multiple blocks to improve compression ratio.
+ *
+ * Returns combined size of all blocks (which includes headers), or a ZSTD error code.
+ */
+static size_t ZSTD_compressBlock_splitBlock_internal(ZSTD_CCtx* zc, void* dst, size_t dstCapacity,
+                                                     const void* src, size_t blockSize, U32 lastBlock, U32 nbSeq) {
+    size_t cSize = 0;
+    const BYTE* ip = (const BYTE*)src;
+    BYTE* op = (BYTE*)dst;
+    U32 partitions[MAX_NB_SPLITS];
+    size_t i = 0;
+    size_t srcBytesTotal = 0;
+    size_t numSplits = ZSTD_deriveBlockSplits(zc, partitions, nbSeq);
+    seqStore_t nextSeqStore;
+    seqStore_t currSeqStore;
+
+    /* If a block is split and some partitions are emitted as RLE/uncompressed, then repcode history
+     * may become invalid. In order to reconcile potentially invalid repcodes, we keep track of two
+     * separate repcode histories that simulate repcode history on compression and decompression side,
+     * and use the histories to determine whether we must replace a particular repcode with its raw offset.
+     *
+     * 1) cRep gets updated for each partition, regardless of whether the block was emitted as uncompressed
+     *    or RLE. This allows us to retrieve the offset value that an invalid repcode references within
+     *    a nocompress/RLE block.
+     * 2) dRep gets updated only for compressed partitions, and when a repcode gets replaced, will use
+     *    the replacement offset value rather than the original repcode to update the repcode history.
+     *    dRep also will be the final repcode history sent to the next block.
+     *
+     * See ZSTD_seqStore_resolveOffCodes() for more details.
+     */
+    repcodes_t dRep;
+    repcodes_t cRep;
+    ZSTD_memcpy(dRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t));
+    ZSTD_memcpy(cRep.rep, zc->blockState.prevCBlock->rep, sizeof(repcodes_t));
+
+    DEBUGLOG(4, "ZSTD_compressBlock_splitBlock_internal (dstCapacity=%u, dictLimit=%u, nextToUpdate=%u)",
+                (unsigned)dstCapacity, (unsigned)zc->blockState.matchState.window.dictLimit,
+                (unsigned)zc->blockState.matchState.nextToUpdate);
+
+    if (numSplits == 0) {
+        size_t cSizeSingleBlock = ZSTD_compressSeqStore_singleBlock(zc, &zc->seqStore,
+                                                                   &dRep, &cRep,
+                                                                    op, dstCapacity,
+                                                                    ip, blockSize,
+                                                                    lastBlock, 0 /* isPartition */);
+        FORWARD_IF_ERROR(cSizeSingleBlock, "Compressing single block from splitBlock_internal() failed!");
+        DEBUGLOG(5, "ZSTD_compressBlock_splitBlock_internal: No splits");
+        assert(cSizeSingleBlock <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize);
+        return cSizeSingleBlock;
+    }
+
+    ZSTD_deriveSeqStoreChunk(&currSeqStore, &zc->seqStore, 0, partitions[0]);
+    for (i = 0; i <= numSplits; ++i) {
+        size_t srcBytes;
+        size_t cSizeChunk;
+        U32 const lastPartition = (i == numSplits);
+        U32 lastBlockEntireSrc = 0;
+
+        srcBytes = ZSTD_countSeqStoreLiteralsBytes(&currSeqStore) + ZSTD_countSeqStoreMatchBytes(&currSeqStore);
+        srcBytesTotal += srcBytes;
+        if (lastPartition) {
+            /* This is the final partition, need to account for possible last literals */
+            srcBytes += blockSize - srcBytesTotal;
+            lastBlockEntireSrc = lastBlock;
+        } else {
+            ZSTD_deriveSeqStoreChunk(&nextSeqStore, &zc->seqStore, partitions[i], partitions[i+1]);
+        }
+
+        cSizeChunk = ZSTD_compressSeqStore_singleBlock(zc, &currSeqStore,
+                                                      &dRep, &cRep,
+                                                       op, dstCapacity,
+                                                       ip, srcBytes,
+                                                       lastBlockEntireSrc, 1 /* isPartition */);
+        DEBUGLOG(5, "Estimated size: %zu actual size: %zu", ZSTD_buildEntropyStatisticsAndEstimateSubBlockSize(&currSeqStore, zc), cSizeChunk);
+        FORWARD_IF_ERROR(cSizeChunk, "Compressing chunk failed!");
+
+        ip += srcBytes;
+        op += cSizeChunk;
+        dstCapacity -= cSizeChunk;
+        cSize += cSizeChunk;
+        currSeqStore = nextSeqStore;
+        assert(cSizeChunk <= ZSTD_BLOCKSIZE_MAX + ZSTD_blockHeaderSize);
+    }
+    /* cRep and dRep may have diverged during the compression. If so, we use the dRep repcodes
+     * for the next block.
+     */
+    ZSTD_memcpy(zc->blockState.prevCBlock->rep, dRep.rep, sizeof(repcodes_t));
+    return cSize;
+}
+
+static size_t ZSTD_compressBlock_splitBlock(ZSTD_CCtx* zc,
+                                        void* dst, size_t dstCapacity,
+                                        const void* src, size_t srcSize, U32 lastBlock) {
+    const BYTE* ip = (const BYTE*)src;
+    BYTE* op = (BYTE*)dst;
+    U32 nbSeq;
+    size_t cSize;
+    DEBUGLOG(4, "ZSTD_compressBlock_splitBlock");
+
+    {   const size_t bss = ZSTD_buildSeqStore(zc, src, srcSize);
+        FORWARD_IF_ERROR(bss, "ZSTD_buildSeqStore failed");
+        if (bss == ZSTDbss_noCompress) {
+            if (zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
+                zc->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
+            cSize = ZSTD_noCompressBlock(op, dstCapacity, ip, srcSize, lastBlock);
+            FORWARD_IF_ERROR(cSize, "ZSTD_noCompressBlock failed");
+            DEBUGLOG(4, "ZSTD_compressBlock_splitBlock: Nocompress block");
+            return cSize;
+        }
+        nbSeq = (U32)(zc->seqStore.sequences - zc->seqStore.sequencesStart);
+    }
+
+    assert(zc->appliedParams.splitBlocks == 1);
+    cSize = ZSTD_compressBlock_splitBlock_internal(zc, dst, dstCapacity, src, srcSize, lastBlock, nbSeq);
+    FORWARD_IF_ERROR(cSize, "Splitting blocks failed!");
+    return cSize;
 }
 
 static size_t ZSTD_compressBlock_internal(ZSTD_CCtx* zc,
@@ -2434,18 +3733,25 @@
 
     if (zc->seqCollector.collectSequences) {
         ZSTD_copyBlockSequences(zc);
+        ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
         return 0;
     }
 
     /* encode sequences and literals */
-    cSize = ZSTD_compressSequences(&zc->seqStore,
+    cSize = ZSTD_entropyCompressSeqStore(&zc->seqStore,
             &zc->blockState.prevCBlock->entropy, &zc->blockState.nextCBlock->entropy,
             &zc->appliedParams,
             dst, dstCapacity,
             srcSize,
-            zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
+            zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
             zc->bmi2);
 
+    if (zc->seqCollector.collectSequences) {
+        ZSTD_copyBlockSequences(zc);
+        return 0;
+    }
+
+
     if (frame &&
         /* We don't want to emit our first block as a RLE even if it qualifies because
          * doing so will cause the decoder (cli only) to throw a "should consume all input error."
@@ -2461,7 +3767,7 @@
 
 out:
     if (!ZSTD_isError(cSize) && cSize > 1) {
-        ZSTD_confirmRepcodesAndEntropyTables(zc);
+        ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
     }
     /* We check that dictionaries have offset codes available for the first
      * block. After the first block, the offcode table might not have large
@@ -2514,7 +3820,7 @@
                 size_t const maxCSize = srcSize - ZSTD_minGain(srcSize, zc->appliedParams.cParams.strategy);
                 FORWARD_IF_ERROR(cSize, "ZSTD_compressSuperBlock failed");
                 if (cSize != 0 && cSize < maxCSize + ZSTD_blockHeaderSize) {
-                    ZSTD_confirmRepcodesAndEntropyTables(zc);
+                    ZSTD_blockState_confirmRepcodesAndEntropyTables(&zc->blockState);
                     return cSize;
                 }
             }
@@ -2554,9 +3860,9 @@
                                          void const* ip,
                                          void const* iend)
 {
-    if (ZSTD_window_needOverflowCorrection(ms->window, iend)) {
-        U32 const maxDist = (U32)1 << params->cParams.windowLog;
-        U32 const cycleLog = ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy);
+    U32 const cycleLog = ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy);
+    U32 const maxDist = (U32)1 << params->cParams.windowLog;
+    if (ZSTD_window_needOverflowCorrection(ms->window, cycleLog, maxDist, ms->loadedDictEnd, ip, iend)) {
         U32 const correction = ZSTD_window_correctOverflow(&ms->window, cycleLog, maxDist, ip);
         ZSTD_STATIC_ASSERT(ZSTD_CHAINLOG_MAX <= 30);
         ZSTD_STATIC_ASSERT(ZSTD_WINDOWLOG_MAX_32 <= 30);
@@ -2579,7 +3885,7 @@
 *   Frame is supposed already started (header already produced)
 *   @return : compressed size, or an error code
 */
-static size_t ZSTD_compress_frameChunk (ZSTD_CCtx* cctx,
+static size_t ZSTD_compress_frameChunk(ZSTD_CCtx* cctx,
                                      void* dst, size_t dstCapacity,
                                const void* src, size_t srcSize,
                                      U32 lastFrameChunk)
@@ -2593,7 +3899,7 @@
 
     assert(cctx->appliedParams.cParams.windowLog <= ZSTD_WINDOWLOG_MAX);
 
-    DEBUGLOG(5, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize);
+    DEBUGLOG(4, "ZSTD_compress_frameChunk (blockSize=%u)", (unsigned)blockSize);
     if (cctx->appliedParams.fParams.checksumFlag && srcSize)
         XXH64_update(&cctx->xxhState, src, srcSize);
 
@@ -2619,6 +3925,10 @@
                 FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_targetCBlockSize failed");
                 assert(cSize > 0);
                 assert(cSize <= blockSize + ZSTD_blockHeaderSize);
+            } else if (ZSTD_blockSplitterEnabled(&cctx->appliedParams)) {
+                cSize = ZSTD_compressBlock_splitBlock(cctx, op, dstCapacity, ip, blockSize, lastBlock);
+                FORWARD_IF_ERROR(cSize, "ZSTD_compressBlock_splitBlock failed");
+                assert(cSize > 0 || cctx->seqCollector.collectSequences == 1);
             } else {
                 cSize = ZSTD_compressBlock_internal(cctx,
                                         op+ZSTD_blockHeaderSize, dstCapacity-ZSTD_blockHeaderSize,
@@ -2673,7 +3983,6 @@
                     "dst buf is too small to fit worst-case frame header size.");
     DEBUGLOG(4, "ZSTD_writeFrameHeader : dictIDFlag : %u ; dictID : %u ; dictIDSizeCode : %u",
                 !params->fParams.noDictIDFlag, (unsigned)dictID, (unsigned)dictIDSizeCode);
-
     if (params->format == ZSTD_f_zstd1) {
         MEM_writeLE32(dst, ZSTD_MAGICNUMBER);
         pos = 4;
@@ -2699,6 +4008,26 @@
     return pos;
 }
 
+/* ZSTD_writeSkippableFrame_advanced() :
+ * Writes out a skippable frame with the specified magic number variant (16 are supported),
+ * from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15, and the desired source data.
+ *
+ * Returns the total number of bytes written, or a ZSTD error code.
+ */
+size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity,
+                                const void* src, size_t srcSize, unsigned magicVariant) {
+    BYTE* op = (BYTE*)dst;
+    RETURN_ERROR_IF(dstCapacity < srcSize + ZSTD_SKIPPABLEHEADERSIZE /* Skippable frame overhead */,
+                    dstSize_tooSmall, "Not enough room for skippable frame");
+    RETURN_ERROR_IF(srcSize > (unsigned)0xFFFFFFFF, srcSize_wrong, "Src size too large for skippable frame");
+    RETURN_ERROR_IF(magicVariant > 15, parameter_outOfBound, "Skippable frame magic number variant not supported");
+
+    MEM_writeLE32(op, (U32)(ZSTD_MAGIC_SKIPPABLE_START + magicVariant));
+    MEM_writeLE32(op+4, (U32)srcSize);
+    ZSTD_memcpy(op+8, src, srcSize);
+    return srcSize + ZSTD_SKIPPABLEHEADERSIZE;
+}
+
 /* ZSTD_writeLastEmptyBlock() :
  * output an empty Block with end-of-frame mark to complete a frame
  * @return : size of data written into `dst` (== ZSTD_blockHeaderSize (defined in zstd_internal.h))
@@ -2725,6 +4054,7 @@
     cctx->externSeqStore.size = nbSeq;
     cctx->externSeqStore.capacity = nbSeq;
     cctx->externSeqStore.pos = 0;
+    cctx->externSeqStore.posInSequence = 0;
     return 0;
 }
 
@@ -2754,11 +4084,12 @@
 
     if (!srcSize) return fhSize;  /* do not generate an empty block if no input */
 
-    if (!ZSTD_window_update(&ms->window, src, srcSize)) {
+    if (!ZSTD_window_update(&ms->window, src, srcSize, ms->forceNonContiguous)) {
+        ms->forceNonContiguous = 0;
         ms->nextToUpdate = ms->window.dictLimit;
     }
     if (cctx->appliedParams.ldmParams.enableLdm) {
-        ZSTD_window_update(&cctx->ldmState.window, src, srcSize);
+        ZSTD_window_update(&cctx->ldmState.window, src, srcSize, /* forceNonContiguous */ 0);
     }
 
     if (!frame) {
@@ -2826,59 +4157,86 @@
 {
     const BYTE* ip = (const BYTE*) src;
     const BYTE* const iend = ip + srcSize;
-
-    ZSTD_window_update(&ms->window, src, srcSize);
-    ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
-
-    if (params->ldmParams.enableLdm && ls != NULL) {
-        ZSTD_window_update(&ls->window, src, srcSize);
-        ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base);
-    }
+    int const loadLdmDict = params->ldmParams.enableLdm && ls != NULL;
 
     /* Assert that we the ms params match the params we're being given */
     ZSTD_assertEqualCParams(params->cParams, ms->cParams);
 
+    if (srcSize > ZSTD_CHUNKSIZE_MAX) {
+        /* Allow the dictionary to set indices up to exactly ZSTD_CURRENT_MAX.
+         * Dictionaries right at the edge will immediately trigger overflow
+         * correction, but I don't want to insert extra constraints here.
+         */
+        U32 const maxDictSize = ZSTD_CURRENT_MAX - 1;
+        /* We must have cleared our windows when our source is this large. */
+        assert(ZSTD_window_isEmpty(ms->window));
+        if (loadLdmDict)
+            assert(ZSTD_window_isEmpty(ls->window));
+        /* If the dictionary is too large, only load the suffix of the dictionary. */
+        if (srcSize > maxDictSize) {
+            ip = iend - maxDictSize;
+            src = ip;
+            srcSize = maxDictSize;
+        }
+    }
+
+    DEBUGLOG(4, "ZSTD_loadDictionaryContent(): useRowMatchFinder=%d", (int)params->useRowMatchFinder);
+    ZSTD_window_update(&ms->window, src, srcSize, /* forceNonContiguous */ 0);
+    ms->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ms->window.base);
+    ms->forceNonContiguous = params->deterministicRefPrefix;
+
+    if (loadLdmDict) {
+        ZSTD_window_update(&ls->window, src, srcSize, /* forceNonContiguous */ 0);
+        ls->loadedDictEnd = params->forceWindow ? 0 : (U32)(iend - ls->window.base);
+    }
+
     if (srcSize <= HASH_READ_SIZE) return 0;
 
-    while (iend - ip > HASH_READ_SIZE) {
-        size_t const remaining = (size_t)(iend - ip);
-        size_t const chunk = MIN(remaining, ZSTD_CHUNKSIZE_MAX);
-        const BYTE* const ichunk = ip + chunk;
+    ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, iend);
 
-        ZSTD_overflowCorrectIfNeeded(ms, ws, params, ip, ichunk);
+    if (loadLdmDict)
+        ZSTD_ldm_fillHashTable(ls, ip, iend, &params->ldmParams);
 
-        if (params->ldmParams.enableLdm && ls != NULL)
-            ZSTD_ldm_fillHashTable(ls, (const BYTE*)src, (const BYTE*)src + srcSize, &params->ldmParams);
+    switch(params->cParams.strategy)
+    {
+    case ZSTD_fast:
+        ZSTD_fillHashTable(ms, iend, dtlm);
+        break;
+    case ZSTD_dfast:
+        ZSTD_fillDoubleHashTable(ms, iend, dtlm);
+        break;
 
-        switch(params->cParams.strategy)
-        {
-        case ZSTD_fast:
-            ZSTD_fillHashTable(ms, ichunk, dtlm);
-            break;
-        case ZSTD_dfast:
-            ZSTD_fillDoubleHashTable(ms, ichunk, dtlm);
-            break;
-
-        case ZSTD_greedy:
-        case ZSTD_lazy:
-        case ZSTD_lazy2:
-            if (chunk >= HASH_READ_SIZE)
-                ZSTD_insertAndFindFirstIndex(ms, ichunk-HASH_READ_SIZE);
-            break;
-
-        case ZSTD_btlazy2:   /* we want the dictionary table fully sorted */
-        case ZSTD_btopt:
-        case ZSTD_btultra:
-        case ZSTD_btultra2:
-            if (chunk >= HASH_READ_SIZE)
-                ZSTD_updateTree(ms, ichunk-HASH_READ_SIZE, ichunk);
-            break;
-
-        default:
-            assert(0);  /* not possible : not a valid strategy id */
+    case ZSTD_greedy:
+    case ZSTD_lazy:
+    case ZSTD_lazy2:
+        assert(srcSize >= HASH_READ_SIZE);
+        if (ms->dedicatedDictSearch) {
+            assert(ms->chainTable != NULL);
+            ZSTD_dedicatedDictSearch_lazy_loadDictionary(ms, iend-HASH_READ_SIZE);
+        } else {
+            assert(params->useRowMatchFinder != ZSTD_urm_auto);
+            if (params->useRowMatchFinder == ZSTD_urm_enableRowMatchFinder) {
+                size_t const tagTableSize = ((size_t)1 << params->cParams.hashLog) * sizeof(U16);
+                ZSTD_memset(ms->tagTable, 0, tagTableSize);
+                ZSTD_row_update(ms, iend-HASH_READ_SIZE);
+                DEBUGLOG(4, "Using row-based hash table for lazy dict");
+            } else {
+                ZSTD_insertAndFindFirstIndex(ms, iend-HASH_READ_SIZE);
+                DEBUGLOG(4, "Using chain-based hash table for lazy dict");
+            }
         }
+        break;
 
-        ip = ichunk;
+    case ZSTD_btlazy2:   /* we want the dictionary table fully sorted */
+    case ZSTD_btopt:
+    case ZSTD_btultra:
+    case ZSTD_btultra2:
+        assert(srcSize >= HASH_READ_SIZE);
+        ZSTD_updateTree(ms, iend-HASH_READ_SIZE, iend);
+        break;
+
+    default:
+        assert(0);  /* not possible : not a valid strategy id */
     }
 
     ms->nextToUpdate = (U32)(iend - ms->window.base);
@@ -2887,22 +4245,28 @@
 
 
 /* Dictionaries that assign zero probability to symbols that show up causes problems
-   when FSE encoding.  Refuse dictionaries that assign zero probability to symbols
-   that we may encounter during compression.
-   NOTE: This behavior is not standard and could be improved in the future. */
-static size_t ZSTD_checkDictNCount(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue) {
+ * when FSE encoding. Mark dictionaries with zero probability symbols as FSE_repeat_check
+ * and only dictionaries with 100% valid symbols can be assumed valid.
+ */
+static FSE_repeat ZSTD_dictNCountRepeat(short* normalizedCounter, unsigned dictMaxSymbolValue, unsigned maxSymbolValue)
+{
     U32 s;
-    RETURN_ERROR_IF(dictMaxSymbolValue < maxSymbolValue, dictionary_corrupted, "dict fse tables don't have all symbols");
-    for (s = 0; s <= maxSymbolValue; ++s) {
-        RETURN_ERROR_IF(normalizedCounter[s] == 0, dictionary_corrupted, "dict fse tables don't have all symbols");
+    if (dictMaxSymbolValue < maxSymbolValue) {
+        return FSE_repeat_check;
     }
-    return 0;
+    for (s = 0; s <= maxSymbolValue; ++s) {
+        if (normalizedCounter[s] == 0) {
+            return FSE_repeat_check;
+        }
+    }
+    return FSE_repeat_valid;
 }
 
 size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,
-                         short* offcodeNCount, unsigned* offcodeMaxValue,
                          const void* const dict, size_t dictSize)
 {
+    short offcodeNCount[MaxOff+1];
+    unsigned offcodeMaxValue = MaxOff;
     const BYTE* dictPtr = (const BYTE*)dict;    /* skip magic num and dict ID */
     const BYTE* const dictEnd = dictPtr + dictSize;
     dictPtr += 8;
@@ -2924,16 +4288,16 @@
     }
 
     {   unsigned offcodeLog;
-        size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
+        size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
         RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, "");
         RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, "");
-        /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */
         /* fill all offset symbols to avoid garbage at end of table */
         RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(
                 bs->entropy.fse.offcodeCTable,
                 offcodeNCount, MaxOff, offcodeLog,
                 workspace, HUF_WORKSPACE_SIZE)),
             dictionary_corrupted, "");
+        /* Defer checking offcodeMaxValue because we need to know the size of the dictionary content */
         dictPtr += offcodeHeaderSize;
     }
 
@@ -2942,13 +4306,12 @@
         size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
         RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, "");
         RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, "");
-        /* Every match length code must have non-zero probability */
-        FORWARD_IF_ERROR( ZSTD_checkDictNCount(matchlengthNCount, matchlengthMaxValue, MaxML), "");
         RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(
                 bs->entropy.fse.matchlengthCTable,
                 matchlengthNCount, matchlengthMaxValue, matchlengthLog,
                 workspace, HUF_WORKSPACE_SIZE)),
             dictionary_corrupted, "");
+        bs->entropy.fse.matchlength_repeatMode = ZSTD_dictNCountRepeat(matchlengthNCount, matchlengthMaxValue, MaxML);
         dictPtr += matchlengthHeaderSize;
     }
 
@@ -2957,13 +4320,12 @@
         size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
         RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, "");
         RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, "");
-        /* Every literal length code must have non-zero probability */
-        FORWARD_IF_ERROR( ZSTD_checkDictNCount(litlengthNCount, litlengthMaxValue, MaxLL), "");
         RETURN_ERROR_IF(FSE_isError(FSE_buildCTable_wksp(
                 bs->entropy.fse.litlengthCTable,
                 litlengthNCount, litlengthMaxValue, litlengthLog,
                 workspace, HUF_WORKSPACE_SIZE)),
             dictionary_corrupted, "");
+        bs->entropy.fse.litlength_repeatMode = ZSTD_dictNCountRepeat(litlengthNCount, litlengthMaxValue, MaxLL);
         dictPtr += litlengthHeaderSize;
     }
 
@@ -2973,12 +4335,28 @@
     bs->rep[2] = MEM_readLE32(dictPtr+8);
     dictPtr += 12;
 
+    {   size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
+        U32 offcodeMax = MaxOff;
+        if (dictContentSize <= ((U32)-1) - 128 KB) {
+            U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */
+            offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */
+        }
+        /* All offset values <= dictContentSize + 128 KB must be representable for a valid table */
+        bs->entropy.fse.offcode_repeatMode = ZSTD_dictNCountRepeat(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff));
+
+        /* All repCodes must be <= dictContentSize and != 0 */
+        {   U32 u;
+            for (u=0; u<3; u++) {
+                RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted, "");
+                RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted, "");
+    }   }   }
+
     return dictPtr - (const BYTE*)dict;
 }
 
 /* Dictionary format :
  * See :
- * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format
+ * https://github.com/facebook/zstd/blob/release/doc/zstd_compression_format.md#dictionary-format
  */
 /*! ZSTD_loadZstdDictionary() :
  * @return : dictID, or an error code
@@ -2995,42 +4373,23 @@
 {
     const BYTE* dictPtr = (const BYTE*)dict;
     const BYTE* const dictEnd = dictPtr + dictSize;
-    short offcodeNCount[MaxOff+1];
-    unsigned offcodeMaxValue = MaxOff;
     size_t dictID;
     size_t eSize;
-
     ZSTD_STATIC_ASSERT(HUF_WORKSPACE_SIZE >= (1<<MAX(MLFSELog,LLFSELog)));
     assert(dictSize >= 8);
     assert(MEM_readLE32(dictPtr) == ZSTD_MAGIC_DICTIONARY);
 
     dictID = params->fParams.noDictIDFlag ? 0 :  MEM_readLE32(dictPtr + 4 /* skip magic number */ );
-    eSize = ZSTD_loadCEntropy(bs, workspace, offcodeNCount, &offcodeMaxValue, dict, dictSize);
+    eSize = ZSTD_loadCEntropy(bs, workspace, dict, dictSize);
     FORWARD_IF_ERROR(eSize, "ZSTD_loadCEntropy failed");
     dictPtr += eSize;
 
-    {   size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
-        U32 offcodeMax = MaxOff;
-        if (dictContentSize <= ((U32)-1) - 128 KB) {
-            U32 const maxOffset = (U32)dictContentSize + 128 KB; /* The maximum offset that must be supported */
-            offcodeMax = ZSTD_highbit32(maxOffset); /* Calculate minimum offset code required to represent maxOffset */
-        }
-        /* All offset values <= dictContentSize + 128 KB must be representable */
-        FORWARD_IF_ERROR(ZSTD_checkDictNCount(offcodeNCount, offcodeMaxValue, MIN(offcodeMax, MaxOff)), "");
-        /* All repCodes must be <= dictContentSize and != 0*/
-        {   U32 u;
-            for (u=0; u<3; u++) {
-                RETURN_ERROR_IF(bs->rep[u] == 0, dictionary_corrupted, "");
-                RETURN_ERROR_IF(bs->rep[u] > dictContentSize, dictionary_corrupted, "");
-        }   }
-
-        bs->entropy.fse.offcode_repeatMode = FSE_repeat_valid;
-        bs->entropy.fse.matchlength_repeatMode = FSE_repeat_valid;
-        bs->entropy.fse.litlength_repeatMode = FSE_repeat_valid;
+    {
+        size_t const dictContentSize = (size_t)(dictEnd - dictPtr);
         FORWARD_IF_ERROR(ZSTD_loadDictionaryContent(
             ms, NULL, ws, params, dictPtr, dictContentSize, dtlm), "");
-        return dictID;
     }
+    return dictID;
 }
 
 /** ZSTD_compress_insertDictionary() :
@@ -3074,7 +4433,7 @@
 }
 
 #define ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF (128 KB)
-#define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6)
+#define ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER (6ULL)
 
 /*! ZSTD_compressBegin_internal() :
  * @return : 0, or an error code */
@@ -3086,6 +4445,10 @@
                                     const ZSTD_CCtx_params* params, U64 pledgedSrcSize,
                                     ZSTD_buffered_policy_e zbuff)
 {
+    size_t const dictContentSize = cdict ? cdict->dictContentSize : dictSize;
+#if ZSTD_TRACE
+    cctx->traceCtx = (ZSTD_trace_compress_begin != NULL) ? ZSTD_trace_compress_begin(cctx) : 0;
+#endif
     DEBUGLOG(4, "ZSTD_compressBegin_internal: wlog=%u", params->cParams.windowLog);
     /* params are supposed to be fully validated at this point */
     assert(!ZSTD_isError(ZSTD_checkCParams(params->cParams)));
@@ -3100,13 +4463,14 @@
         return ZSTD_resetCCtx_usingCDict(cctx, cdict, params, pledgedSrcSize, zbuff);
     }
 
-    FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, *params, pledgedSrcSize,
+    FORWARD_IF_ERROR( ZSTD_resetCCtx_internal(cctx, params, pledgedSrcSize,
+                                     dictContentSize,
                                      ZSTDcrp_makeClean, zbuff) , "");
     {   size_t const dictID = cdict ?
                 ZSTD_compress_insertDictionary(
                         cctx->blockState.prevCBlock, &cctx->blockState.matchState,
                         &cctx->ldmState, &cctx->workspace, &cctx->appliedParams, cdict->dictContent,
-                        cdict->dictContentSize, dictContentType, dtlm,
+                        cdict->dictContentSize, cdict->dictContentType, dtlm,
                         cctx->entropyWorkspace)
               : ZSTD_compress_insertDictionary(
                         cctx->blockState.prevCBlock, &cctx->blockState.matchState,
@@ -3115,6 +4479,7 @@
         FORWARD_IF_ERROR(dictID, "ZSTD_compress_insertDictionary failed");
         assert(dictID <= UINT_MAX);
         cctx->dictID = (U32)dictID;
+        cctx->dictContentSize = dictContentSize;
     }
     return 0;
 }
@@ -3143,8 +4508,8 @@
                              const void* dict, size_t dictSize,
                                    ZSTD_parameters params, unsigned long long pledgedSrcSize)
 {
-    ZSTD_CCtx_params const cctxParams =
-            ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, &params);
+    ZSTD_CCtx_params cctxParams;
+    ZSTD_CCtxParams_init_internal(&cctxParams, &params, ZSTD_NO_CLEVEL);
     return ZSTD_compressBegin_advanced_internal(cctx,
                                             dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast,
                                             NULL /*cdict*/,
@@ -3153,9 +4518,11 @@
 
 size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel)
 {
-    ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
-    ZSTD_CCtx_params const cctxParams =
-            ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, &params);
+    ZSTD_CCtx_params cctxParams;
+    {
+        ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_noAttachDict);
+        ZSTD_CCtxParams_init_internal(&cctxParams, &params, (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel);
+    }
     DEBUGLOG(4, "ZSTD_compressBegin_usingDict (dictSize=%u)", (unsigned)dictSize);
     return ZSTD_compressBegin_internal(cctx, dict, dictSize, ZSTD_dct_auto, ZSTD_dtlm_fast, NULL,
                                        &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, ZSTDb_not_buffered);
@@ -3209,6 +4576,30 @@
     return op-ostart;
 }
 
+void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize)
+{
+#if ZSTD_TRACE
+    if (cctx->traceCtx && ZSTD_trace_compress_end != NULL) {
+        int const streaming = cctx->inBuffSize > 0 || cctx->outBuffSize > 0 || cctx->appliedParams.nbWorkers > 0;
+        ZSTD_Trace trace;
+        ZSTD_memset(&trace, 0, sizeof(trace));
+        trace.version = ZSTD_VERSION_NUMBER;
+        trace.streaming = streaming;
+        trace.dictionaryID = cctx->dictID;
+        trace.dictionarySize = cctx->dictContentSize;
+        trace.uncompressedSize = cctx->consumedSrcSize;
+        trace.compressedSize = cctx->producedCSize + extraCSize;
+        trace.params = &cctx->appliedParams;
+        trace.cctx = cctx;
+        ZSTD_trace_compress_end(cctx->traceCtx, &trace);
+    }
+    cctx->traceCtx = 0;
+#else
+    (void)cctx;
+    (void)extraCSize;
+#endif
+}
+
 size_t ZSTD_compressEnd (ZSTD_CCtx* cctx,
                          void* dst, size_t dstCapacity,
                    const void* src, size_t srcSize)
@@ -3231,26 +4622,10 @@
             (unsigned)cctx->pledgedSrcSizePlusOne-1,
             (unsigned)cctx->consumedSrcSize);
     }
+    ZSTD_CCtx_trace(cctx, endResult);
     return cSize + endResult;
 }
 
-
-static size_t ZSTD_compress_internal (ZSTD_CCtx* cctx,
-                                      void* dst, size_t dstCapacity,
-                                const void* src, size_t srcSize,
-                                const void* dict,size_t dictSize,
-                                const ZSTD_parameters* params)
-{
-    ZSTD_CCtx_params const cctxParams =
-            ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, params);
-    DEBUGLOG(4, "ZSTD_compress_internal");
-    return ZSTD_compress_advanced_internal(cctx,
-                                           dst, dstCapacity,
-                                           src, srcSize,
-                                           dict, dictSize,
-                                           &cctxParams);
-}
-
 size_t ZSTD_compress_advanced (ZSTD_CCtx* cctx,
                                void* dst, size_t dstCapacity,
                          const void* src, size_t srcSize,
@@ -3259,11 +4634,12 @@
 {
     DEBUGLOG(4, "ZSTD_compress_advanced");
     FORWARD_IF_ERROR(ZSTD_checkCParams(params.cParams), "");
-    return ZSTD_compress_internal(cctx,
-                                  dst, dstCapacity,
-                                  src, srcSize,
-                                  dict, dictSize,
-                                  &params);
+    ZSTD_CCtxParams_init_internal(&cctx->simpleApiParams, &params, ZSTD_NO_CLEVEL);
+    return ZSTD_compress_advanced_internal(cctx,
+                                           dst, dstCapacity,
+                                           src, srcSize,
+                                           dict, dictSize,
+                                           &cctx->simpleApiParams);
 }
 
 /* Internal */
@@ -3287,11 +4663,13 @@
                          const void* dict, size_t dictSize,
                                int compressionLevel)
 {
-    ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, srcSize, dict ? dictSize : 0);
-    ZSTD_CCtx_params cctxParams = ZSTD_assignParamsToCCtxParams(&cctx->requestedParams, &params);
+    {
+        ZSTD_parameters const params = ZSTD_getParams_internal(compressionLevel, srcSize, dict ? dictSize : 0, ZSTD_cpm_noAttachDict);
+        assert(params.fParams.contentSizeFlag == 1);
+        ZSTD_CCtxParams_init_internal(&cctx->simpleApiParams, &params, (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT: compressionLevel);
+    }
     DEBUGLOG(4, "ZSTD_compress_usingDict (srcSize=%u)", (unsigned)srcSize);
-    assert(params.fParams.contentSizeFlag == 1);
-    return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, &cctxParams);
+    return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, dict, dictSize, &cctx->simpleApiParams);
 }
 
 size_t ZSTD_compressCCtx(ZSTD_CCtx* cctx,
@@ -3309,10 +4687,17 @@
                      int compressionLevel)
 {
     size_t result;
+#if ZSTD_COMPRESS_HEAPMODE
+    ZSTD_CCtx* cctx = ZSTD_createCCtx();
+    RETURN_ERROR_IF(!cctx, memory_allocation, "ZSTD_createCCtx failed");
+    result = ZSTD_compressCCtx(cctx, dst, dstCapacity, src, srcSize, compressionLevel);
+    ZSTD_freeCCtx(cctx);
+#else
     ZSTD_CCtx ctxBody;
     ZSTD_initCCtx(&ctxBody, ZSTD_defaultCMem);
     result = ZSTD_compressCCtx(&ctxBody, dst, dstCapacity, src, srcSize, compressionLevel);
     ZSTD_freeCCtxContent(&ctxBody);   /* can't free ctxBody itself, as it's on stack; free only heap content */
+#endif
     return result;
 }
 
@@ -3328,14 +4713,17 @@
     DEBUGLOG(5, "sizeof(ZSTD_CDict) : %u", (unsigned)sizeof(ZSTD_CDict));
     return ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict))
          + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)
-         + ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0)
+         /* enableDedicatedDictSearch == 1 ensures that CDict estimation will not be too small
+          * in case we are using DDS with row-hash. */
+         + ZSTD_sizeof_matchState(&cParams, ZSTD_resolveRowMatchFinderMode(ZSTD_urm_auto, &cParams),
+                                  /* enableDedicatedDictSearch */ 1, /* forCCtx */ 0)
          + (dictLoadMethod == ZSTD_dlm_byRef ? 0
             : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void *))));
 }
 
 size_t ZSTD_estimateCDictSize(size_t dictSize, int compressionLevel)
 {
-    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
+    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_createCDict);
     return ZSTD_estimateCDictSize_advanced(dictSize, cParams, ZSTD_dlm_byCopy);
 }
 
@@ -3353,20 +4741,22 @@
               const void* dictBuffer, size_t dictSize,
                     ZSTD_dictLoadMethod_e dictLoadMethod,
                     ZSTD_dictContentType_e dictContentType,
-                    ZSTD_compressionParameters cParams)
+                    ZSTD_CCtx_params params)
 {
     DEBUGLOG(3, "ZSTD_initCDict_internal (dictContentType:%u)", (unsigned)dictContentType);
-    assert(!ZSTD_checkCParams(cParams));
-    cdict->matchState.cParams = cParams;
+    assert(!ZSTD_checkCParams(params.cParams));
+    cdict->matchState.cParams = params.cParams;
+    cdict->matchState.dedicatedDictSearch = params.enableDedicatedDictSearch;
     if ((dictLoadMethod == ZSTD_dlm_byRef) || (!dictBuffer) || (!dictSize)) {
         cdict->dictContent = dictBuffer;
     } else {
          void *internalBuffer = ZSTD_cwksp_reserve_object(&cdict->workspace, ZSTD_cwksp_align(dictSize, sizeof(void*)));
         RETURN_ERROR_IF(!internalBuffer, memory_allocation, "NULL pointer!");
         cdict->dictContent = internalBuffer;
-        memcpy(internalBuffer, dictBuffer, dictSize);
+        ZSTD_memcpy(internalBuffer, dictBuffer, dictSize);
     }
     cdict->dictContentSize = dictSize;
+    cdict->dictContentType = dictContentType;
 
     cdict->entropyWorkspace = (U32*)ZSTD_cwksp_reserve_object(&cdict->workspace, HUF_WORKSPACE_SIZE);
 
@@ -3376,18 +4766,16 @@
     FORWARD_IF_ERROR(ZSTD_reset_matchState(
         &cdict->matchState,
         &cdict->workspace,
-        &cParams,
+        &params.cParams,
+        params.useRowMatchFinder,
         ZSTDcrp_makeClean,
         ZSTDirp_reset,
         ZSTD_resetTarget_CDict), "");
     /* (Maybe) load the dictionary
      * Skips loading the dictionary if it is < 8 bytes.
      */
-    {   ZSTD_CCtx_params params;
-        memset(&params, 0, sizeof(params));
-        params.compressionLevel = ZSTD_CLEVEL_DEFAULT;
+    {   params.compressionLevel = ZSTD_CLEVEL_DEFAULT;
         params.fParams.contentSizeFlag = 1;
-        params.cParams = cParams;
         {   size_t const dictID = ZSTD_compress_insertDictionary(
                     &cdict->cBlockState, &cdict->matchState, NULL, &cdict->workspace,
                     &params, cdict->dictContent, cdict->dictContentSize,
@@ -3401,66 +4789,129 @@
     return 0;
 }
 
-ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,
+static ZSTD_CDict* ZSTD_createCDict_advanced_internal(size_t dictSize,
                                       ZSTD_dictLoadMethod_e dictLoadMethod,
-                                      ZSTD_dictContentType_e dictContentType,
-                                      ZSTD_compressionParameters cParams, ZSTD_customMem customMem)
+                                      ZSTD_compressionParameters cParams,
+                                      ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
+                                      U32 enableDedicatedDictSearch,
+                                      ZSTD_customMem customMem)
 {
-    DEBUGLOG(3, "ZSTD_createCDict_advanced, mode %u", (unsigned)dictContentType);
-    if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+    if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
 
     {   size_t const workspaceSize =
             ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict)) +
             ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE) +
-            ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0) +
+            ZSTD_sizeof_matchState(&cParams, useRowMatchFinder, enableDedicatedDictSearch, /* forCCtx */ 0) +
             (dictLoadMethod == ZSTD_dlm_byRef ? 0
              : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*))));
-        void* const workspace = ZSTD_malloc(workspaceSize, customMem);
+        void* const workspace = ZSTD_customMalloc(workspaceSize, customMem);
         ZSTD_cwksp ws;
         ZSTD_CDict* cdict;
 
         if (!workspace) {
-            ZSTD_free(workspace, customMem);
+            ZSTD_customFree(workspace, customMem);
             return NULL;
         }
 
-        ZSTD_cwksp_init(&ws, workspace, workspaceSize);
+        ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_dynamic_alloc);
 
         cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));
         assert(cdict != NULL);
         ZSTD_cwksp_move(&cdict->workspace, &ws);
         cdict->customMem = customMem;
-        cdict->compressionLevel = 0; /* signals advanced API usage */
-
-        if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
-                                        dictBuffer, dictSize,
-                                        dictLoadMethod, dictContentType,
-                                        cParams) )) {
-            ZSTD_freeCDict(cdict);
-            return NULL;
-        }
-
+        cdict->compressionLevel = ZSTD_NO_CLEVEL; /* signals advanced API usage */
+        cdict->useRowMatchFinder = useRowMatchFinder;
         return cdict;
     }
 }
 
+ZSTD_CDict* ZSTD_createCDict_advanced(const void* dictBuffer, size_t dictSize,
+                                      ZSTD_dictLoadMethod_e dictLoadMethod,
+                                      ZSTD_dictContentType_e dictContentType,
+                                      ZSTD_compressionParameters cParams,
+                                      ZSTD_customMem customMem)
+{
+    ZSTD_CCtx_params cctxParams;
+    ZSTD_memset(&cctxParams, 0, sizeof(cctxParams));
+    ZSTD_CCtxParams_init(&cctxParams, 0);
+    cctxParams.cParams = cParams;
+    cctxParams.customMem = customMem;
+    return ZSTD_createCDict_advanced2(
+        dictBuffer, dictSize,
+        dictLoadMethod, dictContentType,
+        &cctxParams, customMem);
+}
+
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced2(
+        const void* dict, size_t dictSize,
+        ZSTD_dictLoadMethod_e dictLoadMethod,
+        ZSTD_dictContentType_e dictContentType,
+        const ZSTD_CCtx_params* originalCctxParams,
+        ZSTD_customMem customMem)
+{
+    ZSTD_CCtx_params cctxParams = *originalCctxParams;
+    ZSTD_compressionParameters cParams;
+    ZSTD_CDict* cdict;
+
+    DEBUGLOG(3, "ZSTD_createCDict_advanced2, mode %u", (unsigned)dictContentType);
+    if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+
+    if (cctxParams.enableDedicatedDictSearch) {
+        cParams = ZSTD_dedicatedDictSearch_getCParams(
+            cctxParams.compressionLevel, dictSize);
+        ZSTD_overrideCParams(&cParams, &cctxParams.cParams);
+    } else {
+        cParams = ZSTD_getCParamsFromCCtxParams(
+            &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_createCDict);
+    }
+
+    if (!ZSTD_dedicatedDictSearch_isSupported(&cParams)) {
+        /* Fall back to non-DDSS params */
+        cctxParams.enableDedicatedDictSearch = 0;
+        cParams = ZSTD_getCParamsFromCCtxParams(
+            &cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_createCDict);
+    }
+
+    DEBUGLOG(3, "ZSTD_createCDict_advanced2: DDS: %u", cctxParams.enableDedicatedDictSearch);
+    cctxParams.cParams = cParams;
+    cctxParams.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(cctxParams.useRowMatchFinder, &cParams);
+
+    cdict = ZSTD_createCDict_advanced_internal(dictSize,
+                        dictLoadMethod, cctxParams.cParams,
+                        cctxParams.useRowMatchFinder, cctxParams.enableDedicatedDictSearch,
+                        customMem);
+
+    if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
+                                    dict, dictSize,
+                                    dictLoadMethod, dictContentType,
+                                    cctxParams) )) {
+        ZSTD_freeCDict(cdict);
+        return NULL;
+    }
+
+    return cdict;
+}
+
 ZSTD_CDict* ZSTD_createCDict(const void* dict, size_t dictSize, int compressionLevel)
 {
-    ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
-    ZSTD_CDict* cdict = ZSTD_createCDict_advanced(dict, dictSize,
+    ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_createCDict);
+    ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dict, dictSize,
                                                   ZSTD_dlm_byCopy, ZSTD_dct_auto,
                                                   cParams, ZSTD_defaultCMem);
     if (cdict)
-        cdict->compressionLevel = compressionLevel == 0 ? ZSTD_CLEVEL_DEFAULT : compressionLevel;
+        cdict->compressionLevel = (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel;
     return cdict;
 }
 
 ZSTD_CDict* ZSTD_createCDict_byReference(const void* dict, size_t dictSize, int compressionLevel)
 {
-    ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize);
-    return ZSTD_createCDict_advanced(dict, dictSize,
+    ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, dictSize, ZSTD_cpm_createCDict);
+    ZSTD_CDict* const cdict = ZSTD_createCDict_advanced(dict, dictSize,
                                      ZSTD_dlm_byRef, ZSTD_dct_auto,
                                      cParams, ZSTD_defaultCMem);
+    if (cdict)
+        cdict->compressionLevel = (compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : compressionLevel;
+    return cdict;
 }
 
 size_t ZSTD_freeCDict(ZSTD_CDict* cdict)
@@ -3470,7 +4921,7 @@
         int cdictInWorkspace = ZSTD_cwksp_owns_buffer(&cdict->workspace, cdict);
         ZSTD_cwksp_free(&cdict->workspace, cMem);
         if (!cdictInWorkspace) {
-            ZSTD_free(cdict, cMem);
+            ZSTD_customFree(cdict, cMem);
         }
         return 0;
     }
@@ -3496,19 +4947,22 @@
                                  ZSTD_dictContentType_e dictContentType,
                                  ZSTD_compressionParameters cParams)
 {
-    size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, /* forCCtx */ 0);
+    ZSTD_useRowMatchFinderMode_e const useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(ZSTD_urm_auto, &cParams);
+    /* enableDedicatedDictSearch == 1 ensures matchstate is not too small in case this CDict will be used for DDS + row hash */
+    size_t const matchStateSize = ZSTD_sizeof_matchState(&cParams, useRowMatchFinder, /* enableDedicatedDictSearch */ 1, /* forCCtx */ 0);
     size_t const neededSize = ZSTD_cwksp_alloc_size(sizeof(ZSTD_CDict))
                             + (dictLoadMethod == ZSTD_dlm_byRef ? 0
                                : ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(dictSize, sizeof(void*))))
                             + ZSTD_cwksp_alloc_size(HUF_WORKSPACE_SIZE)
                             + matchStateSize;
     ZSTD_CDict* cdict;
+    ZSTD_CCtx_params params;
 
     if ((size_t)workspace & 7) return NULL;  /* 8-aligned */
 
     {
         ZSTD_cwksp ws;
-        ZSTD_cwksp_init(&ws, workspace, workspaceSize);
+        ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_static_alloc);
         cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));
         if (cdict == NULL) return NULL;
         ZSTD_cwksp_move(&cdict->workspace, &ws);
@@ -3518,10 +4972,15 @@
         (unsigned)workspaceSize, (unsigned)neededSize, (unsigned)(workspaceSize < neededSize));
     if (workspaceSize < neededSize) return NULL;
 
+    ZSTD_CCtxParams_init(&params, 0);
+    params.cParams = cParams;
+    params.useRowMatchFinder = useRowMatchFinder;
+    cdict->useRowMatchFinder = useRowMatchFinder;
+
     if (ZSTD_isError( ZSTD_initCDict_internal(cdict,
                                               dict, dictSize,
                                               dictLoadMethod, dictContentType,
-                                              cParams) ))
+                                              params) ))
         return NULL;
 
     return cdict;
@@ -3533,59 +4992,96 @@
     return cdict->matchState.cParams;
 }
 
+/*! ZSTD_getDictID_fromCDict() :
+ *  Provides the dictID of the dictionary loaded into `cdict`.
+ *  If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
+ *  Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
+unsigned ZSTD_getDictID_fromCDict(const ZSTD_CDict* cdict)
+{
+    if (cdict==NULL) return 0;
+    return cdict->dictID;
+}
+
+/* ZSTD_compressBegin_usingCDict_internal() :
+ * Implementation of various ZSTD_compressBegin_usingCDict* functions.
+ */
+static size_t ZSTD_compressBegin_usingCDict_internal(
+    ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict,
+    ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize)
+{
+    ZSTD_CCtx_params cctxParams;
+    DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_internal");
+    RETURN_ERROR_IF(cdict==NULL, dictionary_wrong, "NULL pointer!");
+    /* Initialize the cctxParams from the cdict */
+    {
+        ZSTD_parameters params;
+        params.fParams = fParams;
+        params.cParams = ( pledgedSrcSize < ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF
+                        || pledgedSrcSize < cdict->dictContentSize * ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER
+                        || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN
+                        || cdict->compressionLevel == 0 ) ?
+                ZSTD_getCParamsFromCDict(cdict)
+              : ZSTD_getCParams(cdict->compressionLevel,
+                                pledgedSrcSize,
+                                cdict->dictContentSize);
+        ZSTD_CCtxParams_init_internal(&cctxParams, &params, cdict->compressionLevel);
+    }
+    /* Increase window log to fit the entire dictionary and source if the
+     * source size is known. Limit the increase to 19, which is the
+     * window log for compression level 1 with the largest source size.
+     */
+    if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) {
+        U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19);
+        U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1;
+        cctxParams.cParams.windowLog = MAX(cctxParams.cParams.windowLog, limitedSrcLog);
+    }
+    return ZSTD_compressBegin_internal(cctx,
+                                        NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast,
+                                        cdict,
+                                        &cctxParams, pledgedSrcSize,
+                                        ZSTDb_not_buffered);
+}
+
+
 /* ZSTD_compressBegin_usingCDict_advanced() :
+ * This function is DEPRECATED.
  * cdict must be != NULL */
 size_t ZSTD_compressBegin_usingCDict_advanced(
     ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict,
     ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize)
 {
-    DEBUGLOG(4, "ZSTD_compressBegin_usingCDict_advanced");
-    RETURN_ERROR_IF(cdict==NULL, dictionary_wrong, "NULL pointer!");
-    {   ZSTD_CCtx_params params = cctx->requestedParams;
-        params.cParams = ( pledgedSrcSize < ZSTD_USE_CDICT_PARAMS_SRCSIZE_CUTOFF
-                        || pledgedSrcSize < cdict->dictContentSize * ZSTD_USE_CDICT_PARAMS_DICTSIZE_MULTIPLIER
-                        || pledgedSrcSize == ZSTD_CONTENTSIZE_UNKNOWN
-                        || cdict->compressionLevel == 0 )
-                      && (params.attachDictPref != ZSTD_dictForceLoad) ?
-                ZSTD_getCParamsFromCDict(cdict)
-              : ZSTD_getCParams(cdict->compressionLevel,
-                                pledgedSrcSize,
-                                cdict->dictContentSize);
-        /* Increase window log to fit the entire dictionary and source if the
-         * source size is known. Limit the increase to 19, which is the
-         * window log for compression level 1 with the largest source size.
-         */
-        if (pledgedSrcSize != ZSTD_CONTENTSIZE_UNKNOWN) {
-            U32 const limitedSrcSize = (U32)MIN(pledgedSrcSize, 1U << 19);
-            U32 const limitedSrcLog = limitedSrcSize > 1 ? ZSTD_highbit32(limitedSrcSize - 1) + 1 : 1;
-            params.cParams.windowLog = MAX(params.cParams.windowLog, limitedSrcLog);
-        }
-        params.fParams = fParams;
-        return ZSTD_compressBegin_internal(cctx,
-                                           NULL, 0, ZSTD_dct_auto, ZSTD_dtlm_fast,
-                                           cdict,
-                                           &params, pledgedSrcSize,
-                                           ZSTDb_not_buffered);
-    }
+    return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, pledgedSrcSize);
 }
 
 /* ZSTD_compressBegin_usingCDict() :
- * pledgedSrcSize=0 means "unknown"
- * if pledgedSrcSize>0, it will enable contentSizeFlag */
+ * cdict must be != NULL */
 size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict)
 {
     ZSTD_frameParameters const fParams = { 0 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
-    DEBUGLOG(4, "ZSTD_compressBegin_usingCDict : dictIDFlag == %u", !fParams.noDictIDFlag);
-    return ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);
+    return ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, ZSTD_CONTENTSIZE_UNKNOWN);
 }
 
+/*! ZSTD_compress_usingCDict_internal():
+ * Implementation of various ZSTD_compress_usingCDict* functions.
+ */
+static size_t ZSTD_compress_usingCDict_internal(ZSTD_CCtx* cctx,
+                                void* dst, size_t dstCapacity,
+                                const void* src, size_t srcSize,
+                                const ZSTD_CDict* cdict, ZSTD_frameParameters fParams)
+{
+    FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_internal(cctx, cdict, fParams, srcSize), ""); /* will check if cdict != NULL */
+    return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
+}
+
+/*! ZSTD_compress_usingCDict_advanced():
+ * This function is DEPRECATED.
+ */
 size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
                                 void* dst, size_t dstCapacity,
                                 const void* src, size_t srcSize,
                                 const ZSTD_CDict* cdict, ZSTD_frameParameters fParams)
 {
-    FORWARD_IF_ERROR(ZSTD_compressBegin_usingCDict_advanced(cctx, cdict, fParams, srcSize), "");   /* will check if cdict != NULL */
-    return ZSTD_compressEnd(cctx, dst, dstCapacity, src, srcSize);
+    return ZSTD_compress_usingCDict_internal(cctx, dst, dstCapacity, src, srcSize, cdict, fParams);
 }
 
 /*! ZSTD_compress_usingCDict() :
@@ -3599,7 +5095,7 @@
                                 const ZSTD_CDict* cdict)
 {
     ZSTD_frameParameters const fParams = { 1 /*content*/, 0 /*checksum*/, 0 /*noDictID*/ };
-    return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, fParams);
+    return ZSTD_compress_usingCDict_internal(cctx, dst, dstCapacity, src, srcSize, cdict, fParams);
 }
 
 
@@ -3640,32 +5136,12 @@
     return ZSTD_compressBound(ZSTD_BLOCKSIZE_MAX) + ZSTD_blockHeaderSize + 4 /* 32-bits hash */ ;
 }
 
-static size_t ZSTD_resetCStream_internal(ZSTD_CStream* cctx,
-                    const void* const dict, size_t const dictSize, ZSTD_dictContentType_e const dictContentType,
-                    const ZSTD_CDict* const cdict,
-                    ZSTD_CCtx_params params, unsigned long long const pledgedSrcSize)
+static ZSTD_cParamMode_e ZSTD_getCParamMode(ZSTD_CDict const* cdict, ZSTD_CCtx_params const* params, U64 pledgedSrcSize)
 {
-    DEBUGLOG(4, "ZSTD_resetCStream_internal");
-    /* Finalize the compression parameters */
-    params.cParams = ZSTD_getCParamsFromCCtxParams(&params, pledgedSrcSize, dictSize);
-    /* params are supposed to be fully validated at this point */
-    assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
-    assert(!((dict) && (cdict)));  /* either dict or cdict, not both */
-
-    FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,
-                                         dict, dictSize, dictContentType, ZSTD_dtlm_fast,
-                                         cdict,
-                                         &params, pledgedSrcSize,
-                                         ZSTDb_buffered) , "");
-
-    cctx->inToCompress = 0;
-    cctx->inBuffPos = 0;
-    cctx->inBuffTarget = cctx->blockSize
-                      + (cctx->blockSize == pledgedSrcSize);   /* for small input: avoid automatic flush on reaching end of block, since it would require to add a 3-bytes null block to end frame */
-    cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0;
-    cctx->streamStage = zcss_load;
-    cctx->frameEnded = 0;
-    return 0;   /* ready to go */
+    if (cdict != NULL && ZSTD_shouldAttachDict(cdict, params, pledgedSrcSize))
+        return ZSTD_cpm_attachDict;
+    else
+        return ZSTD_cpm_noAttachDict;
 }
 
 /* ZSTD_resetCStream():
@@ -3749,7 +5225,7 @@
     FORWARD_IF_ERROR( ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only) , "");
     FORWARD_IF_ERROR( ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize) , "");
     FORWARD_IF_ERROR( ZSTD_checkCParams(params.cParams) , "");
-    zcs->requestedParams = ZSTD_assignParamsToCCtxParams(&zcs->requestedParams, &params);
+    ZSTD_CCtxParams_setZstdParams(&zcs->requestedParams, &params);
     FORWARD_IF_ERROR( ZSTD_CCtx_loadDictionary(zcs, dict, dictSize) , "");
     return 0;
 }
@@ -3815,12 +5291,17 @@
 
     /* check expectations */
     DEBUGLOG(5, "ZSTD_compressStream_generic, flush=%u", (unsigned)flushMode);
-    assert(zcs->inBuff != NULL);
-    assert(zcs->inBuffSize > 0);
-    assert(zcs->outBuff !=  NULL);
-    assert(zcs->outBuffSize > 0);
+    if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
+        assert(zcs->inBuff != NULL);
+        assert(zcs->inBuffSize > 0);
+    }
+    if (zcs->appliedParams.outBufferMode == ZSTD_bm_buffered) {
+        assert(zcs->outBuff !=  NULL);
+        assert(zcs->outBuffSize > 0);
+    }
     assert(output->pos <= output->size);
     assert(input->pos <= input->size);
+    assert((U32)flushMode <= (U32)ZSTD_e_end);
 
     while (someMoreWork) {
         switch(zcs->streamStage)
@@ -3830,7 +5311,8 @@
 
         case zcss_load:
             if ( (flushMode == ZSTD_e_end)
-              && ((size_t)(oend-op) >= ZSTD_compressBound(iend-ip))  /* enough dstCapacity */
+              && ( (size_t)(oend-op) >= ZSTD_compressBound(iend-ip)     /* Enough output space */
+                || zcs->appliedParams.outBufferMode == ZSTD_bm_stable)  /* OR we are allowed to return dstSizeTooSmall */
               && (zcs->inBuffPos == 0) ) {
                 /* shortcut to compression pass directly into output buffer */
                 size_t const cSize = ZSTD_compressEnd(zcs,
@@ -3843,8 +5325,9 @@
                 ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
                 someMoreWork = 0; break;
             }
-            /* complete loading into inBuffer */
-            {   size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos;
+            /* complete loading into inBuffer in buffered mode */
+            if (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered) {
+                size_t const toLoad = zcs->inBuffTarget - zcs->inBuffPos;
                 size_t const loaded = ZSTD_limitCopy(
                                         zcs->inBuff + zcs->inBuffPos, toLoad,
                                         ip, iend-ip);
@@ -3864,31 +5347,49 @@
             }
             /* compress current block (note : this stage cannot be stopped in the middle) */
             DEBUGLOG(5, "stream compression stage (flushMode==%u)", flushMode);
-            {   void* cDst;
+            {   int const inputBuffered = (zcs->appliedParams.inBufferMode == ZSTD_bm_buffered);
+                void* cDst;
                 size_t cSize;
-                size_t const iSize = zcs->inBuffPos - zcs->inToCompress;
                 size_t oSize = oend-op;
-                unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);
-                if (oSize >= ZSTD_compressBound(iSize))
+                size_t const iSize = inputBuffered
+                    ? zcs->inBuffPos - zcs->inToCompress
+                    : MIN((size_t)(iend - ip), zcs->blockSize);
+                if (oSize >= ZSTD_compressBound(iSize) || zcs->appliedParams.outBufferMode == ZSTD_bm_stable)
                     cDst = op;   /* compress into output buffer, to skip flush stage */
                 else
                     cDst = zcs->outBuff, oSize = zcs->outBuffSize;
-                cSize = lastBlock ?
-                        ZSTD_compressEnd(zcs, cDst, oSize,
-                                    zcs->inBuff + zcs->inToCompress, iSize) :
-                        ZSTD_compressContinue(zcs, cDst, oSize,
-                                    zcs->inBuff + zcs->inToCompress, iSize);
-                FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
-                zcs->frameEnded = lastBlock;
-                /* prepare next block */
-                zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize;
-                if (zcs->inBuffTarget > zcs->inBuffSize)
-                    zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize;
-                DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u",
-                         (unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize);
-                if (!lastBlock)
-                    assert(zcs->inBuffTarget <= zcs->inBuffSize);
-                zcs->inToCompress = zcs->inBuffPos;
+                if (inputBuffered) {
+                    unsigned const lastBlock = (flushMode == ZSTD_e_end) && (ip==iend);
+                    cSize = lastBlock ?
+                            ZSTD_compressEnd(zcs, cDst, oSize,
+                                        zcs->inBuff + zcs->inToCompress, iSize) :
+                            ZSTD_compressContinue(zcs, cDst, oSize,
+                                        zcs->inBuff + zcs->inToCompress, iSize);
+                    FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
+                    zcs->frameEnded = lastBlock;
+                    /* prepare next block */
+                    zcs->inBuffTarget = zcs->inBuffPos + zcs->blockSize;
+                    if (zcs->inBuffTarget > zcs->inBuffSize)
+                        zcs->inBuffPos = 0, zcs->inBuffTarget = zcs->blockSize;
+                    DEBUGLOG(5, "inBuffTarget:%u / inBuffSize:%u",
+                            (unsigned)zcs->inBuffTarget, (unsigned)zcs->inBuffSize);
+                    if (!lastBlock)
+                        assert(zcs->inBuffTarget <= zcs->inBuffSize);
+                    zcs->inToCompress = zcs->inBuffPos;
+                } else {
+                    unsigned const lastBlock = (ip + iSize == iend);
+                    assert(flushMode == ZSTD_e_end /* Already validated */);
+                    cSize = lastBlock ?
+                            ZSTD_compressEnd(zcs, cDst, oSize, ip, iSize) :
+                            ZSTD_compressContinue(zcs, cDst, oSize, ip, iSize);
+                    /* Consume the input prior to error checking to mirror buffered mode. */
+                    if (iSize > 0)
+                        ip += iSize;
+                    FORWARD_IF_ERROR(cSize, "%s", lastBlock ? "ZSTD_compressEnd failed" : "ZSTD_compressContinue failed");
+                    zcs->frameEnded = lastBlock;
+                    if (lastBlock)
+                        assert(ip == iend);
+                }
                 if (cDst == op) {  /* no need to flush */
                     op += cSize;
                     if (zcs->frameEnded) {
@@ -3905,6 +5406,7 @@
 	    /* fall-through */
         case zcss_flush:
             DEBUGLOG(5, "flush stage");
+            assert(zcs->appliedParams.outBufferMode == ZSTD_bm_buffered);
             {   size_t const toFlush = zcs->outBuffContentSize - zcs->outBuffFlushedSize;
                 size_t const flushed = ZSTD_limitCopy(op, (size_t)(oend-op),
                             zcs->outBuff + zcs->outBuffFlushedSize, toFlush);
@@ -3959,6 +5461,135 @@
     return ZSTD_nextInputSizeHint_MTorST(zcs);
 }
 
+/* After a compression call set the expected input/output buffer.
+ * This is validated at the start of the next compression call.
+ */
+static void ZSTD_setBufferExpectations(ZSTD_CCtx* cctx, ZSTD_outBuffer const* output, ZSTD_inBuffer const* input)
+{
+    if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
+        cctx->expectedInBuffer = *input;
+    }
+    if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) {
+        cctx->expectedOutBufferSize = output->size - output->pos;
+    }
+}
+
+/* Validate that the input/output buffers match the expectations set by
+ * ZSTD_setBufferExpectations.
+ */
+static size_t ZSTD_checkBufferStability(ZSTD_CCtx const* cctx,
+                                        ZSTD_outBuffer const* output,
+                                        ZSTD_inBuffer const* input,
+                                        ZSTD_EndDirective endOp)
+{
+    if (cctx->appliedParams.inBufferMode == ZSTD_bm_stable) {
+        ZSTD_inBuffer const expect = cctx->expectedInBuffer;
+        if (expect.src != input->src || expect.pos != input->pos || expect.size != input->size)
+            RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer enabled but input differs!");
+        if (endOp != ZSTD_e_end)
+            RETURN_ERROR(srcBuffer_wrong, "ZSTD_c_stableInBuffer can only be used with ZSTD_e_end!");
+    }
+    if (cctx->appliedParams.outBufferMode == ZSTD_bm_stable) {
+        size_t const outBufferSize = output->size - output->pos;
+        if (cctx->expectedOutBufferSize != outBufferSize)
+            RETURN_ERROR(dstBuffer_wrong, "ZSTD_c_stableOutBuffer enabled but output size differs!");
+    }
+    return 0;
+}
+
+static size_t ZSTD_CCtx_init_compressStream2(ZSTD_CCtx* cctx,
+                                             ZSTD_EndDirective endOp,
+                                             size_t inSize) {
+    ZSTD_CCtx_params params = cctx->requestedParams;
+    ZSTD_prefixDict const prefixDict = cctx->prefixDict;
+    FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */
+    ZSTD_memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));   /* single usage */
+    assert(prefixDict.dict==NULL || cctx->cdict==NULL);    /* only one can be set */
+    if (cctx->cdict && !cctx->localDict.cdict) {
+        /* Let the cdict's compression level take priority over the requested params.
+         * But do not take the cdict's compression level if the "cdict" is actually a localDict
+         * generated from ZSTD_initLocalDict().
+         */
+        params.compressionLevel = cctx->cdict->compressionLevel;
+    }
+    DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");
+    if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = inSize + 1;  /* auto-fix pledgedSrcSize */
+    {
+        size_t const dictSize = prefixDict.dict
+                ? prefixDict.dictSize
+                : (cctx->cdict ? cctx->cdict->dictContentSize : 0);
+        ZSTD_cParamMode_e const mode = ZSTD_getCParamMode(cctx->cdict, &params, cctx->pledgedSrcSizePlusOne - 1);
+        params.cParams = ZSTD_getCParamsFromCCtxParams(
+                &params, cctx->pledgedSrcSizePlusOne-1,
+                dictSize, mode);
+    }
+
+    if (ZSTD_CParams_shouldEnableLdm(&params.cParams)) {
+        /* Enable LDM by default for optimal parser and window size >= 128MB */
+        DEBUGLOG(4, "LDM enabled by default (window size >= 128MB, strategy >= btopt)");
+        params.ldmParams.enableLdm = 1;
+    }
+
+    if (ZSTD_CParams_useBlockSplitter(&params.cParams)) {
+        DEBUGLOG(4, "Block splitter enabled by default (window size >= 128K, strategy >= btopt)");
+        params.splitBlocks = 1;
+    }
+
+    params.useRowMatchFinder = ZSTD_resolveRowMatchFinderMode(params.useRowMatchFinder, &params.cParams);
+
+#ifdef ZSTD_MULTITHREAD
+    if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {
+        params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */
+    }
+    if (params.nbWorkers > 0) {
+#if ZSTD_TRACE
+        cctx->traceCtx = (ZSTD_trace_compress_begin != NULL) ? ZSTD_trace_compress_begin(cctx) : 0;
+#endif
+        /* mt context creation */
+        if (cctx->mtctx == NULL) {
+            DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u",
+                        params.nbWorkers);
+            cctx->mtctx = ZSTDMT_createCCtx_advanced((U32)params.nbWorkers, cctx->customMem, cctx->pool);
+            RETURN_ERROR_IF(cctx->mtctx == NULL, memory_allocation, "NULL pointer!");
+        }
+        /* mt compression */
+        DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers);
+        FORWARD_IF_ERROR( ZSTDMT_initCStream_internal(
+                    cctx->mtctx,
+                    prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,
+                    cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) , "");
+        cctx->dictID = cctx->cdict ? cctx->cdict->dictID : 0;
+        cctx->dictContentSize = cctx->cdict ? cctx->cdict->dictContentSize : prefixDict.dictSize;
+        cctx->consumedSrcSize = 0;
+        cctx->producedCSize = 0;
+        cctx->streamStage = zcss_load;
+        cctx->appliedParams = params;
+    } else
+#endif
+    {   U64 const pledgedSrcSize = cctx->pledgedSrcSizePlusOne - 1;
+        assert(!ZSTD_isError(ZSTD_checkCParams(params.cParams)));
+        FORWARD_IF_ERROR( ZSTD_compressBegin_internal(cctx,
+                prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType, ZSTD_dtlm_fast,
+                cctx->cdict,
+                &params, pledgedSrcSize,
+                ZSTDb_buffered) , "");
+        assert(cctx->appliedParams.nbWorkers == 0);
+        cctx->inToCompress = 0;
+        cctx->inBuffPos = 0;
+        if (cctx->appliedParams.inBufferMode == ZSTD_bm_buffered) {
+            /* for small input: avoid automatic flush on reaching end of block, since
+            * it would require to add a 3-bytes null block to end frame
+            */
+            cctx->inBuffTarget = cctx->blockSize + (cctx->blockSize == pledgedSrcSize);
+        } else {
+            cctx->inBuffTarget = 0;
+        }
+        cctx->outBuffContentSize = cctx->outBuffFlushedSize = 0;
+        cctx->streamStage = zcss_load;
+        cctx->frameEnded = 0;
+    }
+    return 0;
+}
 
 size_t ZSTD_compressStream2( ZSTD_CCtx* cctx,
                              ZSTD_outBuffer* output,
@@ -3967,82 +5598,69 @@
 {
     DEBUGLOG(5, "ZSTD_compressStream2, endOp=%u ", (unsigned)endOp);
     /* check conditions */
-    RETURN_ERROR_IF(output->pos > output->size, GENERIC, "invalid buffer");
-    RETURN_ERROR_IF(input->pos  > input->size, GENERIC, "invalid buffer");
-    assert(cctx!=NULL);
+    RETURN_ERROR_IF(output->pos > output->size, dstSize_tooSmall, "invalid output buffer");
+    RETURN_ERROR_IF(input->pos  > input->size, srcSize_wrong, "invalid input buffer");
+    RETURN_ERROR_IF((U32)endOp > (U32)ZSTD_e_end, parameter_outOfBound, "invalid endDirective");
+    assert(cctx != NULL);
 
     /* transparent initialization stage */
     if (cctx->streamStage == zcss_init) {
-        ZSTD_CCtx_params params = cctx->requestedParams;
-        ZSTD_prefixDict const prefixDict = cctx->prefixDict;
-        FORWARD_IF_ERROR( ZSTD_initLocalDict(cctx) , ""); /* Init the local dict if present. */
-        memset(&cctx->prefixDict, 0, sizeof(cctx->prefixDict));   /* single usage */
-        assert(prefixDict.dict==NULL || cctx->cdict==NULL);    /* only one can be set */
-        DEBUGLOG(4, "ZSTD_compressStream2 : transparent init stage");
-        if (endOp == ZSTD_e_end) cctx->pledgedSrcSizePlusOne = input->size + 1;  /* auto-fix pledgedSrcSize */
-        params.cParams = ZSTD_getCParamsFromCCtxParams(
-                &cctx->requestedParams, cctx->pledgedSrcSizePlusOne-1, 0 /*dictSize*/);
-
-
-#ifdef ZSTD_MULTITHREAD
-        if ((cctx->pledgedSrcSizePlusOne-1) <= ZSTDMT_JOBSIZE_MIN) {
-            params.nbWorkers = 0; /* do not invoke multi-threading when src size is too small */
-        }
-        if (params.nbWorkers > 0) {
-            /* mt context creation */
-            if (cctx->mtctx == NULL) {
-                DEBUGLOG(4, "ZSTD_compressStream2: creating new mtctx for nbWorkers=%u",
-                            params.nbWorkers);
-                cctx->mtctx = ZSTDMT_createCCtx_advanced((U32)params.nbWorkers, cctx->customMem);
-                RETURN_ERROR_IF(cctx->mtctx == NULL, memory_allocation, "NULL pointer!");
-            }
-            /* mt compression */
-            DEBUGLOG(4, "call ZSTDMT_initCStream_internal as nbWorkers=%u", params.nbWorkers);
-            FORWARD_IF_ERROR( ZSTDMT_initCStream_internal(
-                        cctx->mtctx,
-                        prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,
-                        cctx->cdict, params, cctx->pledgedSrcSizePlusOne-1) , "");
-            cctx->streamStage = zcss_load;
-            cctx->appliedParams.nbWorkers = params.nbWorkers;
-        } else
-#endif
-        {   FORWARD_IF_ERROR( ZSTD_resetCStream_internal(cctx,
-                            prefixDict.dict, prefixDict.dictSize, prefixDict.dictContentType,
-                            cctx->cdict,
-                            params, cctx->pledgedSrcSizePlusOne-1) , "");
-            assert(cctx->streamStage == zcss_load);
-            assert(cctx->appliedParams.nbWorkers == 0);
-    }   }
+        FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, endOp, input->size), "CompressStream2 initialization failed");
+        ZSTD_setBufferExpectations(cctx, output, input);    /* Set initial buffer expectations now that we've initialized */
+    }
     /* end of transparent initialization stage */
 
+    FORWARD_IF_ERROR(ZSTD_checkBufferStability(cctx, output, input, endOp), "invalid buffers");
     /* compression stage */
 #ifdef ZSTD_MULTITHREAD
     if (cctx->appliedParams.nbWorkers > 0) {
-        int const forceMaxProgress = (endOp == ZSTD_e_flush || endOp == ZSTD_e_end);
         size_t flushMin;
-        assert(forceMaxProgress || endOp == ZSTD_e_continue /* Protection for a new flush type */);
         if (cctx->cParamsChanged) {
             ZSTDMT_updateCParams_whileCompressing(cctx->mtctx, &cctx->requestedParams);
             cctx->cParamsChanged = 0;
         }
-        do {
+        for (;;) {
+            size_t const ipos = input->pos;
+            size_t const opos = output->pos;
             flushMin = ZSTDMT_compressStream_generic(cctx->mtctx, output, input, endOp);
+            cctx->consumedSrcSize += (U64)(input->pos - ipos);
+            cctx->producedCSize += (U64)(output->pos - opos);
             if ( ZSTD_isError(flushMin)
               || (endOp == ZSTD_e_end && flushMin == 0) ) { /* compression completed */
+                if (flushMin == 0)
+                    ZSTD_CCtx_trace(cctx, 0);
                 ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
             }
             FORWARD_IF_ERROR(flushMin, "ZSTDMT_compressStream_generic failed");
-        } while (forceMaxProgress && flushMin != 0 && output->pos < output->size);
+
+            if (endOp == ZSTD_e_continue) {
+                /* We only require some progress with ZSTD_e_continue, not maximal progress.
+                 * We're done if we've consumed or produced any bytes, or either buffer is
+                 * full.
+                 */
+                if (input->pos != ipos || output->pos != opos || input->pos == input->size || output->pos == output->size)
+                    break;
+            } else {
+                assert(endOp == ZSTD_e_flush || endOp == ZSTD_e_end);
+                /* We require maximal progress. We're done when the flush is complete or the
+                 * output buffer is full.
+                 */
+                if (flushMin == 0 || output->pos == output->size)
+                    break;
+            }
+        }
         DEBUGLOG(5, "completed ZSTD_compressStream2 delegating to ZSTDMT_compressStream_generic");
         /* Either we don't require maximum forward progress, we've finished the
          * flush, or we are out of output space.
          */
-        assert(!forceMaxProgress || flushMin == 0 || output->pos == output->size);
+        assert(endOp == ZSTD_e_continue || flushMin == 0 || output->pos == output->size);
+        ZSTD_setBufferExpectations(cctx, output, input);
         return flushMin;
     }
 #endif
     FORWARD_IF_ERROR( ZSTD_compressStream_generic(cctx, output, input, endOp) , "");
     DEBUGLOG(5, "completed ZSTD_compressStream2");
+    ZSTD_setBufferExpectations(cctx, output, input);
     return cctx->outBuffContentSize - cctx->outBuffFlushedSize; /* remaining to flush */
 }
 
@@ -4065,14 +5683,22 @@
                       void* dst, size_t dstCapacity,
                       const void* src, size_t srcSize)
 {
+    ZSTD_bufferMode_e const originalInBufferMode = cctx->requestedParams.inBufferMode;
+    ZSTD_bufferMode_e const originalOutBufferMode = cctx->requestedParams.outBufferMode;
     DEBUGLOG(4, "ZSTD_compress2 (srcSize=%u)", (unsigned)srcSize);
     ZSTD_CCtx_reset(cctx, ZSTD_reset_session_only);
+    /* Enable stable input/output buffers. */
+    cctx->requestedParams.inBufferMode = ZSTD_bm_stable;
+    cctx->requestedParams.outBufferMode = ZSTD_bm_stable;
     {   size_t oPos = 0;
         size_t iPos = 0;
         size_t const result = ZSTD_compressStream2_simpleArgs(cctx,
                                         dst, dstCapacity, &oPos,
                                         src, srcSize, &iPos,
                                         ZSTD_e_end);
+        /* Reset to the original values. */
+        cctx->requestedParams.inBufferMode = originalInBufferMode;
+        cctx->requestedParams.outBufferMode = originalOutBufferMode;
         FORWARD_IF_ERROR(result, "ZSTD_compressStream2_simpleArgs failed");
         if (result != 0) {  /* compression not completed, due to lack of output space */
             assert(oPos == dstCapacity);
@@ -4083,6 +5709,409 @@
     }
 }
 
+typedef struct {
+    U32 idx;             /* Index in array of ZSTD_Sequence */
+    U32 posInSequence;   /* Position within sequence at idx */
+    size_t posInSrc;        /* Number of bytes given by sequences provided so far */
+} ZSTD_sequencePosition;
+
+/* Returns a ZSTD error code if sequence is not valid */
+static size_t ZSTD_validateSequence(U32 offCode, U32 matchLength,
+                                    size_t posInSrc, U32 windowLog, size_t dictSize, U32 minMatch) {
+    size_t offsetBound;
+    U32 windowSize = 1 << windowLog;
+    /* posInSrc represents the amount of data the the decoder would decode up to this point.
+     * As long as the amount of data decoded is less than or equal to window size, offsets may be
+     * larger than the total length of output decoded in order to reference the dict, even larger than
+     * window size. After output surpasses windowSize, we're limited to windowSize offsets again.
+     */
+    offsetBound = posInSrc > windowSize ? (size_t)windowSize : posInSrc + (size_t)dictSize;
+    RETURN_ERROR_IF(offCode > offsetBound + ZSTD_REP_MOVE, corruption_detected, "Offset too large!");
+    RETURN_ERROR_IF(matchLength < minMatch, corruption_detected, "Matchlength too small");
+    return 0;
+}
+
+/* Returns an offset code, given a sequence's raw offset, the ongoing repcode array, and whether litLength == 0 */
+static U32 ZSTD_finalizeOffCode(U32 rawOffset, const U32 rep[ZSTD_REP_NUM], U32 ll0) {
+    U32 offCode = rawOffset + ZSTD_REP_MOVE;
+    U32 repCode = 0;
+
+    if (!ll0 && rawOffset == rep[0]) {
+        repCode = 1;
+    } else if (rawOffset == rep[1]) {
+        repCode = 2 - ll0;
+    } else if (rawOffset == rep[2]) {
+        repCode = 3 - ll0;
+    } else if (ll0 && rawOffset == rep[0] - 1) {
+        repCode = 3;
+    }
+    if (repCode) {
+        /* ZSTD_storeSeq expects a number in the range [0, 2] to represent a repcode */
+        offCode = repCode - 1;
+    }
+    return offCode;
+}
+
+/* Returns 0 on success, and a ZSTD_error otherwise. This function scans through an array of
+ * ZSTD_Sequence, storing the sequences it finds, until it reaches a block delimiter.
+ */
+static size_t ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
+                                                             const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                                             const void* src, size_t blockSize) {
+    U32 idx = seqPos->idx;
+    BYTE const* ip = (BYTE const*)(src);
+    const BYTE* const iend = ip + blockSize;
+    repcodes_t updatedRepcodes;
+    U32 dictSize;
+    U32 litLength;
+    U32 matchLength;
+    U32 ll0;
+    U32 offCode;
+
+    if (cctx->cdict) {
+        dictSize = (U32)cctx->cdict->dictContentSize;
+    } else if (cctx->prefixDict.dict) {
+        dictSize = (U32)cctx->prefixDict.dictSize;
+    } else {
+        dictSize = 0;
+    }
+    ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t));
+    for (; (inSeqs[idx].matchLength != 0 || inSeqs[idx].offset != 0) && idx < inSeqsSize; ++idx) {
+        litLength = inSeqs[idx].litLength;
+        matchLength = inSeqs[idx].matchLength;
+        ll0 = litLength == 0;
+        offCode = ZSTD_finalizeOffCode(inSeqs[idx].offset, updatedRepcodes.rep, ll0);
+        updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0);
+
+        DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength);
+        if (cctx->appliedParams.validateSequences) {
+            seqPos->posInSrc += litLength + matchLength;
+            FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc,
+                                                cctx->appliedParams.cParams.windowLog, dictSize,
+                                                cctx->appliedParams.cParams.minMatch),
+                                                "Sequence validation failed");
+        }
+        RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation,
+                        "Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
+        ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength - MINMATCH);
+        ip += matchLength + litLength;
+    }
+    ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(repcodes_t));
+
+    if (inSeqs[idx].litLength) {
+        DEBUGLOG(6, "Storing last literals of size: %u", inSeqs[idx].litLength);
+        ZSTD_storeLastLiterals(&cctx->seqStore, ip, inSeqs[idx].litLength);
+        ip += inSeqs[idx].litLength;
+        seqPos->posInSrc += inSeqs[idx].litLength;
+    }
+    RETURN_ERROR_IF(ip != iend, corruption_detected, "Blocksize doesn't agree with block delimiter!");
+    seqPos->idx = idx+1;
+    return 0;
+}
+
+/* Returns the number of bytes to move the current read position back by. Only non-zero
+ * if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something
+ * went wrong.
+ *
+ * This function will attempt to scan through blockSize bytes represented by the sequences
+ * in inSeqs, storing any (partial) sequences.
+ *
+ * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to
+ * avoid splitting a match, or to avoid splitting a match such that it would produce a match
+ * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block.
+ */
+static size_t ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
+                                                       const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                                       const void* src, size_t blockSize) {
+    U32 idx = seqPos->idx;
+    U32 startPosInSequence = seqPos->posInSequence;
+    U32 endPosInSequence = seqPos->posInSequence + (U32)blockSize;
+    size_t dictSize;
+    BYTE const* ip = (BYTE const*)(src);
+    BYTE const* iend = ip + blockSize;  /* May be adjusted if we decide to process fewer than blockSize bytes */
+    repcodes_t updatedRepcodes;
+    U32 bytesAdjustment = 0;
+    U32 finalMatchSplit = 0;
+    U32 litLength;
+    U32 matchLength;
+    U32 rawOffset;
+    U32 offCode;
+
+    if (cctx->cdict) {
+        dictSize = cctx->cdict->dictContentSize;
+    } else if (cctx->prefixDict.dict) {
+        dictSize = cctx->prefixDict.dictSize;
+    } else {
+        dictSize = 0;
+    }
+    DEBUGLOG(5, "ZSTD_copySequencesToSeqStore: idx: %u PIS: %u blockSize: %zu", idx, startPosInSequence, blockSize);
+    DEBUGLOG(5, "Start seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
+    ZSTD_memcpy(updatedRepcodes.rep, cctx->blockState.prevCBlock->rep, sizeof(repcodes_t));
+    while (endPosInSequence && idx < inSeqsSize && !finalMatchSplit) {
+        const ZSTD_Sequence currSeq = inSeqs[idx];
+        litLength = currSeq.litLength;
+        matchLength = currSeq.matchLength;
+        rawOffset = currSeq.offset;
+
+        /* Modify the sequence depending on where endPosInSequence lies */
+        if (endPosInSequence >= currSeq.litLength + currSeq.matchLength) {
+            if (startPosInSequence >= litLength) {
+                startPosInSequence -= litLength;
+                litLength = 0;
+                matchLength -= startPosInSequence;
+            } else {
+                litLength -= startPosInSequence;
+            }
+            /* Move to the next sequence */
+            endPosInSequence -= currSeq.litLength + currSeq.matchLength;
+            startPosInSequence = 0;
+            idx++;
+        } else {
+            /* This is the final (partial) sequence we're adding from inSeqs, and endPosInSequence
+               does not reach the end of the match. So, we have to split the sequence */
+            DEBUGLOG(6, "Require a split: diff: %u, idx: %u PIS: %u",
+                     currSeq.litLength + currSeq.matchLength - endPosInSequence, idx, endPosInSequence);
+            if (endPosInSequence > litLength) {
+                U32 firstHalfMatchLength;
+                litLength = startPosInSequence >= litLength ? 0 : litLength - startPosInSequence;
+                firstHalfMatchLength = endPosInSequence - startPosInSequence - litLength;
+                if (matchLength > blockSize && firstHalfMatchLength >= cctx->appliedParams.cParams.minMatch) {
+                    /* Only ever split the match if it is larger than the block size */
+                    U32 secondHalfMatchLength = currSeq.matchLength + currSeq.litLength - endPosInSequence;
+                    if (secondHalfMatchLength < cctx->appliedParams.cParams.minMatch) {
+                        /* Move the endPosInSequence backward so that it creates match of minMatch length */
+                        endPosInSequence -= cctx->appliedParams.cParams.minMatch - secondHalfMatchLength;
+                        bytesAdjustment = cctx->appliedParams.cParams.minMatch - secondHalfMatchLength;
+                        firstHalfMatchLength -= bytesAdjustment;
+                    }
+                    matchLength = firstHalfMatchLength;
+                    /* Flag that we split the last match - after storing the sequence, exit the loop,
+                       but keep the value of endPosInSequence */
+                    finalMatchSplit = 1;
+                } else {
+                    /* Move the position in sequence backwards so that we don't split match, and break to store
+                     * the last literals. We use the original currSeq.litLength as a marker for where endPosInSequence
+                     * should go. We prefer to do this whenever it is not necessary to split the match, or if doing so
+                     * would cause the first half of the match to be too small
+                     */
+                    bytesAdjustment = endPosInSequence - currSeq.litLength;
+                    endPosInSequence = currSeq.litLength;
+                    break;
+                }
+            } else {
+                /* This sequence ends inside the literals, break to store the last literals */
+                break;
+            }
+        }
+        /* Check if this offset can be represented with a repcode */
+        {   U32 ll0 = (litLength == 0);
+            offCode = ZSTD_finalizeOffCode(rawOffset, updatedRepcodes.rep, ll0);
+            updatedRepcodes = ZSTD_updateRep(updatedRepcodes.rep, offCode, ll0);
+        }
+
+        if (cctx->appliedParams.validateSequences) {
+            seqPos->posInSrc += litLength + matchLength;
+            FORWARD_IF_ERROR(ZSTD_validateSequence(offCode, matchLength, seqPos->posInSrc,
+                                                   cctx->appliedParams.cParams.windowLog, dictSize,
+                                                   cctx->appliedParams.cParams.minMatch),
+                                                   "Sequence validation failed");
+        }
+        DEBUGLOG(6, "Storing sequence: (of: %u, ml: %u, ll: %u)", offCode, matchLength, litLength);
+        RETURN_ERROR_IF(idx - seqPos->idx > cctx->seqStore.maxNbSeq, memory_allocation,
+                        "Not enough memory allocated. Try adjusting ZSTD_c_minMatch.");
+        ZSTD_storeSeq(&cctx->seqStore, litLength, ip, iend, offCode, matchLength - MINMATCH);
+        ip += matchLength + litLength;
+    }
+    DEBUGLOG(5, "Ending seq: idx: %u (of: %u ml: %u ll: %u)", idx, inSeqs[idx].offset, inSeqs[idx].matchLength, inSeqs[idx].litLength);
+    assert(idx == inSeqsSize || endPosInSequence <= inSeqs[idx].litLength + inSeqs[idx].matchLength);
+    seqPos->idx = idx;
+    seqPos->posInSequence = endPosInSequence;
+    ZSTD_memcpy(cctx->blockState.nextCBlock->rep, updatedRepcodes.rep, sizeof(repcodes_t));
+
+    iend -= bytesAdjustment;
+    if (ip != iend) {
+        /* Store any last literals */
+        U32 lastLLSize = (U32)(iend - ip);
+        assert(ip <= iend);
+        DEBUGLOG(6, "Storing last literals of size: %u", lastLLSize);
+        ZSTD_storeLastLiterals(&cctx->seqStore, ip, lastLLSize);
+        seqPos->posInSrc += lastLLSize;
+    }
+
+    return bytesAdjustment;
+}
+
+typedef size_t (*ZSTD_sequenceCopier) (ZSTD_CCtx* cctx, ZSTD_sequencePosition* seqPos,
+                                       const ZSTD_Sequence* const inSeqs, size_t inSeqsSize,
+                                       const void* src, size_t blockSize);
+static ZSTD_sequenceCopier ZSTD_selectSequenceCopier(ZSTD_sequenceFormat_e mode) {
+    ZSTD_sequenceCopier sequenceCopier = NULL;
+    assert(ZSTD_cParam_withinBounds(ZSTD_c_blockDelimiters, mode));
+    if (mode == ZSTD_sf_explicitBlockDelimiters) {
+        return ZSTD_copySequencesToSeqStoreExplicitBlockDelim;
+    } else if (mode == ZSTD_sf_noBlockDelimiters) {
+        return ZSTD_copySequencesToSeqStoreNoBlockDelim;
+    }
+    assert(sequenceCopier != NULL);
+    return sequenceCopier;
+}
+
+/* Compress, block-by-block, all of the sequences given.
+ *
+ * Returns the cumulative size of all compressed blocks (including their headers), otherwise a ZSTD error.
+ */
+static size_t ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
+                                              void* dst, size_t dstCapacity,
+                                              const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+                                              const void* src, size_t srcSize) {
+    size_t cSize = 0;
+    U32 lastBlock;
+    size_t blockSize;
+    size_t compressedSeqsSize;
+    size_t remaining = srcSize;
+    ZSTD_sequencePosition seqPos = {0, 0, 0};
+
+    BYTE const* ip = (BYTE const*)src;
+    BYTE* op = (BYTE*)dst;
+    ZSTD_sequenceCopier sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters);
+
+    DEBUGLOG(4, "ZSTD_compressSequences_internal srcSize: %zu, inSeqsSize: %zu", srcSize, inSeqsSize);
+    /* Special case: empty frame */
+    if (remaining == 0) {
+        U32 const cBlockHeader24 = 1 /* last block */ + (((U32)bt_raw)<<1);
+        RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall, "No room for empty frame block header");
+        MEM_writeLE32(op, cBlockHeader24);
+        op += ZSTD_blockHeaderSize;
+        dstCapacity -= ZSTD_blockHeaderSize;
+        cSize += ZSTD_blockHeaderSize;
+    }
+
+    while (remaining) {
+        size_t cBlockSize;
+        size_t additionalByteAdjustment;
+        lastBlock = remaining <= cctx->blockSize;
+        blockSize = lastBlock ? (U32)remaining : (U32)cctx->blockSize;
+        ZSTD_resetSeqStore(&cctx->seqStore);
+        DEBUGLOG(4, "Working on new block. Blocksize: %zu", blockSize);
+
+        additionalByteAdjustment = sequenceCopier(cctx, &seqPos, inSeqs, inSeqsSize, ip, blockSize);
+        FORWARD_IF_ERROR(additionalByteAdjustment, "Bad sequence copy");
+        blockSize -= additionalByteAdjustment;
+
+        /* If blocks are too small, emit as a nocompress block */
+        if (blockSize < MIN_CBLOCK_SIZE+ZSTD_blockHeaderSize+1) {
+            cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
+            FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
+            DEBUGLOG(4, "Block too small, writing out nocompress block: cSize: %zu", cBlockSize);
+            cSize += cBlockSize;
+            ip += blockSize;
+            op += cBlockSize;
+            remaining -= blockSize;
+            dstCapacity -= cBlockSize;
+            continue;
+        }
+
+        compressedSeqsSize = ZSTD_entropyCompressSeqStore(&cctx->seqStore,
+                                &cctx->blockState.prevCBlock->entropy, &cctx->blockState.nextCBlock->entropy,
+                                &cctx->appliedParams,
+                                op + ZSTD_blockHeaderSize /* Leave space for block header */, dstCapacity - ZSTD_blockHeaderSize,
+                                blockSize,
+                                cctx->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */,
+                                cctx->bmi2);
+        FORWARD_IF_ERROR(compressedSeqsSize, "Compressing sequences of block failed");
+        DEBUGLOG(4, "Compressed sequences size: %zu", compressedSeqsSize);
+
+        if (!cctx->isFirstBlock &&
+            ZSTD_maybeRLE(&cctx->seqStore) &&
+            ZSTD_isRLE((BYTE const*)src, srcSize)) {
+            /* We don't want to emit our first block as a RLE even if it qualifies because
+            * doing so will cause the decoder (cli only) to throw a "should consume all input error."
+            * This is only an issue for zstd <= v1.4.3
+            */
+            compressedSeqsSize = 1;
+        }
+
+        if (compressedSeqsSize == 0) {
+            /* ZSTD_noCompressBlock writes the block header as well */
+            cBlockSize = ZSTD_noCompressBlock(op, dstCapacity, ip, blockSize, lastBlock);
+            FORWARD_IF_ERROR(cBlockSize, "Nocompress block failed");
+            DEBUGLOG(4, "Writing out nocompress block, size: %zu", cBlockSize);
+        } else if (compressedSeqsSize == 1) {
+            cBlockSize = ZSTD_rleCompressBlock(op, dstCapacity, *ip, blockSize, lastBlock);
+            FORWARD_IF_ERROR(cBlockSize, "RLE compress block failed");
+            DEBUGLOG(4, "Writing out RLE block, size: %zu", cBlockSize);
+        } else {
+            U32 cBlockHeader;
+            /* Error checking and repcodes update */
+            ZSTD_blockState_confirmRepcodesAndEntropyTables(&cctx->blockState);
+            if (cctx->blockState.prevCBlock->entropy.fse.offcode_repeatMode == FSE_repeat_valid)
+                cctx->blockState.prevCBlock->entropy.fse.offcode_repeatMode = FSE_repeat_check;
+
+            /* Write block header into beginning of block*/
+            cBlockHeader = lastBlock + (((U32)bt_compressed)<<1) + (U32)(compressedSeqsSize << 3);
+            MEM_writeLE24(op, cBlockHeader);
+            cBlockSize = ZSTD_blockHeaderSize + compressedSeqsSize;
+            DEBUGLOG(4, "Writing out compressed block, size: %zu", cBlockSize);
+        }
+
+        cSize += cBlockSize;
+        DEBUGLOG(4, "cSize running total: %zu", cSize);
+
+        if (lastBlock) {
+            break;
+        } else {
+            ip += blockSize;
+            op += cBlockSize;
+            remaining -= blockSize;
+            dstCapacity -= cBlockSize;
+            cctx->isFirstBlock = 0;
+        }
+    }
+
+    return cSize;
+}
+
+size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstCapacity,
+                              const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+                              const void* src, size_t srcSize) {
+    BYTE* op = (BYTE*)dst;
+    size_t cSize = 0;
+    size_t compressedBlocksSize = 0;
+    size_t frameHeaderSize = 0;
+
+    /* Transparent initialization stage, same as compressStream2() */
+    DEBUGLOG(3, "ZSTD_compressSequences()");
+    assert(cctx != NULL);
+    FORWARD_IF_ERROR(ZSTD_CCtx_init_compressStream2(cctx, ZSTD_e_end, srcSize), "CCtx initialization failed");
+    /* Begin writing output, starting with frame header */
+    frameHeaderSize = ZSTD_writeFrameHeader(op, dstCapacity, &cctx->appliedParams, srcSize, cctx->dictID);
+    op += frameHeaderSize;
+    dstCapacity -= frameHeaderSize;
+    cSize += frameHeaderSize;
+    if (cctx->appliedParams.fParams.checksumFlag && srcSize) {
+        XXH64_update(&cctx->xxhState, src, srcSize);
+    }
+    /* cSize includes block header size and compressed sequences size */
+    compressedBlocksSize = ZSTD_compressSequences_internal(cctx,
+                                                           op, dstCapacity,
+                                                           inSeqs, inSeqsSize,
+                                                           src, srcSize);
+    FORWARD_IF_ERROR(compressedBlocksSize, "Compressing blocks failed!");
+    cSize += compressedBlocksSize;
+    dstCapacity -= compressedBlocksSize;
+
+    if (cctx->appliedParams.fParams.checksumFlag) {
+        U32 const checksum = (U32) XXH64_digest(&cctx->xxhState);
+        RETURN_ERROR_IF(dstCapacity<4, dstSize_tooSmall, "no room for checksum");
+        DEBUGLOG(4, "Write checksum : %08X", (unsigned)checksum);
+        MEM_writeLE32((char*)dst + cSize, checksum);
+        cSize += 4;
+    }
+
+    DEBUGLOG(3, "Final compressed size: %zu", cSize);
+    return cSize;
+}
+
 /*======   Finalize   ======*/
 
 /*! ZSTD_flushStream() :
@@ -4115,6 +6144,7 @@
 #define ZSTD_MAX_CLEVEL     22
 int ZSTD_maxCLevel(void) { return ZSTD_MAX_CLEVEL; }
 int ZSTD_minCLevel(void) { return (int)-ZSTD_TARGETLENGTH_MAX; }
+int ZSTD_defaultCLevel(void) { return ZSTD_CLEVEL_DEFAULT; }
 
 static const ZSTD_compressionParameters ZSTD_defaultCParameters[4][ZSTD_MAX_CLEVEL+1] = {
 {   /* "default" - for any srcSize > 256 KB */
@@ -4223,25 +6253,110 @@
 },
 };
 
+static ZSTD_compressionParameters ZSTD_dedicatedDictSearch_getCParams(int const compressionLevel, size_t const dictSize)
+{
+    ZSTD_compressionParameters cParams = ZSTD_getCParams_internal(compressionLevel, 0, dictSize, ZSTD_cpm_createCDict);
+    switch (cParams.strategy) {
+        case ZSTD_fast:
+        case ZSTD_dfast:
+            break;
+        case ZSTD_greedy:
+        case ZSTD_lazy:
+        case ZSTD_lazy2:
+            cParams.hashLog += ZSTD_LAZY_DDSS_BUCKET_LOG;
+            break;
+        case ZSTD_btlazy2:
+        case ZSTD_btopt:
+        case ZSTD_btultra:
+        case ZSTD_btultra2:
+            break;
+    }
+    return cParams;
+}
+
+static int ZSTD_dedicatedDictSearch_isSupported(
+        ZSTD_compressionParameters const* cParams)
+{
+    return (cParams->strategy >= ZSTD_greedy)
+        && (cParams->strategy <= ZSTD_lazy2)
+        && (cParams->hashLog > cParams->chainLog)
+        && (cParams->chainLog <= 24);
+}
+
+/**
+ * Reverses the adjustment applied to cparams when enabling dedicated dict
+ * search. This is used to recover the params set to be used in the working
+ * context. (Otherwise, those tables would also grow.)
+ */
+static void ZSTD_dedicatedDictSearch_revertCParams(
+        ZSTD_compressionParameters* cParams) {
+    switch (cParams->strategy) {
+        case ZSTD_fast:
+        case ZSTD_dfast:
+            break;
+        case ZSTD_greedy:
+        case ZSTD_lazy:
+        case ZSTD_lazy2:
+            cParams->hashLog -= ZSTD_LAZY_DDSS_BUCKET_LOG;
+            if (cParams->hashLog < ZSTD_HASHLOG_MIN) {
+                cParams->hashLog = ZSTD_HASHLOG_MIN;
+            }
+            break;
+        case ZSTD_btlazy2:
+        case ZSTD_btopt:
+        case ZSTD_btultra:
+        case ZSTD_btultra2:
+            break;
+    }
+}
+
+static U64 ZSTD_getCParamRowSize(U64 srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode)
+{
+    switch (mode) {
+    case ZSTD_cpm_unknown:
+    case ZSTD_cpm_noAttachDict:
+    case ZSTD_cpm_createCDict:
+        break;
+    case ZSTD_cpm_attachDict:
+        dictSize = 0;
+        break;
+    default:
+        assert(0);
+        break;
+    }
+    {   int const unknown = srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN;
+        size_t const addedSize = unknown && dictSize > 0 ? 500 : 0;
+        return unknown && dictSize == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : srcSizeHint+dictSize+addedSize;
+    }
+}
+
 /*! ZSTD_getCParams_internal() :
  * @return ZSTD_compressionParameters structure for a selected compression level, srcSize and dictSize.
  *  Note: srcSizeHint 0 means 0, use ZSTD_CONTENTSIZE_UNKNOWN for unknown.
- *        Use dictSize == 0 for unknown or unused. */
-static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)
+ *        Use dictSize == 0 for unknown or unused.
+ *  Note: `mode` controls how we treat the `dictSize`. See docs for `ZSTD_cParamMode_e`. */
+static ZSTD_compressionParameters ZSTD_getCParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode)
 {
-    int const unknown = srcSizeHint == ZSTD_CONTENTSIZE_UNKNOWN;
-    size_t const addedSize = unknown && dictSize > 0 ? 500 : 0;
-    U64 const rSize = unknown && dictSize == 0 ? ZSTD_CONTENTSIZE_UNKNOWN : srcSizeHint+dictSize+addedSize;
+    U64 const rSize = ZSTD_getCParamRowSize(srcSizeHint, dictSize, mode);
     U32 const tableID = (rSize <= 256 KB) + (rSize <= 128 KB) + (rSize <= 16 KB);
-    int row = compressionLevel;
+    int row;
     DEBUGLOG(5, "ZSTD_getCParams_internal (cLevel=%i)", compressionLevel);
+
+    /* row */
     if (compressionLevel == 0) row = ZSTD_CLEVEL_DEFAULT;   /* 0 == default */
-    if (compressionLevel < 0) row = 0;   /* entry 0 is baseline for fast mode */
-    if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;
+    else if (compressionLevel < 0) row = 0;   /* entry 0 is baseline for fast mode */
+    else if (compressionLevel > ZSTD_MAX_CLEVEL) row = ZSTD_MAX_CLEVEL;
+    else row = compressionLevel;
+
     {   ZSTD_compressionParameters cp = ZSTD_defaultCParameters[tableID][row];
-        if (compressionLevel < 0) cp.targetLength = (unsigned)(-compressionLevel);   /* acceleration factor */
+        DEBUGLOG(5, "ZSTD_getCParams_internal selected tableID: %u row: %u strat: %u", tableID, row, (U32)cp.strategy);
+        /* acceleration factor */
+        if (compressionLevel < 0) {
+            int const clampedCompressionLevel = MAX(ZSTD_minCLevel(), compressionLevel);
+            cp.targetLength = (unsigned)(-clampedCompressionLevel);
+        }
         /* refine parameters based on srcSize & dictSize */
-        return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize);
+        return ZSTD_adjustCParams_internal(cp, srcSizeHint, dictSize, mode);
     }
 }
 
@@ -4251,18 +6366,18 @@
 ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize)
 {
     if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN;
-    return ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize);
+    return ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown);
 }
 
 /*! ZSTD_getParams() :
  *  same idea as ZSTD_getCParams()
  * @return a `ZSTD_parameters` structure (instead of `ZSTD_compressionParameters`).
  *  Fields of `ZSTD_frameParameters` are set to default values */
-static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {
+static ZSTD_parameters ZSTD_getParams_internal(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode) {
     ZSTD_parameters params;
-    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize);
+    ZSTD_compressionParameters const cParams = ZSTD_getCParams_internal(compressionLevel, srcSizeHint, dictSize, mode);
     DEBUGLOG(5, "ZSTD_getParams (cLevel=%i)", compressionLevel);
-    memset(&params, 0, sizeof(params));
+    ZSTD_memset(&params, 0, sizeof(params));
     params.cParams = cParams;
     params.fParams.contentSizeFlag = 1;
     return params;
@@ -4274,5 +6389,5 @@
  *  Fields of `ZSTD_frameParameters` are set to default values */
 ZSTD_parameters ZSTD_getParams(int compressionLevel, unsigned long long srcSizeHint, size_t dictSize) {
     if (srcSizeHint == 0) srcSizeHint = ZSTD_CONTENTSIZE_UNKNOWN;
-    return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize);
+    return ZSTD_getParams_internal(compressionLevel, srcSizeHint, dictSize, ZSTD_cpm_unknown);
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
index db73f6c..3b04fd0 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -28,7 +28,6 @@
 extern "C" {
 #endif
 
-
 /*-*************************************
 *  Constants
 ***************************************/
@@ -64,7 +63,7 @@
 } ZSTD_localDict;
 
 typedef struct {
-    U32 CTable[HUF_CTABLE_SIZE_U32(255)];
+    HUF_CElt CTable[HUF_CTABLE_SIZE_U32(255)];
     HUF_repeat repeatMode;
 } ZSTD_hufCTables_t;
 
@@ -82,12 +81,76 @@
     ZSTD_fseCTables_t fse;
 } ZSTD_entropyCTables_t;
 
+/***********************************************
+*  Entropy buffer statistics structs and funcs *
+***********************************************/
+/** ZSTD_hufCTablesMetadata_t :
+ *  Stores Literals Block Type for a super-block in hType, and
+ *  huffman tree description in hufDesBuffer.
+ *  hufDesSize refers to the size of huffman tree description in bytes.
+ *  This metadata is populated in ZSTD_buildBlockEntropyStats_literals() */
 typedef struct {
-    U32 off;
-    U32 len;
+    symbolEncodingType_e hType;
+    BYTE hufDesBuffer[ZSTD_MAX_HUF_HEADER_SIZE];
+    size_t hufDesSize;
+} ZSTD_hufCTablesMetadata_t;
+
+/** ZSTD_fseCTablesMetadata_t :
+ *  Stores symbol compression modes for a super-block in {ll, ol, ml}Type, and
+ *  fse tables in fseTablesBuffer.
+ *  fseTablesSize refers to the size of fse tables in bytes.
+ *  This metadata is populated in ZSTD_buildBlockEntropyStats_sequences() */
+typedef struct {
+    symbolEncodingType_e llType;
+    symbolEncodingType_e ofType;
+    symbolEncodingType_e mlType;
+    BYTE fseTablesBuffer[ZSTD_MAX_FSE_HEADERS_SIZE];
+    size_t fseTablesSize;
+    size_t lastCountSize; /* This is to account for bug in 1.3.4. More detail in ZSTD_entropyCompressSeqStore_internal() */
+} ZSTD_fseCTablesMetadata_t;
+
+typedef struct {
+    ZSTD_hufCTablesMetadata_t hufMetadata;
+    ZSTD_fseCTablesMetadata_t fseMetadata;
+} ZSTD_entropyCTablesMetadata_t;
+
+/** ZSTD_buildBlockEntropyStats() :
+ *  Builds entropy for the block.
+ *  @return : 0 on success or error code */
+size_t ZSTD_buildBlockEntropyStats(seqStore_t* seqStorePtr,
+                             const ZSTD_entropyCTables_t* prevEntropy,
+                                   ZSTD_entropyCTables_t* nextEntropy,
+                             const ZSTD_CCtx_params* cctxParams,
+                                   ZSTD_entropyCTablesMetadata_t* entropyMetadata,
+                                   void* workspace, size_t wkspSize);
+
+/*********************************
+*  Compression internals structs *
+*********************************/
+
+typedef struct {
+    U32 off;            /* Offset code (offset + ZSTD_REP_MOVE) for the match */
+    U32 len;            /* Raw length of match */
 } ZSTD_match_t;
 
 typedef struct {
+    U32 offset;         /* Offset of sequence */
+    U32 litLength;      /* Length of literals prior to match */
+    U32 matchLength;    /* Raw length of match */
+} rawSeq;
+
+typedef struct {
+  rawSeq* seq;          /* The start of the sequences */
+  size_t pos;           /* The index in seq where reading stopped. pos <= size. */
+  size_t posInSequence; /* The position within the sequence at seq[pos] where reading
+                           stopped. posInSequence <= seq[pos].litLength + seq[pos].matchLength */
+  size_t size;          /* The number of sequences. <= capacity. */
+  size_t capacity;      /* The capacity starting from `seq` pointer */
+} rawSeqStore_t;
+
+UNUSED_ATTR static const rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0, 0};
+
+typedef struct {
     int price;
     U32 off;
     U32 mlen;
@@ -125,14 +188,21 @@
 } ZSTD_compressedBlockState_t;
 
 typedef struct {
-    BYTE const* nextSrc;    /* next block here to continue on current prefix */
-    BYTE const* base;       /* All regular indexes relative to this position */
-    BYTE const* dictBase;   /* extDict indexes relative to this position */
-    U32 dictLimit;          /* below that point, need extDict */
-    U32 lowLimit;           /* below that point, no more valid data */
+    BYTE const* nextSrc;       /* next block here to continue on current prefix */
+    BYTE const* base;          /* All regular indexes relative to this position */
+    BYTE const* dictBase;      /* extDict indexes relative to this position */
+    U32 dictLimit;             /* below that point, need extDict */
+    U32 lowLimit;              /* below that point, no more valid data */
+    U32 nbOverflowCorrections; /* Number of times overflow correction has run since
+                                * ZSTD_window_init(). Useful for debugging coredumps
+                                * and for ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY.
+                                */
 } ZSTD_window_t;
 
 typedef struct ZSTD_matchState_t ZSTD_matchState_t;
+
+#define ZSTD_ROW_HASH_CACHE_SIZE 8       /* Size of prefetching hash cache for row-based matchfinder */
+
 struct ZSTD_matchState_t {
     ZSTD_window_t window;   /* State for window round buffer management */
     U32 loadedDictEnd;      /* index of end of dictionary, within context's referential.
@@ -144,12 +214,24 @@
                              */
     U32 nextToUpdate;       /* index from which to continue table update */
     U32 hashLog3;           /* dispatch table for matches of len==3 : larger == faster, more memory */
+
+    U32 rowHashLog;                          /* For row-based matchfinder: Hashlog based on nb of rows in the hashTable.*/
+    U16* tagTable;                           /* For row-based matchFinder: A row-based table containing the hashes and head index. */
+    U32 hashCache[ZSTD_ROW_HASH_CACHE_SIZE]; /* For row-based matchFinder: a cache of hashes to improve speed */
+
     U32* hashTable;
     U32* hashTable3;
     U32* chainTable;
+
+    U32 forceNonContiguous; /* Non-zero if we should force non-contiguous load for the next window update. */
+
+    int dedicatedDictSearch;  /* Indicates whether this matchState is using the
+                               * dedicated dictionary search structure.
+                               */
     optState_t opt;         /* optimal parser state */
     const ZSTD_matchState_t* dictMatchState;
     ZSTD_compressionParameters cParams;
+    const rawSeqStore_t* ldmSeqStore;
 };
 
 typedef struct {
@@ -164,12 +246,21 @@
 } ldmEntry_t;
 
 typedef struct {
+    BYTE const* split;
+    U32 hash;
+    U32 checksum;
+    ldmEntry_t* bucket;
+} ldmMatchCandidate_t;
+
+#define LDM_BATCH_SIZE 64
+
+typedef struct {
     ZSTD_window_t window;   /* State for the window round buffer management */
     ldmEntry_t* hashTable;
     U32 loadedDictEnd;
     BYTE* bucketOffsets;    /* Next position in bucket to insert entry */
-    U64 hashPower;          /* Used to compute the rolling hash.
-                             * Depends on ldmParams.minMatchLength */
+    size_t splitIndices[LDM_BATCH_SIZE];
+    ldmMatchCandidate_t matchCandidates[LDM_BATCH_SIZE];
 } ldmState_t;
 
 typedef struct {
@@ -182,19 +273,6 @@
 } ldmParams_t;
 
 typedef struct {
-    U32 offset;
-    U32 litLength;
-    U32 matchLength;
-} rawSeq;
-
-typedef struct {
-  rawSeq* seq;     /* The start of the sequences */
-  size_t pos;      /* The position where reading stopped. <= size. */
-  size_t size;     /* The number of sequences. <= capacity. */
-  size_t capacity; /* The capacity starting from `seq` pointer */
-} rawSeqStore_t;
-
-typedef struct {
     int collectSequences;
     ZSTD_Sequence* seqStart;
     size_t seqIndex;
@@ -228,17 +306,52 @@
     /* Long distance matching parameters */
     ldmParams_t ldmParams;
 
+    /* Dedicated dict search algorithm trigger */
+    int enableDedicatedDictSearch;
+
+    /* Input/output buffer modes */
+    ZSTD_bufferMode_e inBufferMode;
+    ZSTD_bufferMode_e outBufferMode;
+
+    /* Sequence compression API */
+    ZSTD_sequenceFormat_e blockDelimiters;
+    int validateSequences;
+
+    /* Block splitting */
+    int splitBlocks;
+
+    /* Param for deciding whether to use row-based matchfinder */
+    ZSTD_useRowMatchFinderMode_e useRowMatchFinder;
+
+    /* Always load a dictionary in ext-dict mode (not prefix mode)? */
+    int deterministicRefPrefix;
+
     /* Internal use, for createCCtxParams() and freeCCtxParams() only */
     ZSTD_customMem customMem;
 };  /* typedef'd to ZSTD_CCtx_params within "zstd.h" */
 
+#define COMPRESS_SEQUENCES_WORKSPACE_SIZE (sizeof(unsigned) * (MaxSeq + 2))
+#define ENTROPY_WORKSPACE_SIZE (HUF_WORKSPACE_SIZE + COMPRESS_SEQUENCES_WORKSPACE_SIZE)
+
+/**
+ * Indicates whether this compression proceeds directly from user-provided
+ * source buffer to user-provided destination buffer (ZSTDb_not_buffered), or
+ * whether the context needs to buffer the input/output (ZSTDb_buffered).
+ */
+typedef enum {
+    ZSTDb_not_buffered,
+    ZSTDb_buffered
+} ZSTD_buffered_policy_e;
+
 struct ZSTD_CCtx_s {
     ZSTD_compressionStage_e stage;
     int cParamsChanged;                  /* == 1 if cParams(except wlog) or compression level are changed in requestedParams. Triggers transmission of new params to ZSTDMT (if available) then reset to 0. */
     int bmi2;                            /* == 1 if the CPU supports BMI2 and 0 otherwise. CPU support is determined dynamically once per context lifetime. */
     ZSTD_CCtx_params requestedParams;
     ZSTD_CCtx_params appliedParams;
+    ZSTD_CCtx_params simpleApiParams;    /* Param storage used by the simple API - not sticky. Must only be used in top-level simple API functions for storage. */
     U32   dictID;
+    size_t dictContentSize;
 
     ZSTD_cwksp workspace; /* manages buffer for dynamic allocations */
     size_t blockSize;
@@ -247,6 +360,7 @@
     unsigned long long producedCSize;
     XXH64_state_t xxhState;
     ZSTD_customMem customMem;
+    ZSTD_threadPool* pool;
     size_t staticSize;
     SeqCollector seqCollector;
     int isFirstBlock;
@@ -258,7 +372,10 @@
     size_t maxNbLdmSequences;
     rawSeqStore_t externSeqStore; /* Mutable reference to external sequences */
     ZSTD_blockState_t blockState;
-    U32* entropyWorkspace;  /* entropy workspace of HUF_WORKSPACE_SIZE bytes */
+    U32* entropyWorkspace;  /* entropy workspace of ENTROPY_WORKSPACE_SIZE bytes */
+
+    /* Wether we are streaming or not */
+    ZSTD_buffered_policy_e bufferedPolicy;
 
     /* streaming */
     char*  inBuff;
@@ -273,6 +390,10 @@
     ZSTD_cStreamStage streamStage;
     U32    frameEnded;
 
+    /* Stable in/out buffer verification */
+    ZSTD_inBuffer expectedInBuffer;
+    size_t expectedOutBufferSize;
+
     /* Dictionary */
     ZSTD_localDict localDict;
     const ZSTD_CDict* cdict;
@@ -282,17 +403,46 @@
 #ifdef ZSTD_MULTITHREAD
     ZSTDMT_CCtx* mtctx;
 #endif
+
+    /* Tracing */
+#if ZSTD_TRACE
+    ZSTD_TraceCtx traceCtx;
+#endif
 };
 
 typedef enum { ZSTD_dtlm_fast, ZSTD_dtlm_full } ZSTD_dictTableLoadMethod_e;
 
-typedef enum { ZSTD_noDict = 0, ZSTD_extDict = 1, ZSTD_dictMatchState = 2 } ZSTD_dictMode_e;
+typedef enum {
+    ZSTD_noDict = 0,
+    ZSTD_extDict = 1,
+    ZSTD_dictMatchState = 2,
+    ZSTD_dedicatedDictSearch = 3
+} ZSTD_dictMode_e;
 
+typedef enum {
+    ZSTD_cpm_noAttachDict = 0,  /* Compression with ZSTD_noDict or ZSTD_extDict.
+                                 * In this mode we use both the srcSize and the dictSize
+                                 * when selecting and adjusting parameters.
+                                 */
+    ZSTD_cpm_attachDict = 1,    /* Compression with ZSTD_dictMatchState or ZSTD_dedicatedDictSearch.
+                                 * In this mode we only take the srcSize into account when selecting
+                                 * and adjusting parameters.
+                                 */
+    ZSTD_cpm_createCDict = 2,   /* Creating a CDict.
+                                 * In this mode we take both the source size and the dictionary size
+                                 * into account when selecting and adjusting the parameters.
+                                 */
+    ZSTD_cpm_unknown = 3,       /* ZSTD_getCParams, ZSTD_getParams, ZSTD_adjustParams.
+                                 * We don't know what these parameters are for. We default to the legacy
+                                 * behavior of taking both the source size and the dict size into account
+                                 * when selecting and adjusting parameters.
+                                 */
+} ZSTD_cParamMode_e;
 
 typedef size_t (*ZSTD_blockCompressor) (
         ZSTD_matchState_t* bs, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
-ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_dictMode_e dictMode);
+ZSTD_blockCompressor ZSTD_selectBlockCompressor(ZSTD_strategy strat, ZSTD_useRowMatchFinderMode_e rowMatchfinderMode, ZSTD_dictMode_e dictMode);
 
 
 MEM_STATIC U32 ZSTD_LLcode(U32 litLength)
@@ -345,7 +495,7 @@
             newReps.rep[1] = rep[0];
             newReps.rep[0] = currentOffset;
         } else {   /* repCode == 0 */
-            memcpy(&newReps, rep, sizeof(newReps));
+            ZSTD_memcpy(&newReps, rep, sizeof(newReps));
         }
     }
     return newReps;
@@ -372,7 +522,7 @@
     RETURN_ERROR_IF(srcSize + ZSTD_blockHeaderSize > dstCapacity,
                     dstSize_tooSmall, "dst buf too small for uncompressed block");
     MEM_writeLE24(dst, cBlockHeader24);
-    memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize);
+    ZSTD_memcpy((BYTE*)dst + ZSTD_blockHeaderSize, src, srcSize);
     return ZSTD_blockHeaderSize + srcSize;
 }
 
@@ -469,8 +619,8 @@
 
     /* literal Length */
     if (litLength>0xFFFF) {
-        assert(seqStorePtr->longLengthID == 0); /* there can only be a single long length */
-        seqStorePtr->longLengthID = 1;
+        assert(seqStorePtr->longLengthType == ZSTD_llt_none); /* there can only be a single long length */
+        seqStorePtr->longLengthType = ZSTD_llt_literalLength;
         seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
     }
     seqStorePtr->sequences[0].litLength = (U16)litLength;
@@ -480,8 +630,8 @@
 
     /* match Length */
     if (mlBase>0xFFFF) {
-        assert(seqStorePtr->longLengthID == 0); /* there can only be a single long length */
-        seqStorePtr->longLengthID = 2;
+        assert(seqStorePtr->longLengthType == ZSTD_llt_none); /* there can only be a single long length */
+        seqStorePtr->longLengthType = ZSTD_llt_matchLength;
         seqStorePtr->longLengthPos = (U32)(seqStorePtr->sequences - seqStorePtr->sequencesStart);
     }
     seqStorePtr->sequences[0].matchLength = (U16)mlBase;
@@ -498,8 +648,12 @@
     if (MEM_isLittleEndian()) {
         if (MEM_64bits()) {
 #       if defined(_MSC_VER) && defined(_WIN64)
-            unsigned long r = 0;
-            return _BitScanForward64( &r, (U64)val ) ? (unsigned)(r >> 3) : 0;
+#           if STATIC_BMI2
+                return _tzcnt_u64(val) >> 3;
+#           else
+                unsigned long r = 0;
+                return _BitScanForward64( &r, (U64)val ) ? (unsigned)(r >> 3) : 0;
+#           endif
 #       elif defined(__GNUC__) && (__GNUC__ >= 4)
             return (__builtin_ctzll((U64)val) >> 3);
 #       else
@@ -530,8 +684,12 @@
     } else {  /* Big Endian CPU */
         if (MEM_64bits()) {
 #       if defined(_MSC_VER) && defined(_WIN64)
-            unsigned long r = 0;
-            return _BitScanReverse64( &r, val ) ? (unsigned)(r >> 3) : 0;
+#           if STATIC_BMI2
+			    return _lzcnt_u64(val) >> 3;
+#           else
+			    unsigned long r = 0;
+			    return _BitScanReverse64(&r, (U64)val) ? (unsigned)(r >> 3) : 0;
+#           endif
 #       elif defined(__GNUC__) && (__GNUC__ >= 4)
             return (__builtin_clzll(val) >> 3);
 #       else
@@ -626,7 +784,8 @@
 static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
 static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); }
 
-MEM_STATIC size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
+MEM_STATIC FORCE_INLINE_ATTR
+size_t ZSTD_hashPtr(const void* p, U32 hBits, U32 mls)
 {
     switch(mls)
     {
@@ -723,6 +882,13 @@
     window->dictLimit = end;
 }
 
+MEM_STATIC U32 ZSTD_window_isEmpty(ZSTD_window_t const window)
+{
+    return window.dictLimit == 1 &&
+           window.lowLimit == 1 &&
+           (window.nextSrc - window.base) == 1;
+}
+
 /**
  * ZSTD_window_hasExtDict():
  * Returns non-zero if the window has a non-empty extDict.
@@ -742,20 +908,74 @@
     return ZSTD_window_hasExtDict(ms->window) ?
         ZSTD_extDict :
         ms->dictMatchState != NULL ?
-            ZSTD_dictMatchState :
+            (ms->dictMatchState->dedicatedDictSearch ? ZSTD_dedicatedDictSearch : ZSTD_dictMatchState) :
             ZSTD_noDict;
 }
 
+/* Defining this macro to non-zero tells zstd to run the overflow correction
+ * code much more frequently. This is very inefficient, and should only be
+ * used for tests and fuzzers.
+ */
+#ifndef ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY
+#  ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+#    define ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY 1
+#  else
+#    define ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY 0
+#  endif
+#endif
+
+/**
+ * ZSTD_window_canOverflowCorrect():
+ * Returns non-zero if the indices are large enough for overflow correction
+ * to work correctly without impacting compression ratio.
+ */
+MEM_STATIC U32 ZSTD_window_canOverflowCorrect(ZSTD_window_t const window,
+                                              U32 cycleLog,
+                                              U32 maxDist,
+                                              U32 loadedDictEnd,
+                                              void const* src)
+{
+    U32 const cycleSize = 1u << cycleLog;
+    U32 const curr = (U32)((BYTE const*)src - window.base);
+    U32 const minIndexToOverflowCorrect = cycleSize + MAX(maxDist, cycleSize);
+
+    /* Adjust the min index to backoff the overflow correction frequency,
+     * so we don't waste too much CPU in overflow correction. If this
+     * computation overflows we don't really care, we just need to make
+     * sure it is at least minIndexToOverflowCorrect.
+     */
+    U32 const adjustment = window.nbOverflowCorrections + 1;
+    U32 const adjustedIndex = MAX(minIndexToOverflowCorrect * adjustment,
+                                  minIndexToOverflowCorrect);
+    U32 const indexLargeEnough = curr > adjustedIndex;
+
+    /* Only overflow correct early if the dictionary is invalidated already,
+     * so we don't hurt compression ratio.
+     */
+    U32 const dictionaryInvalidated = curr > maxDist + loadedDictEnd;
+
+    return indexLargeEnough && dictionaryInvalidated;
+}
+
 /**
  * ZSTD_window_needOverflowCorrection():
  * Returns non-zero if the indices are getting too large and need overflow
  * protection.
  */
 MEM_STATIC U32 ZSTD_window_needOverflowCorrection(ZSTD_window_t const window,
+                                                  U32 cycleLog,
+                                                  U32 maxDist,
+                                                  U32 loadedDictEnd,
+                                                  void const* src,
                                                   void const* srcEnd)
 {
-    U32 const current = (U32)((BYTE const*)srcEnd - window.base);
-    return current > ZSTD_CURRENT_MAX;
+    U32 const curr = (U32)((BYTE const*)srcEnd - window.base);
+    if (ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY) {
+        if (ZSTD_window_canOverflowCorrect(window, cycleLog, maxDist, loadedDictEnd, src)) {
+            return 1;
+        }
+    }
+    return curr > ZSTD_CURRENT_MAX;
 }
 
 /**
@@ -766,7 +986,6 @@
  *
  * The least significant cycleLog bits of the indices must remain the same,
  * which may be 0. Every index up to maxDist in the past must be valid.
- * NOTE: (maxDist & cycleMask) must be zero.
  */
 MEM_STATIC U32 ZSTD_window_correctOverflow(ZSTD_window_t* window, U32 cycleLog,
                                            U32 maxDist, void const* src)
@@ -790,17 +1009,25 @@
      * 3. (cctx->lowLimit + 1<<windowLog) < 1<<32:
      *    windowLog <= 31 ==> 3<<29 + 1<<windowLog < 7<<29 < 1<<32.
      */
-    U32 const cycleMask = (1U << cycleLog) - 1;
-    U32 const current = (U32)((BYTE const*)src - window->base);
-    U32 const currentCycle0 = current & cycleMask;
+    U32 const cycleSize = 1u << cycleLog;
+    U32 const cycleMask = cycleSize - 1;
+    U32 const curr = (U32)((BYTE const*)src - window->base);
+    U32 const currentCycle0 = curr & cycleMask;
     /* Exclude zero so that newCurrent - maxDist >= 1. */
-    U32 const currentCycle1 = currentCycle0 == 0 ? (1U << cycleLog) : currentCycle0;
-    U32 const newCurrent = currentCycle1 + maxDist;
-    U32 const correction = current - newCurrent;
-    assert((maxDist & cycleMask) == 0);
-    assert(current > newCurrent);
-    /* Loose bound, should be around 1<<29 (see above) */
-    assert(correction > 1<<28);
+    U32 const currentCycle1 = currentCycle0 == 0 ? cycleSize : currentCycle0;
+    U32 const newCurrent = currentCycle1 + MAX(maxDist, cycleSize);
+    U32 const correction = curr - newCurrent;
+    /* maxDist must be a power of two so that:
+     *   (newCurrent & cycleMask) == (curr & cycleMask)
+     * This is required to not corrupt the chains / binary tree.
+     */
+    assert((maxDist & (maxDist - 1)) == 0);
+    assert((curr & cycleMask) == (newCurrent & cycleMask));
+    assert(curr > newCurrent);
+    if (!ZSTD_WINDOW_OVERFLOW_CORRECT_FREQUENTLY) {
+        /* Loose bound, should be around 1<<29 (see above) */
+        assert(correction > 1<<28);
+    }
 
     window->base += correction;
     window->dictBase += correction;
@@ -816,6 +1043,8 @@
     assert(window->lowLimit <= newCurrent);
     assert(window->dictLimit <= newCurrent);
 
+    ++window->nbOverflowCorrections;
+
     DEBUGLOG(4, "Correction of 0x%x bytes to lowLimit=0x%x", correction,
              window->lowLimit);
     return correction;
@@ -919,12 +1148,13 @@
 }
 
 MEM_STATIC void ZSTD_window_init(ZSTD_window_t* window) {
-    memset(window, 0, sizeof(*window));
+    ZSTD_memset(window, 0, sizeof(*window));
     window->base = (BYTE const*)"";
     window->dictBase = (BYTE const*)"";
     window->dictLimit = 1;    /* start from 1, so that 1st position is valid */
     window->lowLimit = 1;     /* it ensures first and later CCtx usages compress the same */
     window->nextSrc = window->base + 1;   /* see issue #1241 */
+    window->nbOverflowCorrections = 0;
 }
 
 /**
@@ -935,7 +1165,8 @@
  * Returns non-zero if the segment is contiguous.
  */
 MEM_STATIC U32 ZSTD_window_update(ZSTD_window_t* window,
-                                  void const* src, size_t srcSize)
+                                  void const* src, size_t srcSize,
+                                  int forceNonContiguous)
 {
     BYTE const* const ip = (BYTE const*)src;
     U32 contiguous = 1;
@@ -945,7 +1176,7 @@
     assert(window->base != NULL);
     assert(window->dictBase != NULL);
     /* Check if blocks follow each other */
-    if (src != window->nextSrc) {
+    if (src != window->nextSrc || forceNonContiguous) {
         /* not contiguous */
         size_t const distanceFromBase = (size_t)(window->nextSrc - window->base);
         DEBUGLOG(5, "Non contiguous blocks, new segment starts at %u", window->dictLimit);
@@ -973,12 +1204,16 @@
 /**
  * Returns the lowest allowed match index. It may either be in the ext-dict or the prefix.
  */
-MEM_STATIC U32 ZSTD_getLowestMatchIndex(const ZSTD_matchState_t* ms, U32 current, unsigned windowLog)
+MEM_STATIC U32 ZSTD_getLowestMatchIndex(const ZSTD_matchState_t* ms, U32 curr, unsigned windowLog)
 {
     U32    const maxDistance = 1U << windowLog;
     U32    const lowestValid = ms->window.lowLimit;
-    U32    const withinWindow = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid;
+    U32    const withinWindow = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid;
     U32    const isDictionary = (ms->loadedDictEnd != 0);
+    /* When using a dictionary the entire dictionary is valid if a single byte of the dictionary
+     * is within the window. We invalidate the dictionary (and set loadedDictEnd to 0) when it isn't
+     * valid for the entire block. So this check is sufficient to find the lowest valid match index.
+     */
     U32    const matchLowest = isDictionary ? lowestValid : withinWindow;
     return matchLowest;
 }
@@ -986,12 +1221,15 @@
 /**
  * Returns the lowest allowed match index in the prefix.
  */
-MEM_STATIC U32 ZSTD_getLowestPrefixIndex(const ZSTD_matchState_t* ms, U32 current, unsigned windowLog)
+MEM_STATIC U32 ZSTD_getLowestPrefixIndex(const ZSTD_matchState_t* ms, U32 curr, unsigned windowLog)
 {
     U32    const maxDistance = 1U << windowLog;
     U32    const lowestValid = ms->window.dictLimit;
-    U32    const withinWindow = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid;
+    U32    const withinWindow = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid;
     U32    const isDictionary = (ms->loadedDictEnd != 0);
+    /* When computing the lowest prefix index we need to take the dictionary into account to handle
+     * the edge case where the dictionary and the source are contiguous in memory.
+     */
     U32    const matchLowest = isDictionary ? lowestValid : withinWindow;
     return matchLowest;
 }
@@ -1045,7 +1283,6 @@
  * assumptions : magic number supposed already checked
  *               and dictSize >= 8 */
 size_t ZSTD_loadCEntropy(ZSTD_compressedBlockState_t* bs, void* workspace,
-                         short* offcodeNCount, unsigned* offcodeMaxValue,
                          const void* const dict, size_t dictSize);
 
 void ZSTD_reset_compressedBlockState(ZSTD_compressedBlockState_t* bs);
@@ -1061,7 +1298,7 @@
  * Note: srcSizeHint == 0 means 0!
  */
 ZSTD_compressionParameters ZSTD_getCParamsFromCCtxParams(
-        const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize);
+        const ZSTD_CCtx_params* CCtxParams, U64 srcSizeHint, size_t dictSize, ZSTD_cParamMode_e mode);
 
 /*! ZSTD_initCStream_internal() :
  *  Private use only. Init streaming operation.
@@ -1122,4 +1359,9 @@
  *  condition for correct operation : hashLog > 1 */
 U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat);
 
+/** ZSTD_CCtx_trace() :
+ *  Trace the end of a compression call.
+ */
+void ZSTD_CCtx_trace(ZSTD_CCtx* cctx, size_t extraCSize);
+
 #endif /* ZSTD_COMPRESS_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_literals.c b/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
index 17e7168..008337b 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_literals.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -15,7 +15,7 @@
 
 size_t ZSTD_noCompressLiterals (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
-    BYTE* const ostart = (BYTE* const)dst;
+    BYTE* const ostart = (BYTE*)dst;
     U32   const flSize = 1 + (srcSize>31) + (srcSize>4095);
 
     RETURN_ERROR_IF(srcSize + flSize > dstCapacity, dstSize_tooSmall, "");
@@ -35,14 +35,14 @@
             assert(0);
     }
 
-    memcpy(ostart + flSize, src, srcSize);
+    ZSTD_memcpy(ostart + flSize, src, srcSize);
     DEBUGLOG(5, "Raw literals: %u -> %u", (U32)srcSize, (U32)(srcSize + flSize));
     return srcSize + flSize;
 }
 
 size_t ZSTD_compressRleLiteralsBlock (void* dst, size_t dstCapacity, const void* src, size_t srcSize)
 {
-    BYTE* const ostart = (BYTE* const)dst;
+    BYTE* const ostart = (BYTE*)dst;
     U32   const flSize = 1 + (srcSize>31) + (srcSize>4095);
 
     (void)dstCapacity;  /* dstCapacity already guaranteed to be >=4, hence large enough */
@@ -86,7 +86,7 @@
                 disableLiteralCompression, (U32)srcSize);
 
     /* Prepare nextEntropy assuming reusing the existing table */
-    memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+    ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
 
     if (disableLiteralCompression)
         return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
@@ -117,12 +117,12 @@
         }
     }
 
-    if ((cLitSize==0) | (cLitSize >= srcSize - minGain) | ERR_isError(cLitSize)) {
-        memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+    if ((cLitSize==0) || (cLitSize >= srcSize - minGain) || ERR_isError(cLitSize)) {
+        ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
         return ZSTD_noCompressLiterals(dst, dstCapacity, src, srcSize);
     }
     if (cLitSize==1) {
-        memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
+        ZSTD_memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
         return ZSTD_compressRleLiteralsBlock(dst, dstCapacity, src, srcSize);
     }
 
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_literals.h b/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
index 8b08705..9904c0c 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_literals.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
index f9f8097..611eabd 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -51,6 +51,19 @@
 }
 
 /**
+ * Returns true if we should use ncount=-1 else we should
+ * use ncount=1 for low probability symbols instead.
+ */
+static unsigned ZSTD_useLowProbCount(size_t const nbSeq)
+{
+    /* Heuristic: This should cover most blocks <= 16K and
+     * start to fade out after 16K to about 32K depending on
+     * comprssibility.
+     */
+    return nbSeq >= 2048;
+}
+
+/**
  * Returns the cost in bytes of encoding the normalized count header.
  * Returns an error if any of the helper functions return an error.
  */
@@ -60,7 +73,7 @@
     BYTE wksp[FSE_NCOUNTBOUND];
     S16 norm[MaxSeq + 1];
     const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);
-    FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq, max), "");
+    FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq, max, ZSTD_useLowProbCount(nbSeq)), "");
     return FSE_writeNCount(wksp, sizeof(wksp), norm, max, tableLog);
 }
 
@@ -72,6 +85,8 @@
 {
     unsigned cost = 0;
     unsigned s;
+
+    assert(total > 0);
     for (s = 0; s <= max; ++s) {
         unsigned norm = (unsigned)((256 * count[s]) / total);
         if (count[s] != 0 && norm == 0)
@@ -219,6 +234,11 @@
     return set_compressed;
 }
 
+typedef struct {
+    S16 norm[MaxSeq + 1];
+    U32 wksp[FSE_BUILD_CTABLE_WORKSPACE_SIZE_U32(MaxSeq, MaxFSELog)];
+} ZSTD_BuildCTableWksp;
+
 size_t
 ZSTD_buildCTable(void* dst, size_t dstCapacity,
                 FSE_CTable* nextCTable, U32 FSELog, symbolEncodingType_e type,
@@ -239,13 +259,13 @@
         *op = codeTable[0];
         return 1;
     case set_repeat:
-        memcpy(nextCTable, prevCTable, prevCTableSize);
+        ZSTD_memcpy(nextCTable, prevCTable, prevCTableSize);
         return 0;
     case set_basic:
         FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, defaultNorm, defaultMax, defaultNormLog, entropyWorkspace, entropyWorkspaceSize), "");  /* note : could be pre-calculated */
         return 0;
     case set_compressed: {
-        S16 norm[MaxSeq + 1];
+        ZSTD_BuildCTableWksp* wksp = (ZSTD_BuildCTableWksp*)entropyWorkspace;
         size_t nbSeq_1 = nbSeq;
         const U32 tableLog = FSE_optimalTableLog(FSELog, nbSeq, max);
         if (count[codeTable[nbSeq-1]] > 1) {
@@ -253,10 +273,12 @@
             nbSeq_1--;
         }
         assert(nbSeq_1 > 1);
-        FORWARD_IF_ERROR(FSE_normalizeCount(norm, tableLog, count, nbSeq_1, max), "");
-        {   size_t const NCountSize = FSE_writeNCount(op, oend - op, norm, max, tableLog);   /* overflow protected */
+        assert(entropyWorkspaceSize >= sizeof(ZSTD_BuildCTableWksp));
+        (void)entropyWorkspaceSize;
+        FORWARD_IF_ERROR(FSE_normalizeCount(wksp->norm, tableLog, count, nbSeq_1, max, ZSTD_useLowProbCount(nbSeq_1)), "");
+        {   size_t const NCountSize = FSE_writeNCount(op, oend - op, wksp->norm, max, tableLog);   /* overflow protected */
             FORWARD_IF_ERROR(NCountSize, "FSE_writeNCount failed");
-            FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, norm, max, tableLog, entropyWorkspace, entropyWorkspaceSize), "");
+            FORWARD_IF_ERROR(FSE_buildCTable_wksp(nextCTable, wksp->norm, max, tableLog, wksp->wksp, sizeof(wksp->wksp)), "");
             return NCountSize;
         }
     }
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
index 68c6f9a..7991364 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_sequences.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
index b693866..e4e4506 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -15,288 +15,10 @@
 
 #include "../common/zstd_internal.h"  /* ZSTD_getSequenceLength */
 #include "hist.h"                     /* HIST_countFast_wksp */
-#include "zstd_compress_internal.h"
+#include "zstd_compress_internal.h"   /* ZSTD_[huf|fse|entropy]CTablesMetadata_t */
 #include "zstd_compress_sequences.h"
 #include "zstd_compress_literals.h"
 
-/*-*************************************
-*  Superblock entropy buffer structs
-***************************************/
-/** ZSTD_hufCTablesMetadata_t :
- *  Stores Literals Block Type for a super-block in hType, and
- *  huffman tree description in hufDesBuffer.
- *  hufDesSize refers to the size of huffman tree description in bytes.
- *  This metadata is populated in ZSTD_buildSuperBlockEntropy_literal() */
-typedef struct {
-    symbolEncodingType_e hType;
-    BYTE hufDesBuffer[500]; /* TODO give name to this value */
-    size_t hufDesSize;
-} ZSTD_hufCTablesMetadata_t;
-
-/** ZSTD_fseCTablesMetadata_t :
- *  Stores symbol compression modes for a super-block in {ll, ol, ml}Type, and
- *  fse tables in fseTablesBuffer.
- *  fseTablesSize refers to the size of fse tables in bytes.
- *  This metadata is populated in ZSTD_buildSuperBlockEntropy_sequences() */
-typedef struct {
-    symbolEncodingType_e llType;
-    symbolEncodingType_e ofType;
-    symbolEncodingType_e mlType;
-    BYTE fseTablesBuffer[500]; /* TODO give name to this value */
-    size_t fseTablesSize;
-    size_t lastCountSize; /* This is to account for bug in 1.3.4. More detail in ZSTD_compressSubBlock_sequences() */
-} ZSTD_fseCTablesMetadata_t;
-
-typedef struct {
-    ZSTD_hufCTablesMetadata_t hufMetadata;
-    ZSTD_fseCTablesMetadata_t fseMetadata;
-} ZSTD_entropyCTablesMetadata_t;
-
-
-/** ZSTD_buildSuperBlockEntropy_literal() :
- *  Builds entropy for the super-block literals.
- *  Stores literals block type (raw, rle, compressed, repeat) and
- *  huffman description table to hufMetadata.
- *  @return : size of huffman description table or error code */
-static size_t ZSTD_buildSuperBlockEntropy_literal(void* const src, size_t srcSize,
-                                            const ZSTD_hufCTables_t* prevHuf,
-                                                  ZSTD_hufCTables_t* nextHuf,
-                                                  ZSTD_hufCTablesMetadata_t* hufMetadata,
-                                                  const int disableLiteralsCompression,
-                                                  void* workspace, size_t wkspSize)
-{
-    BYTE* const wkspStart = (BYTE*)workspace;
-    BYTE* const wkspEnd = wkspStart + wkspSize;
-    BYTE* const countWkspStart = wkspStart;
-    unsigned* const countWksp = (unsigned*)workspace;
-    const size_t countWkspSize = (HUF_SYMBOLVALUE_MAX + 1) * sizeof(unsigned);
-    BYTE* const nodeWksp = countWkspStart + countWkspSize;
-    const size_t nodeWkspSize = wkspEnd-nodeWksp;
-    unsigned maxSymbolValue = 255;
-    unsigned huffLog = HUF_TABLELOG_DEFAULT;
-    HUF_repeat repeat = prevHuf->repeatMode;
-
-    DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy_literal (srcSize=%zu)", srcSize);
-
-    /* Prepare nextEntropy assuming reusing the existing table */
-    memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-
-    if (disableLiteralsCompression) {
-        DEBUGLOG(5, "set_basic - disabled");
-        hufMetadata->hType = set_basic;
-        return 0;
-    }
-
-    /* small ? don't even attempt compression (speed opt) */
-#   define COMPRESS_LITERALS_SIZE_MIN 63
-    {   size_t const minLitSize = (prevHuf->repeatMode == HUF_repeat_valid) ? 6 : COMPRESS_LITERALS_SIZE_MIN;
-        if (srcSize <= minLitSize) {
-            DEBUGLOG(5, "set_basic - too small");
-            hufMetadata->hType = set_basic;
-            return 0;
-        }
-    }
-
-    /* Scan input and build symbol stats */
-    {   size_t const largest = HIST_count_wksp (countWksp, &maxSymbolValue, (const BYTE*)src, srcSize, workspace, wkspSize);
-        FORWARD_IF_ERROR(largest, "HIST_count_wksp failed");
-        if (largest == srcSize) {
-            DEBUGLOG(5, "set_rle");
-            hufMetadata->hType = set_rle;
-            return 0;
-        }
-        if (largest <= (srcSize >> 7)+4) {
-            DEBUGLOG(5, "set_basic - no gain");
-            hufMetadata->hType = set_basic;
-            return 0;
-        }
-    }
-
-    /* Validate the previous Huffman table */
-    if (repeat == HUF_repeat_check && !HUF_validateCTable((HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue)) {
-        repeat = HUF_repeat_none;
-    }
-
-    /* Build Huffman Tree */
-    memset(nextHuf->CTable, 0, sizeof(nextHuf->CTable));
-    huffLog = HUF_optimalTableLog(huffLog, srcSize, maxSymbolValue);
-    {   size_t const maxBits = HUF_buildCTable_wksp((HUF_CElt*)nextHuf->CTable, countWksp,
-                                                    maxSymbolValue, huffLog,
-                                                    nodeWksp, nodeWkspSize);
-        FORWARD_IF_ERROR(maxBits, "HUF_buildCTable_wksp");
-        huffLog = (U32)maxBits;
-        {   /* Build and write the CTable */
-            size_t const newCSize = HUF_estimateCompressedSize(
-                    (HUF_CElt*)nextHuf->CTable, countWksp, maxSymbolValue);
-            size_t const hSize = HUF_writeCTable(
-                    hufMetadata->hufDesBuffer, sizeof(hufMetadata->hufDesBuffer),
-                    (HUF_CElt*)nextHuf->CTable, maxSymbolValue, huffLog);
-            /* Check against repeating the previous CTable */
-            if (repeat != HUF_repeat_none) {
-                size_t const oldCSize = HUF_estimateCompressedSize(
-                        (HUF_CElt const*)prevHuf->CTable, countWksp, maxSymbolValue);
-                if (oldCSize < srcSize && (oldCSize <= hSize + newCSize || hSize + 12 >= srcSize)) {
-                    DEBUGLOG(5, "set_repeat - smaller");
-                    memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-                    hufMetadata->hType = set_repeat;
-                    return 0;
-                }
-            }
-            if (newCSize + hSize >= srcSize) {
-                DEBUGLOG(5, "set_basic - no gains");
-                memcpy(nextHuf, prevHuf, sizeof(*prevHuf));
-                hufMetadata->hType = set_basic;
-                return 0;
-            }
-            DEBUGLOG(5, "set_compressed (hSize=%u)", (U32)hSize);
-            hufMetadata->hType = set_compressed;
-            nextHuf->repeatMode = HUF_repeat_check;
-            return hSize;
-        }
-    }
-}
-
-/** ZSTD_buildSuperBlockEntropy_sequences() :
- *  Builds entropy for the super-block sequences.
- *  Stores symbol compression modes and fse table to fseMetadata.
- *  @return : size of fse tables or error code */
-static size_t ZSTD_buildSuperBlockEntropy_sequences(seqStore_t* seqStorePtr,
-                                              const ZSTD_fseCTables_t* prevEntropy,
-                                                    ZSTD_fseCTables_t* nextEntropy,
-                                              const ZSTD_CCtx_params* cctxParams,
-                                                    ZSTD_fseCTablesMetadata_t* fseMetadata,
-                                                    void* workspace, size_t wkspSize)
-{
-    BYTE* const wkspStart = (BYTE*)workspace;
-    BYTE* const wkspEnd = wkspStart + wkspSize;
-    BYTE* const countWkspStart = wkspStart;
-    unsigned* const countWksp = (unsigned*)workspace;
-    const size_t countWkspSize = (MaxSeq + 1) * sizeof(unsigned);
-    BYTE* const cTableWksp = countWkspStart + countWkspSize;
-    const size_t cTableWkspSize = wkspEnd-cTableWksp;
-    ZSTD_strategy const strategy = cctxParams->cParams.strategy;
-    FSE_CTable* CTable_LitLength = nextEntropy->litlengthCTable;
-    FSE_CTable* CTable_OffsetBits = nextEntropy->offcodeCTable;
-    FSE_CTable* CTable_MatchLength = nextEntropy->matchlengthCTable;
-    const BYTE* const ofCodeTable = seqStorePtr->ofCode;
-    const BYTE* const llCodeTable = seqStorePtr->llCode;
-    const BYTE* const mlCodeTable = seqStorePtr->mlCode;
-    size_t const nbSeq = seqStorePtr->sequences - seqStorePtr->sequencesStart;
-    BYTE* const ostart = fseMetadata->fseTablesBuffer;
-    BYTE* const oend = ostart + sizeof(fseMetadata->fseTablesBuffer);
-    BYTE* op = ostart;
-
-    assert(cTableWkspSize >= (1 << MaxFSELog) * sizeof(FSE_FUNCTION_TYPE));
-    DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy_sequences (nbSeq=%zu)", nbSeq);
-    memset(workspace, 0, wkspSize);
-
-    fseMetadata->lastCountSize = 0;
-    /* convert length/distances into codes */
-    ZSTD_seqToCodes(seqStorePtr);
-    /* build CTable for Literal Lengths */
-    {   U32 LLtype;
-        unsigned max = MaxLL;
-        size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, llCodeTable, nbSeq, workspace, wkspSize);  /* can't fail */
-        DEBUGLOG(5, "Building LL table");
-        nextEntropy->litlength_repeatMode = prevEntropy->litlength_repeatMode;
-        LLtype = ZSTD_selectEncodingType(&nextEntropy->litlength_repeatMode,
-                                        countWksp, max, mostFrequent, nbSeq,
-                                        LLFSELog, prevEntropy->litlengthCTable,
-                                        LL_defaultNorm, LL_defaultNormLog,
-                                        ZSTD_defaultAllowed, strategy);
-        assert(set_basic < set_compressed && set_rle < set_compressed);
-        assert(!(LLtype < set_compressed && nextEntropy->litlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
-        {   size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_LitLength, LLFSELog, (symbolEncodingType_e)LLtype,
-                                                    countWksp, max, llCodeTable, nbSeq, LL_defaultNorm, LL_defaultNormLog, MaxLL,
-                                                    prevEntropy->litlengthCTable, sizeof(prevEntropy->litlengthCTable),
-                                                    cTableWksp, cTableWkspSize);
-            FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for LitLens failed");
-            if (LLtype == set_compressed)
-                fseMetadata->lastCountSize = countSize;
-            op += countSize;
-            fseMetadata->llType = (symbolEncodingType_e) LLtype;
-    }   }
-    /* build CTable for Offsets */
-    {   U32 Offtype;
-        unsigned max = MaxOff;
-        size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, ofCodeTable, nbSeq, workspace, wkspSize);  /* can't fail */
-        /* We can only use the basic table if max <= DefaultMaxOff, otherwise the offsets are too large */
-        ZSTD_defaultPolicy_e const defaultPolicy = (max <= DefaultMaxOff) ? ZSTD_defaultAllowed : ZSTD_defaultDisallowed;
-        DEBUGLOG(5, "Building OF table");
-        nextEntropy->offcode_repeatMode = prevEntropy->offcode_repeatMode;
-        Offtype = ZSTD_selectEncodingType(&nextEntropy->offcode_repeatMode,
-                                        countWksp, max, mostFrequent, nbSeq,
-                                        OffFSELog, prevEntropy->offcodeCTable,
-                                        OF_defaultNorm, OF_defaultNormLog,
-                                        defaultPolicy, strategy);
-        assert(!(Offtype < set_compressed && nextEntropy->offcode_repeatMode != FSE_repeat_none)); /* We don't copy tables */
-        {   size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_OffsetBits, OffFSELog, (symbolEncodingType_e)Offtype,
-                                                    countWksp, max, ofCodeTable, nbSeq, OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
-                                                    prevEntropy->offcodeCTable, sizeof(prevEntropy->offcodeCTable),
-                                                    cTableWksp, cTableWkspSize);
-            FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for Offsets failed");
-            if (Offtype == set_compressed)
-                fseMetadata->lastCountSize = countSize;
-            op += countSize;
-            fseMetadata->ofType = (symbolEncodingType_e) Offtype;
-    }   }
-    /* build CTable for MatchLengths */
-    {   U32 MLtype;
-        unsigned max = MaxML;
-        size_t const mostFrequent = HIST_countFast_wksp(countWksp, &max, mlCodeTable, nbSeq, workspace, wkspSize);   /* can't fail */
-        DEBUGLOG(5, "Building ML table (remaining space : %i)", (int)(oend-op));
-        nextEntropy->matchlength_repeatMode = prevEntropy->matchlength_repeatMode;
-        MLtype = ZSTD_selectEncodingType(&nextEntropy->matchlength_repeatMode,
-                                        countWksp, max, mostFrequent, nbSeq,
-                                        MLFSELog, prevEntropy->matchlengthCTable,
-                                        ML_defaultNorm, ML_defaultNormLog,
-                                        ZSTD_defaultAllowed, strategy);
-        assert(!(MLtype < set_compressed && nextEntropy->matchlength_repeatMode != FSE_repeat_none)); /* We don't copy tables */
-        {   size_t const countSize = ZSTD_buildCTable(op, oend - op, CTable_MatchLength, MLFSELog, (symbolEncodingType_e)MLtype,
-                                                    countWksp, max, mlCodeTable, nbSeq, ML_defaultNorm, ML_defaultNormLog, MaxML,
-                                                    prevEntropy->matchlengthCTable, sizeof(prevEntropy->matchlengthCTable),
-                                                    cTableWksp, cTableWkspSize);
-            FORWARD_IF_ERROR(countSize, "ZSTD_buildCTable for MatchLengths failed");
-            if (MLtype == set_compressed)
-                fseMetadata->lastCountSize = countSize;
-            op += countSize;
-            fseMetadata->mlType = (symbolEncodingType_e) MLtype;
-    }   }
-    assert((size_t) (op-ostart) <= sizeof(fseMetadata->fseTablesBuffer));
-    return op-ostart;
-}
-
-
-/** ZSTD_buildSuperBlockEntropy() :
- *  Builds entropy for the super-block.
- *  @return : 0 on success or error code */
-static size_t
-ZSTD_buildSuperBlockEntropy(seqStore_t* seqStorePtr,
-                      const ZSTD_entropyCTables_t* prevEntropy,
-                            ZSTD_entropyCTables_t* nextEntropy,
-                      const ZSTD_CCtx_params* cctxParams,
-                            ZSTD_entropyCTablesMetadata_t* entropyMetadata,
-                            void* workspace, size_t wkspSize)
-{
-    size_t const litSize = seqStorePtr->lit - seqStorePtr->litStart;
-    DEBUGLOG(5, "ZSTD_buildSuperBlockEntropy");
-    entropyMetadata->hufMetadata.hufDesSize =
-        ZSTD_buildSuperBlockEntropy_literal(seqStorePtr->litStart, litSize,
-                                            &prevEntropy->huf, &nextEntropy->huf,
-                                            &entropyMetadata->hufMetadata,
-                                            ZSTD_disableLiteralsCompression(cctxParams),
-                                            workspace, wkspSize);
-    FORWARD_IF_ERROR(entropyMetadata->hufMetadata.hufDesSize, "ZSTD_buildSuperBlockEntropy_literal failed");
-    entropyMetadata->fseMetadata.fseTablesSize =
-        ZSTD_buildSuperBlockEntropy_sequences(seqStorePtr,
-                                              &prevEntropy->fse, &nextEntropy->fse,
-                                              cctxParams,
-                                              &entropyMetadata->fseMetadata,
-                                              workspace, wkspSize);
-    FORWARD_IF_ERROR(entropyMetadata->fseMetadata.fseTablesSize, "ZSTD_buildSuperBlockEntropy_sequences failed");
-    return 0;
-}
-
 /** ZSTD_compressSubBlock_literal() :
  *  Compresses literals section for a sub-block.
  *  When we have to write the Huffman table we will sometimes choose a header
@@ -304,7 +26,7 @@
  *  before we know the table size + compressed size, so we have a bound on the
  *  table size. If we guessed incorrectly, we fall back to uncompressed literals.
  *
- *  We write the header when writeEntropy=1 and set entropyWrriten=1 when we succeeded
+ *  We write the header when writeEntropy=1 and set entropyWritten=1 when we succeeded
  *  in writing the header, otherwise it is set to 0.
  *
  *  hufMetadata->hType has literals block type info.
@@ -348,7 +70,7 @@
     assert(hufMetadata->hType == set_compressed || hufMetadata->hType == set_repeat);
 
     if (writeEntropy && hufMetadata->hType == set_compressed) {
-        memcpy(op, hufMetadata->hufDesBuffer, hufMetadata->hufDesSize);
+        ZSTD_memcpy(op, hufMetadata->hufDesBuffer, hufMetadata->hufDesSize);
         op += hufMetadata->hufDesSize;
         cLitSize += hufMetadata->hufDesSize;
         DEBUGLOG(5, "ZSTD_compressSubBlock_literal (hSize=%zu)", hufMetadata->hufDesSize);
@@ -474,7 +196,7 @@
         const U32 MLtype = fseMetadata->mlType;
         DEBUGLOG(5, "ZSTD_compressSubBlock_sequences (fseTablesSize=%zu)", fseMetadata->fseTablesSize);
         *seqHead = (BYTE)((LLtype<<6) + (Offtype<<4) + (MLtype<<2));
-        memcpy(op, fseMetadata->fseTablesBuffer, fseMetadata->fseTablesSize);
+        ZSTD_memcpy(op, fseMetadata->fseTablesBuffer, fseMetadata->fseTablesSize);
         op += fseMetadata->fseTablesSize;
     } else {
         const U32 repeat = set_repeat;
@@ -603,7 +325,7 @@
                         const BYTE* codeTable, unsigned maxCode,
                         size_t nbSeq, const FSE_CTable* fseCTable,
                         const U32* additionalBits,
-                        short const* defaultNorm, U32 defaultNormLog,
+                        short const* defaultNorm, U32 defaultNormLog, U32 defaultMax,
                         void* workspace, size_t wkspSize)
 {
     unsigned* const countWksp = (unsigned*)workspace;
@@ -615,7 +337,11 @@
 
     HIST_countFast_wksp(countWksp, &max, codeTable, nbSeq, workspace, wkspSize);  /* can't fail */
     if (type == set_basic) {
-        cSymbolTypeSizeEstimateInBits = ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max);
+        /* We selected this encoding type, so it must be valid. */
+        assert(max <= defaultMax);
+        cSymbolTypeSizeEstimateInBits = max <= defaultMax
+                ? ZSTD_crossEntropyCost(defaultNorm, defaultNormLog, countWksp, max)
+                : ERROR(GENERIC);
     } else if (type == set_rle) {
         cSymbolTypeSizeEstimateInBits = 0;
     } else if (type == set_compressed || type == set_repeat) {
@@ -639,19 +365,20 @@
                                                   void* workspace, size_t wkspSize,
                                                   int writeEntropy)
 {
-    size_t sequencesSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */
+    size_t const sequencesSectionHeaderSize = 3; /* Use hard coded size of 3 bytes */
     size_t cSeqSizeEstimate = 0;
+    if (nbSeq == 0) return sequencesSectionHeaderSize;
     cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->ofType, ofCodeTable, MaxOff,
                                          nbSeq, fseTables->offcodeCTable, NULL,
-                                         OF_defaultNorm, OF_defaultNormLog,
+                                         OF_defaultNorm, OF_defaultNormLog, DefaultMaxOff,
                                          workspace, wkspSize);
     cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->llType, llCodeTable, MaxLL,
                                          nbSeq, fseTables->litlengthCTable, LL_bits,
-                                         LL_defaultNorm, LL_defaultNormLog,
+                                         LL_defaultNorm, LL_defaultNormLog, MaxLL,
                                          workspace, wkspSize);
     cSeqSizeEstimate += ZSTD_estimateSubBlockSize_symbolType(fseMetadata->mlType, mlCodeTable, MaxML,
                                          nbSeq, fseTables->matchlengthCTable, ML_bits,
-                                         ML_defaultNorm, ML_defaultNormLog,
+                                         ML_defaultNorm, ML_defaultNormLog, MaxML,
                                          workspace, wkspSize);
     if (writeEntropy) cSeqSizeEstimate += fseMetadata->fseTablesSize;
     return cSeqSizeEstimate + sequencesSectionHeaderSize;
@@ -790,7 +517,7 @@
     } while (!lastSequence);
     if (writeLitEntropy) {
         DEBUGLOG(5, "ZSTD_compressSubBlock_multi has literal entropy tables unwritten");
-        memcpy(&nextCBlock->entropy.huf, &prevCBlock->entropy.huf, sizeof(prevCBlock->entropy.huf));
+        ZSTD_memcpy(&nextCBlock->entropy.huf, &prevCBlock->entropy.huf, sizeof(prevCBlock->entropy.huf));
     }
     if (writeSeqEntropy && ZSTD_needSequenceEntropyTables(&entropyMetadata->fseMetadata)) {
         /* If we haven't written our entropy tables, then we've violated our contract and
@@ -809,11 +536,11 @@
         if (sp < send) {
             seqDef const* seq;
             repcodes_t rep;
-            memcpy(&rep, prevCBlock->rep, sizeof(rep)); 
+            ZSTD_memcpy(&rep, prevCBlock->rep, sizeof(rep));
             for (seq = sstart; seq < sp; ++seq) {
                 rep = ZSTD_updateRep(rep.rep, seq->offset - 1, ZSTD_getSequenceLength(seqStorePtr, seq).litLength == 0);
             }
-            memcpy(nextCBlock->rep, &rep, sizeof(rep));
+            ZSTD_memcpy(nextCBlock->rep, &rep, sizeof(rep));
         }
     }
     DEBUGLOG(5, "ZSTD_compressSubBlock_multi compressed");
@@ -826,12 +553,12 @@
                                unsigned lastBlock) {
     ZSTD_entropyCTablesMetadata_t entropyMetadata;
 
-    FORWARD_IF_ERROR(ZSTD_buildSuperBlockEntropy(&zc->seqStore,
+    FORWARD_IF_ERROR(ZSTD_buildBlockEntropyStats(&zc->seqStore,
           &zc->blockState.prevCBlock->entropy,
           &zc->blockState.nextCBlock->entropy,
           &zc->appliedParams,
           &entropyMetadata,
-          zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */), "");
+          zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */), "");
 
     return ZSTD_compressSubBlock_multi(&zc->seqStore,
             zc->blockState.prevCBlock,
@@ -841,5 +568,5 @@
             dst, dstCapacity,
             src, srcSize,
             zc->bmi2, lastBlock,
-            zc->entropyWorkspace, HUF_WORKSPACE_SIZE /* statically allocated in resetCCtx */);
+            zc->entropyWorkspace, ENTROPY_WORKSPACE_SIZE /* statically allocated in resetCCtx */);
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
index 07f4cb1..176f9b1 100644
--- a/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
+++ b/Utilities/cmzstd/lib/compress/zstd_compress_superblock.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_cwksp.h b/Utilities/cmzstd/lib/compress/zstd_cwksp.h
index a25c926..2656d26 100644
--- a/Utilities/cmzstd/lib/compress/zstd_cwksp.h
+++ b/Utilities/cmzstd/lib/compress/zstd_cwksp.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -35,6 +35,10 @@
 #define ZSTD_CWKSP_ASAN_REDZONE_SIZE 128
 #endif
 
+
+/* Set our tables and aligneds to align by 64 bytes */
+#define ZSTD_CWKSP_ALIGNMENT_BYTES 64
+
 /*-*************************************
 *  Structures
 ***************************************/
@@ -45,6 +49,16 @@
 } ZSTD_cwksp_alloc_phase_e;
 
 /**
+ * Used to describe whether the workspace is statically allocated (and will not
+ * necessarily ever be freed), or if it's dynamically allocated and we can
+ * expect a well-formed caller to free this.
+ */
+typedef enum {
+    ZSTD_cwksp_dynamic_alloc,
+    ZSTD_cwksp_static_alloc
+} ZSTD_cwksp_static_alloc_e;
+
+/**
  * Zstd fits all its internal datastructures into a single continuous buffer,
  * so that it only needs to perform a single OS allocation (or so that a buffer
  * can be provided to it and it can perform no allocations at all). This buffer
@@ -92,7 +106,7 @@
  *
  * - Static objects: this is optionally the enclosing ZSTD_CCtx or ZSTD_CDict,
  *   so that literally everything fits in a single buffer. Note: if present,
- *   this must be the first object in the workspace, since ZSTD_free{CCtx,
+ *   this must be the first object in the workspace, since ZSTD_customFree{CCtx,
  *   CDict}() rely on a pointer comparison to see whether one or two frees are
  *   required.
  *
@@ -107,10 +121,11 @@
  * - Tables: these are any of several different datastructures (hash tables,
  *   chain tables, binary trees) that all respect a common format: they are
  *   uint32_t arrays, all of whose values are between 0 and (nextSrc - base).
- *   Their sizes depend on the cparams.
+ *   Their sizes depend on the cparams. These tables are 64-byte aligned.
  *
  * - Aligned: these buffers are used for various purposes that require 4 byte
- *   alignment, but don't require any initialization before they're used.
+ *   alignment, but don't require any initialization before they're used. These
+ *   buffers are each aligned to 64 bytes.
  *
  * - Buffers: these buffers are used for various purposes that don't require
  *   any alignment or initialization before they're used. This means they can
@@ -123,8 +138,7 @@
  *
  * 1. Objects
  * 2. Buffers
- * 3. Aligned
- * 4. Tables
+ * 3. Aligned/Tables
  *
  * Attempts to reserve objects of different types out of order will fail.
  */
@@ -137,9 +151,10 @@
     void* tableValidEnd;
     void* allocStart;
 
-    int allocFailed;
+    BYTE allocFailed;
     int workspaceOversizedDuration;
     ZSTD_cwksp_alloc_phase_e phase;
+    ZSTD_cwksp_static_alloc_e isStatic;
 } ZSTD_cwksp;
 
 /*-*************************************
@@ -176,39 +191,123 @@
  * Since tables aren't currently redzoned, you don't need to call through this
  * to figure out how much space you need for the matchState tables. Everything
  * else is though.
+ *
+ * Do not use for sizing aligned buffers. Instead, use ZSTD_cwksp_aligned_alloc_size().
  */
 MEM_STATIC size_t ZSTD_cwksp_alloc_size(size_t size) {
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+    if (size == 0)
+        return 0;
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
     return size + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;
 #else
     return size;
 #endif
 }
 
-MEM_STATIC void ZSTD_cwksp_internal_advance_phase(
+/**
+ * Returns an adjusted alloc size that is the nearest larger multiple of 64 bytes.
+ * Used to determine the number of bytes required for a given "aligned".
+ */
+MEM_STATIC size_t ZSTD_cwksp_aligned_alloc_size(size_t size) {
+    return ZSTD_cwksp_alloc_size(ZSTD_cwksp_align(size, ZSTD_CWKSP_ALIGNMENT_BYTES));
+}
+
+/**
+ * Returns the amount of additional space the cwksp must allocate
+ * for internal purposes (currently only alignment).
+ */
+MEM_STATIC size_t ZSTD_cwksp_slack_space_required(void) {
+    /* For alignment, the wksp will always allocate an additional n_1=[1, 64] bytes
+     * to align the beginning of tables section, as well as another n_2=[0, 63] bytes
+     * to align the beginning of the aligned secion.
+     *
+     * n_1 + n_2 == 64 bytes if the cwksp is freshly allocated, due to tables and
+     * aligneds being sized in multiples of 64 bytes.
+     */
+    size_t const slackSpace = ZSTD_CWKSP_ALIGNMENT_BYTES;
+    return slackSpace;
+}
+
+
+/**
+ * Return the number of additional bytes required to align a pointer to the given number of bytes.
+ * alignBytes must be a power of two.
+ */
+MEM_STATIC size_t ZSTD_cwksp_bytes_to_align_ptr(void* ptr, const size_t alignBytes) {
+    size_t const alignBytesMask = alignBytes - 1;
+    size_t const bytes = (alignBytes - ((size_t)ptr & (alignBytesMask))) & alignBytesMask;
+    assert((alignBytes & alignBytesMask) == 0);
+    assert(bytes != ZSTD_CWKSP_ALIGNMENT_BYTES);
+    return bytes;
+}
+
+/**
+ * Internal function. Do not use directly.
+ * Reserves the given number of bytes within the aligned/buffer segment of the wksp, which
+ * counts from the end of the wksp. (as opposed to the object/table segment)
+ *
+ * Returns a pointer to the beginning of that space.
+ */
+MEM_STATIC void* ZSTD_cwksp_reserve_internal_buffer_space(ZSTD_cwksp* ws, size_t const bytes) {
+    void* const alloc = (BYTE*)ws->allocStart - bytes;
+    void* const bottom = ws->tableEnd;
+    DEBUGLOG(5, "cwksp: reserving %p %zd bytes, %zd bytes remaining",
+        alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);
+    ZSTD_cwksp_assert_internal_consistency(ws);
+    assert(alloc >= bottom);
+    if (alloc < bottom) {
+        DEBUGLOG(4, "cwksp: alloc failed!");
+        ws->allocFailed = 1;
+        return NULL;
+    }
+    if (alloc < ws->tableValidEnd) {
+        ws->tableValidEnd = alloc;
+    }
+    ws->allocStart = alloc;
+    return alloc;
+}
+
+/**
+ * Moves the cwksp to the next phase, and does any necessary allocations.
+ * Returns a 0 on success, or zstd error
+ */
+MEM_STATIC size_t ZSTD_cwksp_internal_advance_phase(
         ZSTD_cwksp* ws, ZSTD_cwksp_alloc_phase_e phase) {
     assert(phase >= ws->phase);
     if (phase > ws->phase) {
+        /* Going from allocating objects to allocating buffers */
         if (ws->phase < ZSTD_cwksp_alloc_buffers &&
                 phase >= ZSTD_cwksp_alloc_buffers) {
             ws->tableValidEnd = ws->objectEnd;
         }
+
+        /* Going from allocating buffers to allocating aligneds/tables */
         if (ws->phase < ZSTD_cwksp_alloc_aligned &&
                 phase >= ZSTD_cwksp_alloc_aligned) {
-            /* If unaligned allocations down from a too-large top have left us
-             * unaligned, we need to realign our alloc ptr. Technically, this
-             * can consume space that is unaccounted for in the neededSpace
-             * calculation. However, I believe this can only happen when the
-             * workspace is too large, and specifically when it is too large
-             * by a larger margin than the space that will be consumed. */
-            /* TODO: cleaner, compiler warning friendly way to do this??? */
-            ws->allocStart = (BYTE*)ws->allocStart - ((size_t)ws->allocStart & (sizeof(U32)-1));
-            if (ws->allocStart < ws->tableValidEnd) {
-                ws->tableValidEnd = ws->allocStart;
+            {   /* Align the start of the "aligned" to 64 bytes. Use [1, 64] bytes. */
+                size_t const bytesToAlign =
+                    ZSTD_CWKSP_ALIGNMENT_BYTES - ZSTD_cwksp_bytes_to_align_ptr(ws->allocStart, ZSTD_CWKSP_ALIGNMENT_BYTES);
+                DEBUGLOG(5, "reserving aligned alignment addtl space: %zu", bytesToAlign);
+                ZSTD_STATIC_ASSERT((ZSTD_CWKSP_ALIGNMENT_BYTES & (ZSTD_CWKSP_ALIGNMENT_BYTES - 1)) == 0); /* power of 2 */
+                RETURN_ERROR_IF(!ZSTD_cwksp_reserve_internal_buffer_space(ws, bytesToAlign),
+                                memory_allocation, "aligned phase - alignment initial allocation failed!");
+            }
+            {   /* Align the start of the tables to 64 bytes. Use [0, 63] bytes */
+                void* const alloc = ws->objectEnd;
+                size_t const bytesToAlign = ZSTD_cwksp_bytes_to_align_ptr(alloc, ZSTD_CWKSP_ALIGNMENT_BYTES);
+                void* const end = (BYTE*)alloc + bytesToAlign;
+                DEBUGLOG(5, "reserving table alignment addtl space: %zu", bytesToAlign);
+                RETURN_ERROR_IF(end > ws->workspaceEnd, memory_allocation,
+                                "table phase - alignment initial allocation failed!");
+                ws->objectEnd = end;
+                ws->tableEnd = end;
+                ws->tableValidEnd = end;
             }
         }
         ws->phase = phase;
+        ZSTD_cwksp_assert_internal_consistency(ws);
     }
+    return 0;
 }
 
 /**
@@ -224,34 +323,26 @@
 MEM_STATIC void* ZSTD_cwksp_reserve_internal(
         ZSTD_cwksp* ws, size_t bytes, ZSTD_cwksp_alloc_phase_e phase) {
     void* alloc;
-    void* bottom = ws->tableEnd;
-    ZSTD_cwksp_internal_advance_phase(ws, phase);
-    alloc = (BYTE *)ws->allocStart - bytes;
-
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
-    /* over-reserve space */
-    alloc = (BYTE *)alloc - 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;
-#endif
-
-    DEBUGLOG(5, "cwksp: reserving %p %zd bytes, %zd bytes remaining",
-        alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);
-    ZSTD_cwksp_assert_internal_consistency(ws);
-    assert(alloc >= bottom);
-    if (alloc < bottom) {
-        DEBUGLOG(4, "cwksp: alloc failed!");
-        ws->allocFailed = 1;
+    if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase)) || bytes == 0) {
         return NULL;
     }
-    if (alloc < ws->tableValidEnd) {
-        ws->tableValidEnd = alloc;
-    }
-    ws->allocStart = alloc;
 
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+    /* over-reserve space */
+    bytes += 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;
+#endif
+
+    alloc = ZSTD_cwksp_reserve_internal_buffer_space(ws, bytes);
+
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
     /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on
      * either size. */
-    alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
-    __asan_unpoison_memory_region(alloc, bytes);
+    if (alloc) {
+        alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
+        if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
+            __asan_unpoison_memory_region(alloc, bytes);
+        }
+    }
 #endif
 
     return alloc;
@@ -265,28 +356,36 @@
 }
 
 /**
- * Reserves and returns memory sized on and aligned on sizeof(unsigned).
+ * Reserves and returns memory sized on and aligned on ZSTD_CWKSP_ALIGNMENT_BYTES (64 bytes).
  */
 MEM_STATIC void* ZSTD_cwksp_reserve_aligned(ZSTD_cwksp* ws, size_t bytes) {
-    assert((bytes & (sizeof(U32)-1)) == 0);
-    return ZSTD_cwksp_reserve_internal(ws, ZSTD_cwksp_align(bytes, sizeof(U32)), ZSTD_cwksp_alloc_aligned);
+    void* ptr = ZSTD_cwksp_reserve_internal(ws, ZSTD_cwksp_align(bytes, ZSTD_CWKSP_ALIGNMENT_BYTES),
+                                            ZSTD_cwksp_alloc_aligned);
+    assert(((size_t)ptr & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0);
+    return ptr;
 }
 
 /**
- * Aligned on sizeof(unsigned). These buffers have the special property that
+ * Aligned on 64 bytes. These buffers have the special property that
  * their values remain constrained, allowing us to re-use them without
  * memset()-ing them.
  */
 MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) {
     const ZSTD_cwksp_alloc_phase_e phase = ZSTD_cwksp_alloc_aligned;
-    void* alloc = ws->tableEnd;
-    void* end = (BYTE *)alloc + bytes;
-    void* top = ws->allocStart;
+    void* alloc;
+    void* end;
+    void* top;
+
+    if (ZSTD_isError(ZSTD_cwksp_internal_advance_phase(ws, phase))) {
+        return NULL;
+    }
+    alloc = ws->tableEnd;
+    end = (BYTE *)alloc + bytes;
+    top = ws->allocStart;
 
     DEBUGLOG(5, "cwksp: reserving %p table %zd bytes, %zd bytes remaining",
         alloc, bytes, ZSTD_cwksp_available_space(ws) - bytes);
     assert((bytes & (sizeof(U32)-1)) == 0);
-    ZSTD_cwksp_internal_advance_phase(ws, phase);
     ZSTD_cwksp_assert_internal_consistency(ws);
     assert(end <= top);
     if (end > top) {
@@ -296,10 +395,14 @@
     }
     ws->tableEnd = end;
 
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
-    __asan_unpoison_memory_region(alloc, bytes);
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+    if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
+        __asan_unpoison_memory_region(alloc, bytes);
+    }
 #endif
 
+    assert((bytes & (ZSTD_CWKSP_ALIGNMENT_BYTES-1)) == 0);
+    assert(((size_t)alloc & (ZSTD_CWKSP_ALIGNMENT_BYTES-1))== 0);
     return alloc;
 }
 
@@ -311,7 +414,7 @@
     void* alloc = ws->objectEnd;
     void* end = (BYTE*)alloc + roundedBytes;
 
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
     /* over-reserve space */
     end = (BYTE *)end + 2 * ZSTD_CWKSP_ASAN_REDZONE_SIZE;
 #endif
@@ -332,11 +435,13 @@
     ws->tableEnd = end;
     ws->tableValidEnd = end;
 
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
     /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on
      * either size. */
     alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
-    __asan_unpoison_memory_region(alloc, bytes);
+    if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
+        __asan_unpoison_memory_region(alloc, bytes);
+    }
 #endif
 
     return alloc;
@@ -345,7 +450,7 @@
 MEM_STATIC void ZSTD_cwksp_mark_tables_dirty(ZSTD_cwksp* ws) {
     DEBUGLOG(4, "cwksp: ZSTD_cwksp_mark_tables_dirty");
 
-#if defined (MEMORY_SANITIZER) && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
+#if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
     /* To validate that the table re-use logic is sound, and that we don't
      * access table space that we haven't cleaned, we re-"poison" the table
      * space every time we mark it dirty. */
@@ -380,7 +485,7 @@
     assert(ws->tableValidEnd >= ws->objectEnd);
     assert(ws->tableValidEnd <= ws->allocStart);
     if (ws->tableValidEnd < ws->tableEnd) {
-        memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd);
+        ZSTD_memset(ws->tableValidEnd, 0, (BYTE*)ws->tableEnd - (BYTE*)ws->tableValidEnd);
     }
     ZSTD_cwksp_mark_tables_clean(ws);
 }
@@ -392,8 +497,12 @@
 MEM_STATIC void ZSTD_cwksp_clear_tables(ZSTD_cwksp* ws) {
     DEBUGLOG(4, "cwksp: clearing tables!");
 
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
-    {
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+    /* We don't do this when the workspace is statically allocated, because
+     * when that is the case, we have no capability to hook into the end of the
+     * workspace's lifecycle to unpoison the memory.
+     */
+    if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
         size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;
         __asan_poison_memory_region(ws->objectEnd, size);
     }
@@ -410,7 +519,7 @@
 MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) {
     DEBUGLOG(4, "cwksp: clearing!");
 
-#if defined (MEMORY_SANITIZER) && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
+#if ZSTD_MEMORY_SANITIZER && !defined (ZSTD_MSAN_DONT_POISON_WORKSPACE)
     /* To validate that the context re-use logic is sound, and that we don't
      * access stuff that this compression hasn't initialized, we re-"poison"
      * the workspace (or at least the non-static, non-table parts of it)
@@ -421,8 +530,12 @@
     }
 #endif
 
-#if defined (ADDRESS_SANITIZER) && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
-    {
+#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
+    /* We don't do this when the workspace is statically allocated, because
+     * when that is the case, we have no capability to hook into the end of the
+     * workspace's lifecycle to unpoison the memory.
+     */
+    if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
         size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->objectEnd;
         __asan_poison_memory_region(ws->objectEnd, size);
     }
@@ -442,7 +555,7 @@
  * Any existing values in the workspace are ignored (the previously managed
  * buffer, if present, must be separately freed).
  */
-MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size) {
+MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size, ZSTD_cwksp_static_alloc_e isStatic) {
     DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size);
     assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */
     ws->workspace = start;
@@ -450,39 +563,45 @@
     ws->objectEnd = ws->workspace;
     ws->tableValidEnd = ws->objectEnd;
     ws->phase = ZSTD_cwksp_alloc_objects;
+    ws->isStatic = isStatic;
     ZSTD_cwksp_clear(ws);
     ws->workspaceOversizedDuration = 0;
     ZSTD_cwksp_assert_internal_consistency(ws);
 }
 
 MEM_STATIC size_t ZSTD_cwksp_create(ZSTD_cwksp* ws, size_t size, ZSTD_customMem customMem) {
-    void* workspace = ZSTD_malloc(size, customMem);
+    void* workspace = ZSTD_customMalloc(size, customMem);
     DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size);
     RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!");
-    ZSTD_cwksp_init(ws, workspace, size);
+    ZSTD_cwksp_init(ws, workspace, size, ZSTD_cwksp_dynamic_alloc);
     return 0;
 }
 
 MEM_STATIC void ZSTD_cwksp_free(ZSTD_cwksp* ws, ZSTD_customMem customMem) {
     void *ptr = ws->workspace;
     DEBUGLOG(4, "cwksp: freeing workspace");
-    memset(ws, 0, sizeof(ZSTD_cwksp));
-    ZSTD_free(ptr, customMem);
+    ZSTD_memset(ws, 0, sizeof(ZSTD_cwksp));
+    ZSTD_customFree(ptr, customMem);
 }
 
 /**
  * Moves the management of a workspace from one cwksp to another. The src cwksp
- * is left in an invalid state (src must be re-init()'ed before its used again).
+ * is left in an invalid state (src must be re-init()'ed before it's used again).
  */
 MEM_STATIC void ZSTD_cwksp_move(ZSTD_cwksp* dst, ZSTD_cwksp* src) {
     *dst = *src;
-    memset(src, 0, sizeof(ZSTD_cwksp));
+    ZSTD_memset(src, 0, sizeof(ZSTD_cwksp));
 }
 
 MEM_STATIC size_t ZSTD_cwksp_sizeof(const ZSTD_cwksp* ws) {
     return (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->workspace);
 }
 
+MEM_STATIC size_t ZSTD_cwksp_used(const ZSTD_cwksp* ws) {
+    return (size_t)((BYTE*)ws->tableEnd - (BYTE*)ws->workspace)
+         + (size_t)((BYTE*)ws->workspaceEnd - (BYTE*)ws->allocStart);
+}
+
 MEM_STATIC int ZSTD_cwksp_reserve_failed(const ZSTD_cwksp* ws) {
     return ws->allocFailed;
 }
@@ -491,6 +610,24 @@
 *  Functions Checking Free Space
 ***************************************/
 
+/* ZSTD_alignmentSpaceWithinBounds() :
+ * Returns if the estimated space needed for a wksp is within an acceptable limit of the
+ * actual amount of space used.
+ */
+MEM_STATIC int ZSTD_cwksp_estimated_space_within_bounds(const ZSTD_cwksp* const ws,
+                                                        size_t const estimatedSpace, int resizedWorkspace) {
+    if (resizedWorkspace) {
+        /* Resized/newly allocated wksp should have exact bounds */
+        return ZSTD_cwksp_used(ws) == estimatedSpace;
+    } else {
+        /* Due to alignment, when reusing a workspace, we can actually consume 63 fewer or more bytes
+         * than estimatedSpace. See the comments in zstd_cwksp.h for details.
+         */
+        return (ZSTD_cwksp_used(ws) >= estimatedSpace - 63) && (ZSTD_cwksp_used(ws) <= estimatedSpace + 63);
+    }
+}
+
+
 MEM_STATIC size_t ZSTD_cwksp_available_space(ZSTD_cwksp* ws) {
     return (size_t)((BYTE*)ws->allocStart - (BYTE*)ws->tableEnd);
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.c b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
index 27eed66..d0d3a78 100644
--- a/Utilities/cmzstd/lib/compress/zstd_double_fast.c
+++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -31,15 +31,15 @@
      * is empty.
      */
     for (; ip + fastHashFillStep - 1 <= iend; ip += fastHashFillStep) {
-        U32 const current = (U32)(ip - base);
+        U32 const curr = (U32)(ip - base);
         U32 i;
         for (i = 0; i < fastHashFillStep; ++i) {
             size_t const smHash = ZSTD_hashPtr(ip + i, hBitsS, mls);
             size_t const lgHash = ZSTD_hashPtr(ip + i, hBitsL, 8);
             if (i == 0)
-                hashSmall[smHash] = current + i;
+                hashSmall[smHash] = curr + i;
             if (i == 0 || hashLarge[lgHash] == 0)
-                hashLarge[lgHash] = current + i;
+                hashLarge[lgHash] = curr + i;
             /* Only load extra positions for ZSTD_dtlm_full */
             if (dtlm == ZSTD_dtlm_fast)
                 break;
@@ -108,9 +108,9 @@
     /* init */
     ip += (dictAndPrefixLength == 0);
     if (dictMode == ZSTD_noDict) {
-        U32 const current = (U32)(ip - base);
-        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog);
-        U32 const maxRep = current - windowLow;
+        U32 const curr = (U32)(ip - base);
+        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog);
+        U32 const maxRep = curr - windowLow;
         if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
         if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
     }
@@ -129,17 +129,17 @@
         size_t const h = ZSTD_hashPtr(ip, hBitsS, mls);
         size_t const dictHL = ZSTD_hashPtr(ip, dictHBitsL, 8);
         size_t const dictHS = ZSTD_hashPtr(ip, dictHBitsS, mls);
-        U32 const current = (U32)(ip-base);
+        U32 const curr = (U32)(ip-base);
         U32 const matchIndexL = hashLong[h2];
         U32 matchIndexS = hashSmall[h];
         const BYTE* matchLong = base + matchIndexL;
         const BYTE* match = base + matchIndexS;
-        const U32 repIndex = current + 1 - offset_1;
+        const U32 repIndex = curr + 1 - offset_1;
         const BYTE* repMatch = (dictMode == ZSTD_dictMatchState
                             && repIndex < prefixLowestIndex) ?
                                dictBase + (repIndex - dictIndexDelta) :
                                base + repIndex;
-        hashLong[h2] = hashSmall[h] = current;   /* update hash tables */
+        hashLong[h2] = hashSmall[h] = curr;   /* update hash tables */
 
         /* check dictMatchState repcode */
         if (dictMode == ZSTD_dictMatchState
@@ -177,7 +177,7 @@
 
             if (dictMatchL > dictStart && MEM_read64(dictMatchL) == MEM_read64(ip)) {
                 mLength = ZSTD_count_2segments(ip+8, dictMatchL+8, iend, dictEnd, prefixLowest) + 8;
-                offset = (U32)(current - dictMatchIndexL - dictIndexDelta);
+                offset = (U32)(curr - dictMatchIndexL - dictIndexDelta);
                 while (((ip>anchor) & (dictMatchL>dictStart)) && (ip[-1] == dictMatchL[-1])) { ip--; dictMatchL--; mLength++; } /* catch up */
                 goto _match_found;
         }   }
@@ -209,7 +209,7 @@
             size_t const dictHLNext = ZSTD_hashPtr(ip+1, dictHBitsL, 8);
             U32 const matchIndexL3 = hashLong[hl3];
             const BYTE* matchL3 = base + matchIndexL3;
-            hashLong[hl3] = current + 1;
+            hashLong[hl3] = curr + 1;
 
             /* check prefix long +1 match */
             if (matchIndexL3 > prefixLowestIndex) {
@@ -228,7 +228,7 @@
                 if (dictMatchL3 > dictStart && MEM_read64(dictMatchL3) == MEM_read64(ip+1)) {
                     mLength = ZSTD_count_2segments(ip+1+8, dictMatchL3+8, iend, dictEnd, prefixLowest) + 8;
                     ip++;
-                    offset = (U32)(current + 1 - dictMatchIndexL3 - dictIndexDelta);
+                    offset = (U32)(curr + 1 - dictMatchIndexL3 - dictIndexDelta);
                     while (((ip>anchor) & (dictMatchL3>dictStart)) && (ip[-1] == dictMatchL3[-1])) { ip--; dictMatchL3--; mLength++; } /* catch up */
                     goto _match_found;
         }   }   }
@@ -236,7 +236,7 @@
         /* if no long +1 match, explore the short match we found */
         if (dictMode == ZSTD_dictMatchState && matchIndexS < prefixLowestIndex) {
             mLength = ZSTD_count_2segments(ip+4, match+4, iend, dictEnd, prefixLowest) + 4;
-            offset = (U32)(current - matchIndexS);
+            offset = (U32)(curr - matchIndexS);
             while (((ip>anchor) & (match>dictStart)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; } /* catch up */
         } else {
             mLength = ZSTD_count(ip+4, match+4, iend) + 4;
@@ -260,7 +260,7 @@
         if (ip <= ilimit) {
             /* Complementary insertion */
             /* done after iLimit test, as candidates could be > iend-8 */
-            {   U32 const indexToInsert = current+2;
+            {   U32 const indexToInsert = curr+2;
                 hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;
                 hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
                 hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;
@@ -401,15 +401,15 @@
         const BYTE* const matchLongBase = matchLongIndex < prefixStartIndex ? dictBase : base;
         const BYTE* matchLong = matchLongBase + matchLongIndex;
 
-        const U32 current = (U32)(ip-base);
-        const U32 repIndex = current + 1 - offset_1;   /* offset_1 expected <= current +1 */
+        const U32 curr = (U32)(ip-base);
+        const U32 repIndex = curr + 1 - offset_1;   /* offset_1 expected <= curr +1 */
         const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
         const BYTE* const repMatch = repBase + repIndex;
         size_t mLength;
-        hashSmall[hSmall] = hashLong[hLong] = current;   /* update hash table */
+        hashSmall[hSmall] = hashLong[hLong] = curr;   /* update hash table */
 
         if ((((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex doesn't overlap dict + prefix */
-            & (repIndex > dictStartIndex))
+            & (offset_1 < curr+1 - dictStartIndex)) /* note: we are searching at curr+1 */
           && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
             const BYTE* repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
             mLength = ZSTD_count_2segments(ip+1+4, repMatch+4, iend, repMatchEnd, prefixStart) + 4;
@@ -421,7 +421,7 @@
                 const BYTE* const lowMatchPtr = matchLongIndex < prefixStartIndex ? dictStart : prefixStart;
                 U32 offset;
                 mLength = ZSTD_count_2segments(ip+8, matchLong+8, iend, matchEnd, prefixStart) + 8;
-                offset = current - matchLongIndex;
+                offset = curr - matchLongIndex;
                 while (((ip>anchor) & (matchLong>lowMatchPtr)) && (ip[-1] == matchLong[-1])) { ip--; matchLong--; mLength++; }   /* catch up */
                 offset_2 = offset_1;
                 offset_1 = offset;
@@ -433,19 +433,19 @@
                 const BYTE* const match3Base = matchIndex3 < prefixStartIndex ? dictBase : base;
                 const BYTE* match3 = match3Base + matchIndex3;
                 U32 offset;
-                hashLong[h3] = current + 1;
+                hashLong[h3] = curr + 1;
                 if ( (matchIndex3 > dictStartIndex) && (MEM_read64(match3) == MEM_read64(ip+1)) ) {
                     const BYTE* const matchEnd = matchIndex3 < prefixStartIndex ? dictEnd : iend;
                     const BYTE* const lowMatchPtr = matchIndex3 < prefixStartIndex ? dictStart : prefixStart;
                     mLength = ZSTD_count_2segments(ip+9, match3+8, iend, matchEnd, prefixStart) + 8;
                     ip++;
-                    offset = current+1 - matchIndex3;
+                    offset = curr+1 - matchIndex3;
                     while (((ip>anchor) & (match3>lowMatchPtr)) && (ip[-1] == match3[-1])) { ip--; match3--; mLength++; } /* catch up */
                 } else {
                     const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;
                     const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;
                     mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
-                    offset = current - matchIndex;
+                    offset = curr - matchIndex;
                     while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; }   /* catch up */
                 }
                 offset_2 = offset_1;
@@ -464,7 +464,7 @@
         if (ip <= ilimit) {
             /* Complementary insertion */
             /* done after iLimit test, as candidates could be > iend-8 */
-            {   U32 const indexToInsert = current+2;
+            {   U32 const indexToInsert = curr+2;
                 hashLong[ZSTD_hashPtr(base+indexToInsert, hBitsL, 8)] = indexToInsert;
                 hashLong[ZSTD_hashPtr(ip-2, hBitsL, 8)] = (U32)(ip-2-base);
                 hashSmall[ZSTD_hashPtr(base+indexToInsert, hBitsS, mls)] = indexToInsert;
@@ -477,7 +477,7 @@
                 U32 const repIndex2 = current2 - offset_2;
                 const BYTE* repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
                 if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3)   /* intentional overflow : ensure repIndex2 doesn't overlap dict + prefix */
-                    & (repIndex2 > dictStartIndex))
+                    & (offset_2 < current2 - dictStartIndex))
                   && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
                     const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
                     size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
diff --git a/Utilities/cmzstd/lib/compress/zstd_double_fast.h b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
index 14d944d..e16b7b0 100644
--- a/Utilities/cmzstd/lib/compress/zstd_double_fast.h
+++ b/Utilities/cmzstd/lib/compress/zstd_double_fast.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.c b/Utilities/cmzstd/lib/compress/zstd_fast.c
index 85a3a7a..4edc04d 100644
--- a/Utilities/cmzstd/lib/compress/zstd_fast.c
+++ b/Utilities/cmzstd/lib/compress/zstd_fast.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -29,16 +29,16 @@
      * Insert the other positions if their hash entry is empty.
      */
     for ( ; ip + fastHashFillStep < iend + 2; ip += fastHashFillStep) {
-        U32 const current = (U32)(ip - base);
+        U32 const curr = (U32)(ip - base);
         size_t const hash0 = ZSTD_hashPtr(ip, hBits, mls);
-        hashTable[hash0] = current;
+        hashTable[hash0] = curr;
         if (dtlm == ZSTD_dtlm_fast) continue;
         /* Only load extra positions for ZSTD_dtlm_full */
         {   U32 p;
             for (p = 1; p < fastHashFillStep; ++p) {
                 size_t const hash = ZSTD_hashPtr(ip + p, hBits, mls);
                 if (hashTable[hash] == 0) {  /* not yet filled */
-                    hashTable[hash] = current + p;
+                    hashTable[hash] = curr + p;
     }   }   }   }
 }
 
@@ -72,9 +72,9 @@
     DEBUGLOG(5, "ZSTD_compressBlock_fast_generic");
     ip0 += (ip0 == prefixStart);
     ip1 = ip0 + 1;
-    {   U32 const current = (U32)(ip0 - base);
-        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, cParams->windowLog);
-        U32 const maxRep = current - windowLow;
+    {   U32 const curr = (U32)(ip0 - base);
+        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, cParams->windowLog);
+        U32 const maxRep = curr - windowLow;
         if (offset_2 > maxRep) offsetSaved = offset_2, offset_2 = 0;
         if (offset_1 > maxRep) offsetSaved = offset_1, offset_1 = 0;
     }
@@ -242,7 +242,7 @@
     assert(endIndex - prefixStartIndex <= maxDistance);
     (void)maxDistance; (void)endIndex;   /* these variables are not used when assert() is disabled */
 
-    /* ensure there will be no no underflow
+    /* ensure there will be no underflow
      * when translating a dict index into a local index */
     assert(prefixStartIndex >= (U32)(dictEnd - dictBase));
 
@@ -258,14 +258,14 @@
     while (ip < ilimit) {   /* < instead of <=, because repcode check at (ip+1) */
         size_t mLength;
         size_t const h = ZSTD_hashPtr(ip, hlog, mls);
-        U32 const current = (U32)(ip-base);
+        U32 const curr = (U32)(ip-base);
         U32 const matchIndex = hashTable[h];
         const BYTE* match = base + matchIndex;
-        const U32 repIndex = current + 1 - offset_1;
+        const U32 repIndex = curr + 1 - offset_1;
         const BYTE* repMatch = (repIndex < prefixStartIndex) ?
                                dictBase + (repIndex - dictIndexDelta) :
                                base + repIndex;
-        hashTable[h] = current;   /* update hash table */
+        hashTable[h] = curr;   /* update hash table */
 
         if ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow : ensure repIndex isn't overlapping dict + prefix */
           && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
@@ -284,7 +284,7 @@
                 continue;
             } else {
                 /* found a dict match */
-                U32 const offset = (U32)(current-dictMatchIndex-dictIndexDelta);
+                U32 const offset = (U32)(curr-dictMatchIndex-dictIndexDelta);
                 mLength = ZSTD_count_2segments(ip+4, dictMatch+4, iend, dictEnd, prefixStart) + 4;
                 while (((ip>anchor) & (dictMatch>dictStart))
                      && (ip[-1] == dictMatch[-1])) {
@@ -316,8 +316,8 @@
 
         if (ip <= ilimit) {
             /* Fill Table */
-            assert(base+current+2 > istart);  /* check base overflow */
-            hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2;  /* here because current+2 could be > iend-8 */
+            assert(base+curr+2 > istart);  /* check base overflow */
+            hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2;  /* here because curr+2 could be > iend-8 */
             hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
 
             /* check immediate repcode */
@@ -410,15 +410,15 @@
         const U32    matchIndex = hashTable[h];
         const BYTE* const matchBase = matchIndex < prefixStartIndex ? dictBase : base;
         const BYTE*  match = matchBase + matchIndex;
-        const U32    current = (U32)(ip-base);
-        const U32    repIndex = current + 1 - offset_1;
+        const U32    curr = (U32)(ip-base);
+        const U32    repIndex = curr + 1 - offset_1;
         const BYTE* const repBase = repIndex < prefixStartIndex ? dictBase : base;
         const BYTE* const repMatch = repBase + repIndex;
-        hashTable[h] = current;   /* update hash table */
-        DEBUGLOG(7, "offset_1 = %u , current = %u", offset_1, current);
-        assert(offset_1 <= current +1);   /* check repIndex */
+        hashTable[h] = curr;   /* update hash table */
+        DEBUGLOG(7, "offset_1 = %u , curr = %u", offset_1, curr);
 
-        if ( (((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */ & (repIndex > dictStartIndex))
+        if ( ( ((U32)((prefixStartIndex-1) - repIndex) >= 3) /* intentional underflow */
+             & (offset_1 < curr+1 - dictStartIndex) ) /* note: we are searching at curr+1 */
            && (MEM_read32(repMatch) == MEM_read32(ip+1)) ) {
             const BYTE* const repMatchEnd = repIndex < prefixStartIndex ? dictEnd : iend;
             size_t const rLength = ZSTD_count_2segments(ip+1 +4, repMatch +4, iend, repMatchEnd, prefixStart) + 4;
@@ -435,7 +435,7 @@
             }
             {   const BYTE* const matchEnd = matchIndex < prefixStartIndex ? dictEnd : iend;
                 const BYTE* const lowMatchPtr = matchIndex < prefixStartIndex ? dictStart : prefixStart;
-                U32 const offset = current - matchIndex;
+                U32 const offset = curr - matchIndex;
                 size_t mLength = ZSTD_count_2segments(ip+4, match+4, iend, matchEnd, prefixStart) + 4;
                 while (((ip>anchor) & (match>lowMatchPtr)) && (ip[-1] == match[-1])) { ip--; match--; mLength++; }   /* catch up */
                 offset_2 = offset_1; offset_1 = offset;  /* update offset history */
@@ -446,14 +446,14 @@
 
         if (ip <= ilimit) {
             /* Fill Table */
-            hashTable[ZSTD_hashPtr(base+current+2, hlog, mls)] = current+2;
+            hashTable[ZSTD_hashPtr(base+curr+2, hlog, mls)] = curr+2;
             hashTable[ZSTD_hashPtr(ip-2, hlog, mls)] = (U32)(ip-2-base);
             /* check immediate repcode */
             while (ip <= ilimit) {
                 U32 const current2 = (U32)(ip-base);
                 U32 const repIndex2 = current2 - offset_2;
                 const BYTE* const repMatch2 = repIndex2 < prefixStartIndex ? dictBase + repIndex2 : base + repIndex2;
-                if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (repIndex2 > dictStartIndex))  /* intentional overflow */
+                if ( (((U32)((prefixStartIndex-1) - repIndex2) >= 3) & (offset_2 < curr - dictStartIndex))  /* intentional overflow */
                    && (MEM_read32(repMatch2) == MEM_read32(ip)) ) {
                     const BYTE* const repEnd2 = repIndex2 < prefixStartIndex ? dictEnd : iend;
                     size_t const repLength2 = ZSTD_count_2segments(ip+4, repMatch2+4, iend, repEnd2, prefixStart) + 4;
diff --git a/Utilities/cmzstd/lib/compress/zstd_fast.h b/Utilities/cmzstd/lib/compress/zstd_fast.h
index cf6aaa8..0d4a0c1 100644
--- a/Utilities/cmzstd/lib/compress/zstd_fast.h
+++ b/Utilities/cmzstd/lib/compress/zstd_fast.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.c b/Utilities/cmzstd/lib/compress/zstd_lazy.c
index 4cf5c88..6ce897d 100644
--- a/Utilities/cmzstd/lib/compress/zstd_lazy.c
+++ b/Utilities/cmzstd/lib/compress/zstd_lazy.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -58,11 +58,11 @@
 
 /** ZSTD_insertDUBT1() :
  *  sort one already inserted but unsorted position
- *  assumption : current >= btlow == (current - btmask)
+ *  assumption : curr >= btlow == (curr - btmask)
  *  doesn't fail */
 static void
 ZSTD_insertDUBT1(ZSTD_matchState_t* ms,
-                 U32 current, const BYTE* inputEnd,
+                 U32 curr, const BYTE* inputEnd,
                  U32 nbCompares, U32 btLow,
                  const ZSTD_dictMode_e dictMode)
 {
@@ -74,41 +74,41 @@
     const BYTE* const base = ms->window.base;
     const BYTE* const dictBase = ms->window.dictBase;
     const U32 dictLimit = ms->window.dictLimit;
-    const BYTE* const ip = (current>=dictLimit) ? base + current : dictBase + current;
-    const BYTE* const iend = (current>=dictLimit) ? inputEnd : dictBase + dictLimit;
+    const BYTE* const ip = (curr>=dictLimit) ? base + curr : dictBase + curr;
+    const BYTE* const iend = (curr>=dictLimit) ? inputEnd : dictBase + dictLimit;
     const BYTE* const dictEnd = dictBase + dictLimit;
     const BYTE* const prefixStart = base + dictLimit;
     const BYTE* match;
-    U32* smallerPtr = bt + 2*(current&btMask);
+    U32* smallerPtr = bt + 2*(curr&btMask);
     U32* largerPtr  = smallerPtr + 1;
     U32 matchIndex = *smallerPtr;   /* this candidate is unsorted : next sorted candidate is reached through *smallerPtr, while *largerPtr contains previous unsorted candidate (which is already saved and can be overwritten) */
     U32 dummy32;   /* to be nullified at the end */
     U32 const windowValid = ms->window.lowLimit;
     U32 const maxDistance = 1U << cParams->windowLog;
-    U32 const windowLow = (current - windowValid > maxDistance) ? current - maxDistance : windowValid;
+    U32 const windowLow = (curr - windowValid > maxDistance) ? curr - maxDistance : windowValid;
 
 
     DEBUGLOG(8, "ZSTD_insertDUBT1(%u) (dictLimit=%u, lowLimit=%u)",
-                current, dictLimit, windowLow);
-    assert(current >= btLow);
+                curr, dictLimit, windowLow);
+    assert(curr >= btLow);
     assert(ip < iend);   /* condition for ZSTD_count */
 
     while (nbCompares-- && (matchIndex > windowLow)) {
         U32* const nextPtr = bt + 2*(matchIndex & btMask);
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
-        assert(matchIndex < current);
+        assert(matchIndex < curr);
         /* note : all candidates are now supposed sorted,
          * but it's still possible to have nextPtr[1] == ZSTD_DUBT_UNSORTED_MARK
          * when a real index has the same value as ZSTD_DUBT_UNSORTED_MARK */
 
         if ( (dictMode != ZSTD_extDict)
           || (matchIndex+matchLength >= dictLimit)  /* both in current segment*/
-          || (current < dictLimit) /* both in extDict */) {
+          || (curr < dictLimit) /* both in extDict */) {
             const BYTE* const mBase = ( (dictMode != ZSTD_extDict)
                                      || (matchIndex+matchLength >= dictLimit)) ?
                                         base : dictBase;
             assert( (matchIndex+matchLength >= dictLimit)   /* might be wrong if extDict is incorrectly set to 0 */
-                 || (current < dictLimit) );
+                 || (curr < dictLimit) );
             match = mBase + matchIndex;
             matchLength += ZSTD_count(ip+matchLength, match+matchLength, iend);
         } else {
@@ -119,7 +119,7 @@
         }
 
         DEBUGLOG(8, "ZSTD_insertDUBT1: comparing %u with %u : found %u common bytes ",
-                    current, matchIndex, (U32)matchLength);
+                    curr, matchIndex, (U32)matchLength);
 
         if (ip+matchLength == iend) {   /* equal : no way to know if inf or sup */
             break;   /* drop , to guarantee consistency ; miss a bit of compression, but other solutions can corrupt tree */
@@ -168,7 +168,7 @@
 
     const BYTE* const base = ms->window.base;
     const BYTE* const prefixStart = base + ms->window.dictLimit;
-    U32         const current = (U32)(ip-base);
+    U32         const curr = (U32)(ip-base);
     const BYTE* const dictBase = dms->window.base;
     const BYTE* const dictEnd = dms->window.nextSrc;
     U32         const dictHighLimit = (U32)(dms->window.nextSrc - dms->window.base);
@@ -195,10 +195,10 @@
 
         if (matchLength > bestLength) {
             U32 matchIndex = dictMatchIndex + dictIndexDelta;
-            if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) {
+            if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) ) {
                 DEBUGLOG(9, "ZSTD_DUBT_findBetterDictMatch(%u) : found better match length %u -> %u and offsetCode %u -> %u (dictMatchIndex %u, matchIndex %u)",
-                    current, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, ZSTD_REP_MOVE + current - matchIndex, dictMatchIndex, matchIndex);
-                bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex;
+                    curr, (U32)bestLength, (U32)matchLength, (U32)*offsetPtr, ZSTD_REP_MOVE + curr - matchIndex, dictMatchIndex, matchIndex);
+                bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + curr - matchIndex;
             }
             if (ip+matchLength == iend) {   /* reached end of input : ip[matchLength] is not valid, no way to know if it's larger or smaller than match */
                 break;   /* drop, to guarantee consistency (miss a little bit of compression) */
@@ -218,9 +218,9 @@
     }
 
     if (bestLength >= MINMATCH) {
-        U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+        U32 const mIndex = curr - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
         DEBUGLOG(8, "ZSTD_DUBT_findBetterDictMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
-                    current, (U32)bestLength, (U32)*offsetPtr, mIndex);
+                    curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
     }
     return bestLength;
 
@@ -241,13 +241,13 @@
     U32          matchIndex  = hashTable[h];
 
     const BYTE* const base = ms->window.base;
-    U32    const current = (U32)(ip-base);
-    U32    const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog);
+    U32    const curr = (U32)(ip-base);
+    U32    const windowLow = ZSTD_getLowestMatchIndex(ms, curr, cParams->windowLog);
 
     U32*   const bt = ms->chainTable;
     U32    const btLog  = cParams->chainLog - 1;
     U32    const btMask = (1 << btLog) - 1;
-    U32    const btLow = (btMask >= current) ? 0 : current - btMask;
+    U32    const btLow = (btMask >= curr) ? 0 : curr - btMask;
     U32    const unsortLimit = MAX(btLow, windowLow);
 
     U32*         nextCandidate = bt + 2*(matchIndex&btMask);
@@ -256,8 +256,9 @@
     U32          nbCandidates = nbCompares;
     U32          previousCandidate = 0;
 
-    DEBUGLOG(7, "ZSTD_DUBT_findBestMatch (%u) ", current);
+    DEBUGLOG(7, "ZSTD_DUBT_findBestMatch (%u) ", curr);
     assert(ip <= iend-8);   /* required for h calculation */
+    assert(dictMode != ZSTD_dedicatedDictSearch);
 
     /* reach end of unsorted candidates list */
     while ( (matchIndex > unsortLimit)
@@ -299,14 +300,14 @@
         const U32 dictLimit = ms->window.dictLimit;
         const BYTE* const dictEnd = dictBase + dictLimit;
         const BYTE* const prefixStart = base + dictLimit;
-        U32* smallerPtr = bt + 2*(current&btMask);
-        U32* largerPtr  = bt + 2*(current&btMask) + 1;
-        U32 matchEndIdx = current + 8 + 1;
+        U32* smallerPtr = bt + 2*(curr&btMask);
+        U32* largerPtr  = bt + 2*(curr&btMask) + 1;
+        U32 matchEndIdx = curr + 8 + 1;
         U32 dummy32;   /* to be nullified at the end */
         size_t bestLength = 0;
 
         matchIndex  = hashTable[h];
-        hashTable[h] = current;   /* Update Hash Table */
+        hashTable[h] = curr;   /* Update Hash Table */
 
         while (nbCompares-- && (matchIndex > windowLow)) {
             U32* const nextPtr = bt + 2*(matchIndex & btMask);
@@ -326,8 +327,8 @@
             if (matchLength > bestLength) {
                 if (matchLength > matchEndIdx - matchIndex)
                     matchEndIdx = matchIndex + (U32)matchLength;
-                if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(current-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
-                    bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + current - matchIndex;
+                if ( (4*(int)(matchLength-bestLength)) > (int)(ZSTD_highbit32(curr-matchIndex+1) - ZSTD_highbit32((U32)offsetPtr[0]+1)) )
+                    bestLength = matchLength, *offsetPtr = ZSTD_REP_MOVE + curr - matchIndex;
                 if (ip+matchLength == iend) {   /* equal : no way to know if inf or sup */
                     if (dictMode == ZSTD_dictMatchState) {
                         nbCompares = 0; /* in addition to avoiding checking any
@@ -363,12 +364,12 @@
                     mls, dictMode);
         }
 
-        assert(matchEndIdx > current+8); /* ensure nextToUpdate is increased */
+        assert(matchEndIdx > curr+8); /* ensure nextToUpdate is increased */
         ms->nextToUpdate = matchEndIdx - 8;   /* skip repetitive patterns */
         if (bestLength >= MINMATCH) {
-            U32 const mIndex = current - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
+            U32 const mIndex = curr - ((U32)*offsetPtr - ZSTD_REP_MOVE); (void)mIndex;
             DEBUGLOG(8, "ZSTD_DUBT_findBestMatch(%u) : found match of length %u and offsetCode %u (pos %u)",
-                        current, (U32)bestLength, (U32)*offsetPtr, mIndex);
+                        curr, (U32)bestLength, (U32)*offsetPtr, mIndex);
         }
         return bestLength;
     }
@@ -437,6 +438,220 @@
     }
 }
 
+/***********************************
+* Dedicated dict search
+***********************************/
+
+void ZSTD_dedicatedDictSearch_lazy_loadDictionary(ZSTD_matchState_t* ms, const BYTE* const ip)
+{
+    const BYTE* const base = ms->window.base;
+    U32 const target = (U32)(ip - base);
+    U32* const hashTable = ms->hashTable;
+    U32* const chainTable = ms->chainTable;
+    U32 const chainSize = 1 << ms->cParams.chainLog;
+    U32 idx = ms->nextToUpdate;
+    U32 const minChain = chainSize < target ? target - chainSize : idx;
+    U32 const bucketSize = 1 << ZSTD_LAZY_DDSS_BUCKET_LOG;
+    U32 const cacheSize = bucketSize - 1;
+    U32 const chainAttempts = (1 << ms->cParams.searchLog) - cacheSize;
+    U32 const chainLimit = chainAttempts > 255 ? 255 : chainAttempts;
+
+    /* We know the hashtable is oversized by a factor of `bucketSize`.
+     * We are going to temporarily pretend `bucketSize == 1`, keeping only a
+     * single entry. We will use the rest of the space to construct a temporary
+     * chaintable.
+     */
+    U32 const hashLog = ms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG;
+    U32* const tmpHashTable = hashTable;
+    U32* const tmpChainTable = hashTable + ((size_t)1 << hashLog);
+    U32 const tmpChainSize = ((1 << ZSTD_LAZY_DDSS_BUCKET_LOG) - 1) << hashLog;
+    U32 const tmpMinChain = tmpChainSize < target ? target - tmpChainSize : idx;
+    U32 hashIdx;
+
+    assert(ms->cParams.chainLog <= 24);
+    assert(ms->cParams.hashLog > ms->cParams.chainLog);
+    assert(idx != 0);
+    assert(tmpMinChain <= minChain);
+
+    /* fill conventional hash table and conventional chain table */
+    for ( ; idx < target; idx++) {
+        U32 const h = (U32)ZSTD_hashPtr(base + idx, hashLog, ms->cParams.minMatch);
+        if (idx >= tmpMinChain) {
+            tmpChainTable[idx - tmpMinChain] = hashTable[h];
+        }
+        tmpHashTable[h] = idx;
+    }
+
+    /* sort chains into ddss chain table */
+    {
+        U32 chainPos = 0;
+        for (hashIdx = 0; hashIdx < (1U << hashLog); hashIdx++) {
+            U32 count;
+            U32 countBeyondMinChain = 0;
+            U32 i = tmpHashTable[hashIdx];
+            for (count = 0; i >= tmpMinChain && count < cacheSize; count++) {
+                /* skip through the chain to the first position that won't be
+                 * in the hash cache bucket */
+                if (i < minChain) {
+                    countBeyondMinChain++;
+                }
+                i = tmpChainTable[i - tmpMinChain];
+            }
+            if (count == cacheSize) {
+                for (count = 0; count < chainLimit;) {
+                    if (i < minChain) {
+                        if (!i || ++countBeyondMinChain > cacheSize) {
+                            /* only allow pulling `cacheSize` number of entries
+                             * into the cache or chainTable beyond `minChain`,
+                             * to replace the entries pulled out of the
+                             * chainTable into the cache. This lets us reach
+                             * back further without increasing the total number
+                             * of entries in the chainTable, guaranteeing the
+                             * DDSS chain table will fit into the space
+                             * allocated for the regular one. */
+                            break;
+                        }
+                    }
+                    chainTable[chainPos++] = i;
+                    count++;
+                    if (i < tmpMinChain) {
+                        break;
+                    }
+                    i = tmpChainTable[i - tmpMinChain];
+                }
+            } else {
+                count = 0;
+            }
+            if (count) {
+                tmpHashTable[hashIdx] = ((chainPos - count) << 8) + count;
+            } else {
+                tmpHashTable[hashIdx] = 0;
+            }
+        }
+        assert(chainPos <= chainSize); /* I believe this is guaranteed... */
+    }
+
+    /* move chain pointers into the last entry of each hash bucket */
+    for (hashIdx = (1 << hashLog); hashIdx; ) {
+        U32 const bucketIdx = --hashIdx << ZSTD_LAZY_DDSS_BUCKET_LOG;
+        U32 const chainPackedPointer = tmpHashTable[hashIdx];
+        U32 i;
+        for (i = 0; i < cacheSize; i++) {
+            hashTable[bucketIdx + i] = 0;
+        }
+        hashTable[bucketIdx + bucketSize - 1] = chainPackedPointer;
+    }
+
+    /* fill the buckets of the hash table */
+    for (idx = ms->nextToUpdate; idx < target; idx++) {
+        U32 const h = (U32)ZSTD_hashPtr(base + idx, hashLog, ms->cParams.minMatch)
+                   << ZSTD_LAZY_DDSS_BUCKET_LOG;
+        U32 i;
+        /* Shift hash cache down 1. */
+        for (i = cacheSize - 1; i; i--)
+            hashTable[h + i] = hashTable[h + i - 1];
+        hashTable[h] = idx;
+    }
+
+    ms->nextToUpdate = target;
+}
+
+/* Returns the longest match length found in the dedicated dict search structure.
+ * If none are longer than the argument ml, then ml will be returned.
+ */
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_dedicatedDictSearch_lazy_search(size_t* offsetPtr, size_t ml, U32 nbAttempts,
+                                            const ZSTD_matchState_t* const dms,
+                                            const BYTE* const ip, const BYTE* const iLimit,
+                                            const BYTE* const prefixStart, const U32 curr,
+                                            const U32 dictLimit, const size_t ddsIdx) {
+    const U32 ddsLowestIndex  = dms->window.dictLimit;
+    const BYTE* const ddsBase = dms->window.base;
+    const BYTE* const ddsEnd  = dms->window.nextSrc;
+    const U32 ddsSize         = (U32)(ddsEnd - ddsBase);
+    const U32 ddsIndexDelta   = dictLimit - ddsSize;
+    const U32 bucketSize      = (1 << ZSTD_LAZY_DDSS_BUCKET_LOG);
+    const U32 bucketLimit     = nbAttempts < bucketSize - 1 ? nbAttempts : bucketSize - 1;
+    U32 ddsAttempt;
+    U32 matchIndex;
+
+    for (ddsAttempt = 0; ddsAttempt < bucketSize - 1; ddsAttempt++) {
+        PREFETCH_L1(ddsBase + dms->hashTable[ddsIdx + ddsAttempt]);
+    }
+
+    {
+        U32 const chainPackedPointer = dms->hashTable[ddsIdx + bucketSize - 1];
+        U32 const chainIndex = chainPackedPointer >> 8;
+
+        PREFETCH_L1(&dms->chainTable[chainIndex]);
+    }
+
+    for (ddsAttempt = 0; ddsAttempt < bucketLimit; ddsAttempt++) {
+        size_t currentMl=0;
+        const BYTE* match;
+        matchIndex = dms->hashTable[ddsIdx + ddsAttempt];
+        match = ddsBase + matchIndex;
+
+        if (!matchIndex) {
+            return ml;
+        }
+
+        /* guaranteed by table construction */
+        (void)ddsLowestIndex;
+        assert(matchIndex >= ddsLowestIndex);
+        assert(match+4 <= ddsEnd);
+        if (MEM_read32(match) == MEM_read32(ip)) {
+            /* assumption : matchIndex <= dictLimit-4 (by table construction) */
+            currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, ddsEnd, prefixStart) + 4;
+        }
+
+        /* save best solution */
+        if (currentMl > ml) {
+            ml = currentMl;
+            *offsetPtr = curr - (matchIndex + ddsIndexDelta) + ZSTD_REP_MOVE;
+            if (ip+currentMl == iLimit) {
+                /* best possible, avoids read overflow on next attempt */
+                return ml;
+            }
+        }
+    }
+
+    {
+        U32 const chainPackedPointer = dms->hashTable[ddsIdx + bucketSize - 1];
+        U32 chainIndex = chainPackedPointer >> 8;
+        U32 const chainLength = chainPackedPointer & 0xFF;
+        U32 const chainAttempts = nbAttempts - ddsAttempt;
+        U32 const chainLimit = chainAttempts > chainLength ? chainLength : chainAttempts;
+        U32 chainAttempt;
+
+        for (chainAttempt = 0 ; chainAttempt < chainLimit; chainAttempt++) {
+            PREFETCH_L1(ddsBase + dms->chainTable[chainIndex + chainAttempt]);
+        }
+
+        for (chainAttempt = 0 ; chainAttempt < chainLimit; chainAttempt++, chainIndex++) {
+            size_t currentMl=0;
+            const BYTE* match;
+            matchIndex = dms->chainTable[chainIndex];
+            match = ddsBase + matchIndex;
+
+            /* guaranteed by table construction */
+            assert(matchIndex >= ddsLowestIndex);
+            assert(match+4 <= ddsEnd);
+            if (MEM_read32(match) == MEM_read32(ip)) {
+                /* assumption : matchIndex <= dictLimit-4 (by table construction) */
+                currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, ddsEnd, prefixStart) + 4;
+            }
+
+            /* save best solution */
+            if (currentMl > ml) {
+                ml = currentMl;
+                *offsetPtr = curr - (matchIndex + ddsIndexDelta) + ZSTD_REP_MOVE;
+                if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
+            }
+        }
+    }
+    return ml;
+}
 
 
 /* *********************************
@@ -446,7 +661,7 @@
 
 /* Update chains up to ip (excluded)
    Assumption : always within prefix (i.e. not within extDict) */
-static U32 ZSTD_insertAndFindFirstIndex_internal(
+FORCE_INLINE_TEMPLATE U32 ZSTD_insertAndFindFirstIndex_internal(
                         ZSTD_matchState_t* ms,
                         const ZSTD_compressionParameters* const cParams,
                         const BYTE* ip, U32 const mls)
@@ -475,7 +690,6 @@
     return ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, ms->cParams.minMatch);
 }
 
-
 /* inlining is important to hardwire a hot branch (template emulation) */
 FORCE_INLINE_TEMPLATE
 size_t ZSTD_HcFindBestMatch_generic (
@@ -493,20 +707,33 @@
     const U32 dictLimit = ms->window.dictLimit;
     const BYTE* const prefixStart = base + dictLimit;
     const BYTE* const dictEnd = dictBase + dictLimit;
-    const U32 current = (U32)(ip-base);
+    const U32 curr = (U32)(ip-base);
     const U32 maxDistance = 1U << cParams->windowLog;
     const U32 lowestValid = ms->window.lowLimit;
-    const U32 withinMaxDistance = (current - lowestValid > maxDistance) ? current - maxDistance : lowestValid;
+    const U32 withinMaxDistance = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid;
     const U32 isDictionary = (ms->loadedDictEnd != 0);
     const U32 lowLimit = isDictionary ? lowestValid : withinMaxDistance;
-    const U32 minChain = current > chainSize ? current - chainSize : 0;
+    const U32 minChain = curr > chainSize ? curr - chainSize : 0;
     U32 nbAttempts = 1U << cParams->searchLog;
     size_t ml=4-1;
 
-    /* HC4 match finder */
-    U32 matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls);
+    const ZSTD_matchState_t* const dms = ms->dictMatchState;
+    const U32 ddsHashLog = dictMode == ZSTD_dedicatedDictSearch
+                         ? dms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG : 0;
+    const size_t ddsIdx = dictMode == ZSTD_dedicatedDictSearch
+                        ? ZSTD_hashPtr(ip, ddsHashLog, mls) << ZSTD_LAZY_DDSS_BUCKET_LOG : 0;
 
-    for ( ; (matchIndex>lowLimit) & (nbAttempts>0) ; nbAttempts--) {
+    U32 matchIndex;
+
+    if (dictMode == ZSTD_dedicatedDictSearch) {
+        const U32* entry = &dms->hashTable[ddsIdx];
+        PREFETCH_L1(entry);
+    }
+
+    /* HC4 match finder */
+    matchIndex = ZSTD_insertAndFindFirstIndex_internal(ms, cParams, ip, mls);
+
+    for ( ; (matchIndex>=lowLimit) & (nbAttempts>0) ; nbAttempts--) {
         size_t currentMl=0;
         if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) {
             const BYTE* const match = base + matchIndex;
@@ -523,7 +750,7 @@
         /* save best solution */
         if (currentMl > ml) {
             ml = currentMl;
-            *offsetPtr = current - matchIndex + ZSTD_REP_MOVE;
+            *offsetPtr = curr - matchIndex + ZSTD_REP_MOVE;
             if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
         }
 
@@ -531,8 +758,10 @@
         matchIndex = NEXT_IN_CHAIN(matchIndex, chainMask);
     }
 
-    if (dictMode == ZSTD_dictMatchState) {
-        const ZSTD_matchState_t* const dms = ms->dictMatchState;
+    if (dictMode == ZSTD_dedicatedDictSearch) {
+        ml = ZSTD_dedicatedDictSearch_lazy_search(offsetPtr, ml, nbAttempts, dms,
+                                                  ip, iLimit, prefixStart, curr, dictLimit, ddsIdx);
+    } else if (dictMode == ZSTD_dictMatchState) {
         const U32* const dmsChainTable = dms->chainTable;
         const U32 dmsChainSize         = (1 << dms->cParams.chainLog);
         const U32 dmsChainMask         = dmsChainSize - 1;
@@ -545,7 +774,7 @@
 
         matchIndex = dms->hashTable[ZSTD_hashPtr(ip, dms->cParams.hashLog, mls)];
 
-        for ( ; (matchIndex>dmsLowestIndex) & (nbAttempts>0) ; nbAttempts--) {
+        for ( ; (matchIndex>=dmsLowestIndex) & (nbAttempts>0) ; nbAttempts--) {
             size_t currentMl=0;
             const BYTE* const match = dmsBase + matchIndex;
             assert(match+4 <= dmsEnd);
@@ -555,11 +784,12 @@
             /* save best solution */
             if (currentMl > ml) {
                 ml = currentMl;
-                *offsetPtr = current - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE;
+                *offsetPtr = curr - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE;
                 if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
             }
 
             if (matchIndex <= dmsMinChain) break;
+
             matchIndex = dmsChainTable[matchIndex & dmsChainMask];
         }
     }
@@ -600,6 +830,22 @@
 }
 
 
+static size_t ZSTD_HcFindBestMatch_dedicatedDictSearch_selectMLS (
+                        ZSTD_matchState_t* ms,
+                        const BYTE* ip, const BYTE* const iLimit,
+                        size_t* offsetPtr)
+{
+    switch(ms->cParams.minMatch)
+    {
+    default : /* includes case 3 */
+    case 4 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, ZSTD_dedicatedDictSearch);
+    case 5 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, ZSTD_dedicatedDictSearch);
+    case 7 :
+    case 6 : return ZSTD_HcFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, ZSTD_dedicatedDictSearch);
+    }
+}
+
+
 FORCE_INLINE_TEMPLATE size_t ZSTD_HcFindBestMatch_extDict_selectMLS (
                         ZSTD_matchState_t* ms,
                         const BYTE* ip, const BYTE* const iLimit,
@@ -615,11 +861,658 @@
     }
 }
 
+/* *********************************
+* (SIMD) Row-based matchfinder
+***********************************/
+/* Constants for row-based hash */
+#define ZSTD_ROW_HASH_TAG_OFFSET 1                               /* byte offset of hashes in the match state's tagTable from the beginning of a row */
+#define ZSTD_ROW_HASH_TAG_BITS 8                                 /* nb bits to use for the tag */
+#define ZSTD_ROW_HASH_TAG_MASK ((1u << ZSTD_ROW_HASH_TAG_BITS) - 1)
+
+#define ZSTD_ROW_HASH_CACHE_MASK (ZSTD_ROW_HASH_CACHE_SIZE - 1)
+
+typedef U32 ZSTD_VecMask;   /* Clarifies when we are interacting with a U32 representing a mask of matches */
+
+#if !defined(ZSTD_NO_INTRINSICS) && defined(__SSE2__) /* SIMD SSE version */
+
+#include <emmintrin.h>
+typedef __m128i ZSTD_Vec128;
+
+/* Returns a 128-bit container with 128-bits from src */
+static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) {
+  return _mm_loadu_si128((ZSTD_Vec128 const*)src);
+}
+
+/* Returns a ZSTD_Vec128 with the byte "val" packed 16 times */
+static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) {
+  return _mm_set1_epi8((char)val);
+}
+
+/* Do byte-by-byte comparison result of x and y. Then collapse 128-bit resultant mask
+ * into a 32-bit mask that is the MSB of each byte.
+ * */
+static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) {
+  return (ZSTD_VecMask)_mm_movemask_epi8(_mm_cmpeq_epi8(x, y));
+}
+
+typedef struct {
+  __m128i fst;
+  __m128i snd;
+} ZSTD_Vec256;
+
+static ZSTD_Vec256 ZSTD_Vec256_read(const void* const ptr) {
+  ZSTD_Vec256 v;
+  v.fst = ZSTD_Vec128_read(ptr);
+  v.snd = ZSTD_Vec128_read((ZSTD_Vec128 const*)ptr + 1);
+  return v;
+}
+
+static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) {
+  ZSTD_Vec256 v;
+  v.fst = ZSTD_Vec128_set8(val);
+  v.snd = ZSTD_Vec128_set8(val);
+  return v;
+}
+
+static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) {
+  ZSTD_VecMask fstMask;
+  ZSTD_VecMask sndMask;
+  fstMask = ZSTD_Vec128_cmpMask8(x.fst, y.fst);
+  sndMask = ZSTD_Vec128_cmpMask8(x.snd, y.snd);
+  return fstMask | (sndMask << 16);
+}
+
+#elif !defined(ZSTD_NO_INTRINSICS) && defined(__ARM_NEON) /* SIMD ARM NEON Version */
+
+#include <arm_neon.h>
+typedef uint8x16_t ZSTD_Vec128;
+
+static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) {
+  return vld1q_u8((const BYTE* const)src);
+}
+
+static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) {
+  return vdupq_n_u8(val);
+}
+
+/* Mimics '_mm_movemask_epi8()' from SSE */
+static U32 ZSTD_vmovmaskq_u8(ZSTD_Vec128 val) {
+    /* Shift out everything but the MSB bits in each byte */
+    uint16x8_t highBits = vreinterpretq_u16_u8(vshrq_n_u8(val, 7));
+    /* Merge the even lanes together with vsra (right shift and add) */
+    uint32x4_t paired16 = vreinterpretq_u32_u16(vsraq_n_u16(highBits, highBits, 7));
+    uint64x2_t paired32 = vreinterpretq_u64_u32(vsraq_n_u32(paired16, paired16, 14));
+    uint8x16_t paired64 = vreinterpretq_u8_u64(vsraq_n_u64(paired32, paired32, 28));
+    /* Extract the low 8 bits from each lane, merge */
+    return vgetq_lane_u8(paired64, 0) | ((U32)vgetq_lane_u8(paired64, 8) << 8);
+}
+
+static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) {
+  return (ZSTD_VecMask)ZSTD_vmovmaskq_u8(vceqq_u8(x, y));
+}
+
+typedef struct {
+    uint8x16_t fst;
+    uint8x16_t snd;
+} ZSTD_Vec256;
+
+static ZSTD_Vec256 ZSTD_Vec256_read(const void* const ptr) {
+  ZSTD_Vec256 v;
+  v.fst = ZSTD_Vec128_read(ptr);
+  v.snd = ZSTD_Vec128_read((ZSTD_Vec128 const*)ptr + 1);
+  return v;
+}
+
+static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) {
+  ZSTD_Vec256 v;
+  v.fst = ZSTD_Vec128_set8(val);
+  v.snd = ZSTD_Vec128_set8(val);
+  return v;
+}
+
+static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) {
+  ZSTD_VecMask fstMask;
+  ZSTD_VecMask sndMask;
+  fstMask = ZSTD_Vec128_cmpMask8(x.fst, y.fst);
+  sndMask = ZSTD_Vec128_cmpMask8(x.snd, y.snd);
+  return fstMask | (sndMask << 16);
+}
+
+#else /* Scalar fallback version */
+
+#define VEC128_NB_SIZE_T (16 / sizeof(size_t))
+typedef struct {
+    size_t vec[VEC128_NB_SIZE_T];
+} ZSTD_Vec128;
+
+static ZSTD_Vec128 ZSTD_Vec128_read(const void* const src) {
+    ZSTD_Vec128 ret;
+    ZSTD_memcpy(ret.vec, src, VEC128_NB_SIZE_T*sizeof(size_t));
+    return ret;
+}
+
+static ZSTD_Vec128 ZSTD_Vec128_set8(BYTE val) {
+    ZSTD_Vec128 ret = { {0} };
+    int startBit = sizeof(size_t) * 8 - 8;
+    for (;startBit >= 0; startBit -= 8) {
+        unsigned j = 0;
+        for (;j < VEC128_NB_SIZE_T; ++j) {
+            ret.vec[j] |= ((size_t)val << startBit);
+        }
+    }
+    return ret;
+}
+
+/* Compare x to y, byte by byte, generating a "matches" bitfield */
+static ZSTD_VecMask ZSTD_Vec128_cmpMask8(ZSTD_Vec128 x, ZSTD_Vec128 y) {
+    ZSTD_VecMask res = 0;
+    unsigned i = 0;
+    unsigned l = 0;
+    for (; i < VEC128_NB_SIZE_T; ++i) {
+        const size_t cmp1 = x.vec[i];
+        const size_t cmp2 = y.vec[i];
+        unsigned j = 0;
+        for (; j < sizeof(size_t); ++j, ++l) {
+            if (((cmp1 >> j*8) & 0xFF) == ((cmp2 >> j*8) & 0xFF)) {
+                res |= ((U32)1 << (j+i*sizeof(size_t)));
+            }
+        }
+    }
+    return res;
+}
+
+#define VEC256_NB_SIZE_T 2*VEC128_NB_SIZE_T
+typedef struct {
+    size_t vec[VEC256_NB_SIZE_T];
+} ZSTD_Vec256;
+
+static ZSTD_Vec256 ZSTD_Vec256_read(const void* const src) {
+    ZSTD_Vec256 ret;
+    ZSTD_memcpy(ret.vec, src, VEC256_NB_SIZE_T*sizeof(size_t));
+    return ret;
+}
+
+static ZSTD_Vec256 ZSTD_Vec256_set8(BYTE val) {
+    ZSTD_Vec256 ret = { {0} };
+    int startBit = sizeof(size_t) * 8 - 8;
+    for (;startBit >= 0; startBit -= 8) {
+        unsigned j = 0;
+        for (;j < VEC256_NB_SIZE_T; ++j) {
+            ret.vec[j] |= ((size_t)val << startBit);
+        }
+    }
+    return ret;
+}
+
+/* Compare x to y, byte by byte, generating a "matches" bitfield */
+static ZSTD_VecMask ZSTD_Vec256_cmpMask8(ZSTD_Vec256 x, ZSTD_Vec256 y) {
+    ZSTD_VecMask res = 0;
+    unsigned i = 0;
+    unsigned l = 0;
+    for (; i < VEC256_NB_SIZE_T; ++i) {
+        const size_t cmp1 = x.vec[i];
+        const size_t cmp2 = y.vec[i];
+        unsigned j = 0;
+        for (; j < sizeof(size_t); ++j, ++l) {
+            if (((cmp1 >> j*8) & 0xFF) == ((cmp2 >> j*8) & 0xFF)) {
+                res |= ((U32)1 << (j+i*sizeof(size_t)));
+            }
+        }
+    }
+    return res;
+}
+
+#endif /* !defined(ZSTD_NO_INTRINSICS) && defined(__SSE2__) */
+
+/* ZSTD_VecMask_next():
+ * Starting from the LSB, returns the idx of the next non-zero bit.
+ * Basically counting the nb of trailing zeroes.
+ */
+static U32 ZSTD_VecMask_next(ZSTD_VecMask val) {
+#   if defined(_MSC_VER)   /* Visual */
+    unsigned long r=0;
+    return _BitScanForward(&r, val) ? (U32)r : 0;
+#   elif defined(__GNUC__) && (__GNUC__ >= 3)
+    return (U32)__builtin_ctz(val);
+#   else
+    /* Software ctz version: http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup */
+    static const U32 multiplyDeBruijnBitPosition[32] =
+    {
+        0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
+		31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+    };
+	return multiplyDeBruijnBitPosition[((U32)((val & -(int)val) * 0x077CB531U)) >> 27];
+
+#   endif
+}
+
+/* ZSTD_VecMask_rotateRight():
+ * Rotates a bitfield to the right by "rotation" bits.
+ * If the rotation is greater than totalBits, the returned mask is 0.
+ */
+FORCE_INLINE_TEMPLATE ZSTD_VecMask
+ZSTD_VecMask_rotateRight(ZSTD_VecMask mask, U32 const rotation, U32 const totalBits) {
+  if (rotation == 0)
+    return mask;
+  switch (totalBits) {
+    default:
+      assert(0);
+    case 16:
+      return (mask >> rotation) | (U16)(mask << (16 - rotation));
+    case 32:
+      return (mask >> rotation) | (U32)(mask << (32 - rotation));
+  }
+}
+
+/* ZSTD_row_nextIndex():
+ * Returns the next index to insert at within a tagTable row, and updates the "head"
+ * value to reflect the update. Essentially cycles backwards from [0, {entries per row})
+ */
+FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextIndex(BYTE* const tagRow, U32 const rowMask) {
+  U32 const next = (*tagRow - 1) & rowMask;
+  *tagRow = (BYTE)next;
+  return next;
+}
+
+/* ZSTD_isAligned():
+ * Checks that a pointer is aligned to "align" bytes which must be a power of 2.
+ */
+MEM_STATIC int ZSTD_isAligned(void const* ptr, size_t align) {
+    assert((align & (align - 1)) == 0);
+    return (((size_t)ptr) & (align - 1)) == 0;
+}
+
+/* ZSTD_row_prefetch():
+ * Performs prefetching for the hashTable and tagTable at a given row.
+ */
+FORCE_INLINE_TEMPLATE void ZSTD_row_prefetch(U32 const* hashTable, U16 const* tagTable, U32 const relRow, U32 const rowLog) {
+    PREFETCH_L1(hashTable + relRow);
+    if (rowLog == 5) {
+        PREFETCH_L1(hashTable + relRow + 16);
+    }
+    PREFETCH_L1(tagTable + relRow);
+    assert(rowLog == 4 || rowLog == 5);
+    assert(ZSTD_isAligned(hashTable + relRow, 64));                 /* prefetched hash row always 64-byte aligned */
+    assert(ZSTD_isAligned(tagTable + relRow, (size_t)1 << rowLog)); /* prefetched tagRow sits on a multiple of 32 or 64 bytes */
+}
+
+/* ZSTD_row_fillHashCache():
+ * Fill up the hash cache starting at idx, prefetching up to ZSTD_ROW_HASH_CACHE_SIZE entries,
+ * but not beyond iLimit.
+ */
+static void ZSTD_row_fillHashCache(ZSTD_matchState_t* ms, const BYTE* base,
+                                   U32 const rowLog, U32 const mls,
+                                   U32 idx, const BYTE* const iLimit)
+{
+    U32 const* const hashTable = ms->hashTable;
+    U16 const* const tagTable = ms->tagTable;
+    U32 const hashLog = ms->rowHashLog;
+    U32 const maxElemsToPrefetch = (base + idx) > iLimit ? 0 : (U32)(iLimit - (base + idx) + 1);
+    U32 const lim = idx + MIN(ZSTD_ROW_HASH_CACHE_SIZE, maxElemsToPrefetch);
+
+    for (; idx < lim; ++idx) {
+        U32 const hash = (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
+        U32 const row = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
+        ZSTD_row_prefetch(hashTable, tagTable, row, rowLog);
+        ms->hashCache[idx & ZSTD_ROW_HASH_CACHE_MASK] = hash;
+    }
+
+    DEBUGLOG(6, "ZSTD_row_fillHashCache(): [%u %u %u %u %u %u %u %u]", ms->hashCache[0], ms->hashCache[1],
+                                                     ms->hashCache[2], ms->hashCache[3], ms->hashCache[4],
+                                                     ms->hashCache[5], ms->hashCache[6], ms->hashCache[7]);
+}
+
+/* ZSTD_row_nextCachedHash():
+ * Returns the hash of base + idx, and replaces the hash in the hash cache with the byte at
+ * base + idx + ZSTD_ROW_HASH_CACHE_SIZE. Also prefetches the appropriate rows from hashTable and tagTable.
+ */
+FORCE_INLINE_TEMPLATE U32 ZSTD_row_nextCachedHash(U32* cache, U32 const* hashTable,
+                                                  U16 const* tagTable, BYTE const* base,
+                                                  U32 idx, U32 const hashLog,
+                                                  U32 const rowLog, U32 const mls)
+{
+    U32 const newHash = (U32)ZSTD_hashPtr(base+idx+ZSTD_ROW_HASH_CACHE_SIZE, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
+    U32 const row = (newHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
+    ZSTD_row_prefetch(hashTable, tagTable, row, rowLog);
+    {   U32 const hash = cache[idx & ZSTD_ROW_HASH_CACHE_MASK];
+        cache[idx & ZSTD_ROW_HASH_CACHE_MASK] = newHash;
+        return hash;
+    }
+}
+
+/* ZSTD_row_update_internal():
+ * Inserts the byte at ip into the appropriate position in the hash table.
+ * Determines the relative row, and the position within the {16, 32} entry row to insert at.
+ */
+FORCE_INLINE_TEMPLATE void ZSTD_row_update_internal(ZSTD_matchState_t* ms, const BYTE* ip,
+                                                    U32 const mls, U32 const rowLog,
+                                                    U32 const rowMask, U32 const useCache)
+{
+    U32* const hashTable = ms->hashTable;
+    U16* const tagTable = ms->tagTable;
+    U32 const hashLog = ms->rowHashLog;
+    const BYTE* const base = ms->window.base;
+    const U32 target = (U32)(ip - base);
+    U32 idx = ms->nextToUpdate;
+
+    DEBUGLOG(6, "ZSTD_row_update_internal(): nextToUpdate=%u, current=%u", idx, target);
+    for (; idx < target; ++idx) {
+        U32 const hash = useCache ? ZSTD_row_nextCachedHash(ms->hashCache, hashTable, tagTable, base, idx, hashLog, rowLog, mls)
+                                  : (U32)ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
+        U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
+        U32* const row = hashTable + relRow;
+        BYTE* tagRow = (BYTE*)(tagTable + relRow);  /* Though tagTable is laid out as a table of U16, each tag is only 1 byte.
+                                                       Explicit cast allows us to get exact desired position within each row */
+        U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask);
+
+        assert(hash == ZSTD_hashPtr(base + idx, hashLog + ZSTD_ROW_HASH_TAG_BITS, mls));
+        ((BYTE*)tagRow)[pos + ZSTD_ROW_HASH_TAG_OFFSET] = hash & ZSTD_ROW_HASH_TAG_MASK;
+        row[pos] = idx;
+    }
+    ms->nextToUpdate = target;
+}
+
+/* ZSTD_row_update():
+ * External wrapper for ZSTD_row_update_internal(). Used for filling the hashtable during dictionary
+ * processing.
+ */
+void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip) {
+    const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5;
+    const U32 rowMask = (1u << rowLog) - 1;
+    const U32 mls = MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */);
+
+    DEBUGLOG(5, "ZSTD_row_update(), rowLog=%u", rowLog);
+    ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 0 /* dont use cache */);
+}
+
+/* Returns a ZSTD_VecMask (U32) that has the nth bit set to 1 if the newly-computed "tag" matches
+ * the hash at the nth position in a row of the tagTable.
+ */
+FORCE_INLINE_TEMPLATE
+ZSTD_VecMask ZSTD_row_getMatchMask(const BYTE* const tagRow, const BYTE tag, const U32 head, const U32 rowEntries) {
+    ZSTD_VecMask matches = 0;
+    if (rowEntries == 16) {
+        ZSTD_Vec128 hashes        = ZSTD_Vec128_read(tagRow + ZSTD_ROW_HASH_TAG_OFFSET);
+        ZSTD_Vec128 expandedTags  = ZSTD_Vec128_set8(tag);
+        matches                   = ZSTD_Vec128_cmpMask8(hashes, expandedTags);
+    } else if (rowEntries == 32) {
+        ZSTD_Vec256 hashes        = ZSTD_Vec256_read(tagRow + ZSTD_ROW_HASH_TAG_OFFSET);
+        ZSTD_Vec256 expandedTags  = ZSTD_Vec256_set8(tag);
+        matches                   = ZSTD_Vec256_cmpMask8(hashes, expandedTags);
+    } else {
+        assert(0);
+    }
+    /* Each row is a circular buffer beginning at the value of "head". So we must rotate the "matches" bitfield
+        to match up with the actual layout of the entries within the hashTable */
+    return ZSTD_VecMask_rotateRight(matches, head, rowEntries);
+}
+
+/* The high-level approach of the SIMD row based match finder is as follows:
+ * - Figure out where to insert the new entry:
+ *      - Generate a hash from a byte along with an additional 1-byte "short hash". The additional byte is our "tag"
+ *      - The hashTable is effectively split into groups or "rows" of 16 or 32 entries of U32, and the hash determines
+ *        which row to insert into.
+ *      - Determine the correct position within the row to insert the entry into. Each row of 16 or 32 can
+ *        be considered as a circular buffer with a "head" index that resides in the tagTable.
+ *      - Also insert the "tag" into the equivalent row and position in the tagTable.
+ *          - Note: The tagTable has 17 or 33 1-byte entries per row, due to 16 or 32 tags, and 1 "head" entry.
+ *                  The 17 or 33 entry rows are spaced out to occur every 32 or 64 bytes, respectively,
+ *                  for alignment/performance reasons, leaving some bytes unused.
+ * - Use SIMD to efficiently compare the tags in the tagTable to the 1-byte "short hash" and
+ *   generate a bitfield that we can cycle through to check the collisions in the hash table.
+ * - Pick the longest match.
+ */
+FORCE_INLINE_TEMPLATE
+size_t ZSTD_RowFindBestMatch_generic (
+                        ZSTD_matchState_t* ms,
+                        const BYTE* const ip, const BYTE* const iLimit,
+                        size_t* offsetPtr,
+                        const U32 mls, const ZSTD_dictMode_e dictMode,
+                        const U32 rowLog)
+{
+    U32* const hashTable = ms->hashTable;
+    U16* const tagTable = ms->tagTable;
+    U32* const hashCache = ms->hashCache;
+    const U32 hashLog = ms->rowHashLog;
+    const ZSTD_compressionParameters* const cParams = &ms->cParams;
+    const BYTE* const base = ms->window.base;
+    const BYTE* const dictBase = ms->window.dictBase;
+    const U32 dictLimit = ms->window.dictLimit;
+    const BYTE* const prefixStart = base + dictLimit;
+    const BYTE* const dictEnd = dictBase + dictLimit;
+    const U32 curr = (U32)(ip-base);
+    const U32 maxDistance = 1U << cParams->windowLog;
+    const U32 lowestValid = ms->window.lowLimit;
+    const U32 withinMaxDistance = (curr - lowestValid > maxDistance) ? curr - maxDistance : lowestValid;
+    const U32 isDictionary = (ms->loadedDictEnd != 0);
+    const U32 lowLimit = isDictionary ? lowestValid : withinMaxDistance;
+    const U32 rowEntries = (1U << rowLog);
+    const U32 rowMask = rowEntries - 1;
+    const U32 cappedSearchLog = MIN(cParams->searchLog, rowLog); /* nb of searches is capped at nb entries per row */
+    U32 nbAttempts = 1U << cappedSearchLog;
+    size_t ml=4-1;
+
+    /* DMS/DDS variables that may be referenced laster */
+    const ZSTD_matchState_t* const dms = ms->dictMatchState;
+    size_t ddsIdx;
+    U32 ddsExtraAttempts; /* cctx hash tables are limited in searches, but allow extra searches into DDS */
+    U32 dmsTag;
+    U32* dmsRow;
+    BYTE* dmsTagRow;
+
+    if (dictMode == ZSTD_dedicatedDictSearch) {
+        const U32 ddsHashLog = dms->cParams.hashLog - ZSTD_LAZY_DDSS_BUCKET_LOG;
+        {   /* Prefetch DDS hashtable entry */
+            ddsIdx = ZSTD_hashPtr(ip, ddsHashLog, mls) << ZSTD_LAZY_DDSS_BUCKET_LOG;
+            PREFETCH_L1(&dms->hashTable[ddsIdx]);
+        }
+        ddsExtraAttempts = cParams->searchLog > rowLog ? 1U << (cParams->searchLog - rowLog) : 0;
+    }
+
+    if (dictMode == ZSTD_dictMatchState) {
+        /* Prefetch DMS rows */
+        U32* const dmsHashTable = dms->hashTable;
+        U16* const dmsTagTable = dms->tagTable;
+        U32 const dmsHash = (U32)ZSTD_hashPtr(ip, dms->rowHashLog + ZSTD_ROW_HASH_TAG_BITS, mls);
+        U32 const dmsRelRow = (dmsHash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
+        dmsTag = dmsHash & ZSTD_ROW_HASH_TAG_MASK;
+        dmsTagRow = (BYTE*)(dmsTagTable + dmsRelRow);
+        dmsRow = dmsHashTable + dmsRelRow;
+        ZSTD_row_prefetch(dmsHashTable, dmsTagTable, dmsRelRow, rowLog);
+    }
+
+    /* Update the hashTable and tagTable up to (but not including) ip */
+    ZSTD_row_update_internal(ms, ip, mls, rowLog, rowMask, 1 /* useCache */);
+    {   /* Get the hash for ip, compute the appropriate row */
+        U32 const hash = ZSTD_row_nextCachedHash(hashCache, hashTable, tagTable, base, curr, hashLog, rowLog, mls);
+        U32 const relRow = (hash >> ZSTD_ROW_HASH_TAG_BITS) << rowLog;
+        U32 const tag = hash & ZSTD_ROW_HASH_TAG_MASK;
+        U32* const row = hashTable + relRow;
+        BYTE* tagRow = (BYTE*)(tagTable + relRow);
+        U32 const head = *tagRow & rowMask;
+        U32 matchBuffer[32 /* maximum nb entries per row */];
+        size_t numMatches = 0;
+        size_t currMatch = 0;
+        ZSTD_VecMask matches = ZSTD_row_getMatchMask(tagRow, (BYTE)tag, head, rowEntries);
+
+        /* Cycle through the matches and prefetch */
+        for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
+            U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+            U32 const matchIndex = row[matchPos];
+            assert(numMatches < rowEntries);
+            if (matchIndex < lowLimit)
+                break;
+            if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) {
+                PREFETCH_L1(base + matchIndex);
+            } else {
+                PREFETCH_L1(dictBase + matchIndex);
+            }
+            matchBuffer[numMatches++] = matchIndex;
+        }
+
+        /* Speed opt: insert current byte into hashtable too. This allows us to avoid one iteration of the loop
+           in ZSTD_row_update_internal() at the next search. */
+        {
+            U32 const pos = ZSTD_row_nextIndex(tagRow, rowMask);
+            tagRow[pos + ZSTD_ROW_HASH_TAG_OFFSET] = (BYTE)tag;
+            row[pos] = ms->nextToUpdate++;
+        }
+
+        /* Return the longest match */
+        for (; currMatch < numMatches; ++currMatch) {
+            U32 const matchIndex = matchBuffer[currMatch];
+            size_t currentMl=0;
+            assert(matchIndex < curr);
+            assert(matchIndex >= lowLimit);
+
+            if ((dictMode != ZSTD_extDict) || matchIndex >= dictLimit) {
+                const BYTE* const match = base + matchIndex;
+                assert(matchIndex >= dictLimit);   /* ensures this is true if dictMode != ZSTD_extDict */
+                if (match[ml] == ip[ml])   /* potentially better */
+                    currentMl = ZSTD_count(ip, match, iLimit);
+            } else {
+                const BYTE* const match = dictBase + matchIndex;
+                assert(match+4 <= dictEnd);
+                if (MEM_read32(match) == MEM_read32(ip))   /* assumption : matchIndex <= dictLimit-4 (by table construction) */
+                    currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, dictEnd, prefixStart) + 4;
+            }
+
+            /* Save best solution */
+            if (currentMl > ml) {
+                ml = currentMl;
+                *offsetPtr = curr - matchIndex + ZSTD_REP_MOVE;
+                if (ip+currentMl == iLimit) break; /* best possible, avoids read overflow on next attempt */
+            }
+        }
+    }
+
+    if (dictMode == ZSTD_dedicatedDictSearch) {
+        ml = ZSTD_dedicatedDictSearch_lazy_search(offsetPtr, ml, nbAttempts + ddsExtraAttempts, dms,
+                                                  ip, iLimit, prefixStart, curr, dictLimit, ddsIdx);
+    } else if (dictMode == ZSTD_dictMatchState) {
+        /* TODO: Measure and potentially add prefetching to DMS */
+        const U32 dmsLowestIndex       = dms->window.dictLimit;
+        const BYTE* const dmsBase      = dms->window.base;
+        const BYTE* const dmsEnd       = dms->window.nextSrc;
+        const U32 dmsSize              = (U32)(dmsEnd - dmsBase);
+        const U32 dmsIndexDelta        = dictLimit - dmsSize;
+
+        {   U32 const head = *dmsTagRow & rowMask;
+            U32 matchBuffer[32 /* maximum nb row entries */];
+            size_t numMatches = 0;
+            size_t currMatch = 0;
+            ZSTD_VecMask matches = ZSTD_row_getMatchMask(dmsTagRow, (BYTE)dmsTag, head, rowEntries);
+
+            for (; (matches > 0) && (nbAttempts > 0); --nbAttempts, matches &= (matches - 1)) {
+                U32 const matchPos = (head + ZSTD_VecMask_next(matches)) & rowMask;
+                U32 const matchIndex = dmsRow[matchPos];
+                if (matchIndex < dmsLowestIndex)
+                    break;
+                PREFETCH_L1(dmsBase + matchIndex);
+                matchBuffer[numMatches++] = matchIndex;
+            }
+
+            /* Return the longest match */
+            for (; currMatch < numMatches; ++currMatch) {
+                U32 const matchIndex = matchBuffer[currMatch];
+                size_t currentMl=0;
+                assert(matchIndex >= dmsLowestIndex);
+                assert(matchIndex < curr);
+
+                {   const BYTE* const match = dmsBase + matchIndex;
+                    assert(match+4 <= dmsEnd);
+                    if (MEM_read32(match) == MEM_read32(ip))
+                        currentMl = ZSTD_count_2segments(ip+4, match+4, iLimit, dmsEnd, prefixStart) + 4;
+                }
+
+                if (currentMl > ml) {
+                    ml = currentMl;
+                    *offsetPtr = curr - (matchIndex + dmsIndexDelta) + ZSTD_REP_MOVE;
+                    if (ip+currentMl == iLimit) break;
+                }
+            }
+        }
+    }
+    return ml;
+}
+
+/* Inlining is important to hardwire a hot branch (template emulation) */
+FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_selectMLS (
+                        ZSTD_matchState_t* ms,
+                        const BYTE* ip, const BYTE* const iLimit,
+                        const ZSTD_dictMode_e dictMode, size_t* offsetPtr, const U32 rowLog)
+{
+    switch(ms->cParams.minMatch)
+    {
+    default : /* includes case 3 */
+    case 4 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 4, dictMode, rowLog);
+    case 5 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 5, dictMode, rowLog);
+    case 7 :
+    case 6 : return ZSTD_RowFindBestMatch_generic(ms, ip, iLimit, offsetPtr, 6, dictMode, rowLog);
+    }
+}
+
+FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_selectRowLog (
+                        ZSTD_matchState_t* ms,
+                        const BYTE* ip, const BYTE* const iLimit,
+                        size_t* offsetPtr)
+{
+    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
+    switch(cappedSearchLog)
+    {
+    default :
+    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_noDict, offsetPtr, 4);
+    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_noDict, offsetPtr, 5);
+    }
+}
+
+FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_dictMatchState_selectRowLog(
+                        ZSTD_matchState_t* ms,
+                        const BYTE* ip, const BYTE* const iLimit,
+                        size_t* offsetPtr)
+{
+    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
+    switch(cappedSearchLog)
+    {
+    default :
+    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dictMatchState, offsetPtr, 4);
+    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dictMatchState, offsetPtr, 5);
+    }
+}
+
+FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_dedicatedDictSearch_selectRowLog(
+                        ZSTD_matchState_t* ms,
+                        const BYTE* ip, const BYTE* const iLimit,
+                        size_t* offsetPtr)
+{
+    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
+    switch(cappedSearchLog)
+    {
+    default :
+    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dedicatedDictSearch, offsetPtr, 4);
+    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_dedicatedDictSearch, offsetPtr, 5);
+    }
+}
+
+FORCE_INLINE_TEMPLATE size_t ZSTD_RowFindBestMatch_extDict_selectRowLog (
+                        ZSTD_matchState_t* ms,
+                        const BYTE* ip, const BYTE* const iLimit,
+                        size_t* offsetPtr)
+{
+    const U32 cappedSearchLog = MIN(ms->cParams.searchLog, 5);
+    switch(cappedSearchLog)
+    {
+    default :
+    case 4 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_extDict, offsetPtr, 4);
+    case 5 : return ZSTD_RowFindBestMatch_selectMLS(ms, ip, iLimit, ZSTD_extDict, offsetPtr, 5);
+    }
+}
+
 
 /* *******************************
 *  Common parser - lazy strategy
 *********************************/
-typedef enum { search_hashChain, search_binaryTree } searchMethod_e;
+typedef enum { search_hashChain=0, search_binaryTree=1, search_rowHash=2 } searchMethod_e;
 
 FORCE_INLINE_TEMPLATE size_t
 ZSTD_compressBlock_lazy_generic(
@@ -633,53 +1526,85 @@
     const BYTE* ip = istart;
     const BYTE* anchor = istart;
     const BYTE* const iend = istart + srcSize;
-    const BYTE* const ilimit = iend - 8;
+    const BYTE* const ilimit = searchMethod == search_rowHash ? iend - 8 - ZSTD_ROW_HASH_CACHE_SIZE : iend - 8;
     const BYTE* const base = ms->window.base;
     const U32 prefixLowestIndex = ms->window.dictLimit;
     const BYTE* const prefixLowest = base + prefixLowestIndex;
+    const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5;
 
     typedef size_t (*searchMax_f)(
                         ZSTD_matchState_t* ms,
                         const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
-    searchMax_f const searchMax = dictMode == ZSTD_dictMatchState ?
-        (searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_dictMatchState_selectMLS
-                                         : ZSTD_HcFindBestMatch_dictMatchState_selectMLS) :
-        (searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_selectMLS
-                                         : ZSTD_HcFindBestMatch_selectMLS);
+
+    /**
+     * This table is indexed first by the four ZSTD_dictMode_e values, and then
+     * by the two searchMethod_e values. NULLs are placed for configurations
+     * that should never occur (extDict modes go to the other implementation
+     * below and there is no DDSS for binary tree search yet).
+     */
+    const searchMax_f searchFuncs[4][3] = {
+        {
+            ZSTD_HcFindBestMatch_selectMLS,
+            ZSTD_BtFindBestMatch_selectMLS,
+            ZSTD_RowFindBestMatch_selectRowLog
+        },
+        {
+            NULL,
+            NULL,
+            NULL
+        },
+        {
+            ZSTD_HcFindBestMatch_dictMatchState_selectMLS,
+            ZSTD_BtFindBestMatch_dictMatchState_selectMLS,
+            ZSTD_RowFindBestMatch_dictMatchState_selectRowLog
+        },
+        {
+            ZSTD_HcFindBestMatch_dedicatedDictSearch_selectMLS,
+            NULL,
+            ZSTD_RowFindBestMatch_dedicatedDictSearch_selectRowLog
+        }
+    };
+
+    searchMax_f const searchMax = searchFuncs[dictMode][(int)searchMethod];
     U32 offset_1 = rep[0], offset_2 = rep[1], savedOffset=0;
 
+    const int isDMS = dictMode == ZSTD_dictMatchState;
+    const int isDDS = dictMode == ZSTD_dedicatedDictSearch;
+    const int isDxS = isDMS || isDDS;
     const ZSTD_matchState_t* const dms = ms->dictMatchState;
-    const U32 dictLowestIndex      = dictMode == ZSTD_dictMatchState ?
-                                     dms->window.dictLimit : 0;
-    const BYTE* const dictBase     = dictMode == ZSTD_dictMatchState ?
-                                     dms->window.base : NULL;
-    const BYTE* const dictLowest   = dictMode == ZSTD_dictMatchState ?
-                                     dictBase + dictLowestIndex : NULL;
-    const BYTE* const dictEnd      = dictMode == ZSTD_dictMatchState ?
-                                     dms->window.nextSrc : NULL;
-    const U32 dictIndexDelta       = dictMode == ZSTD_dictMatchState ?
+    const U32 dictLowestIndex      = isDxS ? dms->window.dictLimit : 0;
+    const BYTE* const dictBase     = isDxS ? dms->window.base : NULL;
+    const BYTE* const dictLowest   = isDxS ? dictBase + dictLowestIndex : NULL;
+    const BYTE* const dictEnd      = isDxS ? dms->window.nextSrc : NULL;
+    const U32 dictIndexDelta       = isDxS ?
                                      prefixLowestIndex - (U32)(dictEnd - dictBase) :
                                      0;
     const U32 dictAndPrefixLength = (U32)((ip - prefixLowest) + (dictEnd - dictLowest));
 
-    DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u)", (U32)dictMode);
+    assert(searchMax != NULL);
 
-    /* init */
+    DEBUGLOG(5, "ZSTD_compressBlock_lazy_generic (dictMode=%u) (searchFunc=%u)", (U32)dictMode, (U32)searchMethod);
     ip += (dictAndPrefixLength == 0);
     if (dictMode == ZSTD_noDict) {
-        U32 const current = (U32)(ip - base);
-        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, current, ms->cParams.windowLog);
-        U32 const maxRep = current - windowLow;
+        U32 const curr = (U32)(ip - base);
+        U32 const windowLow = ZSTD_getLowestPrefixIndex(ms, curr, ms->cParams.windowLog);
+        U32 const maxRep = curr - windowLow;
         if (offset_2 > maxRep) savedOffset = offset_2, offset_2 = 0;
         if (offset_1 > maxRep) savedOffset = offset_1, offset_1 = 0;
     }
-    if (dictMode == ZSTD_dictMatchState) {
+    if (isDxS) {
         /* dictMatchState repCode checks don't currently handle repCode == 0
          * disabling. */
         assert(offset_1 <= dictAndPrefixLength);
         assert(offset_2 <= dictAndPrefixLength);
     }
 
+    if (searchMethod == search_rowHash) {
+        ZSTD_row_fillHashCache(ms, base, rowLog,
+                            MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */),
+                            ms->nextToUpdate, ilimit);
+    }
+
     /* Match Loop */
 #if defined(__GNUC__) && defined(__x86_64__)
     /* I've measured random a 5% speed loss on levels 5 & 6 (greedy) when the
@@ -693,9 +1618,9 @@
         const BYTE* start=ip+1;
 
         /* check repCode */
-        if (dictMode == ZSTD_dictMatchState) {
+        if (isDxS) {
             const U32 repIndex = (U32)(ip - base) + 1 - offset_1;
-            const BYTE* repMatch = (dictMode == ZSTD_dictMatchState
+            const BYTE* repMatch = ((dictMode == ZSTD_dictMatchState || dictMode == ZSTD_dedicatedDictSearch)
                                 && repIndex < prefixLowestIndex) ?
                                    dictBase + (repIndex - dictIndexDelta) :
                                    base + repIndex;
@@ -736,7 +1661,7 @@
                 if ((mlRep >= 4) && (gain2 > gain1))
                     matchLength = mlRep, offset = 0, start = ip;
             }
-            if (dictMode == ZSTD_dictMatchState) {
+            if (isDxS) {
                 const U32 repIndex = (U32)(ip - base) - offset_1;
                 const BYTE* repMatch = repIndex < prefixLowestIndex ?
                                dictBase + (repIndex - dictIndexDelta) :
@@ -771,7 +1696,7 @@
                     if ((mlRep >= 4) && (gain2 > gain1))
                         matchLength = mlRep, offset = 0, start = ip;
                 }
-                if (dictMode == ZSTD_dictMatchState) {
+                if (isDxS) {
                     const U32 repIndex = (U32)(ip - base) - offset_1;
                     const BYTE* repMatch = repIndex < prefixLowestIndex ?
                                    dictBase + (repIndex - dictIndexDelta) :
@@ -809,7 +1734,7 @@
                      && (start[-1] == (start-(offset-ZSTD_REP_MOVE))[-1]) )  /* only search for offset within prefix */
                     { start--; matchLength++; }
             }
-            if (dictMode == ZSTD_dictMatchState) {
+            if (isDxS) {
                 U32 const matchIndex = (U32)((start-base) - (offset - ZSTD_REP_MOVE));
                 const BYTE* match = (matchIndex < prefixLowestIndex) ? dictBase + matchIndex - dictIndexDelta : base + matchIndex;
                 const BYTE* const mStart = (matchIndex < prefixLowestIndex) ? dictLowest : prefixLowest;
@@ -825,12 +1750,11 @@
         }
 
         /* check immediate repcode */
-        if (dictMode == ZSTD_dictMatchState) {
+        if (isDxS) {
             while (ip <= ilimit) {
                 U32 const current2 = (U32)(ip-base);
                 U32 const repIndex = current2 - offset_2;
-                const BYTE* repMatch = dictMode == ZSTD_dictMatchState
-                    && repIndex < prefixLowestIndex ?
+                const BYTE* repMatch = repIndex < prefixLowestIndex ?
                         dictBase - dictIndexDelta + repIndex :
                         base + repIndex;
                 if ( ((U32)((prefixLowestIndex-1) - (U32)repIndex) >= 3 /* intentional overflow */)
@@ -925,6 +1849,92 @@
 }
 
 
+size_t ZSTD_compressBlock_lazy2_dedicatedDictSearch(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 2, ZSTD_dedicatedDictSearch);
+}
+
+size_t ZSTD_compressBlock_lazy_dedicatedDictSearch(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 1, ZSTD_dedicatedDictSearch);
+}
+
+size_t ZSTD_compressBlock_greedy_dedicatedDictSearch(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_hashChain, 0, ZSTD_dedicatedDictSearch);
+}
+
+/* Row-based matchfinder */
+size_t ZSTD_compressBlock_lazy2_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_lazy_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 1, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_greedy_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 0, ZSTD_noDict);
+}
+
+size_t ZSTD_compressBlock_lazy2_dictMatchState_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_lazy_dictMatchState_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 1, ZSTD_dictMatchState);
+}
+
+size_t ZSTD_compressBlock_greedy_dictMatchState_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 0, ZSTD_dictMatchState);
+}
+
+
+size_t ZSTD_compressBlock_lazy2_dedicatedDictSearch_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2, ZSTD_dedicatedDictSearch);
+}
+
+size_t ZSTD_compressBlock_lazy_dedicatedDictSearch_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 1, ZSTD_dedicatedDictSearch);
+}
+
+size_t ZSTD_compressBlock_greedy_dedicatedDictSearch_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 0, ZSTD_dedicatedDictSearch);
+}
+
 FORCE_INLINE_TEMPLATE
 size_t ZSTD_compressBlock_lazy_extDict_generic(
                         ZSTD_matchState_t* ms, seqStore_t* seqStore,
@@ -936,7 +1946,7 @@
     const BYTE* ip = istart;
     const BYTE* anchor = istart;
     const BYTE* const iend = istart + srcSize;
-    const BYTE* const ilimit = iend - 8;
+    const BYTE* const ilimit = searchMethod == search_rowHash ? iend - 8 - ZSTD_ROW_HASH_CACHE_SIZE : iend - 8;
     const BYTE* const base = ms->window.base;
     const U32 dictLimit = ms->window.dictLimit;
     const BYTE* const prefixStart = base + dictLimit;
@@ -944,18 +1954,28 @@
     const BYTE* const dictEnd  = dictBase + dictLimit;
     const BYTE* const dictStart  = dictBase + ms->window.lowLimit;
     const U32 windowLog = ms->cParams.windowLog;
+    const U32 rowLog = ms->cParams.searchLog < 5 ? 4 : 5;
 
     typedef size_t (*searchMax_f)(
                         ZSTD_matchState_t* ms,
                         const BYTE* ip, const BYTE* iLimit, size_t* offsetPtr);
-    searchMax_f searchMax = searchMethod==search_binaryTree ? ZSTD_BtFindBestMatch_extDict_selectMLS : ZSTD_HcFindBestMatch_extDict_selectMLS;
-
+    const searchMax_f searchFuncs[3] = {
+        ZSTD_HcFindBestMatch_extDict_selectMLS,
+        ZSTD_BtFindBestMatch_extDict_selectMLS,
+        ZSTD_RowFindBestMatch_extDict_selectRowLog
+    };
+    searchMax_f searchMax = searchFuncs[(int)searchMethod];
     U32 offset_1 = rep[0], offset_2 = rep[1];
 
-    DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic");
+    DEBUGLOG(5, "ZSTD_compressBlock_lazy_extDict_generic (searchFunc=%u)", (U32)searchMethod);
 
     /* init */
     ip += (ip == prefixStart);
+    if (searchMethod == search_rowHash) {
+        ZSTD_row_fillHashCache(ms, base, rowLog,
+                               MIN(ms->cParams.minMatch, 6 /* mls caps out at 6 */),
+                               ms->nextToUpdate, ilimit);
+    }
 
     /* Match Loop */
 #if defined(__GNUC__) && defined(__x86_64__)
@@ -968,14 +1988,15 @@
         size_t matchLength=0;
         size_t offset=0;
         const BYTE* start=ip+1;
-        U32 current = (U32)(ip-base);
+        U32 curr = (U32)(ip-base);
 
         /* check repCode */
-        {   const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current+1, windowLog);
-            const U32 repIndex = (U32)(current+1 - offset_1);
+        {   const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr+1, windowLog);
+            const U32 repIndex = (U32)(curr+1 - offset_1);
             const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
             const BYTE* const repMatch = repBase + repIndex;
-            if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow))   /* intentional overflow */
+            if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow */
+               & (offset_1 < curr+1 - windowLow) ) /* note: we are searching at curr+1 */
             if (MEM_read32(ip+1) == MEM_read32(repMatch)) {
                 /* repcode detected we should take it */
                 const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
@@ -999,14 +2020,15 @@
         if (depth>=1)
         while (ip<ilimit) {
             ip ++;
-            current++;
+            curr++;
             /* check repCode */
             if (offset) {
-                const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current, windowLog);
-                const U32 repIndex = (U32)(current - offset_1);
+                const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog);
+                const U32 repIndex = (U32)(curr - offset_1);
                 const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
                 const BYTE* const repMatch = repBase + repIndex;
-                if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow))  /* intentional overflow */
+                if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments  */
+                   & (offset_1 < curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
                 if (MEM_read32(ip) == MEM_read32(repMatch)) {
                     /* repcode detected */
                     const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
@@ -1030,14 +2052,15 @@
             /* let's find an even better one */
             if ((depth==2) && (ip<ilimit)) {
                 ip ++;
-                current++;
+                curr++;
                 /* check repCode */
                 if (offset) {
-                    const U32 windowLow = ZSTD_getLowestMatchIndex(ms, current, windowLog);
-                    const U32 repIndex = (U32)(current - offset_1);
+                    const U32 windowLow = ZSTD_getLowestMatchIndex(ms, curr, windowLog);
+                    const U32 repIndex = (U32)(curr - offset_1);
                     const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
                     const BYTE* const repMatch = repBase + repIndex;
-                    if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow))  /* intentional overflow */
+                    if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments  */
+                       & (offset_1 < curr - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
                     if (MEM_read32(ip) == MEM_read32(repMatch)) {
                         /* repcode detected */
                         const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
@@ -1083,7 +2106,8 @@
             const U32 repIndex = repCurrent - offset_2;
             const BYTE* const repBase = repIndex < dictLimit ? dictBase : base;
             const BYTE* const repMatch = repBase + repIndex;
-            if (((U32)((dictLimit-1) - repIndex) >= 3) & (repIndex > windowLow))  /* intentional overflow */
+            if ( ((U32)((dictLimit-1) - repIndex) >= 3) /* intentional overflow : do not test positions overlapping 2 memory segments  */
+               & (offset_2 < repCurrent - windowLow) ) /* equivalent to `curr > repIndex >= windowLow` */
             if (MEM_read32(ip) == MEM_read32(repMatch)) {
                 /* repcode detected we should take it */
                 const BYTE* const repEnd = repIndex < dictLimit ? dictEnd : iend;
@@ -1136,3 +2160,26 @@
 {
     return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_binaryTree, 2);
 }
+
+size_t ZSTD_compressBlock_greedy_extDict_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+{
+    return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 0);
+}
+
+size_t ZSTD_compressBlock_lazy_extDict_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+
+{
+    return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 1);
+}
+
+size_t ZSTD_compressBlock_lazy2_extDict_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize)
+
+{
+    return ZSTD_compressBlock_lazy_extDict_generic(ms, seqStore, rep, src, srcSize, search_rowHash, 2);
+}
diff --git a/Utilities/cmzstd/lib/compress/zstd_lazy.h b/Utilities/cmzstd/lib/compress/zstd_lazy.h
index 581936f..150f7b3 100644
--- a/Utilities/cmzstd/lib/compress/zstd_lazy.h
+++ b/Utilities/cmzstd/lib/compress/zstd_lazy.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -17,7 +17,18 @@
 
 #include "zstd_compress_internal.h"
 
+/**
+ * Dedicated Dictionary Search Structure bucket log. In the
+ * ZSTD_dedicatedDictSearch mode, the hashTable has
+ * 2 ** ZSTD_LAZY_DDSS_BUCKET_LOG entries in each bucket, rather than just
+ * one.
+ */
+#define ZSTD_LAZY_DDSS_BUCKET_LOG 2
+
 U32 ZSTD_insertAndFindFirstIndex(ZSTD_matchState_t* ms, const BYTE* ip);
+void ZSTD_row_update(ZSTD_matchState_t* const ms, const BYTE* ip);
+
+void ZSTD_dedicatedDictSearch_lazy_loadDictionary(ZSTD_matchState_t* ms, const BYTE* const ip);
 
 void ZSTD_preserveUnsortedMark (U32* const table, U32 const size, U32 const reducerValue);  /*! used in ZSTD_reduceIndex(). preemptively increase value of ZSTD_DUBT_UNSORTED_MARK */
 
@@ -33,6 +44,15 @@
 size_t ZSTD_compressBlock_greedy(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
 
 size_t ZSTD_compressBlock_btlazy2_dictMatchState(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
@@ -46,6 +66,34 @@
 size_t ZSTD_compressBlock_greedy_dictMatchState(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_dictMatchState_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_dictMatchState_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy_dictMatchState_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+
+size_t ZSTD_compressBlock_lazy2_dedicatedDictSearch(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_dedicatedDictSearch(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy_dedicatedDictSearch(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_dedicatedDictSearch_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_dedicatedDictSearch_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy_dedicatedDictSearch_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
 
 size_t ZSTD_compressBlock_greedy_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
@@ -56,9 +104,19 @@
 size_t ZSTD_compressBlock_lazy2_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_greedy_extDict_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy_extDict_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
+size_t ZSTD_compressBlock_lazy2_extDict_row(
+        ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+        void const* src, size_t srcSize);
 size_t ZSTD_compressBlock_btlazy2_extDict(
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         void const* src, size_t srcSize);
+        
 
 #if defined (__cplusplus)
 }
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.c b/Utilities/cmzstd/lib/compress/zstd_ldm.c
index 8c47948..fa4ebea 100644
--- a/Utilities/cmzstd/lib/compress/zstd_ldm.c
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -11,13 +11,126 @@
 #include "zstd_ldm.h"
 
 #include "../common/debug.h"
+#include "../common/xxhash.h"
 #include "zstd_fast.h"          /* ZSTD_fillHashTable() */
 #include "zstd_double_fast.h"   /* ZSTD_fillDoubleHashTable() */
+#include "zstd_ldm_geartab.h"
 
 #define LDM_BUCKET_SIZE_LOG 3
 #define LDM_MIN_MATCH_LENGTH 64
 #define LDM_HASH_RLOG 7
-#define LDM_HASH_CHAR_OFFSET 10
+
+typedef struct {
+    U64 rolling;
+    U64 stopMask;
+} ldmRollingHashState_t;
+
+/** ZSTD_ldm_gear_init():
+ *
+ * Initializes the rolling hash state such that it will honor the
+ * settings in params. */
+static void ZSTD_ldm_gear_init(ldmRollingHashState_t* state, ldmParams_t const* params)
+{
+    unsigned maxBitsInMask = MIN(params->minMatchLength, 64);
+    unsigned hashRateLog = params->hashRateLog;
+
+    state->rolling = ~(U32)0;
+
+    /* The choice of the splitting criterion is subject to two conditions:
+     *   1. it has to trigger on average every 2^(hashRateLog) bytes;
+     *   2. ideally, it has to depend on a window of minMatchLength bytes.
+     *
+     * In the gear hash algorithm, bit n depends on the last n bytes;
+     * so in order to obtain a good quality splitting criterion it is
+     * preferable to use bits with high weight.
+     *
+     * To match condition 1 we use a mask with hashRateLog bits set
+     * and, because of the previous remark, we make sure these bits
+     * have the highest possible weight while still respecting
+     * condition 2.
+     */
+    if (hashRateLog > 0 && hashRateLog <= maxBitsInMask) {
+        state->stopMask = (((U64)1 << hashRateLog) - 1) << (maxBitsInMask - hashRateLog);
+    } else {
+        /* In this degenerate case we simply honor the hash rate. */
+        state->stopMask = ((U64)1 << hashRateLog) - 1;
+    }
+}
+
+/** ZSTD_ldm_gear_reset()
+ * Feeds [data, data + minMatchLength) into the hash without registering any
+ * splits. This effectively resets the hash state. This is used when skipping
+ * over data, either at the beginning of a block, or skipping sections.
+ */
+static void ZSTD_ldm_gear_reset(ldmRollingHashState_t* state,
+                                BYTE const* data, size_t minMatchLength)
+{
+    U64 hash = state->rolling;
+    size_t n = 0;
+
+#define GEAR_ITER_ONCE() do {                                  \
+        hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
+        n += 1;                                                \
+    } while (0)
+    while (n + 3 < minMatchLength) {
+        GEAR_ITER_ONCE();
+        GEAR_ITER_ONCE();
+        GEAR_ITER_ONCE();
+        GEAR_ITER_ONCE();
+    }
+    while (n < minMatchLength) {
+        GEAR_ITER_ONCE();
+    }
+#undef GEAR_ITER_ONCE
+}
+
+/** ZSTD_ldm_gear_feed():
+ *
+ * Registers in the splits array all the split points found in the first
+ * size bytes following the data pointer. This function terminates when
+ * either all the data has been processed or LDM_BATCH_SIZE splits are
+ * present in the splits array.
+ *
+ * Precondition: The splits array must not be full.
+ * Returns: The number of bytes processed. */
+static size_t ZSTD_ldm_gear_feed(ldmRollingHashState_t* state,
+                                 BYTE const* data, size_t size,
+                                 size_t* splits, unsigned* numSplits)
+{
+    size_t n;
+    U64 hash, mask;
+
+    hash = state->rolling;
+    mask = state->stopMask;
+    n = 0;
+
+#define GEAR_ITER_ONCE() do { \
+        hash = (hash << 1) + ZSTD_ldm_gearTab[data[n] & 0xff]; \
+        n += 1; \
+        if (UNLIKELY((hash & mask) == 0)) { \
+            splits[*numSplits] = n; \
+            *numSplits += 1; \
+            if (*numSplits == LDM_BATCH_SIZE) \
+                goto done; \
+        } \
+    } while (0)
+
+    while (n + 3 < size) {
+        GEAR_ITER_ONCE();
+        GEAR_ITER_ONCE();
+        GEAR_ITER_ONCE();
+        GEAR_ITER_ONCE();
+    }
+    while (n < size) {
+        GEAR_ITER_ONCE();
+    }
+
+#undef GEAR_ITER_ONCE
+
+done:
+    state->rolling = hash;
+    return n;
+}
 
 void ZSTD_ldm_adjustParameters(ldmParams_t* params,
                                ZSTD_compressionParameters const* cParams)
@@ -27,13 +140,6 @@
     DEBUGLOG(4, "ZSTD_ldm_adjustParameters");
     if (!params->bucketSizeLog) params->bucketSizeLog = LDM_BUCKET_SIZE_LOG;
     if (!params->minMatchLength) params->minMatchLength = LDM_MIN_MATCH_LENGTH;
-    if (cParams->strategy >= ZSTD_btopt) {
-      /* Get out of the way of the optimal parser */
-      U32 const minMatch = MAX(cParams->targetLength, params->minMatchLength);
-      assert(minMatch >= ZSTD_LDM_MINMATCH_MIN);
-      assert(minMatch <= ZSTD_LDM_MINMATCH_MAX);
-      params->minMatchLength = minMatch;
-    }
     if (params->hashLog == 0) {
         params->hashLog = MAX(ZSTD_HASHLOG_MIN, params->windowLog - LDM_HASH_RLOG);
         assert(params->hashLog <= ZSTD_HASHLOG_MAX);
@@ -61,41 +167,6 @@
     return params.enableLdm ? (maxChunkSize / params.minMatchLength) : 0;
 }
 
-/** ZSTD_ldm_getSmallHash() :
- *  numBits should be <= 32
- *  If numBits==0, returns 0.
- *  @return : the most significant numBits of value. */
-static U32 ZSTD_ldm_getSmallHash(U64 value, U32 numBits)
-{
-    assert(numBits <= 32);
-    return numBits == 0 ? 0 : (U32)(value >> (64 - numBits));
-}
-
-/** ZSTD_ldm_getChecksum() :
- *  numBitsToDiscard should be <= 32
- *  @return : the next most significant 32 bits after numBitsToDiscard */
-static U32 ZSTD_ldm_getChecksum(U64 hash, U32 numBitsToDiscard)
-{
-    assert(numBitsToDiscard <= 32);
-    return (hash >> (64 - 32 - numBitsToDiscard)) & 0xFFFFFFFF;
-}
-
-/** ZSTD_ldm_getTag() ;
- *  Given the hash, returns the most significant numTagBits bits
- *  after (32 + hbits) bits.
- *
- *  If there are not enough bits remaining, return the last
- *  numTagBits bits. */
-static U32 ZSTD_ldm_getTag(U64 hash, U32 hbits, U32 numTagBits)
-{
-    assert(numTagBits < 32 && hbits <= 32);
-    if (32 - hbits < numTagBits) {
-        return hash & (((U32)1 << numTagBits) - 1);
-    } else {
-        return (hash >> (32 - hbits - numTagBits)) & (((U32)1 << numTagBits) - 1);
-    }
-}
-
 /** ZSTD_ldm_getBucket() :
  *  Returns a pointer to the start of the bucket associated with hash. */
 static ldmEntry_t* ZSTD_ldm_getBucket(
@@ -110,38 +181,12 @@
                                  size_t const hash, const ldmEntry_t entry,
                                  ldmParams_t const ldmParams)
 {
-    BYTE* const bucketOffsets = ldmState->bucketOffsets;
-    *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + bucketOffsets[hash]) = entry;
-    bucketOffsets[hash]++;
-    bucketOffsets[hash] &= ((U32)1 << ldmParams.bucketSizeLog) - 1;
-}
+    BYTE* const pOffset = ldmState->bucketOffsets + hash;
+    unsigned const offset = *pOffset;
 
-/** ZSTD_ldm_makeEntryAndInsertByTag() :
- *
- *  Gets the small hash, checksum, and tag from the rollingHash.
- *
- *  If the tag matches (1 << ldmParams.hashRateLog)-1, then
- *  creates an ldmEntry from the offset, and inserts it into the hash table.
- *
- *  hBits is the length of the small hash, which is the most significant hBits
- *  of rollingHash. The checksum is the next 32 most significant bits, followed
- *  by ldmParams.hashRateLog bits that make up the tag. */
-static void ZSTD_ldm_makeEntryAndInsertByTag(ldmState_t* ldmState,
-                                             U64 const rollingHash,
-                                             U32 const hBits,
-                                             U32 const offset,
-                                             ldmParams_t const ldmParams)
-{
-    U32 const tag = ZSTD_ldm_getTag(rollingHash, hBits, ldmParams.hashRateLog);
-    U32 const tagMask = ((U32)1 << ldmParams.hashRateLog) - 1;
-    if (tag == tagMask) {
-        U32 const hash = ZSTD_ldm_getSmallHash(rollingHash, hBits);
-        U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
-        ldmEntry_t entry;
-        entry.offset = offset;
-        entry.checksum = checksum;
-        ZSTD_ldm_insertEntry(ldmState, hash, entry, ldmParams);
-    }
+    *(ZSTD_ldm_getBucket(ldmState, hash, ldmParams) + offset) = entry;
+    *pOffset = (BYTE)((offset + 1) & ((1u << ldmParams.bucketSizeLog) - 1));
+
 }
 
 /** ZSTD_ldm_countBackwardsMatch() :
@@ -150,10 +195,10 @@
  *  We count only bytes where pMatch >= pBase and pIn >= pAnchor. */
 static size_t ZSTD_ldm_countBackwardsMatch(
             const BYTE* pIn, const BYTE* pAnchor,
-            const BYTE* pMatch, const BYTE* pBase)
+            const BYTE* pMatch, const BYTE* pMatchBase)
 {
     size_t matchLength = 0;
-    while (pIn > pAnchor && pMatch > pBase && pIn[-1] == pMatch[-1]) {
+    while (pIn > pAnchor && pMatch > pMatchBase && pIn[-1] == pMatch[-1]) {
         pIn--;
         pMatch--;
         matchLength++;
@@ -161,6 +206,27 @@
     return matchLength;
 }
 
+/** ZSTD_ldm_countBackwardsMatch_2segments() :
+ *  Returns the number of bytes that match backwards from pMatch,
+ *  even with the backwards match spanning 2 different segments.
+ *
+ *  On reaching `pMatchBase`, start counting from mEnd */
+static size_t ZSTD_ldm_countBackwardsMatch_2segments(
+                    const BYTE* pIn, const BYTE* pAnchor,
+                    const BYTE* pMatch, const BYTE* pMatchBase,
+                    const BYTE* pExtDictStart, const BYTE* pExtDictEnd)
+{
+    size_t matchLength = ZSTD_ldm_countBackwardsMatch(pIn, pAnchor, pMatch, pMatchBase);
+    if (pMatch - matchLength != pMatchBase || pMatchBase == pExtDictStart) {
+        /* If backwards match is entirely in the extDict or prefix, immediately return */
+        return matchLength;
+    }
+    DEBUGLOG(7, "ZSTD_ldm_countBackwardsMatch_2segments: found 2-parts backwards match (length in prefix==%zu)", matchLength);
+    matchLength += ZSTD_ldm_countBackwardsMatch(pIn - matchLength, pAnchor, pExtDictEnd, pExtDictStart);
+    DEBUGLOG(7, "final backwards match length = %zu", matchLength);
+    return matchLength;
+}
+
 /** ZSTD_ldm_fillFastTables() :
  *
  *  Fills the relevant tables for the ZSTD_fast and ZSTD_dfast strategies.
@@ -198,43 +264,42 @@
     return 0;
 }
 
-/** ZSTD_ldm_fillLdmHashTable() :
- *
- *  Fills hashTable from (lastHashed + 1) to iend (non-inclusive).
- *  lastHash is the rolling hash that corresponds to lastHashed.
- *
- *  Returns the rolling hash corresponding to position iend-1. */
-static U64 ZSTD_ldm_fillLdmHashTable(ldmState_t* state,
-                                     U64 lastHash, const BYTE* lastHashed,
-                                     const BYTE* iend, const BYTE* base,
-                                     U32 hBits, ldmParams_t const ldmParams)
-{
-    U64 rollingHash = lastHash;
-    const BYTE* cur = lastHashed + 1;
-
-    while (cur < iend) {
-        rollingHash = ZSTD_rollingHash_rotate(rollingHash, cur[-1],
-                                              cur[ldmParams.minMatchLength-1],
-                                              state->hashPower);
-        ZSTD_ldm_makeEntryAndInsertByTag(state,
-                                         rollingHash, hBits,
-                                         (U32)(cur - base), ldmParams);
-        ++cur;
-    }
-    return rollingHash;
-}
-
 void ZSTD_ldm_fillHashTable(
-            ldmState_t* state, const BYTE* ip,
+            ldmState_t* ldmState, const BYTE* ip,
             const BYTE* iend, ldmParams_t const* params)
 {
+    U32 const minMatchLength = params->minMatchLength;
+    U32 const hBits = params->hashLog - params->bucketSizeLog;
+    BYTE const* const base = ldmState->window.base;
+    BYTE const* const istart = ip;
+    ldmRollingHashState_t hashState;
+    size_t* const splits = ldmState->splitIndices;
+    unsigned numSplits;
+
     DEBUGLOG(5, "ZSTD_ldm_fillHashTable");
-    if ((size_t)(iend - ip) >= params->minMatchLength) {
-        U64 startingHash = ZSTD_rollingHash_compute(ip, params->minMatchLength);
-        ZSTD_ldm_fillLdmHashTable(
-            state, startingHash, ip, iend - params->minMatchLength, state->window.base,
-            params->hashLog - params->bucketSizeLog,
-            *params);
+
+    ZSTD_ldm_gear_init(&hashState, params);
+    while (ip < iend) {
+        size_t hashed;
+        unsigned n;
+
+        numSplits = 0;
+        hashed = ZSTD_ldm_gear_feed(&hashState, ip, iend - ip, splits, &numSplits);
+
+        for (n = 0; n < numSplits; n++) {
+            if (ip + splits[n] >= istart + minMatchLength) {
+                BYTE const* const split = ip + splits[n] - minMatchLength;
+                U64 const xxhash = XXH64(split, minMatchLength, 0);
+                U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
+                ldmEntry_t entry;
+
+                entry.offset = (U32)(split - base);
+                entry.checksum = (U32)(xxhash >> 32);
+                ZSTD_ldm_insertEntry(ldmState, hash, entry, *params);
+            }
+        }
+
+        ip += hashed;
     }
 }
 
@@ -246,10 +311,10 @@
  *  (after a long match, only update tables a limited amount). */
 static void ZSTD_ldm_limitTableUpdate(ZSTD_matchState_t* ms, const BYTE* anchor)
 {
-    U32 const current = (U32)(anchor - ms->window.base);
-    if (current > ms->nextToUpdate + 1024) {
+    U32 const curr = (U32)(anchor - ms->window.base);
+    if (curr > ms->nextToUpdate + 1024) {
         ms->nextToUpdate =
-            current - MIN(512, current - ms->nextToUpdate - 1024);
+            curr - MIN(512, curr - ms->nextToUpdate - 1024);
     }
 }
 
@@ -260,11 +325,8 @@
     /* LDM parameters */
     int const extDict = ZSTD_window_hasExtDict(ldmState->window);
     U32 const minMatchLength = params->minMatchLength;
-    U64 const hashPower = ldmState->hashPower;
+    U32 const entsPerBucket = 1U << params->bucketSizeLog;
     U32 const hBits = params->hashLog - params->bucketSizeLog;
-    U32 const ldmBucketSize = 1U << params->bucketSizeLog;
-    U32 const hashRateLog = params->hashRateLog;
-    U32 const ldmTagMask = (1U << params->hashRateLog) - 1;
     /* Prefix and extDict parameters */
     U32 const dictLimit = ldmState->window.dictLimit;
     U32 const lowestIndex = extDict ? ldmState->window.lowLimit : dictLimit;
@@ -276,45 +338,69 @@
     /* Input bounds */
     BYTE const* const istart = (BYTE const*)src;
     BYTE const* const iend = istart + srcSize;
-    BYTE const* const ilimit = iend - MAX(minMatchLength, HASH_READ_SIZE);
+    BYTE const* const ilimit = iend - HASH_READ_SIZE;
     /* Input positions */
     BYTE const* anchor = istart;
     BYTE const* ip = istart;
-    /* Rolling hash */
-    BYTE const* lastHashed = NULL;
-    U64 rollingHash = 0;
+    /* Rolling hash state */
+    ldmRollingHashState_t hashState;
+    /* Arrays for staged-processing */
+    size_t* const splits = ldmState->splitIndices;
+    ldmMatchCandidate_t* const candidates = ldmState->matchCandidates;
+    unsigned numSplits;
 
-    while (ip <= ilimit) {
-        size_t mLength;
-        U32 const current = (U32)(ip - base);
-        size_t forwardMatchLength = 0, backwardMatchLength = 0;
-        ldmEntry_t* bestEntry = NULL;
-        if (ip != istart) {
-            rollingHash = ZSTD_rollingHash_rotate(rollingHash, lastHashed[0],
-                                                  lastHashed[minMatchLength],
-                                                  hashPower);
-        } else {
-            rollingHash = ZSTD_rollingHash_compute(ip, minMatchLength);
-        }
-        lastHashed = ip;
+    if (srcSize < minMatchLength)
+        return iend - anchor;
 
-        /* Do not insert and do not look for a match */
-        if (ZSTD_ldm_getTag(rollingHash, hBits, hashRateLog) != ldmTagMask) {
-           ip++;
-           continue;
+    /* Initialize the rolling hash state with the first minMatchLength bytes */
+    ZSTD_ldm_gear_init(&hashState, params);
+    ZSTD_ldm_gear_reset(&hashState, ip, minMatchLength);
+    ip += minMatchLength;
+
+    while (ip < ilimit) {
+        size_t hashed;
+        unsigned n;
+
+        numSplits = 0;
+        hashed = ZSTD_ldm_gear_feed(&hashState, ip, ilimit - ip,
+                                    splits, &numSplits);
+
+        for (n = 0; n < numSplits; n++) {
+            BYTE const* const split = ip + splits[n] - minMatchLength;
+            U64 const xxhash = XXH64(split, minMatchLength, 0);
+            U32 const hash = (U32)(xxhash & (((U32)1 << hBits) - 1));
+
+            candidates[n].split = split;
+            candidates[n].hash = hash;
+            candidates[n].checksum = (U32)(xxhash >> 32);
+            candidates[n].bucket = ZSTD_ldm_getBucket(ldmState, hash, *params);
+            PREFETCH_L1(candidates[n].bucket);
         }
 
-        /* Get the best entry and compute the match lengths */
-        {
-            ldmEntry_t* const bucket =
-                ZSTD_ldm_getBucket(ldmState,
-                                   ZSTD_ldm_getSmallHash(rollingHash, hBits),
-                                   *params);
-            ldmEntry_t* cur;
-            size_t bestMatchLength = 0;
-            U32 const checksum = ZSTD_ldm_getChecksum(rollingHash, hBits);
+        for (n = 0; n < numSplits; n++) {
+            size_t forwardMatchLength = 0, backwardMatchLength = 0,
+                   bestMatchLength = 0, mLength;
+            U32 offset;
+            BYTE const* const split = candidates[n].split;
+            U32 const checksum = candidates[n].checksum;
+            U32 const hash = candidates[n].hash;
+            ldmEntry_t* const bucket = candidates[n].bucket;
+            ldmEntry_t const* cur;
+            ldmEntry_t const* bestEntry = NULL;
+            ldmEntry_t newEntry;
 
-            for (cur = bucket; cur < bucket + ldmBucketSize; ++cur) {
+            newEntry.offset = (U32)(split - base);
+            newEntry.checksum = checksum;
+
+            /* If a split point would generate a sequence overlapping with
+             * the previous one, we merely register it in the hash table and
+             * move on */
+            if (split < anchor) {
+                ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
+                continue;
+            }
+
+            for (cur = bucket; cur < bucket + entsPerBucket; cur++) {
                 size_t curForwardMatchLength, curBackwardMatchLength,
                        curTotalMatchLength;
                 if (cur->checksum != checksum || cur->offset <= lowestIndex) {
@@ -328,30 +414,23 @@
                         cur->offset < dictLimit ? dictEnd : iend;
                     BYTE const* const lowMatchPtr =
                         cur->offset < dictLimit ? dictStart : lowPrefixPtr;
-
-                    curForwardMatchLength = ZSTD_count_2segments(
-                                                ip, pMatch, iend,
-                                                matchEnd, lowPrefixPtr);
+                    curForwardMatchLength =
+                        ZSTD_count_2segments(split, pMatch, iend, matchEnd, lowPrefixPtr);
                     if (curForwardMatchLength < minMatchLength) {
                         continue;
                     }
-                    curBackwardMatchLength =
-                        ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
-                                                     lowMatchPtr);
-                    curTotalMatchLength = curForwardMatchLength +
-                                          curBackwardMatchLength;
+                    curBackwardMatchLength = ZSTD_ldm_countBackwardsMatch_2segments(
+                            split, anchor, pMatch, lowMatchPtr, dictStart, dictEnd);
                 } else { /* !extDict */
                     BYTE const* const pMatch = base + cur->offset;
-                    curForwardMatchLength = ZSTD_count(ip, pMatch, iend);
+                    curForwardMatchLength = ZSTD_count(split, pMatch, iend);
                     if (curForwardMatchLength < minMatchLength) {
                         continue;
                     }
                     curBackwardMatchLength =
-                        ZSTD_ldm_countBackwardsMatch(ip, anchor, pMatch,
-                                                     lowPrefixPtr);
-                    curTotalMatchLength = curForwardMatchLength +
-                                          curBackwardMatchLength;
+                        ZSTD_ldm_countBackwardsMatch(split, anchor, pMatch, lowPrefixPtr);
                 }
+                curTotalMatchLength = curForwardMatchLength + curBackwardMatchLength;
 
                 if (curTotalMatchLength > bestMatchLength) {
                     bestMatchLength = curTotalMatchLength;
@@ -360,57 +439,54 @@
                     bestEntry = cur;
                 }
             }
-        }
 
-        /* No match found -- continue searching */
-        if (bestEntry == NULL) {
-            ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash,
-                                             hBits, current,
-                                             *params);
-            ip++;
-            continue;
-        }
+            /* No match found -- insert an entry into the hash table
+             * and process the next candidate match */
+            if (bestEntry == NULL) {
+                ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
+                continue;
+            }
 
-        /* Match found */
-        mLength = forwardMatchLength + backwardMatchLength;
-        ip -= backwardMatchLength;
+            /* Match found */
+            offset = (U32)(split - base) - bestEntry->offset;
+            mLength = forwardMatchLength + backwardMatchLength;
+            {
+                rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
 
-        {
-            /* Store the sequence:
-             * ip = current - backwardMatchLength
-             * The match is at (bestEntry->offset - backwardMatchLength)
+                /* Out of sequence storage */
+                if (rawSeqStore->size == rawSeqStore->capacity)
+                    return ERROR(dstSize_tooSmall);
+                seq->litLength = (U32)(split - backwardMatchLength - anchor);
+                seq->matchLength = (U32)mLength;
+                seq->offset = offset;
+                rawSeqStore->size++;
+            }
+
+            /* Insert the current entry into the hash table --- it must be
+             * done after the previous block to avoid clobbering bestEntry */
+            ZSTD_ldm_insertEntry(ldmState, hash, newEntry, *params);
+
+            anchor = split + forwardMatchLength;
+
+            /* If we find a match that ends after the data that we've hashed
+             * then we have a repeating, overlapping, pattern. E.g. all zeros.
+             * If one repetition of the pattern matches our `stopMask` then all
+             * repetitions will. We don't need to insert them all into out table,
+             * only the first one. So skip over overlapping matches.
+             * This is a major speed boost (20x) for compressing a single byte
+             * repeated, when that byte ends up in the table.
              */
-            U32 const matchIndex = bestEntry->offset;
-            U32 const offset = current - matchIndex;
-            rawSeq* const seq = rawSeqStore->seq + rawSeqStore->size;
-
-            /* Out of sequence storage */
-            if (rawSeqStore->size == rawSeqStore->capacity)
-                return ERROR(dstSize_tooSmall);
-            seq->litLength = (U32)(ip - anchor);
-            seq->matchLength = (U32)mLength;
-            seq->offset = offset;
-            rawSeqStore->size++;
+            if (anchor > ip + hashed) {
+                ZSTD_ldm_gear_reset(&hashState, anchor - minMatchLength, minMatchLength);
+                /* Continue the outter loop at anchor (ip + hashed == anchor). */
+                ip = anchor - hashed;
+                break;
+            }
         }
 
-        /* Insert the current entry into the hash table */
-        ZSTD_ldm_makeEntryAndInsertByTag(ldmState, rollingHash, hBits,
-                                         (U32)(lastHashed - base),
-                                         *params);
-
-        assert(ip + backwardMatchLength == lastHashed);
-
-        /* Fill the hash table from lastHashed+1 to ip+mLength*/
-        /* Heuristic: don't need to fill the entire table at end of block */
-        if (ip + mLength <= ilimit) {
-            rollingHash = ZSTD_ldm_fillLdmHashTable(
-                              ldmState, rollingHash, lastHashed,
-                              ip + mLength, base, hBits, *params);
-            lastHashed = ip + mLength - 1;
-        }
-        ip += mLength;
-        anchor = ip;
+        ip += hashed;
     }
+
     return iend - anchor;
 }
 
@@ -459,7 +535,7 @@
 
         assert(chunkStart < iend);
         /* 1. Perform overflow correction if necessary. */
-        if (ZSTD_window_needOverflowCorrection(ldmState->window, chunkEnd)) {
+        if (ZSTD_window_needOverflowCorrection(ldmState->window, 0, maxDist, ldmState->loadedDictEnd, chunkStart, chunkEnd)) {
             U32 const ldmHSize = 1U << params->hashLog;
             U32 const correction = ZSTD_window_correctOverflow(
                 &ldmState->window, /* cycleLog */ 0, maxDist, chunkStart);
@@ -562,14 +638,32 @@
     return sequence;
 }
 
+void ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) {
+    U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);
+    while (currPos && rawSeqStore->pos < rawSeqStore->size) {
+        rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];
+        if (currPos >= currSeq.litLength + currSeq.matchLength) {
+            currPos -= currSeq.litLength + currSeq.matchLength;
+            rawSeqStore->pos++;
+        } else {
+            rawSeqStore->posInSequence = currPos;
+            break;
+        }
+    }
+    if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {
+        rawSeqStore->posInSequence = 0;
+    }
+}
+
 size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
     ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+    ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
     void const* src, size_t srcSize)
 {
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
     unsigned const minMatch = cParams->minMatch;
     ZSTD_blockCompressor const blockCompressor =
-        ZSTD_selectBlockCompressor(cParams->strategy, ZSTD_matchState_dictMode(ms));
+        ZSTD_selectBlockCompressor(cParams->strategy, useRowMatchFinder, ZSTD_matchState_dictMode(ms));
     /* Input bounds */
     BYTE const* const istart = (BYTE const*)src;
     BYTE const* const iend = istart + srcSize;
@@ -577,9 +671,18 @@
     BYTE const* ip = istart;
 
     DEBUGLOG(5, "ZSTD_ldm_blockCompress: srcSize=%zu", srcSize);
+    /* If using opt parser, use LDMs only as candidates rather than always accepting them */
+    if (cParams->strategy >= ZSTD_btopt) {
+        size_t lastLLSize;
+        ms->ldmSeqStore = rawSeqStore;
+        lastLLSize = blockCompressor(ms, seqStore, rep, src, srcSize);
+        ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore, srcSize);
+        return lastLLSize;
+    }
+
     assert(rawSeqStore->pos <= rawSeqStore->size);
     assert(rawSeqStore->size <= rawSeqStore->capacity);
-    /* Loop through each sequence and apply the block compressor to the lits */
+    /* Loop through each sequence and apply the block compressor to the literals */
     while (rawSeqStore->pos < rawSeqStore->size && ip < iend) {
         /* maybeSplitSequence updates rawSeqStore->pos */
         rawSeq const sequence = maybeSplitSequence(rawSeqStore,
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm.h b/Utilities/cmzstd/lib/compress/zstd_ldm.h
index 229ea05..393466f 100644
--- a/Utilities/cmzstd/lib/compress/zstd_ldm.h
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -66,6 +66,7 @@
  */
 size_t ZSTD_ldm_blockCompress(rawSeqStore_t* rawSeqStore,
             ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
+            ZSTD_useRowMatchFinderMode_e useRowMatchFinder,
             void const* src, size_t srcSize);
 
 /**
@@ -73,11 +74,17 @@
  *
  * Skip past `srcSize` bytes worth of sequences in `rawSeqStore`.
  * Avoids emitting matches less than `minMatch` bytes.
- * Must be called for data with is not passed to ZSTD_ldm_blockCompress().
+ * Must be called for data that is not passed to ZSTD_ldm_blockCompress().
  */
 void ZSTD_ldm_skipSequences(rawSeqStore_t* rawSeqStore, size_t srcSize,
     U32 const minMatch);
 
+/* ZSTD_ldm_skipRawSeqStoreBytes():
+ * Moves forward in rawSeqStore by nbBytes, updating fields 'pos' and 'posInSequence'.
+ * Not to be used in conjunction with ZSTD_ldm_skipSequences().
+ * Must be called for data with is not passed to ZSTD_ldm_blockCompress().
+ */
+void ZSTD_ldm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes);
 
 /** ZSTD_ldm_getTableSize() :
  *  Estimate the space needed for long distance matching tables or 0 if LDM is
diff --git a/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h b/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h
new file mode 100644
index 0000000..e5c24d8
--- /dev/null
+++ b/Utilities/cmzstd/lib/compress/zstd_ldm_geartab.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) Yann Collet, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+ */
+
+#ifndef ZSTD_LDM_GEARTAB_H
+#define ZSTD_LDM_GEARTAB_H
+
+static U64 ZSTD_ldm_gearTab[256] = {
+    0xf5b8f72c5f77775c, 0x84935f266b7ac412, 0xb647ada9ca730ccc,
+    0xb065bb4b114fb1de, 0x34584e7e8c3a9fd0, 0x4e97e17c6ae26b05,
+    0x3a03d743bc99a604, 0xcecd042422c4044f, 0x76de76c58524259e,
+    0x9c8528f65badeaca, 0x86563706e2097529, 0x2902475fa375d889,
+    0xafb32a9739a5ebe6, 0xce2714da3883e639, 0x21eaf821722e69e,
+    0x37b628620b628,    0x49a8d455d88caf5,  0x8556d711e6958140,
+    0x4f7ae74fc605c1f,  0x829f0c3468bd3a20, 0x4ffdc885c625179e,
+    0x8473de048a3daf1b, 0x51008822b05646b2, 0x69d75d12b2d1cc5f,
+    0x8c9d4a19159154bc, 0xc3cc10f4abbd4003, 0xd06ddc1cecb97391,
+    0xbe48e6e7ed80302e, 0x3481db31cee03547, 0xacc3f67cdaa1d210,
+    0x65cb771d8c7f96cc, 0x8eb27177055723dd, 0xc789950d44cd94be,
+    0x934feadc3700b12b, 0x5e485f11edbdf182, 0x1e2e2a46fd64767a,
+    0x2969ca71d82efa7c, 0x9d46e9935ebbba2e, 0xe056b67e05e6822b,
+    0x94d73f55739d03a0, 0xcd7010bdb69b5a03, 0x455ef9fcd79b82f4,
+    0x869cb54a8749c161, 0x38d1a4fa6185d225, 0xb475166f94bbe9bb,
+    0xa4143548720959f1, 0x7aed4780ba6b26ba, 0xd0ce264439e02312,
+    0x84366d746078d508, 0xa8ce973c72ed17be, 0x21c323a29a430b01,
+    0x9962d617e3af80ee, 0xab0ce91d9c8cf75b, 0x530e8ee6d19a4dbc,
+    0x2ef68c0cf53f5d72, 0xc03a681640a85506, 0x496e4e9f9c310967,
+    0x78580472b59b14a0, 0x273824c23b388577, 0x66bf923ad45cb553,
+    0x47ae1a5a2492ba86, 0x35e304569e229659, 0x4765182a46870b6f,
+    0x6cbab625e9099412, 0xddac9a2e598522c1, 0x7172086e666624f2,
+    0xdf5003ca503b7837, 0x88c0c1db78563d09, 0x58d51865acfc289d,
+    0x177671aec65224f1, 0xfb79d8a241e967d7, 0x2be1e101cad9a49a,
+    0x6625682f6e29186b, 0x399553457ac06e50, 0x35dffb4c23abb74,
+    0x429db2591f54aade, 0xc52802a8037d1009, 0x6acb27381f0b25f3,
+    0xf45e2551ee4f823b, 0x8b0ea2d99580c2f7, 0x3bed519cbcb4e1e1,
+    0xff452823dbb010a,  0x9d42ed614f3dd267, 0x5b9313c06257c57b,
+    0xa114b8008b5e1442, 0xc1fe311c11c13d4b, 0x66e8763ea34c5568,
+    0x8b982af1c262f05d, 0xee8876faaa75fbb7, 0x8a62a4d0d172bb2a,
+    0xc13d94a3b7449a97, 0x6dbbba9dc15d037c, 0xc786101f1d92e0f1,
+    0xd78681a907a0b79b, 0xf61aaf2962c9abb9, 0x2cfd16fcd3cb7ad9,
+    0x868c5b6744624d21, 0x25e650899c74ddd7, 0xba042af4a7c37463,
+    0x4eb1a539465a3eca, 0xbe09dbf03b05d5ca, 0x774e5a362b5472ba,
+    0x47a1221229d183cd, 0x504b0ca18ef5a2df, 0xdffbdfbde2456eb9,
+    0x46cd2b2fbee34634, 0xf2aef8fe819d98c3, 0x357f5276d4599d61,
+    0x24a5483879c453e3, 0x88026889192b4b9,  0x28da96671782dbec,
+    0x4ef37c40588e9aaa, 0x8837b90651bc9fb3, 0xc164f741d3f0e5d6,
+    0xbc135a0a704b70ba, 0x69cd868f7622ada,  0xbc37ba89e0b9c0ab,
+    0x47c14a01323552f6, 0x4f00794bacee98bb, 0x7107de7d637a69d5,
+    0x88af793bb6f2255e, 0xf3c6466b8799b598, 0xc288c616aa7f3b59,
+    0x81ca63cf42fca3fd, 0x88d85ace36a2674b, 0xd056bd3792389e7,
+    0xe55c396c4e9dd32d, 0xbefb504571e6c0a6, 0x96ab32115e91e8cc,
+    0xbf8acb18de8f38d1, 0x66dae58801672606, 0x833b6017872317fb,
+    0xb87c16f2d1c92864, 0xdb766a74e58b669c, 0x89659f85c61417be,
+    0xc8daad856011ea0c, 0x76a4b565b6fe7eae, 0xa469d085f6237312,
+    0xaaf0365683a3e96c, 0x4dbb746f8424f7b8, 0x638755af4e4acc1,
+    0x3d7807f5bde64486, 0x17be6d8f5bbb7639, 0x903f0cd44dc35dc,
+    0x67b672eafdf1196c, 0xa676ff93ed4c82f1, 0x521d1004c5053d9d,
+    0x37ba9ad09ccc9202, 0x84e54d297aacfb51, 0xa0b4b776a143445,
+    0x820d471e20b348e,  0x1874383cb83d46dc, 0x97edeec7a1efe11c,
+    0xb330e50b1bdc42aa, 0x1dd91955ce70e032, 0xa514cdb88f2939d5,
+    0x2791233fd90db9d3, 0x7b670a4cc50f7a9b, 0x77c07d2a05c6dfa5,
+    0xe3778b6646d0a6fa, 0xb39c8eda47b56749, 0x933ed448addbef28,
+    0xaf846af6ab7d0bf4, 0xe5af208eb666e49,  0x5e6622f73534cd6a,
+    0x297daeca42ef5b6e, 0x862daef3d35539a6, 0xe68722498f8e1ea9,
+    0x981c53093dc0d572, 0xfa09b0bfbf86fbf5, 0x30b1e96166219f15,
+    0x70e7d466bdc4fb83, 0x5a66736e35f2a8e9, 0xcddb59d2b7c1baef,
+    0xd6c7d247d26d8996, 0xea4e39eac8de1ba3, 0x539c8bb19fa3aff2,
+    0x9f90e4c5fd508d8,  0xa34e5956fbaf3385, 0x2e2f8e151d3ef375,
+    0x173691e9b83faec1, 0xb85a8d56bf016379, 0x8382381267408ae3,
+    0xb90f901bbdc0096d, 0x7c6ad32933bcec65, 0x76bb5e2f2c8ad595,
+    0x390f851a6cf46d28, 0xc3e6064da1c2da72, 0xc52a0c101cfa5389,
+    0xd78eaf84a3fbc530, 0x3781b9e2288b997e, 0x73c2f6dea83d05c4,
+    0x4228e364c5b5ed7,  0x9d7a3edf0da43911, 0x8edcfeda24686756,
+    0x5e7667a7b7a9b3a1, 0x4c4f389fa143791d, 0xb08bc1023da7cddc,
+    0x7ab4be3ae529b1cc, 0x754e6132dbe74ff9, 0x71635442a839df45,
+    0x2f6fb1643fbe52de, 0x961e0a42cf7a8177, 0xf3b45d83d89ef2ea,
+    0xee3de4cf4a6e3e9b, 0xcd6848542c3295e7, 0xe4cee1664c78662f,
+    0x9947548b474c68c4, 0x25d73777a5ed8b0b, 0xc915b1d636b7fc,
+    0x21c2ba75d9b0d2da, 0x5f6b5dcf608a64a1, 0xdcf333255ff9570c,
+    0x633b922418ced4ee, 0xc136dde0b004b34a, 0x58cc83b05d4b2f5a,
+    0x5eb424dda28e42d2, 0x62df47369739cd98, 0xb4e0b42485e4ce17,
+    0x16e1f0c1f9a8d1e7, 0x8ec3916707560ebf, 0x62ba6e2df2cc9db3,
+    0xcbf9f4ff77d83a16, 0x78d9d7d07d2bbcc4, 0xef554ce1e02c41f4,
+    0x8d7581127eccf94d, 0xa9b53336cb3c8a05, 0x38c42c0bf45c4f91,
+    0x640893cdf4488863, 0x80ec34bc575ea568, 0x39f324f5b48eaa40,
+    0xe9d9ed1f8eff527f, 0x9224fc058cc5a214, 0xbaba00b04cfe7741,
+    0x309a9f120fcf52af, 0xa558f3ec65626212, 0x424bec8b7adabe2f,
+    0x41622513a6aea433, 0xb88da2d5324ca798, 0xd287733b245528a4,
+    0x9a44697e6d68aec3, 0x7b1093be2f49bb28, 0x50bbec632e3d8aad,
+    0x6cd90723e1ea8283, 0x897b9e7431b02bf3, 0x219efdcb338a7047,
+    0x3b0311f0a27c0656, 0xdb17bf91c0db96e7, 0x8cd4fd6b4e85a5b2,
+    0xfab071054ba6409d, 0x40d6fe831fa9dfd9, 0xaf358debad7d791e,
+    0xeb8d0e25a65e3e58, 0xbbcbd3df14e08580, 0xcf751f27ecdab2b,
+    0x2b4da14f2613d8f4
+};
+
+#endif /* ZSTD_LDM_GEARTAB_H */
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.c b/Utilities/cmzstd/lib/compress/zstd_opt.c
index 36fff05..402a7e5 100644
--- a/Utilities/cmzstd/lib/compress/zstd_opt.c
+++ b/Utilities/cmzstd/lib/compress/zstd_opt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
+ * Copyright (c) Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -386,32 +386,32 @@
     const BYTE* const dictEnd = dictBase + dictLimit;
     const BYTE* const prefixStart = base + dictLimit;
     const BYTE* match;
-    const U32 current = (U32)(ip-base);
-    const U32 btLow = btMask >= current ? 0 : current - btMask;
-    U32* smallerPtr = bt + 2*(current&btMask);
+    const U32 curr = (U32)(ip-base);
+    const U32 btLow = btMask >= curr ? 0 : curr - btMask;
+    U32* smallerPtr = bt + 2*(curr&btMask);
     U32* largerPtr  = smallerPtr + 1;
     U32 dummy32;   /* to be nullified at the end */
     U32 const windowLow = ms->window.lowLimit;
-    U32 matchEndIdx = current+8+1;
+    U32 matchEndIdx = curr+8+1;
     size_t bestLength = 8;
     U32 nbCompares = 1U << cParams->searchLog;
 #ifdef ZSTD_C_PREDICT
-    U32 predictedSmall = *(bt + 2*((current-1)&btMask) + 0);
-    U32 predictedLarge = *(bt + 2*((current-1)&btMask) + 1);
+    U32 predictedSmall = *(bt + 2*((curr-1)&btMask) + 0);
+    U32 predictedLarge = *(bt + 2*((curr-1)&btMask) + 1);
     predictedSmall += (predictedSmall>0);
     predictedLarge += (predictedLarge>0);
 #endif /* ZSTD_C_PREDICT */
 
-    DEBUGLOG(8, "ZSTD_insertBt1 (%u)", current);
+    DEBUGLOG(8, "ZSTD_insertBt1 (%u)", curr);
 
     assert(ip <= iend-8);   /* required for h calculation */
-    hashTable[h] = current;   /* Update Hash Table */
+    hashTable[h] = curr;   /* Update Hash Table */
 
     assert(windowLow > 0);
     while (nbCompares-- && (matchIndex >= windowLow)) {
         U32* const nextPtr = bt + 2*(matchIndex & btMask);
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
-        assert(matchIndex < current);
+        assert(matchIndex < curr);
 
 #ifdef ZSTD_C_PREDICT   /* note : can create issues when hlog small <= 11 */
         const U32* predictPtr = bt + 2*((matchIndex-1) & btMask);   /* written this way, as bt is a roll buffer */
@@ -474,8 +474,8 @@
     *smallerPtr = *largerPtr = 0;
     {   U32 positions = 0;
         if (bestLength > 384) positions = MIN(192, (U32)(bestLength - 384));   /* speed optimization */
-        assert(matchEndIdx > current + 8);
-        return MAX(positions, matchEndIdx - (current + 8));
+        assert(matchEndIdx > curr + 8);
+        return MAX(positions, matchEndIdx - (curr + 8));
     }
 }
 
@@ -519,7 +519,7 @@
     const ZSTD_compressionParameters* const cParams = &ms->cParams;
     U32 const sufficient_len = MIN(cParams->targetLength, ZSTD_OPT_NUM -1);
     const BYTE* const base = ms->window.base;
-    U32 const current = (U32)(ip-base);
+    U32 const curr = (U32)(ip-base);
     U32 const hashLog = cParams->hashLog;
     U32 const minMatch = (mls==3) ? 3 : 4;
     U32* const hashTable = ms->hashTable;
@@ -533,12 +533,12 @@
     U32 const dictLimit = ms->window.dictLimit;
     const BYTE* const dictEnd = dictBase + dictLimit;
     const BYTE* const prefixStart = base + dictLimit;
-    U32 const btLow = (btMask >= current) ? 0 : current - btMask;
-    U32 const windowLow = ZSTD_getLowestMatchIndex(ms, current, cParams->windowLog);
+    U32 const btLow = (btMask >= curr) ? 0 : curr - btMask;
+    U32 const windowLow = ZSTD_getLowestMatchIndex(ms, curr, cParams->windowLog);
     U32 const matchLow = windowLow ? windowLow : 1;
-    U32* smallerPtr = bt + 2*(current&btMask);
-    U32* largerPtr  = bt + 2*(current&btMask) + 1;
-    U32 matchEndIdx = current+8+1;   /* farthest referenced position of any match => detects repetitive patterns */
+    U32* smallerPtr = bt + 2*(curr&btMask);
+    U32* largerPtr  = bt + 2*(curr&btMask) + 1;
+    U32 matchEndIdx = curr+8+1;   /* farthest referenced position of any match => detects repetitive patterns */
     U32 dummy32;   /* to be nullified at the end */
     U32 mnum = 0;
     U32 nbCompares = 1U << cParams->searchLog;
@@ -557,7 +557,7 @@
     U32         const dmsBtLow      = dictMode == ZSTD_dictMatchState && dmsBtMask < dmsHighLimit - dmsLowLimit ? dmsHighLimit - dmsBtMask : dmsLowLimit;
 
     size_t bestLength = lengthToBeat-1;
-    DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", current);
+    DEBUGLOG(8, "ZSTD_insertBtAndGetAllMatches: current=%u", curr);
 
     /* check repCode */
     assert(ll0 <= 1);   /* necessarily 1 or 0 */
@@ -565,29 +565,29 @@
         U32 repCode;
         for (repCode = ll0; repCode < lastR; repCode++) {
             U32 const repOffset = (repCode==ZSTD_REP_NUM) ? (rep[0] - 1) : rep[repCode];
-            U32 const repIndex = current - repOffset;
+            U32 const repIndex = curr - repOffset;
             U32 repLen = 0;
-            assert(current >= dictLimit);
-            if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < current-dictLimit) {  /* equivalent to `current > repIndex >= dictLimit` */
+            assert(curr >= dictLimit);
+            if (repOffset-1 /* intentional overflow, discards 0 and -1 */ < curr-dictLimit) {  /* equivalent to `curr > repIndex >= dictLimit` */
                 /* We must validate the repcode offset because when we're using a dictionary the
                  * valid offset range shrinks when the dictionary goes out of bounds.
                  */
                 if ((repIndex >= windowLow) & (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repOffset, minMatch))) {
                     repLen = (U32)ZSTD_count(ip+minMatch, ip+minMatch-repOffset, iLimit) + minMatch;
                 }
-            } else {  /* repIndex < dictLimit || repIndex >= current */
+            } else {  /* repIndex < dictLimit || repIndex >= curr */
                 const BYTE* const repMatch = dictMode == ZSTD_dictMatchState ?
                                              dmsBase + repIndex - dmsIndexDelta :
                                              dictBase + repIndex;
-                assert(current >= windowLow);
+                assert(curr >= windowLow);
                 if ( dictMode == ZSTD_extDict
-                  && ( ((repOffset-1) /*intentional overflow*/ < current - windowLow)  /* equivalent to `current > repIndex >= windowLow` */
+                  && ( ((repOffset-1) /*intentional overflow*/ < curr - windowLow)  /* equivalent to `curr > repIndex >= windowLow` */
                      & (((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */)
                   && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {
                     repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dictEnd, prefixStart) + minMatch;
                 }
                 if (dictMode == ZSTD_dictMatchState
-                  && ( ((repOffset-1) /*intentional overflow*/ < current - (dmsLowLimit + dmsIndexDelta))  /* equivalent to `current > repIndex >= dmsLowLimit` */
+                  && ( ((repOffset-1) /*intentional overflow*/ < curr - (dmsLowLimit + dmsIndexDelta))  /* equivalent to `curr > repIndex >= dmsLowLimit` */
                      & ((U32)((dictLimit-1) - repIndex) >= 3) ) /* intentional overflow : do not test positions overlapping 2 memory segments */
                   && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch)) ) {
                     repLen = (U32)ZSTD_count_2segments(ip+minMatch, repMatch+minMatch, iLimit, dmsEnd, prefixStart) + minMatch;
@@ -609,7 +609,7 @@
     if ((mls == 3) /*static*/ && (bestLength < mls)) {
         U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(ms, nextToUpdate3, ip);
         if ((matchIndex3 >= matchLow)
-          & (current - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
+          & (curr - matchIndex3 < (1<<18)) /*heuristic : longer distance likely too expensive*/ ) {
             size_t mlen;
             if ((dictMode == ZSTD_noDict) /*static*/ || (dictMode == ZSTD_dictMatchState) /*static*/ || (matchIndex3 >= dictLimit)) {
                 const BYTE* const match = base + matchIndex3;
@@ -624,26 +624,26 @@
                 DEBUGLOG(8, "found small match with hlog3, of length %u",
                             (U32)mlen);
                 bestLength = mlen;
-                assert(current > matchIndex3);
+                assert(curr > matchIndex3);
                 assert(mnum==0);  /* no prior solution */
-                matches[0].off = (current - matchIndex3) + ZSTD_REP_MOVE;
+                matches[0].off = (curr - matchIndex3) + ZSTD_REP_MOVE;
                 matches[0].len = (U32)mlen;
                 mnum = 1;
                 if ( (mlen > sufficient_len) |
                      (ip+mlen == iLimit) ) {  /* best possible length */
-                    ms->nextToUpdate = current+1;  /* skip insertion */
+                    ms->nextToUpdate = curr+1;  /* skip insertion */
                     return 1;
         }   }   }
         /* no dictMatchState lookup: dicts don't have a populated HC3 table */
     }
 
-    hashTable[h] = current;   /* Update Hash Table */
+    hashTable[h] = curr;   /* Update Hash Table */
 
     while (nbCompares-- && (matchIndex >= matchLow)) {
         U32* const nextPtr = bt + 2*(matchIndex & btMask);
         const BYTE* match;
         size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger);   /* guaranteed minimum nb of common bytes */
-        assert(current > matchIndex);
+        assert(curr > matchIndex);
 
         if ((dictMode == ZSTD_noDict) || (dictMode == ZSTD_dictMatchState) || (matchIndex+matchLength >= dictLimit)) {
             assert(matchIndex+matchLength >= dictLimit);  /* ensure the condition is correct when !extDict */
@@ -660,12 +660,12 @@
 
         if (matchLength > bestLength) {
             DEBUGLOG(8, "found match of length %u at distance %u (offCode=%u)",
-                    (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE);
+                    (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE);
             assert(matchEndIdx > matchIndex);
             if (matchLength > matchEndIdx - matchIndex)
                 matchEndIdx = matchIndex + (U32)matchLength;
             bestLength = matchLength;
-            matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE;
+            matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE;
             matches[mnum].len = (U32)matchLength;
             mnum++;
             if ( (matchLength > ZSTD_OPT_NUM)
@@ -708,11 +708,11 @@
             if (matchLength > bestLength) {
                 matchIndex = dictMatchIndex + dmsIndexDelta;
                 DEBUGLOG(8, "found dms match of length %u at distance %u (offCode=%u)",
-                        (U32)matchLength, current - matchIndex, current - matchIndex + ZSTD_REP_MOVE);
+                        (U32)matchLength, curr - matchIndex, curr - matchIndex + ZSTD_REP_MOVE);
                 if (matchLength > matchEndIdx - matchIndex)
                     matchEndIdx = matchIndex + (U32)matchLength;
                 bestLength = matchLength;
-                matches[mnum].off = (current - matchIndex) + ZSTD_REP_MOVE;
+                matches[mnum].off = (curr - matchIndex) + ZSTD_REP_MOVE;
                 matches[mnum].len = (U32)matchLength;
                 mnum++;
                 if ( (matchLength > ZSTD_OPT_NUM)
@@ -733,7 +733,7 @@
         }
     }
 
-    assert(matchEndIdx > current+8);
+    assert(matchEndIdx > curr+8);
     ms->nextToUpdate = matchEndIdx - 8;  /* skip repetitive patterns */
     return mnum;
 }
@@ -764,6 +764,140 @@
     }
 }
 
+/*************************
+*  LDM helper functions  *
+*************************/
+
+/* Struct containing info needed to make decision about ldm inclusion */
+typedef struct {
+    rawSeqStore_t seqStore;         /* External match candidates store for this block */
+    U32 startPosInBlock;            /* Start position of the current match candidate */
+    U32 endPosInBlock;              /* End position of the current match candidate */
+    U32 offset;                     /* Offset of the match candidate */
+} ZSTD_optLdm_t;
+
+/* ZSTD_optLdm_skipRawSeqStoreBytes():
+ * Moves forward in rawSeqStore by nbBytes, which will update the fields 'pos' and 'posInSequence'.
+ */
+static void ZSTD_optLdm_skipRawSeqStoreBytes(rawSeqStore_t* rawSeqStore, size_t nbBytes) {
+    U32 currPos = (U32)(rawSeqStore->posInSequence + nbBytes);
+    while (currPos && rawSeqStore->pos < rawSeqStore->size) {
+        rawSeq currSeq = rawSeqStore->seq[rawSeqStore->pos];
+        if (currPos >= currSeq.litLength + currSeq.matchLength) {
+            currPos -= currSeq.litLength + currSeq.matchLength;
+            rawSeqStore->pos++;
+        } else {
+            rawSeqStore->posInSequence = currPos;
+            break;
+        }
+    }
+    if (currPos == 0 || rawSeqStore->pos == rawSeqStore->size) {
+        rawSeqStore->posInSequence = 0;
+    }
+}
+
+/* ZSTD_opt_getNextMatchAndUpdateSeqStore():
+ * Calculates the beginning and end of the next match in the current block.
+ * Updates 'pos' and 'posInSequence' of the ldmSeqStore.
+ */
+static void ZSTD_opt_getNextMatchAndUpdateSeqStore(ZSTD_optLdm_t* optLdm, U32 currPosInBlock,
+                                                   U32 blockBytesRemaining) {
+    rawSeq currSeq;
+    U32 currBlockEndPos;
+    U32 literalsBytesRemaining;
+    U32 matchBytesRemaining;
+
+    /* Setting match end position to MAX to ensure we never use an LDM during this block */
+    if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) {
+        optLdm->startPosInBlock = UINT_MAX;
+        optLdm->endPosInBlock = UINT_MAX;
+        return;
+    }
+    /* Calculate appropriate bytes left in matchLength and litLength after adjusting
+       based on ldmSeqStore->posInSequence */
+    currSeq = optLdm->seqStore.seq[optLdm->seqStore.pos];
+    assert(optLdm->seqStore.posInSequence <= currSeq.litLength + currSeq.matchLength);
+    currBlockEndPos = currPosInBlock + blockBytesRemaining;
+    literalsBytesRemaining = (optLdm->seqStore.posInSequence < currSeq.litLength) ?
+            currSeq.litLength - (U32)optLdm->seqStore.posInSequence :
+            0;
+    matchBytesRemaining = (literalsBytesRemaining == 0) ?
+            currSeq.matchLength - ((U32)optLdm->seqStore.posInSequence - currSeq.litLength) :
+            currSeq.matchLength;
+
+    /* If there are more literal bytes than bytes remaining in block, no ldm is possible */
+    if (literalsBytesRemaining >= blockBytesRemaining) {
+        optLdm->startPosInBlock = UINT_MAX;
+        optLdm->endPosInBlock = UINT_MAX;
+        ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, blockBytesRemaining);
+        return;
+    }
+
+    /* Matches may be < MINMATCH by this process. In that case, we will reject them
+       when we are deciding whether or not to add the ldm */
+    optLdm->startPosInBlock = currPosInBlock + literalsBytesRemaining;
+    optLdm->endPosInBlock = optLdm->startPosInBlock + matchBytesRemaining;
+    optLdm->offset = currSeq.offset;
+
+    if (optLdm->endPosInBlock > currBlockEndPos) {
+        /* Match ends after the block ends, we can't use the whole match */
+        optLdm->endPosInBlock = currBlockEndPos;
+        ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, currBlockEndPos - currPosInBlock);
+    } else {
+        /* Consume nb of bytes equal to size of sequence left */
+        ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, literalsBytesRemaining + matchBytesRemaining);
+    }
+}
+
+/* ZSTD_optLdm_maybeAddMatch():
+ * Adds a match if it's long enough, based on it's 'matchStartPosInBlock'
+ * and 'matchEndPosInBlock', into 'matches'. Maintains the correct ordering of 'matches'
+ */
+static void ZSTD_optLdm_maybeAddMatch(ZSTD_match_t* matches, U32* nbMatches,
+                                      ZSTD_optLdm_t* optLdm, U32 currPosInBlock) {
+    U32 posDiff = currPosInBlock - optLdm->startPosInBlock;
+    /* Note: ZSTD_match_t actually contains offCode and matchLength (before subtracting MINMATCH) */
+    U32 candidateMatchLength = optLdm->endPosInBlock - optLdm->startPosInBlock - posDiff;
+    U32 candidateOffCode = optLdm->offset + ZSTD_REP_MOVE;
+
+    /* Ensure that current block position is not outside of the match */
+    if (currPosInBlock < optLdm->startPosInBlock
+      || currPosInBlock >= optLdm->endPosInBlock
+      || candidateMatchLength < MINMATCH) {
+        return;
+    }
+
+    if (*nbMatches == 0 || ((candidateMatchLength > matches[*nbMatches-1].len) && *nbMatches < ZSTD_OPT_NUM)) {
+        DEBUGLOG(6, "ZSTD_optLdm_maybeAddMatch(): Adding ldm candidate match (offCode: %u matchLength %u) at block position=%u",
+                 candidateOffCode, candidateMatchLength, currPosInBlock);
+        matches[*nbMatches].len = candidateMatchLength;
+        matches[*nbMatches].off = candidateOffCode;
+        (*nbMatches)++;
+    }
+}
+
+/* ZSTD_optLdm_processMatchCandidate():
+ * Wrapper function to update ldm seq store and call ldm functions as necessary.
+ */
+static void ZSTD_optLdm_processMatchCandidate(ZSTD_optLdm_t* optLdm, ZSTD_match_t* matches, U32* nbMatches,
+                                              U32 currPosInBlock, U32 remainingBytes) {
+    if (optLdm->seqStore.size == 0 || optLdm->seqStore.pos >= optLdm->seqStore.size) {
+        return;
+    }
+
+    if (currPosInBlock >= optLdm->endPosInBlock) {
+        if (currPosInBlock > optLdm->endPosInBlock) {
+            /* The position at which ZSTD_optLdm_processMatchCandidate() is called is not necessarily
+             * at the end of a match from the ldm seq store, and will often be some bytes
+             * over beyond matchEndPosInBlock. As such, we need to correct for these "overshoots"
+             */
+            U32 posOvershoot = currPosInBlock - optLdm->endPosInBlock;
+            ZSTD_optLdm_skipRawSeqStoreBytes(&optLdm->seqStore, posOvershoot);
+        } 
+        ZSTD_opt_getNextMatchAndUpdateSeqStore(optLdm, currPosInBlock, remainingBytes);
+    }
+    ZSTD_optLdm_maybeAddMatch(matches, nbMatches, optLdm, currPosInBlock);
+}
 
 /*-*******************************
 *  Optimal parser
@@ -817,6 +951,11 @@
     ZSTD_optimal_t* const opt = optStatePtr->priceTable;
     ZSTD_match_t* const matches = optStatePtr->matchTable;
     ZSTD_optimal_t lastSequence;
+    ZSTD_optLdm_t optLdm;
+
+    optLdm.seqStore = ms->ldmSeqStore ? *ms->ldmSeqStore : kNullRawSeqStore;
+    optLdm.endPosInBlock = optLdm.startPosInBlock = optLdm.offset = 0;
+    ZSTD_opt_getNextMatchAndUpdateSeqStore(&optLdm, (U32)(ip-istart), (U32)(iend-ip));
 
     /* init */
     DEBUGLOG(5, "ZSTD_compressBlock_opt_generic: current=%u, prefix=%u, nextToUpdate=%u",
@@ -832,7 +971,9 @@
         /* find first match */
         {   U32 const litlen = (U32)(ip - anchor);
             U32 const ll0 = !litlen;
-            U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch);
+            U32 nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, ip, iend, dictMode, rep, ll0, minMatch);
+            ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches,
+                                              (U32)(ip-istart), (U32)(iend - ip));
             if (!nbMatches) { ip++; continue; }
 
             /* initialize opt[0] */
@@ -925,9 +1066,9 @@
             if (opt[cur].mlen != 0) {
                 U32 const prev = cur - opt[cur].mlen;
                 repcodes_t newReps = ZSTD_updateRep(opt[prev].rep, opt[cur].off, opt[cur].litlen==0);
-                memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t));
+                ZSTD_memcpy(opt[cur].rep, &newReps, sizeof(repcodes_t));
             } else {
-                memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t));
+                ZSTD_memcpy(opt[cur].rep, opt[cur - 1].rep, sizeof(repcodes_t));
             }
 
             /* last match must start at a minimum distance of 8 from oend */
@@ -945,8 +1086,12 @@
                 U32 const litlen = (opt[cur].mlen == 0) ? opt[cur].litlen : 0;
                 U32 const previousPrice = opt[cur].price;
                 U32 const basePrice = previousPrice + ZSTD_litLengthPrice(0, optStatePtr, optLevel);
-                U32 const nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch);
+                U32 nbMatches = ZSTD_BtGetAllMatches(matches, ms, &nextToUpdate3, inr, iend, dictMode, opt[cur].rep, ll0, minMatch);
                 U32 matchNb;
+
+                ZSTD_optLdm_processMatchCandidate(&optLdm, matches, &nbMatches,
+                                                  (U32)(inr-istart), (U32)(iend-inr));
+
                 if (!nbMatches) {
                     DEBUGLOG(7, "rPos:%u : no match found", cur);
                     continue;
@@ -1010,9 +1155,9 @@
          */
         if (lastSequence.mlen != 0) {
             repcodes_t reps = ZSTD_updateRep(opt[cur].rep, lastSequence.off, lastSequence.litlen==0);
-            memcpy(rep, &reps, sizeof(reps));
+            ZSTD_memcpy(rep, &reps, sizeof(reps));
         } else {
-            memcpy(rep, opt[cur].rep, sizeof(repcodes_t));
+            ZSTD_memcpy(rep, opt[cur].rep, sizeof(repcodes_t));
         }
 
         {   U32 const storeEnd = cur + 1;
@@ -1110,7 +1255,7 @@
                const void* src, size_t srcSize)
 {
     U32 tmpRep[ZSTD_REP_NUM];  /* updated rep codes will sink here */
-    memcpy(tmpRep, rep, sizeof(tmpRep));
+    ZSTD_memcpy(tmpRep, rep, sizeof(tmpRep));
 
     DEBUGLOG(4, "ZSTD_initStats_ultra (srcSize=%zu)", srcSize);
     assert(ms->opt.litLengthSum == 0);    /* first block */
@@ -1143,7 +1288,7 @@
         ZSTD_matchState_t* ms, seqStore_t* seqStore, U32 rep[ZSTD_REP_NUM],
         const void* src, size_t srcSize)
 {
-    U32 const current = (U32)((const BYTE*)src - ms->window.base);
+    U32 const curr = (U32)((const BYTE*)src - ms->window.base);
     DEBUGLOG(5, "ZSTD_compressBlock_btultra2 (srcSize=%zu)", srcSize);
 
     /* 2-pass strategy:
@@ -1158,7 +1303,7 @@
     if ( (ms->opt.litLengthSum==0)   /* first block */
       && (seqStore->sequences == seqStore->sequencesStart)  /* no ldm */
       && (ms->window.dictLimit == ms->window.lowLimit)   /* no dictionary */
-      && (current == ms->window.dictLimit)   /* start of frame, nothing already loaded nor skipped */
+      && (curr == ms->window.dictLimit)   /* start of frame, nothing already loaded nor skipped */
       && (srcSize > ZSTD_PREDEF_THRESHOLD)
       ) {
         ZSTD_initStats_ultra(ms, seqStore, rep, src, srcSize);
diff --git a/Utilities/cmzstd/lib/compress/zstd_opt.h b/Utilities/cmzstd/lib/compress/zstd_opt.h
index 9aba8a9..627255f 100644
--- a/Utilities/cmzstd/lib/compress/zstd_opt.h
+++ b/Utilities/cmzstd/lib/compress/zstd_opt.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.c b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
index 1e3c8fd..22aa3e1 100644
--- a/Utilities/cmzstd/lib/compress/zstdmt_compress.c
+++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -20,8 +20,7 @@
 
 
 /* ======   Dependencies   ====== */
-#include <string.h>      /* memcpy, memset */
-#include <limits.h>      /* INT_MAX, UINT_MAX */
+#include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memset, INT_MAX, UINT_MAX */
 #include "../common/mem.h"         /* MEM_STATIC */
 #include "../common/pool.h"        /* threadpool */
 #include "../common/threading.h"   /* mutex */
@@ -106,11 +105,11 @@
 static ZSTDMT_bufferPool* ZSTDMT_createBufferPool(unsigned nbWorkers, ZSTD_customMem cMem)
 {
     unsigned const maxNbBuffers = 2*nbWorkers + 3;
-    ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_calloc(
+    ZSTDMT_bufferPool* const bufPool = (ZSTDMT_bufferPool*)ZSTD_customCalloc(
         sizeof(ZSTDMT_bufferPool) + (maxNbBuffers-1) * sizeof(buffer_t), cMem);
     if (bufPool==NULL) return NULL;
     if (ZSTD_pthread_mutex_init(&bufPool->poolMutex, NULL)) {
-        ZSTD_free(bufPool, cMem);
+        ZSTD_customFree(bufPool, cMem);
         return NULL;
     }
     bufPool->bufferSize = 64 KB;
@@ -127,10 +126,10 @@
     if (!bufPool) return;   /* compatibility with free on NULL */
     for (u=0; u<bufPool->totalBuffers; u++) {
         DEBUGLOG(4, "free buffer %2u (address:%08X)", u, (U32)(size_t)bufPool->bTable[u].start);
-        ZSTD_free(bufPool->bTable[u].start, bufPool->cMem);
+        ZSTD_customFree(bufPool->bTable[u].start, bufPool->cMem);
     }
     ZSTD_pthread_mutex_destroy(&bufPool->poolMutex);
-    ZSTD_free(bufPool, bufPool->cMem);
+    ZSTD_customFree(bufPool, bufPool->cMem);
 }
 
 /* only works at initialization, not during compression */
@@ -201,13 +200,13 @@
         }
         /* size conditions not respected : scratch this buffer, create new one */
         DEBUGLOG(5, "ZSTDMT_getBuffer: existing buffer does not meet size conditions => freeing");
-        ZSTD_free(buf.start, bufPool->cMem);
+        ZSTD_customFree(buf.start, bufPool->cMem);
     }
     ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
     /* create new buffer */
     DEBUGLOG(5, "ZSTDMT_getBuffer: create a new buffer");
     {   buffer_t buffer;
-        void* const start = ZSTD_malloc(bSize, bufPool->cMem);
+        void* const start = ZSTD_customMalloc(bSize, bufPool->cMem);
         buffer.start = start;   /* note : start can be NULL if malloc fails ! */
         buffer.capacity = (start==NULL) ? 0 : bSize;
         if (start==NULL) {
@@ -229,13 +228,13 @@
 {
     size_t const bSize = bufPool->bufferSize;
     if (buffer.capacity < bSize) {
-        void* const start = ZSTD_malloc(bSize, bufPool->cMem);
+        void* const start = ZSTD_customMalloc(bSize, bufPool->cMem);
         buffer_t newBuffer;
         newBuffer.start = start;
         newBuffer.capacity = start == NULL ? 0 : bSize;
         if (start != NULL) {
             assert(newBuffer.capacity >= buffer.capacity);
-            memcpy(newBuffer.start, buffer.start, buffer.capacity);
+            ZSTD_memcpy(newBuffer.start, buffer.start, buffer.capacity);
             DEBUGLOG(5, "ZSTDMT_resizeBuffer: created buffer of size %u", (U32)bSize);
             return newBuffer;
         }
@@ -261,14 +260,12 @@
     ZSTD_pthread_mutex_unlock(&bufPool->poolMutex);
     /* Reached bufferPool capacity (should not happen) */
     DEBUGLOG(5, "ZSTDMT_releaseBuffer: pool capacity reached => freeing ");
-    ZSTD_free(buf.start, bufPool->cMem);
+    ZSTD_customFree(buf.start, bufPool->cMem);
 }
 
 
 /* =====   Seq Pool Wrapper   ====== */
 
-static rawSeqStore_t kNullRawSeqStore = {NULL, 0, 0, 0};
-
 typedef ZSTDMT_bufferPool ZSTDMT_seqPool;
 
 static size_t ZSTDMT_sizeof_seqPool(ZSTDMT_seqPool* seqPool)
@@ -278,7 +275,7 @@
 
 static rawSeqStore_t bufferToSeq(buffer_t buffer)
 {
-    rawSeqStore_t seq = {NULL, 0, 0, 0};
+    rawSeqStore_t seq = kNullRawSeqStore;
     seq.seq = (rawSeq*)buffer.start;
     seq.capacity = buffer.capacity / sizeof(rawSeq);
     return seq;
@@ -354,7 +351,7 @@
     for (cid=0; cid<pool->totalCCtx; cid++)
         ZSTD_freeCCtx(pool->cctx[cid]);  /* note : compatible with free on NULL */
     ZSTD_pthread_mutex_destroy(&pool->poolMutex);
-    ZSTD_free(pool, pool->cMem);
+    ZSTD_customFree(pool, pool->cMem);
 }
 
 /* ZSTDMT_createCCtxPool() :
@@ -362,12 +359,12 @@
 static ZSTDMT_CCtxPool* ZSTDMT_createCCtxPool(int nbWorkers,
                                               ZSTD_customMem cMem)
 {
-    ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_calloc(
+    ZSTDMT_CCtxPool* const cctxPool = (ZSTDMT_CCtxPool*) ZSTD_customCalloc(
         sizeof(ZSTDMT_CCtxPool) + (nbWorkers-1)*sizeof(ZSTD_CCtx*), cMem);
     assert(nbWorkers > 0);
     if (!cctxPool) return NULL;
     if (ZSTD_pthread_mutex_init(&cctxPool->poolMutex, NULL)) {
-        ZSTD_free(cctxPool, cMem);
+        ZSTD_customFree(cctxPool, cMem);
         return NULL;
     }
     cctxPool->cMem = cMem;
@@ -475,10 +472,8 @@
         ZSTD_ldm_adjustParameters(&params.ldmParams, &params.cParams);
         assert(params.ldmParams.hashLog >= params.ldmParams.bucketSizeLog);
         assert(params.ldmParams.hashRateLog < 32);
-        serialState->ldmState.hashPower =
-                ZSTD_rollingHash_primePower(params.ldmParams.minMatchLength);
     } else {
-        memset(&params.ldmParams, 0, sizeof(params.ldmParams));
+        ZSTD_memset(&params.ldmParams, 0, sizeof(params.ldmParams));
     }
     serialState->nextJobID = 0;
     if (params.fParams.checksumFlag)
@@ -489,35 +484,35 @@
         size_t const hashSize = ((size_t)1 << hashLog) * sizeof(ldmEntry_t);
         unsigned const bucketLog =
             params.ldmParams.hashLog - params.ldmParams.bucketSizeLog;
-        size_t const bucketSize = (size_t)1 << bucketLog;
         unsigned const prevBucketLog =
             serialState->params.ldmParams.hashLog -
             serialState->params.ldmParams.bucketSizeLog;
+        size_t const numBuckets = (size_t)1 << bucketLog;
         /* Size the seq pool tables */
         ZSTDMT_setNbSeq(seqPool, ZSTD_ldm_getMaxNbSeq(params.ldmParams, jobSize));
         /* Reset the window */
         ZSTD_window_init(&serialState->ldmState.window);
         /* Resize tables and output space if necessary. */
         if (serialState->ldmState.hashTable == NULL || serialState->params.ldmParams.hashLog < hashLog) {
-            ZSTD_free(serialState->ldmState.hashTable, cMem);
-            serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_malloc(hashSize, cMem);
+            ZSTD_customFree(serialState->ldmState.hashTable, cMem);
+            serialState->ldmState.hashTable = (ldmEntry_t*)ZSTD_customMalloc(hashSize, cMem);
         }
         if (serialState->ldmState.bucketOffsets == NULL || prevBucketLog < bucketLog) {
-            ZSTD_free(serialState->ldmState.bucketOffsets, cMem);
-            serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_malloc(bucketSize, cMem);
+            ZSTD_customFree(serialState->ldmState.bucketOffsets, cMem);
+            serialState->ldmState.bucketOffsets = (BYTE*)ZSTD_customMalloc(numBuckets, cMem);
         }
         if (!serialState->ldmState.hashTable || !serialState->ldmState.bucketOffsets)
             return 1;
         /* Zero the tables */
-        memset(serialState->ldmState.hashTable, 0, hashSize);
-        memset(serialState->ldmState.bucketOffsets, 0, bucketSize);
+        ZSTD_memset(serialState->ldmState.hashTable, 0, hashSize);
+        ZSTD_memset(serialState->ldmState.bucketOffsets, 0, numBuckets);
 
         /* Update window state and fill hash table with dict */
         serialState->ldmState.loadedDictEnd = 0;
         if (dictSize > 0) {
             if (dictContentType == ZSTD_dct_rawContent) {
                 BYTE const* const dictEnd = (const BYTE*)dict + dictSize;
-                ZSTD_window_update(&serialState->ldmState.window, dict, dictSize);
+                ZSTD_window_update(&serialState->ldmState.window, dict, dictSize, /* forceNonContiguous */ 0);
                 ZSTD_ldm_fillHashTable(&serialState->ldmState, (const BYTE*)dict, dictEnd, &params.ldmParams);
                 serialState->ldmState.loadedDictEnd = params.forceWindow ? 0 : (U32)(dictEnd - serialState->ldmState.window.base);
             } else {
@@ -537,7 +532,7 @@
 static int ZSTDMT_serialState_init(serialState_t* serialState)
 {
     int initError = 0;
-    memset(serialState, 0, sizeof(*serialState));
+    ZSTD_memset(serialState, 0, sizeof(*serialState));
     initError |= ZSTD_pthread_mutex_init(&serialState->mutex, NULL);
     initError |= ZSTD_pthread_cond_init(&serialState->cond, NULL);
     initError |= ZSTD_pthread_mutex_init(&serialState->ldmWindowMutex, NULL);
@@ -552,8 +547,8 @@
     ZSTD_pthread_cond_destroy(&serialState->cond);
     ZSTD_pthread_mutex_destroy(&serialState->ldmWindowMutex);
     ZSTD_pthread_cond_destroy(&serialState->ldmWindowCond);
-    ZSTD_free(serialState->ldmState.hashTable, cMem);
-    ZSTD_free(serialState->ldmState.bucketOffsets, cMem);
+    ZSTD_customFree(serialState->ldmState.hashTable, cMem);
+    ZSTD_customFree(serialState->ldmState.bucketOffsets, cMem);
 }
 
 static void ZSTDMT_serialState_update(serialState_t* serialState,
@@ -574,7 +569,7 @@
             assert(seqStore.seq != NULL && seqStore.pos == 0 &&
                    seqStore.size == 0 && seqStore.capacity > 0);
             assert(src.size <= serialState->params.jobSize);
-            ZSTD_window_update(&serialState->ldmState.window, src.start, src.size);
+            ZSTD_window_update(&serialState->ldmState.window, src.start, src.size, /* forceNonContiguous */ 0);
             error = ZSTD_ldm_generateSequences(
                 &serialState->ldmState, &seqStore,
                 &serialState->params.ldmParams, src.start, src.size);
@@ -686,6 +681,8 @@
     if (job->jobID != 0) jobParams.fParams.checksumFlag = 0;
     /* Don't run LDM for the chunks, since we handle it externally */
     jobParams.ldmParams.enableLdm = 0;
+    /* Correct nbWorkers to 0. */
+    jobParams.nbWorkers = 0;
 
 
     /* init */
@@ -698,6 +695,10 @@
         {   size_t const forceWindowError = ZSTD_CCtxParams_setParameter(&jobParams, ZSTD_c_forceMaxWindow, !job->firstJob);
             if (ZSTD_isError(forceWindowError)) JOB_ERROR(forceWindowError);
         }
+        if (!job->firstJob) {
+            size_t const err = ZSTD_CCtxParams_setParameter(&jobParams, ZSTD_c_deterministicRefPrefix, 0);
+            if (ZSTD_isError(err)) JOB_ERROR(err);
+        }
         {   size_t const initError = ZSTD_compressBegin_advanced_internal(cctx,
                                         job->prefix.start, job->prefix.size, ZSTD_dct_rawContent, /* load dictionary in "content-only" mode (no header analysis) */
                                         ZSTD_dtlm_fast,
@@ -753,6 +754,13 @@
             if (ZSTD_isError(cSize)) JOB_ERROR(cSize);
             lastCBlockSize = cSize;
     }   }
+    if (!job->firstJob) {
+        /* Double check that we don't have an ext-dict, because then our
+         * repcode invalidation doesn't work.
+         */
+        assert(!ZSTD_window_hasExtDict(cctx->blockState.matchState.window));
+    }
+    ZSTD_CCtx_trace(cctx, 0);
 
 _endJob:
     ZSTDMT_serialState_ensureFinished(job->serial, job->jobID, job->cSize);
@@ -820,7 +828,6 @@
     roundBuff_t roundBuff;
     serialState_t serial;
     rsyncState_t rsync;
-    unsigned singleBlockingThread;
     unsigned jobIDMask;
     unsigned doneJobID;
     unsigned nextJobID;
@@ -832,6 +839,7 @@
     ZSTD_customMem cMem;
     ZSTD_CDict* cdictLocal;
     const ZSTD_CDict* cdict;
+    unsigned providedFactory: 1;
 };
 
 static void ZSTDMT_freeJobsTable(ZSTDMT_jobDescription* jobTable, U32 nbJobs, ZSTD_customMem cMem)
@@ -842,7 +850,7 @@
         ZSTD_pthread_mutex_destroy(&jobTable[jobNb].job_mutex);
         ZSTD_pthread_cond_destroy(&jobTable[jobNb].job_cond);
     }
-    ZSTD_free(jobTable, cMem);
+    ZSTD_customFree(jobTable, cMem);
 }
 
 /* ZSTDMT_allocJobsTable()
@@ -854,7 +862,7 @@
     U32 const nbJobs = 1 << nbJobsLog2;
     U32 jobNb;
     ZSTDMT_jobDescription* const jobTable = (ZSTDMT_jobDescription*)
-                ZSTD_calloc(nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
+                ZSTD_customCalloc(nbJobs * sizeof(ZSTDMT_jobDescription), cMem);
     int initError = 0;
     if (jobTable==NULL) return NULL;
     *nbJobsPtr = nbJobs;
@@ -885,12 +893,12 @@
 
 /* ZSTDMT_CCtxParam_setNbWorkers():
  * Internal use only */
-size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers)
+static size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers)
 {
     return ZSTD_CCtxParams_setParameter(params, ZSTD_c_nbWorkers, (int)nbWorkers);
 }
 
-MEM_STATIC ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced_internal(unsigned nbWorkers, ZSTD_customMem cMem)
+MEM_STATIC ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced_internal(unsigned nbWorkers, ZSTD_customMem cMem, ZSTD_threadPool* pool)
 {
     ZSTDMT_CCtx* mtctx;
     U32 nbJobs = nbWorkers + 2;
@@ -903,12 +911,19 @@
         /* invalid custom allocator */
         return NULL;
 
-    mtctx = (ZSTDMT_CCtx*) ZSTD_calloc(sizeof(ZSTDMT_CCtx), cMem);
+    mtctx = (ZSTDMT_CCtx*) ZSTD_customCalloc(sizeof(ZSTDMT_CCtx), cMem);
     if (!mtctx) return NULL;
     ZSTDMT_CCtxParam_setNbWorkers(&mtctx->params, nbWorkers);
     mtctx->cMem = cMem;
     mtctx->allJobsCompleted = 1;
-    mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem);
+    if (pool != NULL) {
+      mtctx->factory = pool;
+      mtctx->providedFactory = 1;
+    }
+    else {
+      mtctx->factory = POOL_create_advanced(nbWorkers, 0, cMem);
+      mtctx->providedFactory = 0;
+    }
     mtctx->jobs = ZSTDMT_createJobsTable(&nbJobs, cMem);
     assert(nbJobs > 0); assert((nbJobs & (nbJobs - 1)) == 0);  /* ensure nbJobs is a power of 2 */
     mtctx->jobIDMask = nbJobs - 1;
@@ -925,22 +940,18 @@
     return mtctx;
 }
 
-ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem)
+ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers, ZSTD_customMem cMem, ZSTD_threadPool* pool)
 {
 #ifdef ZSTD_MULTITHREAD
-    return ZSTDMT_createCCtx_advanced_internal(nbWorkers, cMem);
+    return ZSTDMT_createCCtx_advanced_internal(nbWorkers, cMem, pool);
 #else
     (void)nbWorkers;
     (void)cMem;
+    (void)pool;
     return NULL;
 #endif
 }
 
-ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers)
-{
-    return ZSTDMT_createCCtx_advanced(nbWorkers, ZSTD_defaultCMem);
-}
-
 
 /* ZSTDMT_releaseAllJobResources() :
  * note : ensure all workers are killed first ! */
@@ -957,7 +968,7 @@
         ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
 
         /* Clear the job description, but keep the mutex/cond */
-        memset(&mtctx->jobs[jobID], 0, sizeof(mtctx->jobs[jobID]));
+        ZSTD_memset(&mtctx->jobs[jobID], 0, sizeof(mtctx->jobs[jobID]));
         mtctx->jobs[jobID].job_mutex = mutex;
         mtctx->jobs[jobID].job_cond = cond;
     }
@@ -984,7 +995,8 @@
 size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx)
 {
     if (mtctx==NULL) return 0;   /* compatible with free on NULL */
-    POOL_free(mtctx->factory);   /* stop and free worker threads */
+    if (!mtctx->providedFactory)
+        POOL_free(mtctx->factory);   /* stop and free worker threads */
     ZSTDMT_releaseAllJobResources(mtctx);  /* release job resources into pools first */
     ZSTDMT_freeJobsTable(mtctx->jobs, mtctx->jobIDMask+1, mtctx->cMem);
     ZSTDMT_freeBufferPool(mtctx->bufPool);
@@ -993,8 +1005,8 @@
     ZSTDMT_serialState_free(&mtctx->serial);
     ZSTD_freeCDict(mtctx->cdictLocal);
     if (mtctx->roundBuff.buffer)
-        ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
-    ZSTD_free(mtctx, mtctx->cMem);
+        ZSTD_customFree(mtctx->roundBuff.buffer, mtctx->cMem);
+    ZSTD_customFree(mtctx, mtctx->cMem);
     return 0;
 }
 
@@ -1011,65 +1023,6 @@
             + mtctx->roundBuff.capacity;
 }
 
-/* Internal only */
-size_t
-ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params,
-                                   ZSTDMT_parameter parameter,
-                                   int value)
-{
-    DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter");
-    switch(parameter)
-    {
-    case ZSTDMT_p_jobSize :
-        DEBUGLOG(4, "ZSTDMT_CCtxParam_setMTCtxParameter : set jobSize to %i", value);
-        return ZSTD_CCtxParams_setParameter(params, ZSTD_c_jobSize, value);
-    case ZSTDMT_p_overlapLog :
-        DEBUGLOG(4, "ZSTDMT_p_overlapLog : %i", value);
-        return ZSTD_CCtxParams_setParameter(params, ZSTD_c_overlapLog, value);
-    case ZSTDMT_p_rsyncable :
-        DEBUGLOG(4, "ZSTD_p_rsyncable : %i", value);
-        return ZSTD_CCtxParams_setParameter(params, ZSTD_c_rsyncable, value);
-    default :
-        return ERROR(parameter_unsupported);
-    }
-}
-
-size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value)
-{
-    DEBUGLOG(4, "ZSTDMT_setMTCtxParameter");
-    return ZSTDMT_CCtxParam_setMTCtxParameter(&mtctx->params, parameter, value);
-}
-
-size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value)
-{
-    switch (parameter) {
-    case ZSTDMT_p_jobSize:
-        return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_jobSize, value);
-    case ZSTDMT_p_overlapLog:
-        return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_overlapLog, value);
-    case ZSTDMT_p_rsyncable:
-        return ZSTD_CCtxParams_getParameter(&mtctx->params, ZSTD_c_rsyncable, value);
-    default:
-        return ERROR(parameter_unsupported);
-    }
-}
-
-/* Sets parameters relevant to the compression job,
- * initializing others to default values. */
-static ZSTD_CCtx_params ZSTDMT_initJobCCtxParams(const ZSTD_CCtx_params* params)
-{
-    ZSTD_CCtx_params jobParams = *params;
-    /* Clear parameters related to multithreading */
-    jobParams.forceWindow = 0;
-    jobParams.nbWorkers = 0;
-    jobParams.jobSize = 0;
-    jobParams.overlapLog = 0;
-    jobParams.rsyncable = 0;
-    memset(&jobParams.ldmParams, 0, sizeof(ldmParams_t));
-    memset(&jobParams.customMem, 0, sizeof(ZSTD_customMem));
-    return jobParams;
-}
-
 
 /* ZSTDMT_resize() :
  * @return : error code if fails, 0 on success */
@@ -1098,7 +1051,7 @@
     DEBUGLOG(5, "ZSTDMT_updateCParams_whileCompressing (level:%i)",
                 compressionLevel);
     mtctx->params.compressionLevel = compressionLevel;
-    {   ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, 0);
+    {   ZSTD_compressionParameters cParams = ZSTD_getCParamsFromCCtxParams(cctxParams, ZSTD_CONTENTSIZE_UNKNOWN, 0, ZSTD_cpm_noAttachDict);
         cParams.windowLog = saved_wlog;
         mtctx->params.cParams = cParams;
     }
@@ -1185,8 +1138,8 @@
     if (params->ldmParams.enableLdm) {
         /* In Long Range Mode, the windowLog is typically oversized.
          * In which case, it's preferable to determine the jobSize
-         * based on chainLog instead. */
-        jobLog = MAX(21, params->cParams.chainLog + 4);
+         * based on cycleLog instead. */
+        jobLog = MAX(21, ZSTD_cycleLog(params->cParams.chainLog, params->cParams.strategy) + 3);
     } else {
         jobLog = MAX(20, params->cParams.windowLog + 2);
     }
@@ -1240,174 +1193,6 @@
     return (ovLog==0) ? 0 : (size_t)1 << ovLog;
 }
 
-static unsigned
-ZSTDMT_computeNbJobs(const ZSTD_CCtx_params* params, size_t srcSize, unsigned nbWorkers)
-{
-    assert(nbWorkers>0);
-    {   size_t const jobSizeTarget = (size_t)1 << ZSTDMT_computeTargetJobLog(params);
-        size_t const jobMaxSize = jobSizeTarget << 2;
-        size_t const passSizeMax = jobMaxSize * nbWorkers;
-        unsigned const multiplier = (unsigned)(srcSize / passSizeMax) + 1;
-        unsigned const nbJobsLarge = multiplier * nbWorkers;
-        unsigned const nbJobsMax = (unsigned)(srcSize / jobSizeTarget) + 1;
-        unsigned const nbJobsSmall = MIN(nbJobsMax, nbWorkers);
-        return (multiplier>1) ? nbJobsLarge : nbJobsSmall;
-}   }
-
-/* ZSTDMT_compress_advanced_internal() :
- * This is a blocking function : it will only give back control to caller after finishing its compression job.
- */
-static size_t
-ZSTDMT_compress_advanced_internal(
-                ZSTDMT_CCtx* mtctx,
-                void* dst, size_t dstCapacity,
-          const void* src, size_t srcSize,
-          const ZSTD_CDict* cdict,
-                ZSTD_CCtx_params params)
-{
-    ZSTD_CCtx_params const jobParams = ZSTDMT_initJobCCtxParams(&params);
-    size_t const overlapSize = ZSTDMT_computeOverlapSize(&params);
-    unsigned const nbJobs = ZSTDMT_computeNbJobs(&params, srcSize, params.nbWorkers);
-    size_t const proposedJobSize = (srcSize + (nbJobs-1)) / nbJobs;
-    size_t const avgJobSize = (((proposedJobSize-1) & 0x1FFFF) < 0x7FFF) ? proposedJobSize + 0xFFFF : proposedJobSize;   /* avoid too small last block */
-    const char* const srcStart = (const char*)src;
-    size_t remainingSrcSize = srcSize;
-    unsigned const compressWithinDst = (dstCapacity >= ZSTD_compressBound(srcSize)) ? nbJobs : (unsigned)(dstCapacity / ZSTD_compressBound(avgJobSize));  /* presumes avgJobSize >= 256 KB, which should be the case */
-    size_t frameStartPos = 0, dstBufferPos = 0;
-    assert(jobParams.nbWorkers == 0);
-    assert(mtctx->cctxPool->totalCCtx == params.nbWorkers);
-
-    params.jobSize = (U32)avgJobSize;
-    DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: nbJobs=%2u (rawSize=%u bytes; fixedSize=%u) ",
-                nbJobs, (U32)proposedJobSize, (U32)avgJobSize);
-
-    if ((nbJobs==1) | (params.nbWorkers<=1)) {   /* fallback to single-thread mode : this is a blocking invocation anyway */
-        ZSTD_CCtx* const cctx = mtctx->cctxPool->cctx[0];
-        DEBUGLOG(4, "ZSTDMT_compress_advanced_internal: fallback to single-thread mode");
-        if (cdict) return ZSTD_compress_usingCDict_advanced(cctx, dst, dstCapacity, src, srcSize, cdict, jobParams.fParams);
-        return ZSTD_compress_advanced_internal(cctx, dst, dstCapacity, src, srcSize, NULL, 0, &jobParams);
-    }
-
-    assert(avgJobSize >= 256 KB);  /* condition for ZSTD_compressBound(A) + ZSTD_compressBound(B) <= ZSTD_compressBound(A+B), required to compress directly into Dst (no additional buffer) */
-    ZSTDMT_setBufferSize(mtctx->bufPool, ZSTD_compressBound(avgJobSize) );
-    /* LDM doesn't even try to load the dictionary in single-ingestion mode */
-    if (ZSTDMT_serialState_reset(&mtctx->serial, mtctx->seqPool, params, avgJobSize, NULL, 0, ZSTD_dct_auto))
-        return ERROR(memory_allocation);
-
-    FORWARD_IF_ERROR( ZSTDMT_expandJobsTable(mtctx, nbJobs) , "");  /* only expands if necessary */
-
-    {   unsigned u;
-        for (u=0; u<nbJobs; u++) {
-            size_t const jobSize = MIN(remainingSrcSize, avgJobSize);
-            size_t const dstBufferCapacity = ZSTD_compressBound(jobSize);
-            buffer_t const dstAsBuffer = { (char*)dst + dstBufferPos, dstBufferCapacity };
-            buffer_t const dstBuffer = u < compressWithinDst ? dstAsBuffer : g_nullBuffer;
-            size_t dictSize = u ? overlapSize : 0;
-
-            mtctx->jobs[u].prefix.start = srcStart + frameStartPos - dictSize;
-            mtctx->jobs[u].prefix.size = dictSize;
-            mtctx->jobs[u].src.start = srcStart + frameStartPos;
-            mtctx->jobs[u].src.size = jobSize; assert(jobSize > 0);  /* avoid job.src.size == 0 */
-            mtctx->jobs[u].consumed = 0;
-            mtctx->jobs[u].cSize = 0;
-            mtctx->jobs[u].cdict = (u==0) ? cdict : NULL;
-            mtctx->jobs[u].fullFrameSize = srcSize;
-            mtctx->jobs[u].params = jobParams;
-            /* do not calculate checksum within sections, but write it in header for first section */
-            mtctx->jobs[u].dstBuff = dstBuffer;
-            mtctx->jobs[u].cctxPool = mtctx->cctxPool;
-            mtctx->jobs[u].bufPool = mtctx->bufPool;
-            mtctx->jobs[u].seqPool = mtctx->seqPool;
-            mtctx->jobs[u].serial = &mtctx->serial;
-            mtctx->jobs[u].jobID = u;
-            mtctx->jobs[u].firstJob = (u==0);
-            mtctx->jobs[u].lastJob = (u==nbJobs-1);
-
-            DEBUGLOG(5, "ZSTDMT_compress_advanced_internal: posting job %u  (%u bytes)", u, (U32)jobSize);
-            DEBUG_PRINTHEX(6, mtctx->jobs[u].prefix.start, 12);
-            POOL_add(mtctx->factory, ZSTDMT_compressionJob, &mtctx->jobs[u]);
-
-            frameStartPos += jobSize;
-            dstBufferPos += dstBufferCapacity;
-            remainingSrcSize -= jobSize;
-    }   }
-
-    /* collect result */
-    {   size_t error = 0, dstPos = 0;
-        unsigned jobID;
-        for (jobID=0; jobID<nbJobs; jobID++) {
-            DEBUGLOG(5, "waiting for job %u ", jobID);
-            ZSTD_PTHREAD_MUTEX_LOCK(&mtctx->jobs[jobID].job_mutex);
-            while (mtctx->jobs[jobID].consumed < mtctx->jobs[jobID].src.size) {
-                DEBUGLOG(5, "waiting for jobCompleted signal from job %u", jobID);
-                ZSTD_pthread_cond_wait(&mtctx->jobs[jobID].job_cond, &mtctx->jobs[jobID].job_mutex);
-            }
-            ZSTD_pthread_mutex_unlock(&mtctx->jobs[jobID].job_mutex);
-            DEBUGLOG(5, "ready to write job %u ", jobID);
-
-            {   size_t const cSize = mtctx->jobs[jobID].cSize;
-                if (ZSTD_isError(cSize)) error = cSize;
-                if ((!error) && (dstPos + cSize > dstCapacity)) error = ERROR(dstSize_tooSmall);
-                if (jobID) {   /* note : job 0 is written directly at dst, which is correct position */
-                    if (!error)
-                        memmove((char*)dst + dstPos, mtctx->jobs[jobID].dstBuff.start, cSize);  /* may overlap when job compressed within dst */
-                    if (jobID >= compressWithinDst) {  /* job compressed into its own buffer, which must be released */
-                        DEBUGLOG(5, "releasing buffer %u>=%u", jobID, compressWithinDst);
-                        ZSTDMT_releaseBuffer(mtctx->bufPool, mtctx->jobs[jobID].dstBuff);
-                }   }
-                mtctx->jobs[jobID].dstBuff = g_nullBuffer;
-                mtctx->jobs[jobID].cSize = 0;
-                dstPos += cSize ;
-            }
-        }  /* for (jobID=0; jobID<nbJobs; jobID++) */
-
-        DEBUGLOG(4, "checksumFlag : %u ", params.fParams.checksumFlag);
-        if (params.fParams.checksumFlag) {
-            U32 const checksum = (U32)XXH64_digest(&mtctx->serial.xxhState);
-            if (dstPos + 4 > dstCapacity) {
-                error = ERROR(dstSize_tooSmall);
-            } else {
-                DEBUGLOG(4, "writing checksum : %08X \n", checksum);
-                MEM_writeLE32((char*)dst + dstPos, checksum);
-                dstPos += 4;
-        }   }
-
-        if (!error) DEBUGLOG(4, "compressed size : %u  ", (U32)dstPos);
-        return error ? error : dstPos;
-    }
-}
-
-size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
-                                void* dst, size_t dstCapacity,
-                          const void* src, size_t srcSize,
-                          const ZSTD_CDict* cdict,
-                                ZSTD_parameters params,
-                                int overlapLog)
-{
-    ZSTD_CCtx_params cctxParams = mtctx->params;
-    cctxParams.cParams = params.cParams;
-    cctxParams.fParams = params.fParams;
-    assert(ZSTD_OVERLAPLOG_MIN <= overlapLog && overlapLog <= ZSTD_OVERLAPLOG_MAX);
-    cctxParams.overlapLog = overlapLog;
-    return ZSTDMT_compress_advanced_internal(mtctx,
-                                             dst, dstCapacity,
-                                             src, srcSize,
-                                             cdict, cctxParams);
-}
-
-
-size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
-                           void* dst, size_t dstCapacity,
-                     const void* src, size_t srcSize,
-                           int compressionLevel)
-{
-    ZSTD_parameters params = ZSTD_getParams(compressionLevel, srcSize, 0);
-    int const overlapLog = ZSTDMT_overlapLog_default(params.cParams.strategy);
-    params.fParams.contentSizeFlag = 1;
-    return ZSTDMT_compress_advanced(mtctx, dst, dstCapacity, src, srcSize, NULL, params, overlapLog);
-}
-
-
 /* ====================================== */
 /* =======      Streaming API     ======= */
 /* ====================================== */
@@ -1432,16 +1217,6 @@
     if (params.jobSize != 0 && params.jobSize < ZSTDMT_JOBSIZE_MIN) params.jobSize = ZSTDMT_JOBSIZE_MIN;
     if (params.jobSize > (size_t)ZSTDMT_JOBSIZE_MAX) params.jobSize = (size_t)ZSTDMT_JOBSIZE_MAX;
 
-    mtctx->singleBlockingThread = (pledgedSrcSize <= ZSTDMT_JOBSIZE_MIN);  /* do not trigger multi-threading when srcSize is too small */
-    if (mtctx->singleBlockingThread) {
-        ZSTD_CCtx_params const singleThreadParams = ZSTDMT_initJobCCtxParams(&params);
-        DEBUGLOG(5, "ZSTDMT_initCStream_internal: switch to single blocking thread mode");
-        assert(singleThreadParams.nbWorkers == 0);
-        return ZSTD_initCStream_internal(mtctx->cctxPool->cctx[0],
-                                         dict, dictSize, cdict,
-                                         &singleThreadParams, pledgedSrcSize);
-    }
-
     DEBUGLOG(4, "ZSTDMT_initCStream_internal: %u workers", params.nbWorkers);
 
     if (mtctx->allJobsCompleted == 0) {   /* previous compression not correctly finished */
@@ -1475,9 +1250,8 @@
 
     if (params.rsyncable) {
         /* Aim for the targetsectionSize as the average job size. */
-        U32 const jobSizeMB = (U32)(mtctx->targetSectionSize >> 20);
-        U32 const rsyncBits = ZSTD_highbit32(jobSizeMB) + 20;
-        assert(jobSizeMB >= 1);
+        U32 const jobSizeKB = (U32)(mtctx->targetSectionSize >> 10);
+        U32 const rsyncBits = (assert(jobSizeKB >= 1), ZSTD_highbit32(jobSizeKB) + 10);
         DEBUGLOG(4, "rsyncLog = %u", rsyncBits);
         mtctx->rsync.hash = 0;
         mtctx->rsync.hitMask = (1ULL << rsyncBits) - 1;
@@ -1504,8 +1278,8 @@
         size_t const capacity = MAX(windowSize, sectionsSize) + slackSize;
         if (mtctx->roundBuff.capacity < capacity) {
             if (mtctx->roundBuff.buffer)
-                ZSTD_free(mtctx->roundBuff.buffer, mtctx->cMem);
-            mtctx->roundBuff.buffer = (BYTE*)ZSTD_malloc(capacity, mtctx->cMem);
+                ZSTD_customFree(mtctx->roundBuff.buffer, mtctx->cMem);
+            mtctx->roundBuff.buffer = (BYTE*)ZSTD_customMalloc(capacity, mtctx->cMem);
             if (mtctx->roundBuff.buffer == NULL) {
                 mtctx->roundBuff.capacity = 0;
                 return ERROR(memory_allocation);
@@ -1530,53 +1304,6 @@
     return 0;
 }
 
-size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
-                             const void* dict, size_t dictSize,
-                                   ZSTD_parameters params,
-                                   unsigned long long pledgedSrcSize)
-{
-    ZSTD_CCtx_params cctxParams = mtctx->params;  /* retrieve sticky params */
-    DEBUGLOG(4, "ZSTDMT_initCStream_advanced (pledgedSrcSize=%u)", (U32)pledgedSrcSize);
-    cctxParams.cParams = params.cParams;
-    cctxParams.fParams = params.fParams;
-    return ZSTDMT_initCStream_internal(mtctx, dict, dictSize, ZSTD_dct_auto, NULL,
-                                       cctxParams, pledgedSrcSize);
-}
-
-size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
-                               const ZSTD_CDict* cdict,
-                                     ZSTD_frameParameters fParams,
-                                     unsigned long long pledgedSrcSize)
-{
-    ZSTD_CCtx_params cctxParams = mtctx->params;
-    if (cdict==NULL) return ERROR(dictionary_wrong);   /* method incompatible with NULL cdict */
-    cctxParams.cParams = ZSTD_getCParamsFromCDict(cdict);
-    cctxParams.fParams = fParams;
-    return ZSTDMT_initCStream_internal(mtctx, NULL, 0 /*dictSize*/, ZSTD_dct_auto, cdict,
-                                       cctxParams, pledgedSrcSize);
-}
-
-
-/* ZSTDMT_resetCStream() :
- * pledgedSrcSize can be zero == unknown (for the time being)
- * prefer using ZSTD_CONTENTSIZE_UNKNOWN,
- * as `0` might mean "empty" in the future */
-size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize)
-{
-    if (!pledgedSrcSize) pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
-    return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, 0, mtctx->params,
-                                       pledgedSrcSize);
-}
-
-size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel) {
-    ZSTD_parameters const params = ZSTD_getParams(compressionLevel, ZSTD_CONTENTSIZE_UNKNOWN, 0);
-    ZSTD_CCtx_params cctxParams = mtctx->params;   /* retrieve sticky params */
-    DEBUGLOG(4, "ZSTDMT_initCStream (cLevel=%i)", compressionLevel);
-    cctxParams.cParams = params.cParams;
-    cctxParams.fParams = params.fParams;
-    return ZSTDMT_initCStream_internal(mtctx, NULL, 0, ZSTD_dct_auto, NULL, cctxParams, ZSTD_CONTENTSIZE_UNKNOWN);
-}
-
 
 /* ZSTDMT_writeLastEmptyBlock()
  * Write a single empty block with an end-of-frame to finish a frame.
@@ -1740,7 +1467,7 @@
             assert(cSize >= mtctx->jobs[wJobID].dstFlushed);
             assert(mtctx->jobs[wJobID].dstBuff.start != NULL);
             if (toFlush > 0) {
-                memcpy((char*)output->dst + output->pos,
+                ZSTD_memcpy((char*)output->dst + output->pos,
                     (const char*)mtctx->jobs[wJobID].dstBuff.start + mtctx->jobs[wJobID].dstFlushed,
                     toFlush);
             }
@@ -1894,7 +1621,7 @@
             return 0;
         }
         ZSTDMT_waitForLdmComplete(mtctx, buffer);
-        memmove(start, mtctx->inBuff.prefix.start, prefixSize);
+        ZSTD_memmove(start, mtctx->inBuff.prefix.start, prefixSize);
         mtctx->inBuff.prefix.start = start;
         mtctx->roundBuff.pos = prefixSize;
     }
@@ -1968,6 +1695,16 @@
         pos = 0;
         prev = (BYTE const*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled - RSYNC_LENGTH;
         hash = ZSTD_rollingHash_compute(prev, RSYNC_LENGTH);
+        if ((hash & hitMask) == hitMask) {
+            /* We're already at a sync point so don't load any more until
+             * we're able to flush this sync point.
+             * This likely happened because the job table was full so we
+             * couldn't add our job.
+             */
+            syncPoint.toLoad = 0;
+            syncPoint.flush = 1;
+            return syncPoint;
+        }
     } else {
         /* We don't have enough bytes buffered to initialize the hash, but
          * we know we have at least RSYNC_LENGTH bytes total.
@@ -2022,34 +1759,11 @@
     assert(output->pos <= output->size);
     assert(input->pos  <= input->size);
 
-    if (mtctx->singleBlockingThread) {  /* delegate to single-thread (synchronous) */
-        return ZSTD_compressStream2(mtctx->cctxPool->cctx[0], output, input, endOp);
-    }
-
     if ((mtctx->frameEnded) && (endOp==ZSTD_e_continue)) {
         /* current frame being ended. Only flush/end are allowed */
         return ERROR(stage_wrong);
     }
 
-    /* single-pass shortcut (note : synchronous-mode) */
-    if ( (!mtctx->params.rsyncable)   /* rsyncable mode is disabled */
-      && (mtctx->nextJobID == 0)      /* just started */
-      && (mtctx->inBuff.filled == 0)  /* nothing buffered */
-      && (!mtctx->jobReady)           /* no job already created */
-      && (endOp == ZSTD_e_end)        /* end order */
-      && (output->size - output->pos >= ZSTD_compressBound(input->size - input->pos)) ) { /* enough space in dst */
-        size_t const cSize = ZSTDMT_compress_advanced_internal(mtctx,
-                (char*)output->dst + output->pos, output->size - output->pos,
-                (const char*)input->src + input->pos, input->size - input->pos,
-                mtctx->cdict, mtctx->params);
-        if (ZSTD_isError(cSize)) return cSize;
-        input->pos = input->size;
-        output->pos += cSize;
-        mtctx->allJobsCompleted = 1;
-        mtctx->frameEnded = 1;
-        return 0;
-    }
-
     /* fill input buffer */
     if ( (!mtctx->jobReady)
       && (input->size > input->pos) ) {   /* support NULL input */
@@ -2072,13 +1786,21 @@
             assert(mtctx->inBuff.buffer.capacity >= mtctx->targetSectionSize);
             DEBUGLOG(5, "ZSTDMT_compressStream_generic: adding %u bytes on top of %u to buffer of size %u",
                         (U32)syncPoint.toLoad, (U32)mtctx->inBuff.filled, (U32)mtctx->targetSectionSize);
-            memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, syncPoint.toLoad);
+            ZSTD_memcpy((char*)mtctx->inBuff.buffer.start + mtctx->inBuff.filled, (const char*)input->src + input->pos, syncPoint.toLoad);
             input->pos += syncPoint.toLoad;
             mtctx->inBuff.filled += syncPoint.toLoad;
             forwardInputProgress = syncPoint.toLoad>0;
         }
-        if ((input->pos < input->size) && (endOp == ZSTD_e_end))
-            endOp = ZSTD_e_flush;   /* can't end now : not all input consumed */
+    }
+    if ((input->pos < input->size) && (endOp == ZSTD_e_end)) {
+        /* Can't end yet because the input is not fully consumed.
+            * We are in one of these cases:
+            * - mtctx->inBuff is NULL & empty: we couldn't get an input buffer so don't create a new job.
+            * - We filled the input buffer: flush this job but don't end the frame.
+            * - We hit a synchronization point: flush this job but don't end the frame.
+            */
+        assert(mtctx->inBuff.filled == 0 || mtctx->inBuff.filled == mtctx->targetSectionSize || mtctx->params.rsyncable);
+        endOp = ZSTD_e_flush;
     }
 
     if ( (mtctx->jobReady)
@@ -2097,47 +1819,3 @@
         return remainingToFlush;
     }
 }
-
-
-size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input)
-{
-    FORWARD_IF_ERROR( ZSTDMT_compressStream_generic(mtctx, output, input, ZSTD_e_continue) , "");
-
-    /* recommended next input size : fill current input buffer */
-    return mtctx->targetSectionSize - mtctx->inBuff.filled;   /* note : could be zero when input buffer is fully filled and no more availability to create new job */
-}
-
-
-static size_t ZSTDMT_flushStream_internal(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_EndDirective endFrame)
-{
-    size_t const srcSize = mtctx->inBuff.filled;
-    DEBUGLOG(5, "ZSTDMT_flushStream_internal");
-
-    if ( mtctx->jobReady     /* one job ready for a worker to pick up */
-      || (srcSize > 0)       /* still some data within input buffer */
-      || ((endFrame==ZSTD_e_end) && !mtctx->frameEnded)) {  /* need a last 0-size block to end frame */
-           DEBUGLOG(5, "ZSTDMT_flushStream_internal : create a new job (%u bytes, end:%u)",
-                        (U32)srcSize, (U32)endFrame);
-        FORWARD_IF_ERROR( ZSTDMT_createCompressionJob(mtctx, srcSize, endFrame) , "");
-    }
-
-    /* check if there is any data available to flush */
-    return ZSTDMT_flushProduced(mtctx, output, 1 /* blockToFlush */, endFrame);
-}
-
-
-size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
-{
-    DEBUGLOG(5, "ZSTDMT_flushStream");
-    if (mtctx->singleBlockingThread)
-        return ZSTD_flushStream(mtctx->cctxPool->cctx[0], output);
-    return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_flush);
-}
-
-size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output)
-{
-    DEBUGLOG(4, "ZSTDMT_endStream");
-    if (mtctx->singleBlockingThread)
-        return ZSTD_endStream(mtctx->cctxPool->cctx[0], output);
-    return ZSTDMT_flushStream_internal(mtctx, output, ZSTD_e_end);
-}
diff --git a/Utilities/cmzstd/lib/compress/zstdmt_compress.h b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
index 89914eb..2fee2ec 100644
--- a/Utilities/cmzstd/lib/compress/zstdmt_compress.h
+++ b/Utilities/cmzstd/lib/compress/zstdmt_compress.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -19,113 +19,57 @@
 /* Note : This is an internal API.
  *        These APIs used to be exposed with ZSTDLIB_API,
  *        because it used to be the only way to invoke MT compression.
- *        Now, it's recommended to use ZSTD_compress2 and ZSTD_compressStream2()
- *        instead.
- *
- *        If you depend on these APIs and can't switch, then define
- *        ZSTD_LEGACY_MULTITHREADED_API when making the dynamic library.
- *        However, we may completely remove these functions in a future
- *        release, so please switch soon.
+ *        Now, you must use ZSTD_compress2 and ZSTD_compressStream2() instead.
  *
  *        This API requires ZSTD_MULTITHREAD to be defined during compilation,
  *        otherwise ZSTDMT_createCCtx*() will fail.
  */
 
-#ifdef ZSTD_LEGACY_MULTITHREADED_API
-#  define ZSTDMT_API ZSTDLIB_API
-#else
-#  define ZSTDMT_API
-#endif
-
 /* ===   Dependencies   === */
-#include <stddef.h>                /* size_t */
+#include "../common/zstd_deps.h"   /* size_t */
 #define ZSTD_STATIC_LINKING_ONLY   /* ZSTD_parameters */
 #include "../zstd.h"            /* ZSTD_inBuffer, ZSTD_outBuffer, ZSTDLIB_API */
 
 
 /* ===   Constants   === */
-#ifndef ZSTDMT_NBWORKERS_MAX
-#  define ZSTDMT_NBWORKERS_MAX 200
+#ifndef ZSTDMT_NBWORKERS_MAX /* a different value can be selected at compile time */
+#  define ZSTDMT_NBWORKERS_MAX ((sizeof(void*)==4) /*32-bit*/ ? 64 : 256)
 #endif
-#ifndef ZSTDMT_JOBSIZE_MIN
-#  define ZSTDMT_JOBSIZE_MIN (1 MB)
+#ifndef ZSTDMT_JOBSIZE_MIN   /* a different value can be selected at compile time */
+#  define ZSTDMT_JOBSIZE_MIN (512 KB)
 #endif
 #define ZSTDMT_JOBLOG_MAX   (MEM_32bits() ? 29 : 30)
 #define ZSTDMT_JOBSIZE_MAX  (MEM_32bits() ? (512 MB) : (1024 MB))
 
 
+/* ========================================================
+ * ===  Private interface, for use by ZSTD_compress.c   ===
+ * ===  Not exposed in libzstd. Never invoke directly   ===
+ * ======================================================== */
+
 /* ===   Memory management   === */
 typedef struct ZSTDMT_CCtx_s ZSTDMT_CCtx;
 /* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */
-ZSTDMT_API ZSTDMT_CCtx* ZSTDMT_createCCtx(unsigned nbWorkers);
-/* Requires ZSTD_MULTITHREAD to be defined during compilation, otherwise it will return NULL. */
-ZSTDMT_API ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers,
-                                                    ZSTD_customMem cMem);
-ZSTDMT_API size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx);
+ZSTDMT_CCtx* ZSTDMT_createCCtx_advanced(unsigned nbWorkers,
+                                        ZSTD_customMem cMem,
+					ZSTD_threadPool *pool);
+size_t ZSTDMT_freeCCtx(ZSTDMT_CCtx* mtctx);
 
-ZSTDMT_API size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx);
-
-
-/* ===   Simple one-pass compression function   === */
-
-ZSTDMT_API size_t ZSTDMT_compressCCtx(ZSTDMT_CCtx* mtctx,
-                                       void* dst, size_t dstCapacity,
-                                 const void* src, size_t srcSize,
-                                       int compressionLevel);
-
-
+size_t ZSTDMT_sizeof_CCtx(ZSTDMT_CCtx* mtctx);
 
 /* ===   Streaming functions   === */
 
-ZSTDMT_API size_t ZSTDMT_initCStream(ZSTDMT_CCtx* mtctx, int compressionLevel);
-ZSTDMT_API size_t ZSTDMT_resetCStream(ZSTDMT_CCtx* mtctx, unsigned long long pledgedSrcSize);  /**< if srcSize is not known at reset time, use ZSTD_CONTENTSIZE_UNKNOWN. Note: for compatibility with older programs, 0 means the same as ZSTD_CONTENTSIZE_UNKNOWN, but it will change in the future to mean "empty" */
+size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx);
 
-ZSTDMT_API size_t ZSTDMT_nextInputSizeHint(const ZSTDMT_CCtx* mtctx);
-ZSTDMT_API size_t ZSTDMT_compressStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output, ZSTD_inBuffer* input);
-
-ZSTDMT_API size_t ZSTDMT_flushStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output);   /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */
-ZSTDMT_API size_t ZSTDMT_endStream(ZSTDMT_CCtx* mtctx, ZSTD_outBuffer* output);     /**< @return : 0 == all flushed; >0 : still some data to be flushed; or an error code (ZSTD_isError()) */
-
-
-/* ===   Advanced functions and parameters  === */
-
-ZSTDMT_API size_t ZSTDMT_compress_advanced(ZSTDMT_CCtx* mtctx,
-                                          void* dst, size_t dstCapacity,
-                                    const void* src, size_t srcSize,
-                                    const ZSTD_CDict* cdict,
-                                          ZSTD_parameters params,
-                                          int overlapLog);
-
-ZSTDMT_API size_t ZSTDMT_initCStream_advanced(ZSTDMT_CCtx* mtctx,
-                                        const void* dict, size_t dictSize,   /* dict can be released after init, a local copy is preserved within zcs */
-                                        ZSTD_parameters params,
-                                        unsigned long long pledgedSrcSize);  /* pledgedSrcSize is optional and can be zero == unknown */
-
-ZSTDMT_API size_t ZSTDMT_initCStream_usingCDict(ZSTDMT_CCtx* mtctx,
-                                        const ZSTD_CDict* cdict,
-                                        ZSTD_frameParameters fparams,
-                                        unsigned long long pledgedSrcSize);  /* note : zero means empty */
-
-/* ZSTDMT_parameter :
- * List of parameters that can be set using ZSTDMT_setMTCtxParameter() */
-typedef enum {
-    ZSTDMT_p_jobSize,     /* Each job is compressed in parallel. By default, this value is dynamically determined depending on compression parameters. Can be set explicitly here. */
-    ZSTDMT_p_overlapLog,  /* Each job may reload a part of previous job to enhance compression ratio; 0 == no overlap, 6(default) == use 1/8th of window, >=9 == use full window. This is a "sticky" parameter : its value will be re-used on next compression job */
-    ZSTDMT_p_rsyncable    /* Enables rsyncable mode. */
-} ZSTDMT_parameter;
-
-/* ZSTDMT_setMTCtxParameter() :
- * allow setting individual parameters, one at a time, among a list of enums defined in ZSTDMT_parameter.
- * The function must be called typically after ZSTD_createCCtx() but __before ZSTDMT_init*() !__
- * Parameters not explicitly reset by ZSTDMT_init*() remain the same in consecutive compression sessions.
- * @return : 0, or an error code (which can be tested using ZSTD_isError()) */
-ZSTDMT_API size_t ZSTDMT_setMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int value);
-
-/* ZSTDMT_getMTCtxParameter() :
- * Query the ZSTDMT_CCtx for a parameter value.
- * @return : 0, or an error code (which can be tested using ZSTD_isError()) */
-ZSTDMT_API size_t ZSTDMT_getMTCtxParameter(ZSTDMT_CCtx* mtctx, ZSTDMT_parameter parameter, int* value);
-
+/*! ZSTDMT_initCStream_internal() :
+ *  Private use only. Init streaming operation.
+ *  expects params to be valid.
+ *  must receive dict, or cdict, or none, but not both.
+ *  @return : 0, or an error code */
+size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
+                    const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
+                    const ZSTD_CDict* cdict,
+                    ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
 
 /*! ZSTDMT_compressStream_generic() :
  *  Combines ZSTDMT_compressStream() with optional ZSTDMT_flushStream() or ZSTDMT_endStream()
@@ -134,16 +78,10 @@
  *           0 if fully flushed
  *           or an error code
  *  note : needs to be init using any ZSTD_initCStream*() variant */
-ZSTDMT_API size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
-                                                ZSTD_outBuffer* output,
-                                                ZSTD_inBuffer* input,
-                                                ZSTD_EndDirective endOp);
-
-
-/* ========================================================
- * ===  Private interface, for use by ZSTD_compress.c   ===
- * ===  Not exposed in libzstd. Never invoke directly   ===
- * ======================================================== */
+size_t ZSTDMT_compressStream_generic(ZSTDMT_CCtx* mtctx,
+                                     ZSTD_outBuffer* output,
+                                     ZSTD_inBuffer* input,
+                                     ZSTD_EndDirective endOp);
 
  /*! ZSTDMT_toFlushNow()
   *  Tell how many bytes are ready to be flushed immediately.
@@ -153,15 +91,6 @@
   *  therefore flushing is limited by speed of oldest job. */
 size_t ZSTDMT_toFlushNow(ZSTDMT_CCtx* mtctx);
 
-/*! ZSTDMT_CCtxParam_setMTCtxParameter()
- *  like ZSTDMT_setMTCtxParameter(), but into a ZSTD_CCtx_Params */
-size_t ZSTDMT_CCtxParam_setMTCtxParameter(ZSTD_CCtx_params* params, ZSTDMT_parameter parameter, int value);
-
-/*! ZSTDMT_CCtxParam_setNbWorkers()
- *  Set nbWorkers, and clamp it.
- *  Also reset jobSize and overlapLog */
-size_t ZSTDMT_CCtxParam_setNbWorkers(ZSTD_CCtx_params* params, unsigned nbWorkers);
-
 /*! ZSTDMT_updateCParams_whileCompressing() :
  *  Updates only a selected set of compression parameters, to remain compatible with current frame.
  *  New parameters will be applied to next compression job. */
@@ -174,17 +103,6 @@
 ZSTD_frameProgression ZSTDMT_getFrameProgression(ZSTDMT_CCtx* mtctx);
 
 
-/*! ZSTDMT_initCStream_internal() :
- *  Private use only. Init streaming operation.
- *  expects params to be valid.
- *  must receive dict, or cdict, or none, but not both.
- *  @return : 0, or an error code */
-size_t ZSTDMT_initCStream_internal(ZSTDMT_CCtx* zcs,
-                    const void* dict, size_t dictSize, ZSTD_dictContentType_e dictContentType,
-                    const ZSTD_CDict* cdict,
-                    ZSTD_CCtx_params params, unsigned long long pledgedSrcSize);
-
-
 #if defined (__cplusplus)
 }
 #endif
diff --git a/Utilities/cmzstd/lib/decompress/huf_decompress.c b/Utilities/cmzstd/lib/decompress/huf_decompress.c
index 68293a1..b93c9a0 100644
--- a/Utilities/cmzstd/lib/decompress/huf_decompress.c
+++ b/Utilities/cmzstd/lib/decompress/huf_decompress.c
@@ -1,7 +1,7 @@
 /* ******************************************************************
  * huff0 huffman decoder,
  * part of Finite State Entropy library
- * Copyright (c) 2013-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  *
  *  You can contact the author at :
  *  - FSE+HUF source repository : https://github.com/Cyan4973/FiniteStateEntropy
@@ -15,7 +15,7 @@
 /* **************************************************************
 *  Dependencies
 ****************************************************************/
-#include <string.h>     /* memcpy, memset */
+#include "../common/zstd_deps.h"  /* ZSTD_memcpy, ZSTD_memset */
 #include "../common/compiler.h"
 #include "../common/bitstream.h"  /* BIT_* */
 #include "../common/fse.h"        /* to compress headers */
@@ -103,7 +103,7 @@
 static DTableDesc HUF_getDTableDesc(const HUF_DTable* table)
 {
     DTableDesc dtd;
-    memcpy(&dtd, table, sizeof(dtd));
+    ZSTD_memcpy(&dtd, table, sizeof(dtd));
     return dtd;
 }
 
@@ -115,29 +115,51 @@
 /*-***************************/
 typedef struct { BYTE byte; BYTE nbBits; } HUF_DEltX1;   /* single-symbol decoding */
 
+/**
+ * Packs 4 HUF_DEltX1 structs into a U64. This is used to lay down 4 entries at
+ * a time.
+ */
+static U64 HUF_DEltX1_set4(BYTE symbol, BYTE nbBits) {
+    U64 D4;
+    if (MEM_isLittleEndian()) {
+        D4 = symbol + (nbBits << 8);
+    } else {
+        D4 = (symbol << 8) + nbBits;
+    }
+    D4 *= 0x0001000100010001ULL;
+    return D4;
+}
+
+typedef struct {
+        U32 rankVal[HUF_TABLELOG_ABSOLUTEMAX + 1];
+        U32 rankStart[HUF_TABLELOG_ABSOLUTEMAX + 1];
+        U32 statsWksp[HUF_READ_STATS_WORKSPACE_SIZE_U32];
+        BYTE symbols[HUF_SYMBOLVALUE_MAX + 1];
+        BYTE huffWeight[HUF_SYMBOLVALUE_MAX + 1];
+} HUF_ReadDTableX1_Workspace;
+
+
 size_t HUF_readDTableX1_wksp(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize)
 {
+    return HUF_readDTableX1_wksp_bmi2(DTable, src, srcSize, workSpace, wkspSize, /* bmi2 */ 0);
+}
+
+size_t HUF_readDTableX1_wksp_bmi2(HUF_DTable* DTable, const void* src, size_t srcSize, void* workSpace, size_t wkspSize, int bmi2)
+{
     U32 tableLog = 0;
     U32 nbSymbols = 0;
     size_t iSize;
     void* const dtPtr = DTable + 1;
     HUF_DEltX1* const dt = (HUF_DEltX1*)dtPtr;
+    HUF_ReadDTableX1_Workspace* wksp = (HUF_ReadDTableX1_Workspace*)workSpace;
 
-    U32* rankVal;
-    BYTE* huffWeight;
-    size_t spaceUsed32 = 0;
-
-    rankVal = (U32 *)workSpace + spaceUsed32;
-    spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1;
-    huffWeight = (BYTE *)((U32 *)workSpace + spaceUsed32);
-    spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
-
-    if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge);
+    DEBUG_STATIC_ASSERT(HUF_DECOMPRESS_WORKSPACE_SIZE >= sizeof(*wksp));
+    if (sizeof(*wksp) > wkspSize) return ERROR(tableLog_tooLarge);
 
     DEBUG_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable));
-    /* memset(huffWeight, 0, sizeof(huffWeight)); */   /* is not necessary, even though some analyzer complain ... */
+    /* ZSTD_memset(huffWeight, 0, sizeof(huffWeight)); */   /* is not necessary, even though some analyzer complain ... */
 
-    iSize = HUF_readStats(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize);
+    iSize = HUF_readStats_wksp(wksp->huffWeight, HUF_SYMBOLVALUE_MAX + 1, wksp->rankVal, &nbSymbols, &tableLog, src, srcSize, wksp->statsWksp, sizeof(wksp->statsWksp), bmi2);
     if (HUF_isError(iSize)) return iSize;
 
     /* Table header */
@@ -145,52 +167,117 @@
         if (tableLog > (U32)(dtd.maxTableLog+1)) return ERROR(tableLog_tooLarge);   /* DTable too small, Huffman tree cannot fit in */
         dtd.tableType = 0;
         dtd.tableLog = (BYTE)tableLog;
-        memcpy(DTable, &dtd, sizeof(dtd));
+        ZSTD_memcpy(DTable, &dtd, sizeof(dtd));
     }
 
-    /* Calculate starting value for each rank */
-    {   U32 n, nextRankStart = 0;
-        for (n=1; n<tableLog+1; n++) {
-            U32 const current = nextRankStart;
-            nextRankStart += (rankVal[n] << (n-1));
-            rankVal[n] = current;
-    }   }
+    /* Compute symbols and rankStart given rankVal:
+     *
+     * rankVal already contains the number of values of each weight.
+     *
+     * symbols contains the symbols ordered by weight. First are the rankVal[0]
+     * weight 0 symbols, followed by the rankVal[1] weight 1 symbols, and so on.
+     * symbols[0] is filled (but unused) to avoid a branch.
+     *
+     * rankStart contains the offset where each rank belongs in the DTable.
+     * rankStart[0] is not filled because there are no entries in the table for
+     * weight 0.
+     */
+    {
+        int n;
+        int nextRankStart = 0;
+        int const unroll = 4;
+        int const nLimit = (int)nbSymbols - unroll + 1;
+        for (n=0; n<(int)tableLog+1; n++) {
+            U32 const curr = nextRankStart;
+            nextRankStart += wksp->rankVal[n];
+            wksp->rankStart[n] = curr;
+        }
+        for (n=0; n < nLimit; n += unroll) {
+            int u;
+            for (u=0; u < unroll; ++u) {
+                size_t const w = wksp->huffWeight[n+u];
+                wksp->symbols[wksp->rankStart[w]++] = (BYTE)(n+u);
+            }
+        }
+        for (; n < (int)nbSymbols; ++n) {
+            size_t const w = wksp->huffWeight[n];
+            wksp->symbols[wksp->rankStart[w]++] = (BYTE)n;
+        }
+    }
 
-    /* fill DTable */
-    {   U32 n;
-        size_t const nEnd = nbSymbols;
-        for (n=0; n<nEnd; n++) {
-            size_t const w = huffWeight[n];
-            size_t const length = (1 << w) >> 1;
-            size_t const uStart = rankVal[w];
-            size_t const uEnd = uStart + length;
-            size_t u;
-            HUF_DEltX1 D;
-            D.byte = (BYTE)n;
-            D.nbBits = (BYTE)(tableLog + 1 - w);
-            rankVal[w] = (U32)uEnd;
-            if (length < 4) {
-                /* Use length in the loop bound so the compiler knows it is short. */
-                for (u = 0; u < length; ++u)
-                    dt[uStart + u] = D;
-            } else {
-                /* Unroll the loop 4 times, we know it is a power of 2. */
-                for (u = uStart; u < uEnd; u += 4) {
-                    dt[u + 0] = D;
-                    dt[u + 1] = D;
-                    dt[u + 2] = D;
-                    dt[u + 3] = D;
-    }   }   }   }
+    /* fill DTable
+     * We fill all entries of each weight in order.
+     * That way length is a constant for each iteration of the outter loop.
+     * We can switch based on the length to a different inner loop which is
+     * optimized for that particular case.
+     */
+    {
+        U32 w;
+        int symbol=wksp->rankVal[0];
+        int rankStart=0;
+        for (w=1; w<tableLog+1; ++w) {
+            int const symbolCount = wksp->rankVal[w];
+            int const length = (1 << w) >> 1;
+            int uStart = rankStart;
+            BYTE const nbBits = (BYTE)(tableLog + 1 - w);
+            int s;
+            int u;
+            switch (length) {
+            case 1:
+                for (s=0; s<symbolCount; ++s) {
+                    HUF_DEltX1 D;
+                    D.byte = wksp->symbols[symbol + s];
+                    D.nbBits = nbBits;
+                    dt[uStart] = D;
+                    uStart += 1;
+                }
+                break;
+            case 2:
+                for (s=0; s<symbolCount; ++s) {
+                    HUF_DEltX1 D;
+                    D.byte = wksp->symbols[symbol + s];
+                    D.nbBits = nbBits;
+                    dt[uStart+0] = D;
+                    dt[uStart+1] = D;
+                    uStart += 2;
+                }
+                break;
+            case 4:
+                for (s=0; s<symbolCount; ++s) {
+                    U64 const D4 = HUF_DEltX1_set4(wksp->symbols[symbol + s], nbBits);
+                    MEM_write64(dt + uStart, D4);
+                    uStart += 4;
+                }
+                break;
+            case 8:
+                for (s=0; s<symbolCount; ++s) {
+                    U64 const D4 = HUF_DEltX1_set4(wksp->symbols[symbol + s], nbBits);
+                    MEM_write64(dt + uStart, D4);
+                    MEM_write64(dt + uStart + 4, D4);
+                    uStart += 8;
+                }
+                break;
+            default:
+                for (s=0; s<symbolCount; ++s) {
+                    U64 const D4 = HUF_DEltX1_set4(wksp->symbols[symbol + s], nbBits);
+                    for (u=0; u < length; u += 16) {
+                        MEM_write64(dt + uStart + u + 0, D4);
+                        MEM_write64(dt + uStart + u + 4, D4);
+                        MEM_write64(dt + uStart + u + 8, D4);
+                        MEM_write64(dt + uStart + u + 12, D4);
+                    }
+                    assert(u == length);
+                    uStart += length;
+                }
+                break;
+            }
+            symbol += symbolCount;
+            rankStart += symbolCount * length;
+        }
+    }
     return iSize;
 }
 
-size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_readDTableX1_wksp(DTable, src, srcSize,
-                                 workSpace, sizeof(workSpace));
-}
-
 FORCE_INLINE_TEMPLATE BYTE
 HUF_decodeSymbolX1(BIT_DStream_t* Dstream, const HUF_DEltX1* dt, const U32 dtLog)
 {
@@ -389,20 +476,6 @@
 }
 
 
-size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
-                              const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize);
-}
-
 size_t HUF_decompress4X1_usingDTable(
           void* dst,  size_t dstSize,
     const void* cSrc, size_t cSrcSize,
@@ -419,8 +492,7 @@
 {
     const BYTE* ip = (const BYTE*) cSrc;
 
-    size_t const hSize = HUF_readDTableX1_wksp (dctx, cSrc, cSrcSize,
-                                                workSpace, wkspSize);
+    size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
     if (HUF_isError(hSize)) return hSize;
     if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
     ip += hSize; cSrcSize -= hSize;
@@ -436,18 +508,6 @@
 }
 
 
-size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
-}
-
 #endif /* HUF_FORCE_DECOMPRESS_X2 */
 
 
@@ -468,13 +528,15 @@
 static void HUF_fillDTableX2Level2(HUF_DEltX2* DTable, U32 sizeLog, const U32 consumed,
                            const U32* rankValOrigin, const int minWeight,
                            const sortedSymbol_t* sortedSymbols, const U32 sortedListSize,
-                           U32 nbBitsBaseline, U16 baseSeq)
+                           U32 nbBitsBaseline, U16 baseSeq, U32* wksp, size_t wkspSize)
 {
     HUF_DEltX2 DElt;
-    U32 rankVal[HUF_TABLELOG_MAX + 1];
+    U32* rankVal = wksp;
 
+    assert(wkspSize >= HUF_TABLELOG_MAX + 1);
+    (void)wkspSize;
     /* get pre-calculated rankVal */
-    memcpy(rankVal, rankValOrigin, sizeof(rankVal));
+    ZSTD_memcpy(rankVal, rankValOrigin, sizeof(U32) * (HUF_TABLELOG_MAX + 1));
 
     /* fill skipped values */
     if (minWeight>1) {
@@ -509,14 +571,18 @@
 static void HUF_fillDTableX2(HUF_DEltX2* DTable, const U32 targetLog,
                            const sortedSymbol_t* sortedList, const U32 sortedListSize,
                            const U32* rankStart, rankVal_t rankValOrigin, const U32 maxWeight,
-                           const U32 nbBitsBaseline)
+                           const U32 nbBitsBaseline, U32* wksp, size_t wkspSize)
 {
-    U32 rankVal[HUF_TABLELOG_MAX + 1];
+    U32* rankVal = wksp;
     const int scaleLog = nbBitsBaseline - targetLog;   /* note : targetLog >= srcLog, hence scaleLog <= 1 */
     const U32 minBits  = nbBitsBaseline - maxWeight;
     U32 s;
 
-    memcpy(rankVal, rankValOrigin, sizeof(rankVal));
+    assert(wkspSize >= HUF_TABLELOG_MAX + 1);
+    wksp += HUF_TABLELOG_MAX + 1;
+    wkspSize -= HUF_TABLELOG_MAX + 1;
+
+    ZSTD_memcpy(rankVal, rankValOrigin, sizeof(U32) * (HUF_TABLELOG_MAX + 1));
 
     /* fill DTable */
     for (s=0; s<sortedListSize; s++) {
@@ -534,7 +600,7 @@
             HUF_fillDTableX2Level2(DTable+start, targetLog-nbBits, nbBits,
                            rankValOrigin[nbBits], minWeight,
                            sortedList+sortedRank, sortedListSize-sortedRank,
-                           nbBitsBaseline, symbol);
+                           nbBitsBaseline, symbol, wksp, wkspSize);
         } else {
             HUF_DEltX2 DElt;
             MEM_writeLE16(&(DElt.sequence), symbol);
@@ -548,6 +614,15 @@
     }
 }
 
+typedef struct {
+    rankValCol_t rankVal[HUF_TABLELOG_MAX];
+    U32 rankStats[HUF_TABLELOG_MAX + 1];
+    U32 rankStart0[HUF_TABLELOG_MAX + 2];
+    sortedSymbol_t sortedSymbol[HUF_SYMBOLVALUE_MAX + 1];
+    BYTE weightList[HUF_SYMBOLVALUE_MAX + 1];
+    U32 calleeWksp[HUF_READ_STATS_WORKSPACE_SIZE_U32];
+} HUF_ReadDTableX2_Workspace;
+
 size_t HUF_readDTableX2_wksp(HUF_DTable* DTable,
                        const void* src, size_t srcSize,
                              void* workSpace, size_t wkspSize)
@@ -560,48 +635,33 @@
     HUF_DEltX2* const dt = (HUF_DEltX2*)dtPtr;
     U32 *rankStart;
 
-    rankValCol_t* rankVal;
-    U32* rankStats;
-    U32* rankStart0;
-    sortedSymbol_t* sortedSymbol;
-    BYTE* weightList;
-    size_t spaceUsed32 = 0;
+    HUF_ReadDTableX2_Workspace* const wksp = (HUF_ReadDTableX2_Workspace*)workSpace;
 
-    rankVal = (rankValCol_t *)((U32 *)workSpace + spaceUsed32);
-    spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2;
-    rankStats = (U32 *)workSpace + spaceUsed32;
-    spaceUsed32 += HUF_TABLELOG_MAX + 1;
-    rankStart0 = (U32 *)workSpace + spaceUsed32;
-    spaceUsed32 += HUF_TABLELOG_MAX + 2;
-    sortedSymbol = (sortedSymbol_t *)workSpace + (spaceUsed32 * sizeof(U32)) / sizeof(sortedSymbol_t);
-    spaceUsed32 += HUF_ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2;
-    weightList = (BYTE *)((U32 *)workSpace + spaceUsed32);
-    spaceUsed32 += HUF_ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2;
+    if (sizeof(*wksp) > wkspSize) return ERROR(GENERIC);
 
-    if ((spaceUsed32 << 2) > wkspSize) return ERROR(tableLog_tooLarge);
-
-    rankStart = rankStart0 + 1;
-    memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1));
+    rankStart = wksp->rankStart0 + 1;
+    ZSTD_memset(wksp->rankStats, 0, sizeof(wksp->rankStats));
+    ZSTD_memset(wksp->rankStart0, 0, sizeof(wksp->rankStart0));
 
     DEBUG_STATIC_ASSERT(sizeof(HUF_DEltX2) == sizeof(HUF_DTable));   /* if compiler fails here, assertion is wrong */
     if (maxTableLog > HUF_TABLELOG_MAX) return ERROR(tableLog_tooLarge);
-    /* memset(weightList, 0, sizeof(weightList)); */  /* is not necessary, even though some analyzer complain ... */
+    /* ZSTD_memset(weightList, 0, sizeof(weightList)); */  /* is not necessary, even though some analyzer complain ... */
 
-    iSize = HUF_readStats(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize);
+    iSize = HUF_readStats_wksp(wksp->weightList, HUF_SYMBOLVALUE_MAX + 1, wksp->rankStats, &nbSymbols, &tableLog, src, srcSize, wksp->calleeWksp, sizeof(wksp->calleeWksp), /* bmi2 */ 0);
     if (HUF_isError(iSize)) return iSize;
 
     /* check result */
     if (tableLog > maxTableLog) return ERROR(tableLog_tooLarge);   /* DTable can't fit code depth */
 
     /* find maxWeight */
-    for (maxW = tableLog; rankStats[maxW]==0; maxW--) {}  /* necessarily finds a solution before 0 */
+    for (maxW = tableLog; wksp->rankStats[maxW]==0; maxW--) {}  /* necessarily finds a solution before 0 */
 
     /* Get start index of each weight */
     {   U32 w, nextRankStart = 0;
         for (w=1; w<maxW+1; w++) {
-            U32 current = nextRankStart;
-            nextRankStart += rankStats[w];
-            rankStart[w] = current;
+            U32 curr = nextRankStart;
+            nextRankStart += wksp->rankStats[w];
+            rankStart[w] = curr;
         }
         rankStart[0] = nextRankStart;   /* put all 0w symbols at the end of sorted list*/
         sizeOfSort = nextRankStart;
@@ -610,57 +670,51 @@
     /* sort symbols by weight */
     {   U32 s;
         for (s=0; s<nbSymbols; s++) {
-            U32 const w = weightList[s];
+            U32 const w = wksp->weightList[s];
             U32 const r = rankStart[w]++;
-            sortedSymbol[r].symbol = (BYTE)s;
-            sortedSymbol[r].weight = (BYTE)w;
+            wksp->sortedSymbol[r].symbol = (BYTE)s;
+            wksp->sortedSymbol[r].weight = (BYTE)w;
         }
         rankStart[0] = 0;   /* forget 0w symbols; this is beginning of weight(1) */
     }
 
     /* Build rankVal */
-    {   U32* const rankVal0 = rankVal[0];
+    {   U32* const rankVal0 = wksp->rankVal[0];
         {   int const rescale = (maxTableLog-tableLog) - 1;   /* tableLog <= maxTableLog */
             U32 nextRankVal = 0;
             U32 w;
             for (w=1; w<maxW+1; w++) {
-                U32 current = nextRankVal;
-                nextRankVal += rankStats[w] << (w+rescale);
-                rankVal0[w] = current;
+                U32 curr = nextRankVal;
+                nextRankVal += wksp->rankStats[w] << (w+rescale);
+                rankVal0[w] = curr;
         }   }
         {   U32 const minBits = tableLog+1 - maxW;
             U32 consumed;
             for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) {
-                U32* const rankValPtr = rankVal[consumed];
+                U32* const rankValPtr = wksp->rankVal[consumed];
                 U32 w;
                 for (w = 1; w < maxW+1; w++) {
                     rankValPtr[w] = rankVal0[w] >> consumed;
     }   }   }   }
 
     HUF_fillDTableX2(dt, maxTableLog,
-                   sortedSymbol, sizeOfSort,
-                   rankStart0, rankVal, maxW,
-                   tableLog+1);
+                   wksp->sortedSymbol, sizeOfSort,
+                   wksp->rankStart0, wksp->rankVal, maxW,
+                   tableLog+1,
+                   wksp->calleeWksp, sizeof(wksp->calleeWksp) / sizeof(U32));
 
     dtd.tableLog = (BYTE)maxTableLog;
     dtd.tableType = 1;
-    memcpy(DTable, &dtd, sizeof(dtd));
+    ZSTD_memcpy(DTable, &dtd, sizeof(dtd));
     return iSize;
 }
 
-size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize)
-{
-  U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-  return HUF_readDTableX2_wksp(DTable, src, srcSize,
-                               workSpace, sizeof(workSpace));
-}
-
 
 FORCE_INLINE_TEMPLATE U32
 HUF_decodeSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog)
 {
     size_t const val = BIT_lookBitsFast(DStream, dtLog);   /* note : dtLog >= 1 */
-    memcpy(op, dt+val, 2);
+    ZSTD_memcpy(op, dt+val, 2);
     BIT_skipBits(DStream, dt[val].nbBits);
     return dt[val].length;
 }
@@ -669,7 +723,7 @@
 HUF_decodeLastSymbolX2(void* op, BIT_DStream_t* DStream, const HUF_DEltX2* dt, const U32 dtLog)
 {
     size_t const val = BIT_lookBitsFast(DStream, dtLog);   /* note : dtLog >= 1 */
-    memcpy(op, dt+val, 1);
+    ZSTD_memcpy(op, dt+val, 1);
     if (dt[val].length==1) BIT_skipBits(DStream, dt[val].nbBits);
     else {
         if (DStream->bitsConsumed < (sizeof(DStream->bitContainer)*8)) {
@@ -890,20 +944,6 @@
 }
 
 
-size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
-                              const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
-}
-
 size_t HUF_decompress4X2_usingDTable(
           void* dst,  size_t dstSize,
     const void* cSrc, size_t cSrcSize,
@@ -937,20 +977,6 @@
 }
 
 
-size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
-                              const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                       workSpace, sizeof(workSpace));
-}
-
-size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
-    return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
-}
-
 #endif /* HUF_FORCE_DECOMPRESS_X1 */
 
 
@@ -1051,67 +1077,6 @@
 }
 
 
-typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
-
-size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2)
-    static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 };
-#endif
-
-    /* validation checks */
-    if (dstSize == 0) return ERROR(dstSize_tooSmall);
-    if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
-    if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
-    if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
-
-    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-        (void)algoNb;
-        assert(algoNb == 0);
-        return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-        (void)algoNb;
-        assert(algoNb == 1);
-        return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize);
-#else
-        return decompress[algoNb](dst, dstSize, cSrc, cSrcSize);
-#endif
-    }
-}
-
-size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    /* validation checks */
-    if (dstSize == 0) return ERROR(dstSize_tooSmall);
-    if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
-    if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
-    if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
-
-    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
-#if defined(HUF_FORCE_DECOMPRESS_X1)
-        (void)algoNb;
-        assert(algoNb == 0);
-        return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
-#elif defined(HUF_FORCE_DECOMPRESS_X2)
-        (void)algoNb;
-        assert(algoNb == 1);
-        return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
-#else
-        return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) :
-                        HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ;
-#endif
-    }
-}
-
-size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                         workSpace, sizeof(workSpace));
-}
-
-
 size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable* dctx, void* dst,
                                      size_t dstSize, const void* cSrc,
                                      size_t cSrcSize, void* workSpace,
@@ -1145,8 +1110,8 @@
     /* validation checks */
     if (dstSize == 0) return ERROR(dstSize_tooSmall);
     if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
-    if (cSrcSize == dstSize) { memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
-    if (cSrcSize == 1) { memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
+    if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
+    if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
 
     {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
 #if defined(HUF_FORCE_DECOMPRESS_X1)
@@ -1168,14 +1133,6 @@
     }
 }
 
-size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
-                             const void* cSrc, size_t cSrcSize)
-{
-    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
-    return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
-                                      workSpace, sizeof(workSpace));
-}
-
 
 size_t HUF_decompress1X_usingDTable_bmi2(void* dst, size_t maxDstSize, const void* cSrc, size_t cSrcSize, const HUF_DTable* DTable, int bmi2)
 {
@@ -1199,7 +1156,7 @@
 {
     const BYTE* ip = (const BYTE*) cSrc;
 
-    size_t const hSize = HUF_readDTableX1_wksp(dctx, cSrc, cSrcSize, workSpace, wkspSize);
+    size_t const hSize = HUF_readDTableX1_wksp_bmi2(dctx, cSrc, cSrcSize, workSpace, wkspSize, bmi2);
     if (HUF_isError(hSize)) return hSize;
     if (hSize >= cSrcSize) return ERROR(srcSize_wrong);
     ip += hSize; cSrcSize -= hSize;
@@ -1246,3 +1203,149 @@
 #endif
     }
 }
+
+#ifndef ZSTD_NO_UNUSED_FUNCTIONS
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_readDTableX1(HUF_DTable* DTable, const void* src, size_t srcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_readDTableX1_wksp(DTable, src, srcSize,
+                                 workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X1_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
+                              const void* cSrc, size_t cSrcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_decompress1X1_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
+                                       workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
+    return HUF_decompress1X1_DCtx (DTable, dst, dstSize, cSrc, cSrcSize);
+}
+#endif
+
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_readDTableX2(HUF_DTable* DTable, const void* src, size_t srcSize)
+{
+  U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+  return HUF_readDTableX2_wksp(DTable, src, srcSize,
+                               workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X2_DCtx(HUF_DTable* DCtx, void* dst, size_t dstSize,
+                              const void* cSrc, size_t cSrcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_decompress1X2_DCtx_wksp(DCtx, dst, dstSize, cSrc, cSrcSize,
+                                       workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
+    return HUF_decompress1X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
+}
+#endif
+
+#ifndef HUF_FORCE_DECOMPRESS_X2
+size_t HUF_decompress4X1_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_decompress4X1_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+                                       workSpace, sizeof(workSpace));
+}
+size_t HUF_decompress4X1 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    HUF_CREATE_STATIC_DTABLEX1(DTable, HUF_TABLELOG_MAX);
+    return HUF_decompress4X1_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
+}
+#endif
+
+#ifndef HUF_FORCE_DECOMPRESS_X1
+size_t HUF_decompress4X2_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
+                              const void* cSrc, size_t cSrcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+                                       workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress4X2 (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    HUF_CREATE_STATIC_DTABLEX2(DTable, HUF_TABLELOG_MAX);
+    return HUF_decompress4X2_DCtx(DTable, dst, dstSize, cSrc, cSrcSize);
+}
+#endif
+
+typedef size_t (*decompressionAlgo)(void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize);
+
+size_t HUF_decompress (void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+#if !defined(HUF_FORCE_DECOMPRESS_X1) && !defined(HUF_FORCE_DECOMPRESS_X2)
+    static const decompressionAlgo decompress[2] = { HUF_decompress4X1, HUF_decompress4X2 };
+#endif
+
+    /* validation checks */
+    if (dstSize == 0) return ERROR(dstSize_tooSmall);
+    if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
+    if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
+    if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
+
+    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+        (void)algoNb;
+        assert(algoNb == 0);
+        return HUF_decompress4X1(dst, dstSize, cSrc, cSrcSize);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+        (void)algoNb;
+        assert(algoNb == 1);
+        return HUF_decompress4X2(dst, dstSize, cSrc, cSrcSize);
+#else
+        return decompress[algoNb](dst, dstSize, cSrc, cSrcSize);
+#endif
+    }
+}
+
+size_t HUF_decompress4X_DCtx (HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    /* validation checks */
+    if (dstSize == 0) return ERROR(dstSize_tooSmall);
+    if (cSrcSize > dstSize) return ERROR(corruption_detected);   /* invalid */
+    if (cSrcSize == dstSize) { ZSTD_memcpy(dst, cSrc, dstSize); return dstSize; }   /* not compressed */
+    if (cSrcSize == 1) { ZSTD_memset(dst, *(const BYTE*)cSrc, dstSize); return dstSize; }   /* RLE */
+
+    {   U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize);
+#if defined(HUF_FORCE_DECOMPRESS_X1)
+        (void)algoNb;
+        assert(algoNb == 0);
+        return HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
+#elif defined(HUF_FORCE_DECOMPRESS_X2)
+        (void)algoNb;
+        assert(algoNb == 1);
+        return HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize);
+#else
+        return algoNb ? HUF_decompress4X2_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) :
+                        HUF_decompress4X1_DCtx(dctx, dst, dstSize, cSrc, cSrcSize) ;
+#endif
+    }
+}
+
+size_t HUF_decompress4X_hufOnly(HUF_DTable* dctx, void* dst, size_t dstSize, const void* cSrc, size_t cSrcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_decompress4X_hufOnly_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+                                         workSpace, sizeof(workSpace));
+}
+
+size_t HUF_decompress1X_DCtx(HUF_DTable* dctx, void* dst, size_t dstSize,
+                             const void* cSrc, size_t cSrcSize)
+{
+    U32 workSpace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32];
+    return HUF_decompress1X_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize,
+                                      workSpace, sizeof(workSpace));
+}
+#endif
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.c b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
index c8cb8ec..ce33547 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_ddict.c
+++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,7 +14,7 @@
 /*-*******************************************************
 *  Dependencies
 *********************************************************/
-#include <string.h>      /* memcpy, memmove, memset */
+#include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */
 #include "../common/cpu.h"         /* bmi2 */
 #include "../common/mem.h"         /* low level memory routines */
 #define FSE_STATIC_LINKING_ONLY
@@ -127,11 +127,11 @@
         ddict->dictContent = dict;
         if (!dict) dictSize = 0;
     } else {
-        void* const internalBuffer = ZSTD_malloc(dictSize, ddict->cMem);
+        void* const internalBuffer = ZSTD_customMalloc(dictSize, ddict->cMem);
         ddict->dictBuffer = internalBuffer;
         ddict->dictContent = internalBuffer;
         if (!internalBuffer) return ERROR(memory_allocation);
-        memcpy(internalBuffer, dict, dictSize);
+        ZSTD_memcpy(internalBuffer, dict, dictSize);
     }
     ddict->dictSize = dictSize;
     ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001);  /* cover both little and big endian */
@@ -147,9 +147,9 @@
                                       ZSTD_dictContentType_e dictContentType,
                                       ZSTD_customMem customMem)
 {
-    if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+    if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
 
-    {   ZSTD_DDict* const ddict = (ZSTD_DDict*) ZSTD_malloc(sizeof(ZSTD_DDict), customMem);
+    {   ZSTD_DDict* const ddict = (ZSTD_DDict*) ZSTD_customMalloc(sizeof(ZSTD_DDict), customMem);
         if (ddict == NULL) return NULL;
         ddict->cMem = customMem;
         {   size_t const initResult = ZSTD_initDDict_internal(ddict,
@@ -198,7 +198,7 @@
     if ((size_t)sBuffer & 7) return NULL;   /* 8-aligned */
     if (sBufferSize < neededSpace) return NULL;
     if (dictLoadMethod == ZSTD_dlm_byCopy) {
-        memcpy(ddict+1, dict, dictSize);  /* local copy */
+        ZSTD_memcpy(ddict+1, dict, dictSize);  /* local copy */
         dict = ddict+1;
     }
     if (ZSTD_isError( ZSTD_initDDict_internal(ddict,
@@ -213,8 +213,8 @@
 {
     if (ddict==NULL) return 0;   /* support free on NULL */
     {   ZSTD_customMem const cMem = ddict->cMem;
-        ZSTD_free(ddict->dictBuffer, cMem);
-        ZSTD_free(ddict, cMem);
+        ZSTD_customFree(ddict->dictBuffer, cMem);
+        ZSTD_customFree(ddict, cMem);
         return 0;
     }
 }
diff --git a/Utilities/cmzstd/lib/decompress/zstd_ddict.h b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
index af307ef..bd03268 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_ddict.h
+++ b/Utilities/cmzstd/lib/decompress/zstd_ddict.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -15,7 +15,7 @@
 /*-*******************************************************
  *  Dependencies
  *********************************************************/
-#include <stddef.h>   /* size_t */
+#include "../common/zstd_deps.h"   /* size_t */
 #include "../zstd.h"     /* ZSTD_DDict, and several public functions */
 
 
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress.c b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
index be5c7cf..910bc03 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress.c
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -55,13 +55,14 @@
 /*-*******************************************************
 *  Dependencies
 *********************************************************/
-#include <string.h>      /* memcpy, memmove, memset */
+#include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */
 #include "../common/cpu.h"         /* bmi2 */
 #include "../common/mem.h"         /* low level memory routines */
 #define FSE_STATIC_LINKING_ONLY
 #include "../common/fse.h"
 #define HUF_STATIC_LINKING_ONLY
 #include "../common/huf.h"
+#include "../common/xxhash.h" /* XXH64_reset, XXH64_update, XXH64_digest, XXH64 */
 #include "../common/zstd_internal.h"  /* blockProperties_t */
 #include "zstd_decompress_internal.h"   /* ZSTD_DCtx */
 #include "zstd_ddict.h"  /* ZSTD_DDictDictContent */
@@ -72,6 +73,144 @@
 #endif
 
 
+
+/*************************************
+ * Multiple DDicts Hashset internals *
+ *************************************/
+
+#define DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT 4
+#define DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT 3   /* These two constants represent SIZE_MULT/COUNT_MULT load factor without using a float.
+                                                     * Currently, that means a 0.75 load factor.
+                                                     * So, if count * COUNT_MULT / size * SIZE_MULT != 0, then we've exceeded
+                                                     * the load factor of the ddict hash set.
+                                                     */
+
+#define DDICT_HASHSET_TABLE_BASE_SIZE 64
+#define DDICT_HASHSET_RESIZE_FACTOR 2
+
+/* Hash function to determine starting position of dict insertion within the table
+ * Returns an index between [0, hashSet->ddictPtrTableSize]
+ */
+static size_t ZSTD_DDictHashSet_getIndex(const ZSTD_DDictHashSet* hashSet, U32 dictID) {
+    const U64 hash = XXH64(&dictID, sizeof(U32), 0);
+    /* DDict ptr table size is a multiple of 2, use size - 1 as mask to get index within [0, hashSet->ddictPtrTableSize) */
+    return hash & (hashSet->ddictPtrTableSize - 1);
+}
+
+/* Adds DDict to a hashset without resizing it.
+ * If inserting a DDict with a dictID that already exists in the set, replaces the one in the set.
+ * Returns 0 if successful, or a zstd error code if something went wrong.
+ */
+static size_t ZSTD_DDictHashSet_emplaceDDict(ZSTD_DDictHashSet* hashSet, const ZSTD_DDict* ddict) {
+    const U32 dictID = ZSTD_getDictID_fromDDict(ddict);
+    size_t idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID);
+    const size_t idxRangeMask = hashSet->ddictPtrTableSize - 1;
+    RETURN_ERROR_IF(hashSet->ddictPtrCount == hashSet->ddictPtrTableSize, GENERIC, "Hash set is full!");
+    DEBUGLOG(4, "Hashed index: for dictID: %u is %zu", dictID, idx);
+    while (hashSet->ddictPtrTable[idx] != NULL) {
+        /* Replace existing ddict if inserting ddict with same dictID */
+        if (ZSTD_getDictID_fromDDict(hashSet->ddictPtrTable[idx]) == dictID) {
+            DEBUGLOG(4, "DictID already exists, replacing rather than adding");
+            hashSet->ddictPtrTable[idx] = ddict;
+            return 0;
+        }
+        idx &= idxRangeMask;
+        idx++;
+    }
+    DEBUGLOG(4, "Final idx after probing for dictID %u is: %zu", dictID, idx);
+    hashSet->ddictPtrTable[idx] = ddict;
+    hashSet->ddictPtrCount++;
+    return 0;
+}
+
+/* Expands hash table by factor of DDICT_HASHSET_RESIZE_FACTOR and
+ * rehashes all values, allocates new table, frees old table.
+ * Returns 0 on success, otherwise a zstd error code.
+ */
+static size_t ZSTD_DDictHashSet_expand(ZSTD_DDictHashSet* hashSet, ZSTD_customMem customMem) {
+    size_t newTableSize = hashSet->ddictPtrTableSize * DDICT_HASHSET_RESIZE_FACTOR;
+    const ZSTD_DDict** newTable = (const ZSTD_DDict**)ZSTD_customCalloc(sizeof(ZSTD_DDict*) * newTableSize, customMem);
+    const ZSTD_DDict** oldTable = hashSet->ddictPtrTable;
+    size_t oldTableSize = hashSet->ddictPtrTableSize;
+    size_t i;
+
+    DEBUGLOG(4, "Expanding DDict hash table! Old size: %zu new size: %zu", oldTableSize, newTableSize);
+    RETURN_ERROR_IF(!newTable, memory_allocation, "Expanded hashset allocation failed!");
+    hashSet->ddictPtrTable = newTable;
+    hashSet->ddictPtrTableSize = newTableSize;
+    hashSet->ddictPtrCount = 0;
+    for (i = 0; i < oldTableSize; ++i) {
+        if (oldTable[i] != NULL) {
+            FORWARD_IF_ERROR(ZSTD_DDictHashSet_emplaceDDict(hashSet, oldTable[i]), "");
+        }
+    }
+    ZSTD_customFree((void*)oldTable, customMem);
+    DEBUGLOG(4, "Finished re-hash");
+    return 0;
+}
+
+/* Fetches a DDict with the given dictID
+ * Returns the ZSTD_DDict* with the requested dictID. If it doesn't exist, then returns NULL.
+ */
+static const ZSTD_DDict* ZSTD_DDictHashSet_getDDict(ZSTD_DDictHashSet* hashSet, U32 dictID) {
+    size_t idx = ZSTD_DDictHashSet_getIndex(hashSet, dictID);
+    const size_t idxRangeMask = hashSet->ddictPtrTableSize - 1;
+    DEBUGLOG(4, "Hashed index: for dictID: %u is %zu", dictID, idx);
+    for (;;) {
+        size_t currDictID = ZSTD_getDictID_fromDDict(hashSet->ddictPtrTable[idx]);
+        if (currDictID == dictID || currDictID == 0) {
+            /* currDictID == 0 implies a NULL ddict entry */
+            break;
+        } else {
+            idx &= idxRangeMask;    /* Goes to start of table when we reach the end */
+            idx++;
+        }
+    }
+    DEBUGLOG(4, "Final idx after probing for dictID %u is: %zu", dictID, idx);
+    return hashSet->ddictPtrTable[idx];
+}
+
+/* Allocates space for and returns a ddict hash set
+ * The hash set's ZSTD_DDict* table has all values automatically set to NULL to begin with.
+ * Returns NULL if allocation failed.
+ */
+static ZSTD_DDictHashSet* ZSTD_createDDictHashSet(ZSTD_customMem customMem) {
+    ZSTD_DDictHashSet* ret = (ZSTD_DDictHashSet*)ZSTD_customMalloc(sizeof(ZSTD_DDictHashSet), customMem);
+    DEBUGLOG(4, "Allocating new hash set");
+    ret->ddictPtrTable = (const ZSTD_DDict**)ZSTD_customCalloc(DDICT_HASHSET_TABLE_BASE_SIZE * sizeof(ZSTD_DDict*), customMem);
+    ret->ddictPtrTableSize = DDICT_HASHSET_TABLE_BASE_SIZE;
+    ret->ddictPtrCount = 0;
+    if (!ret || !ret->ddictPtrTable) {
+        return NULL;
+    }
+    return ret;
+}
+
+/* Frees the table of ZSTD_DDict* within a hashset, then frees the hashset itself.
+ * Note: The ZSTD_DDict* within the table are NOT freed.
+ */
+static void ZSTD_freeDDictHashSet(ZSTD_DDictHashSet* hashSet, ZSTD_customMem customMem) {
+    DEBUGLOG(4, "Freeing ddict hash set");
+    if (hashSet && hashSet->ddictPtrTable) {
+        ZSTD_customFree((void*)hashSet->ddictPtrTable, customMem);
+    }
+    if (hashSet) {
+        ZSTD_customFree(hashSet, customMem);
+    }
+}
+
+/* Public function: Adds a DDict into the ZSTD_DDictHashSet, possibly triggering a resize of the hash set.
+ * Returns 0 on success, or a ZSTD error.
+ */
+static size_t ZSTD_DDictHashSet_addDDict(ZSTD_DDictHashSet* hashSet, const ZSTD_DDict* ddict, ZSTD_customMem customMem) {
+    DEBUGLOG(4, "Adding dict ID: %u to hashset with - Count: %zu Tablesize: %zu", ZSTD_getDictID_fromDDict(ddict), hashSet->ddictPtrCount, hashSet->ddictPtrTableSize);
+    if (hashSet->ddictPtrCount * DDICT_HASHSET_MAX_LOAD_FACTOR_COUNT_MULT / hashSet->ddictPtrTableSize * DDICT_HASHSET_MAX_LOAD_FACTOR_SIZE_MULT != 0) {
+        FORWARD_IF_ERROR(ZSTD_DDictHashSet_expand(hashSet, customMem), "");
+    }
+    FORWARD_IF_ERROR(ZSTD_DDictHashSet_emplaceDDict(hashSet, ddict), "");
+    return 0;
+}
+
 /*-*************************************************************
 *   Context management
 ***************************************************************/
@@ -94,11 +233,19 @@
     return startingInputLength;
 }
 
+static void ZSTD_DCtx_resetParameters(ZSTD_DCtx* dctx)
+{
+    assert(dctx->streamStage == zdss_init);
+    dctx->format = ZSTD_f_zstd1;
+    dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;
+    dctx->outBufferMode = ZSTD_bm_buffered;
+    dctx->forceIgnoreChecksum = ZSTD_d_validateChecksum;
+    dctx->refMultipleDDicts = ZSTD_rmd_refSingleDDict;
+}
+
 static void ZSTD_initDCtx_internal(ZSTD_DCtx* dctx)
 {
-    dctx->format = ZSTD_f_zstd1;  /* ZSTD_decompressBegin() invokes ZSTD_startingInputLength() with argument dctx->format */
     dctx->staticSize  = 0;
-    dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;
     dctx->ddict       = NULL;
     dctx->ddictLocal  = NULL;
     dctx->dictEnd     = NULL;
@@ -113,7 +260,8 @@
     dctx->noForwardProgress = 0;
     dctx->oversizedDuration = 0;
     dctx->bmi2 = ZSTD_cpuid_bmi2(ZSTD_cpuid());
-    dctx->outBufferMode = ZSTD_obm_buffered;
+    dctx->ddictSet = NULL;
+    ZSTD_DCtx_resetParameters(dctx);
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     dctx->dictContentEndForFuzzing = NULL;
 #endif
@@ -134,9 +282,9 @@
 
 ZSTD_DCtx* ZSTD_createDCtx_advanced(ZSTD_customMem customMem)
 {
-    if (!customMem.customAlloc ^ !customMem.customFree) return NULL;
+    if ((!customMem.customAlloc) ^ (!customMem.customFree)) return NULL;
 
-    {   ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_malloc(sizeof(*dctx), customMem);
+    {   ZSTD_DCtx* const dctx = (ZSTD_DCtx*)ZSTD_customMalloc(sizeof(*dctx), customMem);
         if (!dctx) return NULL;
         dctx->customMem = customMem;
         ZSTD_initDCtx_internal(dctx);
@@ -164,13 +312,17 @@
     RETURN_ERROR_IF(dctx->staticSize, memory_allocation, "not compatible with static DCtx");
     {   ZSTD_customMem const cMem = dctx->customMem;
         ZSTD_clearDict(dctx);
-        ZSTD_free(dctx->inBuff, cMem);
+        ZSTD_customFree(dctx->inBuff, cMem);
         dctx->inBuff = NULL;
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
         if (dctx->legacyContext)
             ZSTD_freeLegacyStreamContext(dctx->legacyContext, dctx->previousLegacyVersion);
 #endif
-        ZSTD_free(dctx, cMem);
+        if (dctx->ddictSet) {
+            ZSTD_freeDDictHashSet(dctx->ddictSet, cMem);
+            dctx->ddictSet = NULL;
+        }
+        ZSTD_customFree(dctx, cMem);
         return 0;
     }
 }
@@ -179,7 +331,30 @@
 void ZSTD_copyDCtx(ZSTD_DCtx* dstDCtx, const ZSTD_DCtx* srcDCtx)
 {
     size_t const toCopy = (size_t)((char*)(&dstDCtx->inBuff) - (char*)dstDCtx);
-    memcpy(dstDCtx, srcDCtx, toCopy);  /* no need to copy workspace */
+    ZSTD_memcpy(dstDCtx, srcDCtx, toCopy);  /* no need to copy workspace */
+}
+
+/* Given a dctx with a digested frame params, re-selects the correct ZSTD_DDict based on
+ * the requested dict ID from the frame. If there exists a reference to the correct ZSTD_DDict, then
+ * accordingly sets the ddict to be used to decompress the frame.
+ *
+ * If no DDict is found, then no action is taken, and the ZSTD_DCtx::ddict remains as-is.
+ *
+ * ZSTD_d_refMultipleDDicts must be enabled for this function to be called.
+ */
+static void ZSTD_DCtx_selectFrameDDict(ZSTD_DCtx* dctx) {
+    assert(dctx->refMultipleDDicts && dctx->ddictSet);
+    DEBUGLOG(4, "Adjusting DDict based on requested dict ID from frame");
+    if (dctx->ddict) {
+        const ZSTD_DDict* frameDDict = ZSTD_DDictHashSet_getDDict(dctx->ddictSet, dctx->fParams.dictID);
+        if (frameDDict) {
+            DEBUGLOG(4, "DDict found!");
+            ZSTD_clearDict(dctx);
+            dctx->dictID = dctx->fParams.dictID;
+            dctx->ddict = frameDDict;
+            dctx->dictUses = ZSTD_use_indefinitely;
+        }
+    }
 }
 
 
@@ -246,7 +421,7 @@
     const BYTE* ip = (const BYTE*)src;
     size_t const minInputSize = ZSTD_startingInputLength(format);
 
-    memset(zfhPtr, 0, sizeof(*zfhPtr));   /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */
+    ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr));   /* not strictly necessary, but static analyzer do not understand that zfhPtr is only going to be read only if return value is zero, since they are 2 different signals */
     if (srcSize < minInputSize) return minInputSize;
     RETURN_ERROR_IF(src==NULL, GENERIC, "invalid parameter");
 
@@ -256,7 +431,7 @@
             /* skippable frame */
             if (srcSize < ZSTD_SKIPPABLEHEADERSIZE)
                 return ZSTD_SKIPPABLEHEADERSIZE; /* magic number + frame length */
-            memset(zfhPtr, 0, sizeof(*zfhPtr));
+            ZSTD_memset(zfhPtr, 0, sizeof(*zfhPtr));
             zfhPtr->frameContentSize = MEM_readLE32((const char *)src + ZSTD_FRAMEIDSIZE);
             zfhPtr->frameType = ZSTD_skippableFrame;
             return 0;
@@ -433,12 +608,19 @@
 
 /** ZSTD_decodeFrameHeader() :
  * `headerSize` must be the size provided by ZSTD_frameHeaderSize().
+ * If multiple DDict references are enabled, also will choose the correct DDict to use.
  * @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */
 static size_t ZSTD_decodeFrameHeader(ZSTD_DCtx* dctx, const void* src, size_t headerSize)
 {
     size_t const result = ZSTD_getFrameHeader_advanced(&(dctx->fParams), src, headerSize, dctx->format);
     if (ZSTD_isError(result)) return result;    /* invalid header */
     RETURN_ERROR_IF(result>0, srcSize_wrong, "headerSize too small");
+
+    /* Reference DDict requested by frame if dctx references multiple ddicts */
+    if (dctx->refMultipleDDicts == ZSTD_rmd_refMultipleDDicts && dctx->ddictSet) {
+        ZSTD_DCtx_selectFrameDDict(dctx);
+    }
+
 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     /* Skip the dictID check in fuzzing mode, because it makes the search
      * harder.
@@ -446,7 +628,9 @@
     RETURN_ERROR_IF(dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID),
                     dictionary_wrong, "");
 #endif
-    if (dctx->fParams.checksumFlag) XXH64_reset(&dctx->xxhState, 0);
+    dctx->validateChecksum = (dctx->fParams.checksumFlag && !dctx->forceIgnoreChecksum) ? 1 : 0;
+    if (dctx->validateChecksum) XXH64_reset(&dctx->xxhState, 0);
+    dctx->processedCSize += headerSize;
     return 0;
 }
 
@@ -461,7 +645,7 @@
 static ZSTD_frameSizeInfo ZSTD_findFrameSizeInfo(const void* src, size_t srcSize)
 {
     ZSTD_frameSizeInfo frameSizeInfo;
-    memset(&frameSizeInfo, 0, sizeof(ZSTD_frameSizeInfo));
+    ZSTD_memset(&frameSizeInfo, 0, sizeof(ZSTD_frameSizeInfo));
 
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT >= 1)
     if (ZSTD_isLegacy(src, srcSize))
@@ -516,7 +700,7 @@
             ip += 4;
         }
 
-        frameSizeInfo.compressedSize = ip - ipstart;
+        frameSizeInfo.compressedSize = (size_t)(ip - ipstart);
         frameSizeInfo.decompressedBound = (zfh.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN)
                                         ? zfh.frameContentSize
                                         : nbBlocks * zfh.blockSizeMax;
@@ -569,7 +753,7 @@
 size_t ZSTD_insertBlock(ZSTD_DCtx* dctx, const void* blockStart, size_t blockSize)
 {
     DEBUGLOG(5, "ZSTD_insertBlock: %u bytes", (unsigned)blockSize);
-    ZSTD_checkContinuity(dctx, blockStart);
+    ZSTD_checkContinuity(dctx, blockStart, blockSize);
     dctx->previousDstEnd = (const char*)blockStart + blockSize;
     return blockSize;
 }
@@ -579,12 +763,12 @@
                           const void* src, size_t srcSize)
 {
     DEBUGLOG(5, "ZSTD_copyRawBlock");
+    RETURN_ERROR_IF(srcSize > dstCapacity, dstSize_tooSmall, "");
     if (dst == NULL) {
         if (srcSize == 0) return 0;
         RETURN_ERROR(dstBuffer_null, "");
     }
-    RETURN_ERROR_IF(srcSize > dstCapacity, dstSize_tooSmall, "");
-    memcpy(dst, src, srcSize);
+    ZSTD_memcpy(dst, src, srcSize);
     return srcSize;
 }
 
@@ -592,15 +776,41 @@
                                BYTE b,
                                size_t regenSize)
 {
+    RETURN_ERROR_IF(regenSize > dstCapacity, dstSize_tooSmall, "");
     if (dst == NULL) {
         if (regenSize == 0) return 0;
         RETURN_ERROR(dstBuffer_null, "");
     }
-    RETURN_ERROR_IF(regenSize > dstCapacity, dstSize_tooSmall, "");
-    memset(dst, b, regenSize);
+    ZSTD_memset(dst, b, regenSize);
     return regenSize;
 }
 
+static void ZSTD_DCtx_trace_end(ZSTD_DCtx const* dctx, U64 uncompressedSize, U64 compressedSize, unsigned streaming)
+{
+#if ZSTD_TRACE
+    if (dctx->traceCtx && ZSTD_trace_decompress_end != NULL) {
+        ZSTD_Trace trace;
+        ZSTD_memset(&trace, 0, sizeof(trace));
+        trace.version = ZSTD_VERSION_NUMBER;
+        trace.streaming = streaming;
+        if (dctx->ddict) {
+            trace.dictionaryID = ZSTD_getDictID_fromDDict(dctx->ddict);
+            trace.dictionarySize = ZSTD_DDict_dictSize(dctx->ddict);
+            trace.dictionaryIsCold = dctx->ddictIsCold;
+        }
+        trace.uncompressedSize = (size_t)uncompressedSize;
+        trace.compressedSize = (size_t)compressedSize;
+        trace.dctx = dctx;
+        ZSTD_trace_decompress_end(dctx->traceCtx, &trace);
+    }
+#else
+    (void)dctx;
+    (void)uncompressedSize;
+    (void)compressedSize;
+    (void)streaming;
+#endif
+}
+
 
 /*! ZSTD_decompressFrame() :
  * @dctx must be properly initialized
@@ -610,8 +820,9 @@
                                    void* dst, size_t dstCapacity,
                              const void** srcPtr, size_t *srcSizePtr)
 {
-    const BYTE* ip = (const BYTE*)(*srcPtr);
-    BYTE* const ostart = (BYTE* const)dst;
+    const BYTE* const istart = (const BYTE*)(*srcPtr);
+    const BYTE* ip = istart;
+    BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = dstCapacity != 0 ? ostart + dstCapacity : ostart;
     BYTE* op = ostart;
     size_t remainingSrcSize = *srcSizePtr;
@@ -647,13 +858,13 @@
         switch(blockProperties.blockType)
         {
         case bt_compressed:
-            decodedSize = ZSTD_decompressBlock_internal(dctx, op, oend-op, ip, cBlockSize, /* frame */ 1);
+            decodedSize = ZSTD_decompressBlock_internal(dctx, op, (size_t)(oend-op), ip, cBlockSize, /* frame */ 1);
             break;
         case bt_raw :
-            decodedSize = ZSTD_copyRawBlock(op, oend-op, ip, cBlockSize);
+            decodedSize = ZSTD_copyRawBlock(op, (size_t)(oend-op), ip, cBlockSize);
             break;
         case bt_rle :
-            decodedSize = ZSTD_setRleBlock(op, oend-op, *ip, blockProperties.origSize);
+            decodedSize = ZSTD_setRleBlock(op, (size_t)(oend-op), *ip, blockProperties.origSize);
             break;
         case bt_reserved :
         default:
@@ -661,7 +872,7 @@
         }
 
         if (ZSTD_isError(decodedSize)) return decodedSize;
-        if (dctx->fParams.checksumFlag)
+        if (dctx->validateChecksum)
             XXH64_update(&dctx->xxhState, op, decodedSize);
         if (decodedSize != 0)
             op += decodedSize;
@@ -676,19 +887,21 @@
                         corruption_detected, "");
     }
     if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */
-        U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState);
-        U32 checkRead;
         RETURN_ERROR_IF(remainingSrcSize<4, checksum_wrong, "");
-        checkRead = MEM_readLE32(ip);
-        RETURN_ERROR_IF(checkRead != checkCalc, checksum_wrong, "");
+        if (!dctx->forceIgnoreChecksum) {
+            U32 const checkCalc = (U32)XXH64_digest(&dctx->xxhState);
+            U32 checkRead;
+            checkRead = MEM_readLE32(ip);
+            RETURN_ERROR_IF(checkRead != checkCalc, checksum_wrong, "");
+        }
         ip += 4;
         remainingSrcSize -= 4;
     }
-
+    ZSTD_DCtx_trace_end(dctx, (U64)(op-ostart), (U64)(ip-istart), /* streaming */ 0);
     /* Allow caller to get size read */
     *srcPtr = ip;
     *srcSizePtr = remainingSrcSize;
-    return op-ostart;
+    return (size_t)(op-ostart);
 }
 
 static size_t ZSTD_decompressMultiFrame(ZSTD_DCtx* dctx,
@@ -721,7 +934,7 @@
             decodedSize = ZSTD_decompressLegacy(dst, dstCapacity, src, frameSize, dict, dictSize);
             if (ZSTD_isError(decodedSize)) return decodedSize;
 
-            assert(decodedSize <=- dstCapacity);
+            assert(decodedSize <= dstCapacity);
             dst = (BYTE*)dst + decodedSize;
             dstCapacity -= decodedSize;
 
@@ -753,7 +966,7 @@
              * use this in all cases but ddict */
             FORWARD_IF_ERROR(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize), "");
         }
-        ZSTD_checkContinuity(dctx, dst);
+        ZSTD_checkContinuity(dctx, dst, dstCapacity);
 
         {   const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity,
                                                     &src, &srcSize);
@@ -761,15 +974,13 @@
                 (ZSTD_getErrorCode(res) == ZSTD_error_prefix_unknown)
              && (moreThan1Frame==1),
                 srcSize_wrong,
-                "at least one frame successfully completed, but following "
-                "bytes are garbage: it's more likely to be a srcSize error, "
-                "specifying more bytes than compressed size of frame(s). This "
-                "error message replaces ERROR(prefix_unknown), which would be "
-                "confusing, as the first header is actually correct. Note that "
-                "one could be unlucky, it might be a corruption error instead, "
-                "happening right at the place where we expect zstd magic "
-                "bytes. But this is _much_ less likely than a srcSize field "
-                "error.");
+                "At least one frame successfully completed, "
+                "but following bytes are garbage: "
+                "it's more likely to be a srcSize error, "
+                "specifying more input bytes than size of frame(s). "
+                "Note: one could be unlucky, it might be a corruption error instead, "
+                "happening right at the place where we expect zstd magic bytes. "
+                "But this is _much_ less likely than a srcSize field error.");
             if (ZSTD_isError(res)) return res;
             assert(res <= dstCapacity);
             if (res != 0)
@@ -781,7 +992,7 @@
 
     RETURN_ERROR_IF(srcSize, srcSize_wrong, "input not entirely consumed");
 
-    return (BYTE*)dst - (BYTE*)dststart;
+    return (size_t)((BYTE*)dst - (BYTE*)dststart);
 }
 
 size_t ZSTD_decompress_usingDict(ZSTD_DCtx* dctx,
@@ -890,7 +1101,9 @@
     DEBUGLOG(5, "ZSTD_decompressContinue (srcSize:%u)", (unsigned)srcSize);
     /* Sanity check */
     RETURN_ERROR_IF(srcSize != ZSTD_nextSrcSizeToDecompressWithInputSize(dctx, srcSize), srcSize_wrong, "not allowed");
-    if (dstCapacity) ZSTD_checkContinuity(dctx, dst);
+    ZSTD_checkContinuity(dctx, dst, dstCapacity);
+
+    dctx->processedCSize += srcSize;
 
     switch (dctx->stage)
     {
@@ -899,21 +1112,21 @@
         if (dctx->format == ZSTD_f_zstd1) {  /* allows header */
             assert(srcSize >= ZSTD_FRAMEIDSIZE);  /* to read skippable magic number */
             if ((MEM_readLE32(src) & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {        /* skippable frame */
-                memcpy(dctx->headerBuffer, src, srcSize);
+                ZSTD_memcpy(dctx->headerBuffer, src, srcSize);
                 dctx->expected = ZSTD_SKIPPABLEHEADERSIZE - srcSize;  /* remaining to load to get full skippable frame header */
                 dctx->stage = ZSTDds_decodeSkippableHeader;
                 return 0;
         }   }
         dctx->headerSize = ZSTD_frameHeaderSize_internal(src, srcSize, dctx->format);
         if (ZSTD_isError(dctx->headerSize)) return dctx->headerSize;
-        memcpy(dctx->headerBuffer, src, srcSize);
+        ZSTD_memcpy(dctx->headerBuffer, src, srcSize);
         dctx->expected = dctx->headerSize - srcSize;
         dctx->stage = ZSTDds_decodeFrameHeader;
         return 0;
 
     case ZSTDds_decodeFrameHeader:
         assert(src != NULL);
-        memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, srcSize);
+        ZSTD_memcpy(dctx->headerBuffer + (dctx->headerSize - srcSize), src, srcSize);
         FORWARD_IF_ERROR(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize), "");
         dctx->expected = ZSTD_blockHeaderSize;
         dctx->stage = ZSTDds_decodeBlockHeader;
@@ -977,7 +1190,7 @@
             RETURN_ERROR_IF(rSize > dctx->fParams.blockSizeMax, corruption_detected, "Decompressed Block Size Exceeds Maximum");
             DEBUGLOG(5, "ZSTD_decompressContinue: decoded size from block : %u", (unsigned)rSize);
             dctx->decodedSize += rSize;
-            if (dctx->fParams.checksumFlag) XXH64_update(&dctx->xxhState, dst, rSize);
+            if (dctx->validateChecksum) XXH64_update(&dctx->xxhState, dst, rSize);
             dctx->previousDstEnd = (char*)dst + rSize;
 
             /* Stay on the same stage until we are finished streaming the block. */
@@ -995,6 +1208,7 @@
                     dctx->expected = 4;
                     dctx->stage = ZSTDds_checkChecksum;
                 } else {
+                    ZSTD_DCtx_trace_end(dctx, dctx->decodedSize, dctx->processedCSize, /* streaming */ 1);
                     dctx->expected = 0;   /* ends here */
                     dctx->stage = ZSTDds_getFrameHeaderSize;
                 }
@@ -1007,10 +1221,14 @@
 
     case ZSTDds_checkChecksum:
         assert(srcSize == 4);  /* guaranteed by dctx->expected */
-        {   U32 const h32 = (U32)XXH64_digest(&dctx->xxhState);
-            U32 const check32 = MEM_readLE32(src);
-            DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", (unsigned)h32, (unsigned)check32);
-            RETURN_ERROR_IF(check32 != h32, checksum_wrong, "");
+        {
+            if (dctx->validateChecksum) {
+                U32 const h32 = (U32)XXH64_digest(&dctx->xxhState);
+                U32 const check32 = MEM_readLE32(src);
+                DEBUGLOG(4, "ZSTD_decompressContinue: checksum : calculated %08X :: %08X read", (unsigned)h32, (unsigned)check32);
+                RETURN_ERROR_IF(check32 != h32, checksum_wrong, "");
+            }
+            ZSTD_DCtx_trace_end(dctx, dctx->decodedSize, dctx->processedCSize, /* streaming */ 1);
             dctx->expected = 0;
             dctx->stage = ZSTDds_getFrameHeaderSize;
             return 0;
@@ -1019,7 +1237,7 @@
     case ZSTDds_decodeSkippableHeader:
         assert(src != NULL);
         assert(srcSize <= ZSTD_SKIPPABLEHEADERSIZE);
-        memcpy(dctx->headerBuffer + (ZSTD_SKIPPABLEHEADERSIZE - srcSize), src, srcSize);   /* complete skippable header */
+        ZSTD_memcpy(dctx->headerBuffer + (ZSTD_SKIPPABLEHEADERSIZE - srcSize), src, srcSize);   /* complete skippable header */
         dctx->expected = MEM_readLE32(dctx->headerBuffer + ZSTD_FRAMEIDSIZE);   /* note : dctx->expected can grow seriously large, beyond local buffer size */
         dctx->stage = ZSTDds_skipFrame;
         return 0;
@@ -1075,7 +1293,7 @@
                                                 workspace, workspaceSize);
 #else
         size_t const hSize = HUF_readDTableX2_wksp(entropy->hufTable,
-                                                dictPtr, dictEnd - dictPtr,
+                                                dictPtr, (size_t)(dictEnd - dictPtr),
                                                 workspace, workspaceSize);
 #endif
         RETURN_ERROR_IF(HUF_isError(hSize), dictionary_corrupted, "");
@@ -1084,40 +1302,46 @@
 
     {   short offcodeNCount[MaxOff+1];
         unsigned offcodeMaxValue = MaxOff, offcodeLog;
-        size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd-dictPtr);
+        size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, (size_t)(dictEnd-dictPtr));
         RETURN_ERROR_IF(FSE_isError(offcodeHeaderSize), dictionary_corrupted, "");
         RETURN_ERROR_IF(offcodeMaxValue > MaxOff, dictionary_corrupted, "");
         RETURN_ERROR_IF(offcodeLog > OffFSELog, dictionary_corrupted, "");
         ZSTD_buildFSETable( entropy->OFTable,
                             offcodeNCount, offcodeMaxValue,
                             OF_base, OF_bits,
-                            offcodeLog);
+                            offcodeLog,
+                            entropy->workspace, sizeof(entropy->workspace),
+                            /* bmi2 */0);
         dictPtr += offcodeHeaderSize;
     }
 
     {   short matchlengthNCount[MaxML+1];
         unsigned matchlengthMaxValue = MaxML, matchlengthLog;
-        size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd-dictPtr);
+        size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, (size_t)(dictEnd-dictPtr));
         RETURN_ERROR_IF(FSE_isError(matchlengthHeaderSize), dictionary_corrupted, "");
         RETURN_ERROR_IF(matchlengthMaxValue > MaxML, dictionary_corrupted, "");
         RETURN_ERROR_IF(matchlengthLog > MLFSELog, dictionary_corrupted, "");
         ZSTD_buildFSETable( entropy->MLTable,
                             matchlengthNCount, matchlengthMaxValue,
                             ML_base, ML_bits,
-                            matchlengthLog);
+                            matchlengthLog,
+                            entropy->workspace, sizeof(entropy->workspace),
+                            /* bmi2 */ 0);
         dictPtr += matchlengthHeaderSize;
     }
 
     {   short litlengthNCount[MaxLL+1];
         unsigned litlengthMaxValue = MaxLL, litlengthLog;
-        size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd-dictPtr);
+        size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, (size_t)(dictEnd-dictPtr));
         RETURN_ERROR_IF(FSE_isError(litlengthHeaderSize), dictionary_corrupted, "");
         RETURN_ERROR_IF(litlengthMaxValue > MaxLL, dictionary_corrupted, "");
         RETURN_ERROR_IF(litlengthLog > LLFSELog, dictionary_corrupted, "");
         ZSTD_buildFSETable( entropy->LLTable,
                             litlengthNCount, litlengthMaxValue,
                             LL_base, LL_bits,
-                            litlengthLog);
+                            litlengthLog,
+                            entropy->workspace, sizeof(entropy->workspace),
+                            /* bmi2 */ 0);
         dictPtr += litlengthHeaderSize;
     }
 
@@ -1131,7 +1355,7 @@
             entropy->rep[i] = rep;
     }   }
 
-    return dictPtr - (const BYTE*)dict;
+    return (size_t)(dictPtr - (const BYTE*)dict);
 }
 
 static size_t ZSTD_decompress_insertDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize)
@@ -1158,8 +1382,12 @@
 size_t ZSTD_decompressBegin(ZSTD_DCtx* dctx)
 {
     assert(dctx != NULL);
+#if ZSTD_TRACE
+    dctx->traceCtx = (ZSTD_trace_decompress_begin != NULL) ? ZSTD_trace_decompress_begin(dctx) : 0;
+#endif
     dctx->expected = ZSTD_startingInputLength(dctx->format);  /* dctx->format must be properly set */
     dctx->stage = ZSTDds_getFrameHeaderSize;
+    dctx->processedCSize = 0;
     dctx->decodedSize = 0;
     dctx->previousDstEnd = NULL;
     dctx->prefixStart = NULL;
@@ -1170,7 +1398,7 @@
     dctx->dictID = 0;
     dctx->bType = bt_reserved;
     ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue));
-    memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue));  /* initial repcodes */
+    ZSTD_memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue));  /* initial repcodes */
     dctx->LLTptr = dctx->entropy.LLTable;
     dctx->MLTptr = dctx->entropy.MLTable;
     dctx->OFTptr = dctx->entropy.OFTable;
@@ -1373,6 +1601,16 @@
     if (ddict) {
         dctx->ddict = ddict;
         dctx->dictUses = ZSTD_use_indefinitely;
+        if (dctx->refMultipleDDicts == ZSTD_rmd_refMultipleDDicts) {
+            if (dctx->ddictSet == NULL) {
+                dctx->ddictSet = ZSTD_createDDictHashSet(dctx->customMem);
+                if (!dctx->ddictSet) {
+                    RETURN_ERROR(memory_allocation, "Failed to allocate memory for hash set!");
+                }
+            }
+            assert(!dctx->staticSize);  /* Impossible: ddictSet cannot have been allocated if static dctx */
+            FORWARD_IF_ERROR(ZSTD_DDictHashSet_addDDict(dctx->ddictSet, ddict, dctx->customMem), "");
+        }
     }
     return 0;
 }
@@ -1394,7 +1632,7 @@
 
 size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format)
 {
-    return ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, format);
+    return ZSTD_DCtx_setParameter(dctx, ZSTD_d_format, (int)format);
 }
 
 ZSTD_bounds ZSTD_dParam_getBounds(ZSTD_dParameter dParam)
@@ -1411,8 +1649,16 @@
             ZSTD_STATIC_ASSERT(ZSTD_f_zstd1 < ZSTD_f_zstd1_magicless);
             return bounds;
         case ZSTD_d_stableOutBuffer:
-            bounds.lowerBound = (int)ZSTD_obm_buffered;
-            bounds.upperBound = (int)ZSTD_obm_stable;
+            bounds.lowerBound = (int)ZSTD_bm_buffered;
+            bounds.upperBound = (int)ZSTD_bm_stable;
+            return bounds;
+        case ZSTD_d_forceIgnoreChecksum:
+            bounds.lowerBound = (int)ZSTD_d_validateChecksum;
+            bounds.upperBound = (int)ZSTD_d_ignoreChecksum;
+            return bounds;
+        case ZSTD_d_refMultipleDDicts:
+            bounds.lowerBound = (int)ZSTD_rmd_refSingleDDict;
+            bounds.upperBound = (int)ZSTD_rmd_refMultipleDDicts;
             return bounds;
         default:;
     }
@@ -1436,6 +1682,29 @@
     RETURN_ERROR_IF(!ZSTD_dParam_withinBounds(p, v), parameter_outOfBound, ""); \
 }
 
+size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value)
+{
+    switch (param) {
+        case ZSTD_d_windowLogMax:
+            *value = (int)ZSTD_highbit32((U32)dctx->maxWindowSize);
+            return 0;
+        case ZSTD_d_format:
+            *value = (int)dctx->format;
+            return 0;
+        case ZSTD_d_stableOutBuffer:
+            *value = (int)dctx->outBufferMode;
+            return 0;
+        case ZSTD_d_forceIgnoreChecksum:
+            *value = (int)dctx->forceIgnoreChecksum;
+            return 0;
+        case ZSTD_d_refMultipleDDicts:
+            *value = (int)dctx->refMultipleDDicts;
+            return 0;
+        default:;
+    }
+    RETURN_ERROR(parameter_unsupported, "");
+}
+
 size_t ZSTD_DCtx_setParameter(ZSTD_DCtx* dctx, ZSTD_dParameter dParam, int value)
 {
     RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, "");
@@ -1451,7 +1720,18 @@
             return 0;
         case ZSTD_d_stableOutBuffer:
             CHECK_DBOUNDS(ZSTD_d_stableOutBuffer, value);
-            dctx->outBufferMode = (ZSTD_outBufferMode_e)value;
+            dctx->outBufferMode = (ZSTD_bufferMode_e)value;
+            return 0;
+        case ZSTD_d_forceIgnoreChecksum:
+            CHECK_DBOUNDS(ZSTD_d_forceIgnoreChecksum, value);
+            dctx->forceIgnoreChecksum = (ZSTD_forceIgnoreChecksum_e)value;
+            return 0;
+        case ZSTD_d_refMultipleDDicts:
+            CHECK_DBOUNDS(ZSTD_d_refMultipleDDicts, value);
+            if (dctx->staticSize != 0) {
+                RETURN_ERROR(parameter_unsupported, "Static dctx does not support multiple DDicts!");
+            }
+            dctx->refMultipleDDicts = (ZSTD_refMultipleDDicts_e)value;
             return 0;
         default:;
     }
@@ -1469,8 +1749,7 @@
       || (reset == ZSTD_reset_session_and_parameters) ) {
         RETURN_ERROR_IF(dctx->streamStage != zdss_init, stage_wrong, "");
         ZSTD_clearDict(dctx);
-        dctx->format = ZSTD_f_zstd1;
-        dctx->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT;
+        ZSTD_DCtx_resetParameters(dctx);
     }
     return 0;
 }
@@ -1524,7 +1803,7 @@
 {
     if (ZSTD_DCtx_isOverflow(zds, neededInBuffSize, neededOutBuffSize))
         zds->oversizedDuration++;
-    else 
+    else
         zds->oversizedDuration = 0;
 }
 
@@ -1538,7 +1817,7 @@
 {
     ZSTD_outBuffer const expect = zds->expectedOutBuffer;
     /* No requirement when ZSTD_obm_stable is not enabled. */
-    if (zds->outBufferMode != ZSTD_obm_stable)
+    if (zds->outBufferMode != ZSTD_bm_stable)
         return 0;
     /* Any buffer is allowed in zdss_init, this must be the same for every other call until
      * the context is reset.
@@ -1548,7 +1827,7 @@
     /* The buffer must match our expectation exactly. */
     if (expect.dst == output->dst && expect.pos == output->pos && expect.size == output->size)
         return 0;
-    RETURN_ERROR(dstBuffer_wrong, "ZSTD_obm_stable enabled but output differs!");
+    RETURN_ERROR(dstBuffer_wrong, "ZSTD_d_stableOutBuffer enabled but output differs!");
 }
 
 /* Calls ZSTD_decompressContinue() with the right parameters for ZSTD_decompressStream()
@@ -1560,7 +1839,7 @@
             ZSTD_DStream* zds, char** op, char* oend,
             void const* src, size_t srcSize) {
     int const isSkipFrame = ZSTD_isSkipFrame(zds);
-    if (zds->outBufferMode == ZSTD_obm_buffered) {
+    if (zds->outBufferMode == ZSTD_bm_buffered) {
         size_t const dstSize = isSkipFrame ? 0 : zds->outBuffSize - zds->outStart;
         size_t const decodedSize = ZSTD_decompressContinue(zds,
                 zds->outBuff + zds->outStart, dstSize, src, srcSize);
@@ -1573,14 +1852,14 @@
         }
     } else {
         /* Write directly into the output buffer */
-        size_t const dstSize = isSkipFrame ? 0 : oend - *op;
+        size_t const dstSize = isSkipFrame ? 0 : (size_t)(oend - *op);
         size_t const decodedSize = ZSTD_decompressContinue(zds, *op, dstSize, src, srcSize);
         FORWARD_IF_ERROR(decodedSize, "");
         *op += decodedSize;
         /* Flushing is not needed. */
         zds->streamStage = zdss_read;
         assert(*op <= oend);
-        assert(zds->outBufferMode == ZSTD_obm_stable);
+        assert(zds->outBufferMode == ZSTD_bm_stable);
     }
     return 0;
 }
@@ -1635,6 +1914,9 @@
             }   }
 #endif
             {   size_t const hSize = ZSTD_getFrameHeader_advanced(&zds->fParams, zds->headerBuffer, zds->lhSize, zds->format);
+                if (zds->refMultipleDDicts && zds->ddictSet) {
+                    ZSTD_DCtx_selectFrameDDict(zds);
+                }
                 DEBUGLOG(5, "header size : %u", (U32)hSize);
                 if (ZSTD_isError(hSize)) {
 #if defined(ZSTD_LEGACY_SUPPORT) && (ZSTD_LEGACY_SUPPORT>=1)
@@ -1663,14 +1945,14 @@
                     assert(iend >= ip);
                     if (toLoad > remainingInput) {   /* not enough input to load full header */
                         if (remainingInput > 0) {
-                            memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput);
+                            ZSTD_memcpy(zds->headerBuffer + zds->lhSize, ip, remainingInput);
                             zds->lhSize += remainingInput;
                         }
                         input->pos = input->size;
                         return (MAX((size_t)ZSTD_FRAMEHEADERSIZE_MIN(zds->format), hSize) - zds->lhSize) + ZSTD_blockHeaderSize;   /* remaining header bytes + next block header */
                     }
                     assert(ip != NULL);
-                    memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); zds->lhSize = hSize; ip += toLoad;
+                    ZSTD_memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); zds->lhSize = hSize; ip += toLoad;
                     break;
             }   }
 
@@ -1678,10 +1960,10 @@
             if (zds->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN
                 && zds->fParams.frameType != ZSTD_skippableFrame
                 && (U64)(size_t)(oend-op) >= zds->fParams.frameContentSize) {
-                size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend-istart);
+                size_t const cSize = ZSTD_findFrameCompressedSize(istart, (size_t)(iend-istart));
                 if (cSize <= (size_t)(iend-istart)) {
                     /* shortcut : using single-pass mode */
-                    size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, oend-op, istart, cSize, ZSTD_getDDict(zds));
+                    size_t const decompressedSize = ZSTD_decompress_usingDDict(zds, op, (size_t)(oend-op), istart, cSize, ZSTD_getDDict(zds));
                     if (ZSTD_isError(decompressedSize)) return decompressedSize;
                     DEBUGLOG(4, "shortcut to single-pass ZSTD_decompress_usingDDict()")
                     ip = istart + cSize;
@@ -1693,7 +1975,7 @@
             }   }
 
             /* Check output buffer is large enough for ZSTD_odm_stable. */
-            if (zds->outBufferMode == ZSTD_obm_stable
+            if (zds->outBufferMode == ZSTD_bm_stable
                 && zds->fParams.frameType != ZSTD_skippableFrame
                 && zds->fParams.frameContentSize != ZSTD_CONTENTSIZE_UNKNOWN
                 && (U64)(size_t)(oend-op) < zds->fParams.frameContentSize) {
@@ -1723,7 +2005,7 @@
 
             /* Adapt buffer sizes to frame header instructions */
             {   size_t const neededInBuffSize = MAX(zds->fParams.blockSizeMax, 4 /* frame checksum */);
-                size_t const neededOutBuffSize = zds->outBufferMode == ZSTD_obm_buffered
+                size_t const neededOutBuffSize = zds->outBufferMode == ZSTD_bm_buffered
                         ? ZSTD_decodingBufferSize_min(zds->fParams.windowSize, zds->fParams.frameContentSize)
                         : 0;
 
@@ -1731,7 +2013,7 @@
 
                 {   int const tooSmall = (zds->inBuffSize < neededInBuffSize) || (zds->outBuffSize < neededOutBuffSize);
                     int const tooLarge = ZSTD_DCtx_isOversizedTooLong(zds);
-                    
+
                     if (tooSmall || tooLarge) {
                         size_t const bufferSize = neededInBuffSize + neededOutBuffSize;
                         DEBUGLOG(4, "inBuff  : from %u to %u",
@@ -1745,10 +2027,10 @@
                                 bufferSize > zds->staticSize - sizeof(ZSTD_DCtx),
                                 memory_allocation, "");
                         } else {
-                            ZSTD_free(zds->inBuff, zds->customMem);
+                            ZSTD_customFree(zds->inBuff, zds->customMem);
                             zds->inBuffSize = 0;
                             zds->outBuffSize = 0;
-                            zds->inBuff = (char*)ZSTD_malloc(bufferSize, zds->customMem);
+                            zds->inBuff = (char*)ZSTD_customMalloc(bufferSize, zds->customMem);
                             RETURN_ERROR_IF(zds->inBuff == NULL, memory_allocation, "");
                         }
                         zds->inBuffSize = neededInBuffSize;
@@ -1760,7 +2042,7 @@
 
         case zdss_read:
             DEBUGLOG(5, "stage zdss_read");
-            {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompressWithInputSize(zds, iend - ip);
+            {   size_t const neededInSize = ZSTD_nextSrcSizeToDecompressWithInputSize(zds, (size_t)(iend - ip));
                 DEBUGLOG(5, "neededInSize = %u", (U32)neededInSize);
                 if (neededInSize==0) {  /* end of frame */
                     zds->streamStage = zdss_init;
@@ -1790,7 +2072,7 @@
                     RETURN_ERROR_IF(toLoad > zds->inBuffSize - zds->inPos,
                                     corruption_detected,
                                     "should never happen");
-                    loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend-ip);
+                    loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, (size_t)(iend-ip));
                 }
                 ip += loadedSize;
                 zds->inPos += loadedSize;
@@ -1804,7 +2086,7 @@
             }
         case zdss_flush:
             {   size_t const toFlushSize = zds->outEnd - zds->outStart;
-                size_t const flushedSize = ZSTD_limitCopy(op, oend-op, zds->outBuff + zds->outStart, toFlushSize);
+                size_t const flushedSize = ZSTD_limitCopy(op, (size_t)(oend-op), zds->outBuff + zds->outStart, toFlushSize);
                 op += flushedSize;
                 zds->outStart += flushedSize;
                 if (flushedSize == toFlushSize) {  /* flush completed */
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
index ad3b3d8..349dcdc 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -14,7 +14,7 @@
 /*-*******************************************************
 *  Dependencies
 *********************************************************/
-#include <string.h>      /* memcpy, memmove, memset */
+#include "../common/zstd_deps.h"   /* ZSTD_memcpy, ZSTD_memmove, ZSTD_memset */
 #include "../common/compiler.h"    /* prefetch */
 #include "../common/cpu.h"         /* bmi2 */
 #include "../common/mem.h"         /* low level memory routines */
@@ -44,7 +44,7 @@
 /*_*******************************************************
 *  Memory operations
 **********************************************************/
-static void ZSTD_copy4(void* dst, const void* src) { memcpy(dst, src, 4); }
+static void ZSTD_copy4(void* dst, const void* src) { ZSTD_memcpy(dst, src, 4); }
 
 
 /*-*************************************************************
@@ -166,7 +166,7 @@
                 dctx->litSize = litSize;
                 dctx->litEntropy = 1;
                 if (litEncType==set_compressed) dctx->HUFptr = dctx->entropy.hufTable;
-                memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
+                ZSTD_memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
                 return litCSize + lhSize;
             }
 
@@ -191,10 +191,10 @@
 
                 if (lhSize+litSize+WILDCOPY_OVERLENGTH > srcSize) {  /* risk reading beyond src buffer with wildcopy */
                     RETURN_ERROR_IF(litSize+lhSize > srcSize, corruption_detected, "");
-                    memcpy(dctx->litBuffer, istart+lhSize, litSize);
+                    ZSTD_memcpy(dctx->litBuffer, istart+lhSize, litSize);
                     dctx->litPtr = dctx->litBuffer;
                     dctx->litSize = litSize;
-                    memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
+                    ZSTD_memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH);
                     return lhSize+litSize;
                 }
                 /* direct reference into compressed stream */
@@ -223,7 +223,7 @@
                     break;
                 }
                 RETURN_ERROR_IF(litSize > ZSTD_BLOCKSIZE_MAX, corruption_detected, "");
-                memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH);
+                ZSTD_memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH);
                 dctx->litPtr = dctx->litBuffer;
                 dctx->litSize = litSize;
                 return lhSize+1;
@@ -236,7 +236,7 @@
 
 /* Default FSE distribution tables.
  * These are pre-calculated FSE decoding tables using default distributions as defined in specification :
- * https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#default-distributions
+ * https://github.com/facebook/zstd/blob/release/doc/zstd_compression_format.md#default-distributions
  * They were generated programmatically with following method :
  * - start from default distributions, present in /lib/common/zstd_internal.h
  * - generate tables normally, using ZSTD_buildFSETable()
@@ -364,23 +364,26 @@
  * generate FSE decoding table for one symbol (ll, ml or off)
  * cannot fail if input is valid =>
  * all inputs are presumed validated at this stage */
-void
-ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
+FORCE_INLINE_TEMPLATE
+void ZSTD_buildFSETable_body(ZSTD_seqSymbol* dt,
             const short* normalizedCounter, unsigned maxSymbolValue,
             const U32* baseValue, const U32* nbAdditionalBits,
-            unsigned tableLog)
+            unsigned tableLog, void* wksp, size_t wkspSize)
 {
     ZSTD_seqSymbol* const tableDecode = dt+1;
-    U16 symbolNext[MaxSeq+1];
-
     U32 const maxSV1 = maxSymbolValue + 1;
     U32 const tableSize = 1 << tableLog;
-    U32 highThreshold = tableSize-1;
+
+    U16* symbolNext = (U16*)wksp;
+    BYTE* spread = (BYTE*)(symbolNext + MaxSeq + 1);
+    U32 highThreshold = tableSize - 1;
+
 
     /* Sanity Checks */
     assert(maxSymbolValue <= MaxSeq);
     assert(tableLog <= MaxFSELog);
-
+    assert(wkspSize >= ZSTD_BUILD_FSE_TABLE_WKSP_SIZE);
+    (void)wkspSize;
     /* Init, lay down lowprob symbols */
     {   ZSTD_seqSymbol_header DTableH;
         DTableH.tableLog = tableLog;
@@ -396,16 +399,69 @@
                     assert(normalizedCounter[s]>=0);
                     symbolNext[s] = (U16)normalizedCounter[s];
         }   }   }
-        memcpy(dt, &DTableH, sizeof(DTableH));
+        ZSTD_memcpy(dt, &DTableH, sizeof(DTableH));
     }
 
     /* Spread symbols */
-    {   U32 const tableMask = tableSize-1;
+    assert(tableSize <= 512);
+    /* Specialized symbol spreading for the case when there are
+     * no low probability (-1 count) symbols. When compressing
+     * small blocks we avoid low probability symbols to hit this
+     * case, since header decoding speed matters more.
+     */
+    if (highThreshold == tableSize - 1) {
+        size_t const tableMask = tableSize-1;
+        size_t const step = FSE_TABLESTEP(tableSize);
+        /* First lay down the symbols in order.
+         * We use a uint64_t to lay down 8 bytes at a time. This reduces branch
+         * misses since small blocks generally have small table logs, so nearly
+         * all symbols have counts <= 8. We ensure we have 8 bytes at the end of
+         * our buffer to handle the over-write.
+         */
+        {
+            U64 const add = 0x0101010101010101ull;
+            size_t pos = 0;
+            U64 sv = 0;
+            U32 s;
+            for (s=0; s<maxSV1; ++s, sv += add) {
+                int i;
+                int const n = normalizedCounter[s];
+                MEM_write64(spread + pos, sv);
+                for (i = 8; i < n; i += 8) {
+                    MEM_write64(spread + pos + i, sv);
+                }
+                pos += n;
+            }
+        }
+        /* Now we spread those positions across the table.
+         * The benefit of doing it in two stages is that we avoid the the
+         * variable size inner loop, which caused lots of branch misses.
+         * Now we can run through all the positions without any branch misses.
+         * We unroll the loop twice, since that is what emperically worked best.
+         */
+        {
+            size_t position = 0;
+            size_t s;
+            size_t const unroll = 2;
+            assert(tableSize % unroll == 0); /* FSE_MIN_TABLELOG is 5 */
+            for (s = 0; s < (size_t)tableSize; s += unroll) {
+                size_t u;
+                for (u = 0; u < unroll; ++u) {
+                    size_t const uPosition = (position + (u * step)) & tableMask;
+                    tableDecode[uPosition].baseValue = spread[s + u];
+                }
+                position = (position + (unroll * step)) & tableMask;
+            }
+            assert(position == 0);
+        }
+    } else {
+        U32 const tableMask = tableSize-1;
         U32 const step = FSE_TABLESTEP(tableSize);
         U32 s, position = 0;
         for (s=0; s<maxSV1; s++) {
             int i;
-            for (i=0; i<normalizedCounter[s]; i++) {
+            int const n = normalizedCounter[s];
+            for (i=0; i<n; i++) {
                 tableDecode[position].baseValue = s;
                 position = (position + step) & tableMask;
                 while (position > highThreshold) position = (position + step) & tableMask;   /* lowprob area */
@@ -414,7 +470,8 @@
     }
 
     /* Build Decoding table */
-    {   U32 u;
+    {
+        U32 u;
         for (u=0; u<tableSize; u++) {
             U32 const symbol = tableDecode[u].baseValue;
             U32 const nextState = symbolNext[symbol]++;
@@ -423,7 +480,46 @@
             assert(nbAdditionalBits[symbol] < 255);
             tableDecode[u].nbAdditionalBits = (BYTE)nbAdditionalBits[symbol];
             tableDecode[u].baseValue = baseValue[symbol];
-    }   }
+        }
+    }
+}
+
+/* Avoids the FORCE_INLINE of the _body() function. */
+static void ZSTD_buildFSETable_body_default(ZSTD_seqSymbol* dt,
+            const short* normalizedCounter, unsigned maxSymbolValue,
+            const U32* baseValue, const U32* nbAdditionalBits,
+            unsigned tableLog, void* wksp, size_t wkspSize)
+{
+    ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue,
+            baseValue, nbAdditionalBits, tableLog, wksp, wkspSize);
+}
+
+#if DYNAMIC_BMI2
+TARGET_ATTRIBUTE("bmi2") static void ZSTD_buildFSETable_body_bmi2(ZSTD_seqSymbol* dt,
+            const short* normalizedCounter, unsigned maxSymbolValue,
+            const U32* baseValue, const U32* nbAdditionalBits,
+            unsigned tableLog, void* wksp, size_t wkspSize)
+{
+    ZSTD_buildFSETable_body(dt, normalizedCounter, maxSymbolValue,
+            baseValue, nbAdditionalBits, tableLog, wksp, wkspSize);
+}
+#endif
+
+void ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
+            const short* normalizedCounter, unsigned maxSymbolValue,
+            const U32* baseValue, const U32* nbAdditionalBits,
+            unsigned tableLog, void* wksp, size_t wkspSize, int bmi2)
+{
+#if DYNAMIC_BMI2
+    if (bmi2) {
+        ZSTD_buildFSETable_body_bmi2(dt, normalizedCounter, maxSymbolValue,
+                baseValue, nbAdditionalBits, tableLog, wksp, wkspSize);
+        return;
+    }
+#endif
+    (void)bmi2;
+    ZSTD_buildFSETable_body_default(dt, normalizedCounter, maxSymbolValue,
+            baseValue, nbAdditionalBits, tableLog, wksp, wkspSize);
 }
 
 
@@ -435,7 +531,8 @@
                                  const void* src, size_t srcSize,
                                  const U32* baseValue, const U32* nbAdditionalBits,
                                  const ZSTD_seqSymbol* defaultTable, U32 flagRepeatTable,
-                                 int ddictIsCold, int nbSeq)
+                                 int ddictIsCold, int nbSeq, U32* wksp, size_t wkspSize,
+                                 int bmi2)
 {
     switch(type)
     {
@@ -467,7 +564,7 @@
             size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize);
             RETURN_ERROR_IF(FSE_isError(headerSize), corruption_detected, "");
             RETURN_ERROR_IF(tableLog > maxLog, corruption_detected, "");
-            ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog);
+            ZSTD_buildFSETable(DTableSpace, norm, max, baseValue, nbAdditionalBits, tableLog, wksp, wkspSize, bmi2);
             *DTablePtr = DTableSpace;
             return headerSize;
         }
@@ -480,7 +577,7 @@
 size_t ZSTD_decodeSeqHeaders(ZSTD_DCtx* dctx, int* nbSeqPtr,
                              const void* src, size_t srcSize)
 {
-    const BYTE* const istart = (const BYTE* const)src;
+    const BYTE* const istart = (const BYTE*)src;
     const BYTE* const iend = istart + srcSize;
     const BYTE* ip = istart;
     int nbSeq;
@@ -499,7 +596,8 @@
     if (nbSeq > 0x7F) {
         if (nbSeq == 0xFF) {
             RETURN_ERROR_IF(ip+2 > iend, srcSize_wrong, "");
-            nbSeq = MEM_readLE16(ip) + LONGNBSEQ, ip+=2;
+            nbSeq = MEM_readLE16(ip) + LONGNBSEQ;
+            ip+=2;
         } else {
             RETURN_ERROR_IF(ip >= iend, srcSize_wrong, "");
             nbSeq = ((nbSeq-0x80)<<8) + *ip++;
@@ -520,7 +618,9 @@
                                                       ip, iend-ip,
                                                       LL_base, LL_bits,
                                                       LL_defaultDTable, dctx->fseEntropy,
-                                                      dctx->ddictIsCold, nbSeq);
+                                                      dctx->ddictIsCold, nbSeq,
+                                                      dctx->workspace, sizeof(dctx->workspace),
+                                                      dctx->bmi2);
             RETURN_ERROR_IF(ZSTD_isError(llhSize), corruption_detected, "ZSTD_buildSeqTable failed");
             ip += llhSize;
         }
@@ -530,7 +630,9 @@
                                                       ip, iend-ip,
                                                       OF_base, OF_bits,
                                                       OF_defaultDTable, dctx->fseEntropy,
-                                                      dctx->ddictIsCold, nbSeq);
+                                                      dctx->ddictIsCold, nbSeq,
+                                                      dctx->workspace, sizeof(dctx->workspace),
+                                                      dctx->bmi2);
             RETURN_ERROR_IF(ZSTD_isError(ofhSize), corruption_detected, "ZSTD_buildSeqTable failed");
             ip += ofhSize;
         }
@@ -540,7 +642,9 @@
                                                       ip, iend-ip,
                                                       ML_base, ML_bits,
                                                       ML_defaultDTable, dctx->fseEntropy,
-                                                      dctx->ddictIsCold, nbSeq);
+                                                      dctx->ddictIsCold, nbSeq,
+                                                      dctx->workspace, sizeof(dctx->workspace),
+                                                      dctx->bmi2);
             RETURN_ERROR_IF(ZSTD_isError(mlhSize), corruption_detected, "ZSTD_buildSeqTable failed");
             ip += mlhSize;
         }
@@ -554,7 +658,6 @@
     size_t litLength;
     size_t matchLength;
     size_t offset;
-    const BYTE* match;
 } seq_t;
 
 typedef struct {
@@ -568,9 +671,6 @@
     ZSTD_fseState stateOffb;
     ZSTD_fseState stateML;
     size_t prevOffset[ZSTD_REP_NUM];
-    const BYTE* prefixStart;
-    const BYTE* dictEnd;
-    size_t pos;
 } seqState_t;
 
 /*! ZSTD_overlapCopy8() :
@@ -686,12 +786,12 @@
         RETURN_ERROR_IF(sequence.offset > (size_t)(oLitEnd - virtualStart), corruption_detected, "");
         match = dictEnd - (prefixStart-match);
         if (match + sequence.matchLength <= dictEnd) {
-            memmove(oLitEnd, match, sequence.matchLength);
+            ZSTD_memmove(oLitEnd, match, sequence.matchLength);
             return sequenceLength;
         }
         /* span extDict & currentPrefixSegment */
         {   size_t const length1 = dictEnd - match;
-            memmove(oLitEnd, match, length1);
+            ZSTD_memmove(oLitEnd, match, length1);
             op = oLitEnd + length1;
             sequence.matchLength -= length1;
             match = prefixStart;
@@ -752,12 +852,12 @@
         RETURN_ERROR_IF(UNLIKELY(sequence.offset > (size_t)(oLitEnd - virtualStart)), corruption_detected, "");
         match = dictEnd + (match - prefixStart);
         if (match + sequence.matchLength <= dictEnd) {
-            memmove(oLitEnd, match, sequence.matchLength);
+            ZSTD_memmove(oLitEnd, match, sequence.matchLength);
             return sequenceLength;
         }
         /* span extDict & currentPrefixSegment */
         {   size_t const length1 = dictEnd - match;
-            memmove(oLitEnd, match, length1);
+            ZSTD_memmove(oLitEnd, match, length1);
             op = oLitEnd + length1;
             sequence.matchLength -= length1;
             match = prefixStart;
@@ -832,10 +932,9 @@
         : 0)
 
 typedef enum { ZSTD_lo_isRegularOffset, ZSTD_lo_isLongOffset=1 } ZSTD_longOffset_e;
-typedef enum { ZSTD_p_noPrefetch=0, ZSTD_p_prefetch=1 } ZSTD_prefetch_e;
 
 FORCE_INLINE_TEMPLATE seq_t
-ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets, const ZSTD_prefetch_e prefetch)
+ZSTD_decodeSequence(seqState_t* seqState, const ZSTD_longOffset_e longOffsets)
 {
     seq_t seq;
     ZSTD_seqSymbol const llDInfo = seqState->stateLL.table[seqState->stateLL.state];
@@ -910,14 +1009,6 @@
     DEBUGLOG(6, "seq: litL=%u, matchL=%u, offset=%u",
                 (U32)seq.litLength, (U32)seq.matchLength, (U32)seq.offset);
 
-    if (prefetch == ZSTD_p_prefetch) {
-        size_t const pos = seqState->pos + seq.litLength;
-        const BYTE* const matchBase = (seq.offset > pos) ? seqState->dictEnd : seqState->prefixStart;
-        seq.match = matchBase + pos - seq.offset;  /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted.
-                                                    * No consequence though : no memory access will occur, offset is only used for prefetching */
-        seqState->pos = pos + seq.matchLength;
-    }
-
     /* ANS state update
      * gcc-9.0.0 does 2.5% worse with ZSTD_updateFseStateWithDInfo().
      * clang-9.2.0 does 7% worse with ZSTD_updateFseState().
@@ -948,7 +1039,7 @@
 }
 
 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-static int ZSTD_dictionaryIsActive(ZSTD_DCtx const* dctx, BYTE const* prefixStart, BYTE const* oLitEnd)
+MEM_STATIC int ZSTD_dictionaryIsActive(ZSTD_DCtx const* dctx, BYTE const* prefixStart, BYTE const* oLitEnd)
 {
     size_t const windowSize = dctx->fParams.windowSize;
     /* No dictionary used. */
@@ -969,6 +1060,7 @@
         seq_t const seq,
         BYTE const* prefixStart, BYTE const* virtualStart)
 {
+#if DEBUGLEVEL >= 1
     size_t const windowSize = dctx->fParams.windowSize;
     size_t const sequenceSize = seq.litLength + seq.matchLength;
     BYTE const* const oLitEnd = op + seq.litLength;
@@ -986,6 +1078,9 @@
         /* Offset must be within our window. */
         assert(seq.offset <= windowSize);
     }
+#else
+    (void)dctx, (void)op, (void)oend, (void)seq, (void)prefixStart, (void)virtualStart;
+#endif
 }
 #endif
 
@@ -1000,7 +1095,7 @@
 {
     const BYTE* ip = (const BYTE*)seqStart;
     const BYTE* const iend = ip + seqSize;
-    BYTE* const ostart = (BYTE* const)dst;
+    BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = ostart + maxDstSize;
     BYTE* op = ostart;
     const BYTE* litPtr = dctx->litPtr;
@@ -1014,7 +1109,6 @@
     /* Regen sequences */
     if (nbSeq) {
         seqState_t seqState;
-        size_t error = 0;
         dctx->fseEntropy = 1;
         { U32 i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; }
         RETURN_ERROR_IF(
@@ -1048,13 +1142,14 @@
          * If you see most cycles served out of the DSB you've hit the good case.
          * If it is pretty even then you may be in an okay case.
          *
-         * I've been able to reproduce this issue on the following CPUs:
+         * This issue has been reproduced on the following CPUs:
          *   - Kabylake: Macbook Pro (15-inch, 2019) 2.4 GHz Intel Core i9
          *               Use Instruments->Counters to get DSB/MITE cycles.
          *               I never got performance swings, but I was able to
          *               go from the good case of mostly DSB to half of the
          *               cycles served from MITE.
          *   - Coffeelake: Intel i9-9900k
+         *   - Coffeelake: Intel i7-9700k
          *
          * I haven't been able to reproduce the instability or DSB misses on any
          * of the following CPUS:
@@ -1067,33 +1162,35 @@
          *
          *   https://gist.github.com/terrelln/9889fc06a423fd5ca6e99351564473f4
          */
+        __asm__(".p2align 6");
+        __asm__("nop");
         __asm__(".p2align 5");
         __asm__("nop");
+#  if __GNUC__ >= 9
+        /* better for gcc-9 and gcc-10, worse for clang and gcc-8 */
+        __asm__(".p2align 3");
+#  else
         __asm__(".p2align 4");
+#  endif
 #endif
         for ( ; ; ) {
-            seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_noPrefetch);
+            seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
             size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, prefixStart, vBase, dictEnd);
 #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
             assert(!ZSTD_isError(oneSeqSize));
             if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequence, prefixStart, vBase);
 #endif
+            if (UNLIKELY(ZSTD_isError(oneSeqSize)))
+                return oneSeqSize;
             DEBUGLOG(6, "regenerated sequence size : %u", (U32)oneSeqSize);
+            op += oneSeqSize;
+            if (UNLIKELY(!--nbSeq))
+                break;
             BIT_reloadDStream(&(seqState.DStream));
-            /* gcc and clang both don't like early returns in this loop.
-             * gcc doesn't like early breaks either.
-             * Instead save an error and report it at the end.
-             * When there is an error, don't increment op, so we don't
-             * overwrite.
-             */
-            if (UNLIKELY(ZSTD_isError(oneSeqSize))) error = oneSeqSize;
-            else op += oneSeqSize;
-            if (UNLIKELY(!--nbSeq)) break;
         }
 
         /* check if reached exact end */
         DEBUGLOG(5, "ZSTD_decompressSequences_body: after decode loop, remaining nbSeq : %i", nbSeq);
-        if (ZSTD_isError(error)) return error;
         RETURN_ERROR_IF(nbSeq, corruption_detected, "");
         RETURN_ERROR_IF(BIT_reloadDStream(&seqState.DStream) < BIT_DStream_completed, corruption_detected, "");
         /* save reps for next block */
@@ -1104,7 +1201,7 @@
     {   size_t const lastLLSize = litEnd - litPtr;
         RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
         if (op != NULL) {
-            memcpy(op, litPtr, lastLLSize);
+            ZSTD_memcpy(op, litPtr, lastLLSize);
             op += lastLLSize;
         }
     }
@@ -1124,6 +1221,24 @@
 #endif /* ZSTD_FORCE_DECOMPRESS_SEQUENCES_LONG */
 
 #ifndef ZSTD_FORCE_DECOMPRESS_SEQUENCES_SHORT
+
+FORCE_INLINE_TEMPLATE size_t
+ZSTD_prefetchMatch(size_t prefetchPos, seq_t const sequence,
+                   const BYTE* const prefixStart, const BYTE* const dictEnd)
+{
+    prefetchPos += sequence.litLength;
+    {   const BYTE* const matchBase = (sequence.offset > prefetchPos) ? dictEnd : prefixStart;
+        const BYTE* const match = matchBase + prefetchPos - sequence.offset; /* note : this operation can overflow when seq.offset is really too large, which can only happen when input is corrupted.
+                                                                              * No consequence though : memory address is only used for prefetching, not for dereferencing */
+        PREFETCH_L1(match); PREFETCH_L1(match+CACHELINE_SIZE);   /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */
+    }
+    return prefetchPos + sequence.matchLength;
+}
+
+/* This decoding function employs prefetching
+ * to reduce latency impact of cache misses.
+ * It's generally employed when block contains a significant portion of long-distance matches
+ * or when coupled with a "cold" dictionary */
 FORCE_INLINE_TEMPLATE size_t
 ZSTD_decompressSequencesLong_body(
                                ZSTD_DCtx* dctx,
@@ -1134,7 +1249,7 @@
 {
     const BYTE* ip = (const BYTE*)seqStart;
     const BYTE* const iend = ip + seqSize;
-    BYTE* const ostart = (BYTE* const)dst;
+    BYTE* const ostart = (BYTE*)dst;
     BYTE* const oend = ostart + maxDstSize;
     BYTE* op = ostart;
     const BYTE* litPtr = dctx->litPtr;
@@ -1146,18 +1261,17 @@
 
     /* Regen sequences */
     if (nbSeq) {
-#define STORED_SEQS 4
+#define STORED_SEQS 8
 #define STORED_SEQS_MASK (STORED_SEQS-1)
-#define ADVANCED_SEQS 4
+#define ADVANCED_SEQS STORED_SEQS
         seq_t sequences[STORED_SEQS];
         int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS);
         seqState_t seqState;
         int seqNb;
+        size_t prefetchPos = (size_t)(op-prefixStart); /* track position relative to prefixStart */
+
         dctx->fseEntropy = 1;
         { int i; for (i=0; i<ZSTD_REP_NUM; i++) seqState.prevOffset[i] = dctx->entropy.rep[i]; }
-        seqState.prefixStart = prefixStart;
-        seqState.pos = (size_t)(op-prefixStart);
-        seqState.dictEnd = dictEnd;
         assert(dst != NULL);
         assert(iend >= ip);
         RETURN_ERROR_IF(
@@ -1169,21 +1283,23 @@
 
         /* prepare in advance */
         for (seqNb=0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && (seqNb<seqAdvance); seqNb++) {
-            sequences[seqNb] = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_prefetch);
-            PREFETCH_L1(sequences[seqNb].match); PREFETCH_L1(sequences[seqNb].match + sequences[seqNb].matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */
+            seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
+            prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
+            sequences[seqNb] = sequence;
         }
         RETURN_ERROR_IF(seqNb<seqAdvance, corruption_detected, "");
 
         /* decode and decompress */
         for ( ; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && (seqNb<nbSeq) ; seqNb++) {
-            seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset, ZSTD_p_prefetch);
+            seq_t const sequence = ZSTD_decodeSequence(&seqState, isLongOffset);
             size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], &litPtr, litEnd, prefixStart, dictStart, dictEnd);
 #if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) && defined(FUZZING_ASSERT_VALID_SEQUENCE)
             assert(!ZSTD_isError(oneSeqSize));
             if (frame) ZSTD_assertValidSequence(dctx, op, oend, sequences[(seqNb-ADVANCED_SEQS) & STORED_SEQS_MASK], prefixStart, dictStart);
 #endif
             if (ZSTD_isError(oneSeqSize)) return oneSeqSize;
-            PREFETCH_L1(sequence.match); PREFETCH_L1(sequence.match + sequence.matchLength - 1); /* note : it's safe to invoke PREFETCH() on any memory address, including invalid ones */
+
+            prefetchPos = ZSTD_prefetchMatch(prefetchPos, sequence, prefixStart, dictEnd);
             sequences[seqNb & STORED_SEQS_MASK] = sequence;
             op += oneSeqSize;
         }
@@ -1209,7 +1325,7 @@
     {   size_t const lastLLSize = litEnd - litPtr;
         RETURN_ERROR_IF(lastLLSize > (size_t)(oend-op), dstSize_tooSmall, "");
         if (op != NULL) {
-            memcpy(op, litPtr, lastLLSize);
+            ZSTD_memcpy(op, litPtr, lastLLSize);
             op += lastLLSize;
         }
     }
@@ -1409,9 +1525,9 @@
 }
 
 
-void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst)
+void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst, size_t dstSize)
 {
-    if (dst != dctx->previousDstEnd) {   /* not contiguous */
+    if (dst != dctx->previousDstEnd && dstSize > 0) {   /* not contiguous */
         dctx->dictEnd = dctx->previousDstEnd;
         dctx->virtualStart = (const char*)dst - ((const char*)(dctx->previousDstEnd) - (const char*)(dctx->prefixStart));
         dctx->prefixStart = dst;
@@ -1425,7 +1541,7 @@
                       const void* src, size_t srcSize)
 {
     size_t dSize;
-    ZSTD_checkContinuity(dctx, dst);
+    ZSTD_checkContinuity(dctx, dst, dstCapacity);
     dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize, /* frame */ 0);
     dctx->previousDstEnd = (char*)dst + dSize;
     return dSize;
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
index bf39b73..049a0cd 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_block.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -15,7 +15,7 @@
 /*-*******************************************************
  *  Dependencies
  *********************************************************/
-#include <stddef.h>   /* size_t */
+#include "../common/zstd_deps.h"   /* size_t */
 #include "../zstd.h"    /* DCtx, and some public functions */
 #include "../common/zstd_internal.h"  /* blockProperties_t, and some public functions */
 #include "zstd_decompress_internal.h"  /* ZSTD_seqSymbol */
@@ -48,12 +48,15 @@
  * this function must be called with valid parameters only
  * (dt is large enough, normalizedCounter distribution total is a power of 2, max is within range, etc.)
  * in which case it cannot fail.
+ * The workspace must be 4-byte aligned and at least ZSTD_BUILD_FSE_TABLE_WKSP_SIZE bytes, which is
+ * defined in zstd_decompress_internal.h.
  * Internal use only.
  */
 void ZSTD_buildFSETable(ZSTD_seqSymbol* dt,
              const short* normalizedCounter, unsigned maxSymbolValue,
              const U32* baseValue, const U32* nbAdditionalBits,
-                   unsigned tableLog);
+                   unsigned tableLog, void* wksp, size_t wkspSize,
+                   int bmi2);
 
 
 #endif /* ZSTD_DEC_BLOCK_H */
diff --git a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
index 9ad96c5..ebda0c9 100644
--- a/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
+++ b/Utilities/cmzstd/lib/decompress/zstd_decompress_internal.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -27,26 +27,26 @@
 /*-*******************************************************
  *  Constants
  *********************************************************/
-static const U32 LL_base[MaxLL+1] = {
+static UNUSED_ATTR const U32 LL_base[MaxLL+1] = {
                  0,    1,    2,     3,     4,     5,     6,      7,
                  8,    9,   10,    11,    12,    13,    14,     15,
                 16,   18,   20,    22,    24,    28,    32,     40,
                 48,   64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
                 0x2000, 0x4000, 0x8000, 0x10000 };
 
-static const U32 OF_base[MaxOff+1] = {
+static UNUSED_ATTR const U32 OF_base[MaxOff+1] = {
                  0,        1,       1,       5,     0xD,     0x1D,     0x3D,     0x7D,
                  0xFD,   0x1FD,   0x3FD,   0x7FD,   0xFFD,   0x1FFD,   0x3FFD,   0x7FFD,
                  0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD,
                  0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD, 0x1FFFFFFD, 0x3FFFFFFD, 0x7FFFFFFD };
 
-static const U32 OF_bits[MaxOff+1] = {
+static UNUSED_ATTR const U32 OF_bits[MaxOff+1] = {
                      0,  1,  2,  3,  4,  5,  6,  7,
                      8,  9, 10, 11, 12, 13, 14, 15,
                     16, 17, 18, 19, 20, 21, 22, 23,
                     24, 25, 26, 27, 28, 29, 30, 31 };
 
-static const U32 ML_base[MaxML+1] = {
+static UNUSED_ATTR const U32 ML_base[MaxML+1] = {
                      3,  4,  5,    6,     7,     8,     9,    10,
                     11, 12, 13,   14,    15,    16,    17,    18,
                     19, 20, 21,   22,    23,    24,    25,    26,
@@ -73,12 +73,16 @@
 
  #define SEQSYMBOL_TABLE_SIZE(log)   (1 + (1 << (log)))
 
+#define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE (sizeof(S16) * (MaxSeq + 1) + (1u << MaxFSELog) + sizeof(U64))
+#define ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32 ((ZSTD_BUILD_FSE_TABLE_WKSP_SIZE + sizeof(U32) - 1) / sizeof(U32))
+
 typedef struct {
     ZSTD_seqSymbol LLTable[SEQSYMBOL_TABLE_SIZE(LLFSELog)];    /* Note : Space reserved for FSE Tables */
     ZSTD_seqSymbol OFTable[SEQSYMBOL_TABLE_SIZE(OffFSELog)];   /* is also used as temporary workspace while building hufTable during DDict creation */
     ZSTD_seqSymbol MLTable[SEQSYMBOL_TABLE_SIZE(MLFSELog)];    /* and therefore must be at least HUF_DECOMPRESS_WORKSPACE_SIZE large */
     HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)];  /* can accommodate HUF_decompress4X */
     U32 rep[ZSTD_REP_NUM];
+    U32 workspace[ZSTD_BUILD_FSE_TABLE_WKSP_SIZE_U32];
 } ZSTD_entropyDTables_t;
 
 typedef enum { ZSTDds_getFrameHeaderSize, ZSTDds_decodeFrameHeader,
@@ -95,10 +99,12 @@
     ZSTD_use_once = 1            /* Use the dictionary once and set to ZSTD_dont_use */
 } ZSTD_dictUses_e;
 
-typedef enum {
-    ZSTD_obm_buffered = 0,  /* Buffer the output */
-    ZSTD_obm_stable = 1     /* ZSTD_outBuffer is stable */
-} ZSTD_outBufferMode_e;
+/* Hashset for storing references to multiple ZSTD_DDict within ZSTD_DCtx */
+typedef struct {
+    const ZSTD_DDict** ddictPtrTable;
+    size_t ddictPtrTableSize;
+    size_t ddictPtrCount;
+} ZSTD_DDictHashSet;
 
 struct ZSTD_DCtx_s
 {
@@ -114,6 +120,7 @@
     const void* dictEnd;          /* end of previous segment */
     size_t expected;
     ZSTD_frameHeader fParams;
+    U64 processedCSize;
     U64 decodedSize;
     blockType_e bType;            /* used in ZSTD_decompressContinue(), store blockType between block header decoding and block decompression stages */
     ZSTD_dStage stage;
@@ -122,6 +129,8 @@
     XXH64_state_t xxhState;
     size_t headerSize;
     ZSTD_format_e format;
+    ZSTD_forceIgnoreChecksum_e forceIgnoreChecksum;   /* User specified: if == 1, will ignore checksums in compressed frame. Default == 0 */
+    U32 validateChecksum;         /* if == 1, will validate checksum. Is == 1 if (fParams.checksumFlag == 1) and (forceIgnoreChecksum == 0). */
     const BYTE* litPtr;
     ZSTD_customMem customMem;
     size_t litSize;
@@ -135,6 +144,8 @@
     U32 dictID;
     int ddictIsCold;             /* if == 1 : dictionary is "new" for working context, and presumed "cold" (not in cpu cache) */
     ZSTD_dictUses_e dictUses;
+    ZSTD_DDictHashSet* ddictSet;                    /* Hash set for multiple ddicts */
+    ZSTD_refMultipleDDicts_e refMultipleDDicts;     /* User specified: if == 1, will allow references to multiple DDicts. Default == 0 (disabled) */
 
     /* streaming */
     ZSTD_dStreamStage streamStage;
@@ -152,7 +163,7 @@
     U32 legacyVersion;
     U32 hostageByte;
     int noForwardProgress;
-    ZSTD_outBufferMode_e outBufferMode;
+    ZSTD_bufferMode_e outBufferMode;
     ZSTD_outBuffer expectedOutBuffer;
 
     /* workspace */
@@ -165,6 +176,11 @@
     void const* dictContentBeginForFuzzing;
     void const* dictContentEndForFuzzing;
 #endif
+
+    /* Tracing */
+#if ZSTD_TRACE
+    ZSTD_TraceCtx traceCtx;
+#endif
 };  /* typedef'd to ZSTD_DCtx within "zstd.h" */
 
 
@@ -183,7 +199,7 @@
  *  If yes, do nothing (continue on current segment).
  *  If not, classify previous segment as "external dictionary", and start a new segment.
  *  This function cannot fail. */
-void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst);
+void ZSTD_checkContinuity(ZSTD_DCtx* dctx, const void* dst, size_t dstSize);
 
 
 #endif /* ZSTD_DECOMPRESS_INTERNAL_H */
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff.h b/Utilities/cmzstd/lib/deprecated/zbuff.h
index 03cb14a..b83ea0f 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff.h
+++ b/Utilities/cmzstd/lib/deprecated/zbuff.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_common.c b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
index 579bc4d..e7d01a0 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff_common.c
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_common.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
index 2d20b13..2e72267 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_compress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
index d3c49e8..d73c0f3 100644
--- a/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
+++ b/Utilities/cmzstd/lib/deprecated/zbuff_decompress.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.c b/Utilities/cmzstd/lib/dictBuilder/cover.c
index da54ef1..8364444 100644
--- a/Utilities/cmzstd/lib/dictBuilder/cover.c
+++ b/Utilities/cmzstd/lib/dictBuilder/cover.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -26,47 +26,57 @@
 #include <string.h> /* memset */
 #include <time.h>   /* clock */
 
+#ifndef ZDICT_STATIC_LINKING_ONLY
+#  define ZDICT_STATIC_LINKING_ONLY
+#endif
+
 #include "../common/mem.h" /* read */
 #include "../common/pool.h"
 #include "../common/threading.h"
-#include "cover.h"
 #include "../common/zstd_internal.h" /* includes zstd.h */
-#ifndef ZDICT_STATIC_LINKING_ONLY
-#define ZDICT_STATIC_LINKING_ONLY
-#endif
-#include "zdict.h"
+#include "../zdict.h"
+#include "cover.h"
 
 /*-*************************************
 *  Constants
 ***************************************/
 #define COVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
-#define DEFAULT_SPLITPOINT 1.0
+#define COVER_DEFAULT_SPLITPOINT 1.0
 
 /*-*************************************
 *  Console display
 ***************************************/
+#ifndef LOCALDISPLAYLEVEL
 static int g_displayLevel = 2;
+#endif
+#undef  DISPLAY
 #define DISPLAY(...)                                                           \
   {                                                                            \
     fprintf(stderr, __VA_ARGS__);                                              \
     fflush(stderr);                                                            \
   }
+#undef  LOCALDISPLAYLEVEL
 #define LOCALDISPLAYLEVEL(displayLevel, l, ...)                                \
   if (displayLevel >= l) {                                                     \
     DISPLAY(__VA_ARGS__);                                                      \
   } /* 0 : no display;   1: errors;   2: default;  3: details;  4: debug */
+#undef  DISPLAYLEVEL
 #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__)
 
+#ifndef LOCALDISPLAYUPDATE
+static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100;
+static clock_t g_time = 0;
+#endif
+#undef  LOCALDISPLAYUPDATE
 #define LOCALDISPLAYUPDATE(displayLevel, l, ...)                               \
   if (displayLevel >= l) {                                                     \
-    if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) {             \
+    if ((clock() - g_time > g_refreshRate) || (displayLevel >= 4)) {             \
       g_time = clock();                                                        \
       DISPLAY(__VA_ARGS__);                                                    \
     }                                                                          \
   }
+#undef  DISPLAYUPDATE
 #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__)
-static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
-static clock_t g_time = 0;
 
 /*-*************************************
 * Hash table
@@ -120,9 +130,9 @@
 /**
  * Internal hash function
  */
-static const U32 prime4bytes = 2654435761U;
+static const U32 COVER_prime4bytes = 2654435761U;
 static U32 COVER_map_hash(COVER_map_t *map, U32 key) {
-  return (key * prime4bytes) >> (32 - map->sizeLog);
+  return (key * COVER_prime4bytes) >> (32 - map->sizeLog);
 }
 
 /**
@@ -215,7 +225,7 @@
 } COVER_ctx_t;
 
 /* We need a global context for qsort... */
-static COVER_ctx_t *g_ctx = NULL;
+static COVER_ctx_t *g_coverCtx = NULL;
 
 /*-*************************************
 *  Helper functions
@@ -258,11 +268,11 @@
 
 /**
  * Same as COVER_cmp() except ties are broken by pointer value
- * NOTE: g_ctx must be set to call this function.  A global is required because
+ * NOTE: g_coverCtx must be set to call this function.  A global is required because
  * qsort doesn't take an opaque pointer.
  */
-static int COVER_strict_cmp(const void *lp, const void *rp) {
-  int result = COVER_cmp(g_ctx, lp, rp);
+static int WIN_CDECL COVER_strict_cmp(const void *lp, const void *rp) {
+  int result = COVER_cmp(g_coverCtx, lp, rp);
   if (result == 0) {
     result = lp < rp ? -1 : 1;
   }
@@ -271,8 +281,8 @@
 /**
  * Faster version for d <= 8.
  */
-static int COVER_strict_cmp8(const void *lp, const void *rp) {
-  int result = COVER_cmp8(g_ctx, lp, rp);
+static int WIN_CDECL COVER_strict_cmp8(const void *lp, const void *rp) {
+  int result = COVER_cmp8(g_coverCtx, lp, rp);
   if (result == 0) {
     result = lp < rp ? -1 : 1;
   }
@@ -603,7 +613,7 @@
     /* qsort doesn't take an opaque pointer, so pass as a global.
      * On OpenBSD qsort() is not guaranteed to be stable, their mergesort() is.
      */
-    g_ctx = ctx;
+    g_coverCtx = ctx;
 #if defined(__OpenBSD__)
     mergesort(ctx->suffix, ctx->suffixSize, sizeof(U32),
           (ctx->d <= 8 ? &COVER_strict_cmp8 : &COVER_strict_cmp));
@@ -946,7 +956,7 @@
   free(selection.dictContent);
 }
 
-COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent,
+COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictBufferCapacity,
         size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples,
         size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize) {
 
@@ -954,8 +964,8 @@
   size_t largestCompressed = 0;
   BYTE* customDictContentEnd = customDictContent + dictContentSize;
 
-  BYTE * largestDictbuffer = (BYTE *)malloc(dictContentSize);
-  BYTE * candidateDictBuffer = (BYTE *)malloc(dictContentSize);
+  BYTE * largestDictbuffer = (BYTE *)malloc(dictBufferCapacity);
+  BYTE * candidateDictBuffer = (BYTE *)malloc(dictBufferCapacity);
   double regressionTolerance = ((double)params.shrinkDictMaxRegression / 100.0) + 1.00;
 
   if (!largestDictbuffer || !candidateDictBuffer) {
@@ -967,7 +977,7 @@
   /* Initial dictionary size and compressed size */
   memcpy(largestDictbuffer, customDictContent, dictContentSize);
   dictContentSize = ZDICT_finalizeDictionary(
-    largestDictbuffer, dictContentSize, customDictContent, dictContentSize,
+    largestDictbuffer, dictBufferCapacity, customDictContent, dictContentSize,
     samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams);
 
   if (ZDICT_isError(dictContentSize)) {
@@ -1001,7 +1011,7 @@
   while (dictContentSize < largestDict) {
     memcpy(candidateDictBuffer, largestDictbuffer, largestDict);
     dictContentSize = ZDICT_finalizeDictionary(
-      candidateDictBuffer, dictContentSize, customDictContentEnd - dictContentSize, dictContentSize,
+      candidateDictBuffer, dictBufferCapacity, customDictContentEnd - dictContentSize, dictContentSize,
       samplesBuffer, samplesSizes, nbFinalizeSamples, params.zParams);
 
     if (ZDICT_isError(dictContentSize)) {
@@ -1053,18 +1063,19 @@
  * This function is thread safe if zstd is compiled with multithreaded support.
  * It takes its parameters as an *OWNING* opaque pointer to support threading.
  */
-static void COVER_tryParameters(void *opaque) {
+static void COVER_tryParameters(void *opaque)
+{
   /* Save parameters as local variables */
-  COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t *)opaque;
+  COVER_tryParameters_data_t *const data = (COVER_tryParameters_data_t*)opaque;
   const COVER_ctx_t *const ctx = data->ctx;
   const ZDICT_cover_params_t parameters = data->parameters;
   size_t dictBufferCapacity = data->dictBufferCapacity;
   size_t totalCompressedSize = ERROR(GENERIC);
   /* Allocate space for hash table, dict, and freqs */
   COVER_map_t activeDmers;
-  BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);
+  BYTE* const dict = (BYTE*)malloc(dictBufferCapacity);
   COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC));
-  U32 *freqs = (U32 *)malloc(ctx->suffixSize * sizeof(U32));
+  U32* const freqs = (U32*)malloc(ctx->suffixSize * sizeof(U32));
   if (!COVER_map_init(&activeDmers, parameters.k - parameters.d + 1)) {
     DISPLAYLEVEL(1, "Failed to allocate dmer map: out of memory\n");
     goto _cleanup;
@@ -1079,7 +1090,7 @@
   {
     const size_t tail = COVER_buildDictionary(ctx, freqs, &activeDmers, dict,
                                               dictBufferCapacity, parameters);
-    selection = COVER_selectDict(dict + tail, dictBufferCapacity - tail,
+    selection = COVER_selectDict(dict + tail, dictBufferCapacity, dictBufferCapacity - tail,
         ctx->samples, ctx->samplesSizes, (unsigned)ctx->nbTrainSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets,
         totalCompressedSize);
 
@@ -1094,19 +1105,18 @@
   free(data);
   COVER_map_destroy(&activeDmers);
   COVER_dictSelectionFree(selection);
-  if (freqs) {
-    free(freqs);
-  }
+  free(freqs);
 }
 
 ZDICTLIB_API size_t ZDICT_optimizeTrainFromBuffer_cover(
-    void *dictBuffer, size_t dictBufferCapacity, const void *samplesBuffer,
-    const size_t *samplesSizes, unsigned nbSamples,
-    ZDICT_cover_params_t *parameters) {
+    void* dictBuffer, size_t dictBufferCapacity, const void* samplesBuffer,
+    const size_t* samplesSizes, unsigned nbSamples,
+    ZDICT_cover_params_t* parameters)
+{
   /* constants */
   const unsigned nbThreads = parameters->nbThreads;
   const double splitPoint =
-      parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint;
+      parameters->splitPoint <= 0.0 ? COVER_DEFAULT_SPLITPOINT : parameters->splitPoint;
   const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
   const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d;
   const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k;
diff --git a/Utilities/cmzstd/lib/dictBuilder/cover.h b/Utilities/cmzstd/lib/dictBuilder/cover.h
index f2aa0e3..1aacddd 100644
--- a/Utilities/cmzstd/lib/dictBuilder/cover.h
+++ b/Utilities/cmzstd/lib/dictBuilder/cover.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2020, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -8,6 +8,10 @@
  * You may select, at your option, one of the above-listed licenses.
  */
 
+#ifndef ZDICT_STATIC_LINKING_ONLY
+#  define ZDICT_STATIC_LINKING_ONLY
+#endif
+
 #include <stdio.h>  /* fprintf */
 #include <stdlib.h> /* malloc, free, qsort */
 #include <string.h> /* memset */
@@ -16,10 +20,7 @@
 #include "../common/pool.h"
 #include "../common/threading.h"
 #include "../common/zstd_internal.h" /* includes zstd.h */
-#ifndef ZDICT_STATIC_LINKING_ONLY
-#define ZDICT_STATIC_LINKING_ONLY
-#endif
-#include "zdict.h"
+#include "../zdict.h"
 
 /**
  * COVER_best_t is used for two purposes:
@@ -152,6 +153,6 @@
  * smallest dictionary within a specified regression of the compressed size
  * from the largest dictionary.
  */
- COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent,
+ COVER_dictSelection_t COVER_selectDict(BYTE* customDictContent, size_t dictBufferCapacity,
                        size_t dictContentSize, const BYTE* samplesBuffer, const size_t* samplesSizes, unsigned nbFinalizeSamples,
                        size_t nbCheckSamples, size_t nbSamples, ZDICT_cover_params_t params, size_t* offsets, size_t totalCompressedSize);
diff --git a/Utilities/cmzstd/lib/dictBuilder/divsufsort.c b/Utilities/cmzstd/lib/dictBuilder/divsufsort.c
index ead9220..a2870fb 100644
--- a/Utilities/cmzstd/lib/dictBuilder/divsufsort.c
+++ b/Utilities/cmzstd/lib/dictBuilder/divsufsort.c
@@ -1576,7 +1576,7 @@
     /* Construct the inverse suffix array of type B* suffixes using trsort. */
     trsort(ISAb, SA, m, 1);
 
-    /* Set the sorted order of tyoe B* suffixes. */
+    /* Set the sorted order of type B* suffixes. */
     for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) {
       for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { }
       if(0 <= i) {
diff --git a/Utilities/cmzstd/lib/dictBuilder/fastcover.c b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
index 485c333..ed789f9 100644
--- a/Utilities/cmzstd/lib/dictBuilder/fastcover.c
+++ b/Utilities/cmzstd/lib/dictBuilder/fastcover.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Facebook, Inc.
+ * Copyright (c) Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -16,15 +16,17 @@
 #include <string.h> /* memset */
 #include <time.h>   /* clock */
 
+#ifndef ZDICT_STATIC_LINKING_ONLY
+#  define ZDICT_STATIC_LINKING_ONLY
+#endif
+
 #include "../common/mem.h" /* read */
 #include "../common/pool.h"
 #include "../common/threading.h"
-#include "cover.h"
 #include "../common/zstd_internal.h" /* includes zstd.h */
-#ifndef ZDICT_STATIC_LINKING_ONLY
-#define ZDICT_STATIC_LINKING_ONLY
-#endif
-#include "zdict.h"
+#include "../compress/zstd_compress_internal.h" /* ZSTD_hash*() */
+#include "../zdict.h"
+#include "cover.h"
 
 
 /*-*************************************
@@ -33,7 +35,7 @@
 #define FASTCOVER_MAX_SAMPLES_SIZE (sizeof(size_t) == 8 ? ((unsigned)-1) : ((unsigned)1 GB))
 #define FASTCOVER_MAX_F 31
 #define FASTCOVER_MAX_ACCEL 10
-#define DEFAULT_SPLITPOINT 0.75
+#define FASTCOVER_DEFAULT_SPLITPOINT 0.75
 #define DEFAULT_F 20
 #define DEFAULT_ACCEL 1
 
@@ -41,50 +43,50 @@
 /*-*************************************
 *  Console display
 ***************************************/
+#ifndef LOCALDISPLAYLEVEL
 static int g_displayLevel = 2;
+#endif
+#undef  DISPLAY
 #define DISPLAY(...)                                                           \
   {                                                                            \
     fprintf(stderr, __VA_ARGS__);                                              \
     fflush(stderr);                                                            \
   }
+#undef  LOCALDISPLAYLEVEL
 #define LOCALDISPLAYLEVEL(displayLevel, l, ...)                                \
   if (displayLevel >= l) {                                                     \
     DISPLAY(__VA_ARGS__);                                                      \
   } /* 0 : no display;   1: errors;   2: default;  3: details;  4: debug */
+#undef  DISPLAYLEVEL
 #define DISPLAYLEVEL(l, ...) LOCALDISPLAYLEVEL(g_displayLevel, l, __VA_ARGS__)
 
+#ifndef LOCALDISPLAYUPDATE
+static const clock_t g_refreshRate = CLOCKS_PER_SEC * 15 / 100;
+static clock_t g_time = 0;
+#endif
+#undef  LOCALDISPLAYUPDATE
 #define LOCALDISPLAYUPDATE(displayLevel, l, ...)                               \
   if (displayLevel >= l) {                                                     \
-    if ((clock() - g_time > refreshRate) || (displayLevel >= 4)) {             \
+    if ((clock() - g_time > g_refreshRate) || (displayLevel >= 4)) {             \
       g_time = clock();                                                        \
       DISPLAY(__VA_ARGS__);                                                    \
     }                                                                          \
   }
+#undef  DISPLAYUPDATE
 #define DISPLAYUPDATE(l, ...) LOCALDISPLAYUPDATE(g_displayLevel, l, __VA_ARGS__)
-static const clock_t refreshRate = CLOCKS_PER_SEC * 15 / 100;
-static clock_t g_time = 0;
 
 
 /*-*************************************
 * Hash Functions
 ***************************************/
-static const U64 prime6bytes = 227718039650203ULL;
-static size_t ZSTD_hash6(U64 u, U32 h) { return (size_t)(((u  << (64-48)) * prime6bytes) >> (64-h)) ; }
-static size_t ZSTD_hash6Ptr(const void* p, U32 h) { return ZSTD_hash6(MEM_readLE64(p), h); }
-
-static const U64 prime8bytes = 0xCF1BBCDCB7A56463ULL;
-static size_t ZSTD_hash8(U64 u, U32 h) { return (size_t)(((u) * prime8bytes) >> (64-h)) ; }
-static size_t ZSTD_hash8Ptr(const void* p, U32 h) { return ZSTD_hash8(MEM_readLE64(p), h); }
-
-
 /**
- * Hash the d-byte value pointed to by p and mod 2^f
+ * Hash the d-byte value pointed to by p and mod 2^f into the frequency vector
  */
-static size_t FASTCOVER_hashPtrToIndex(const void* p, U32 h, unsigned d) {
+static size_t FASTCOVER_hashPtrToIndex(const void* p, U32 f, unsigned d) {
   if (d == 6) {
-    return ZSTD_hash6Ptr(p, h) & ((1 << h) - 1);
+    return ZSTD_hash6Ptr(p, f);
   }
-  return ZSTD_hash8Ptr(p, h) & ((1 << h) - 1);
+  return ZSTD_hash8Ptr(p, f);
 }
 
 
@@ -461,20 +463,20 @@
  * This function is thread safe if zstd is compiled with multithreaded support.
  * It takes its parameters as an *OWNING* opaque pointer to support threading.
  */
-static void FASTCOVER_tryParameters(void *opaque)
+static void FASTCOVER_tryParameters(void* opaque)
 {
   /* Save parameters as local variables */
-  FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t *)opaque;
+  FASTCOVER_tryParameters_data_t *const data = (FASTCOVER_tryParameters_data_t*)opaque;
   const FASTCOVER_ctx_t *const ctx = data->ctx;
   const ZDICT_cover_params_t parameters = data->parameters;
   size_t dictBufferCapacity = data->dictBufferCapacity;
   size_t totalCompressedSize = ERROR(GENERIC);
   /* Initialize array to keep track of frequency of dmer within activeSegment */
-  U16* segmentFreqs = (U16 *)calloc(((U64)1 << ctx->f), sizeof(U16));
+  U16* segmentFreqs = (U16*)calloc(((U64)1 << ctx->f), sizeof(U16));
   /* Allocate space for hash table, dict, and freqs */
-  BYTE *const dict = (BYTE * const)malloc(dictBufferCapacity);
+  BYTE *const dict = (BYTE*)malloc(dictBufferCapacity);
   COVER_dictSelection_t selection = COVER_dictSelectionError(ERROR(GENERIC));
-  U32 *freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32));
+  U32* freqs = (U32*) malloc(((U64)1 << ctx->f) * sizeof(U32));
   if (!segmentFreqs || !dict || !freqs) {
     DISPLAYLEVEL(1, "Failed to allocate buffers: out of memory\n");
     goto _cleanup;
@@ -486,7 +488,7 @@
                                                     parameters, segmentFreqs);
 
     const unsigned nbFinalizeSamples = (unsigned)(ctx->nbTrainSamples * ctx->accelParams.finalize / 100);
-    selection = COVER_selectDict(dict + tail, dictBufferCapacity - tail,
+    selection = COVER_selectDict(dict + tail, dictBufferCapacity, dictBufferCapacity - tail,
          ctx->samples, ctx->samplesSizes, nbFinalizeSamples, ctx->nbTrainSamples, ctx->nbSamples, parameters, ctx->offsets,
          totalCompressedSize);
 
@@ -617,7 +619,7 @@
     /* constants */
     const unsigned nbThreads = parameters->nbThreads;
     const double splitPoint =
-        parameters->splitPoint <= 0.0 ? DEFAULT_SPLITPOINT : parameters->splitPoint;
+        parameters->splitPoint <= 0.0 ? FASTCOVER_DEFAULT_SPLITPOINT : parameters->splitPoint;
     const unsigned kMinD = parameters->d == 0 ? 6 : parameters->d;
     const unsigned kMaxD = parameters->d == 0 ? 8 : parameters->d;
     const unsigned kMinK = parameters->k == 0 ? 50 : parameters->k;
diff --git a/Utilities/cmzstd/lib/dictBuilder/zdict.c b/Utilities/cmzstd/lib/dictBuilder/zdict.c
index 6d0b042..459cbe4 100644
--- a/Utilities/cmzstd/lib/dictBuilder/zdict.c
+++ b/Utilities/cmzstd/lib/dictBuilder/zdict.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -23,9 +23,13 @@
 /* Unix Large Files support (>4GB) */
 #define _FILE_OFFSET_BITS 64
 #if (defined(__sun__) && (!defined(__LP64__)))   /* Sun Solaris 32-bits requires specific definitions */
+#  ifndef _LARGEFILE_SOURCE
 #  define _LARGEFILE_SOURCE
+#  endif
 #elif ! defined(__LP64__)                        /* No point defining Large file for 64 bit */
+#  ifndef _LARGEFILE64_SOURCE
 #  define _LARGEFILE64_SOURCE
+#  endif
 #endif
 
 
@@ -37,18 +41,19 @@
 #include <stdio.h>         /* fprintf, fopen, ftello64 */
 #include <time.h>          /* clock */
 
-#include "../common/mem.h"           /* read */
-#include "../common/fse.h"           /* FSE_normalizeCount, FSE_writeNCount */
-#define HUF_STATIC_LINKING_ONLY
-#include "../common/huf.h"           /* HUF_buildCTable, HUF_writeCTable */
-#include "../common/zstd_internal.h" /* includes zstd.h */
-#include "../common/xxhash.h"        /* XXH64 */
-#include "divsufsort.h"
 #ifndef ZDICT_STATIC_LINKING_ONLY
 #  define ZDICT_STATIC_LINKING_ONLY
 #endif
-#include "zdict.h"
+#define HUF_STATIC_LINKING_ONLY
+
+#include "../common/mem.h"           /* read */
+#include "../common/fse.h"           /* FSE_normalizeCount, FSE_writeNCount */
+#include "../common/huf.h"           /* HUF_buildCTable, HUF_writeCTable */
+#include "../common/zstd_internal.h" /* includes zstd.h */
+#include "../common/xxhash.h"        /* XXH64 */
 #include "../compress/zstd_compress_internal.h" /* ZSTD_loadCEntropy() */
+#include "../zdict.h"
+#include "divsufsort.h"
 
 
 /*-*************************************
@@ -62,14 +67,15 @@
 
 #define NOISELENGTH 32
 
-static const int g_compressionLevel_default = 3;
 static const U32 g_selectivity_default = 9;
 
 
 /*-*************************************
 *  Console display
 ***************************************/
+#undef  DISPLAY
 #define DISPLAY(...)         { fprintf(stderr, __VA_ARGS__); fflush( stderr ); }
+#undef  DISPLAYLEVEL
 #define DISPLAYLEVEL(l, ...) if (notificationLevel>=l) { DISPLAY(__VA_ARGS__); }    /* 0 : no display;   1: errors;   2: default;  3: details;  4: debug */
 
 static clock_t ZDICT_clockSpan(clock_t nPrevious) { return clock() - nPrevious; }
@@ -105,20 +111,17 @@
     size_t headerSize;
     if (dictSize <= 8 || MEM_readLE32(dictBuffer) != ZSTD_MAGIC_DICTIONARY) return ERROR(dictionary_corrupted);
 
-    {   unsigned offcodeMaxValue = MaxOff;
-        ZSTD_compressedBlockState_t* bs = (ZSTD_compressedBlockState_t*)malloc(sizeof(ZSTD_compressedBlockState_t));
+    {   ZSTD_compressedBlockState_t* bs = (ZSTD_compressedBlockState_t*)malloc(sizeof(ZSTD_compressedBlockState_t));
         U32* wksp = (U32*)malloc(HUF_WORKSPACE_SIZE);
-        short* offcodeNCount = (short*)malloc((MaxOff+1)*sizeof(short));
-        if (!bs || !wksp || !offcodeNCount) {
+        if (!bs || !wksp) {
             headerSize = ERROR(memory_allocation);
         } else {
             ZSTD_reset_compressedBlockState(bs);
-            headerSize = ZSTD_loadCEntropy(bs, wksp, offcodeNCount, &offcodeMaxValue, dictBuffer, dictSize);
+            headerSize = ZSTD_loadCEntropy(bs, wksp, dictBuffer, dictSize);
         }
 
         free(bs);
         free(wksp);
-        free(offcodeNCount);
     }
 
     return headerSize;
@@ -532,6 +535,7 @@
     clock_t displayClock = 0;
     clock_t const refreshRate = CLOCKS_PER_SEC * 3 / 10;
 
+#   undef  DISPLAYUPDATE
 #   define DISPLAYUPDATE(l, ...) if (notificationLevel>=l) { \
             if (ZDICT_clockSpan(displayClock) > refreshRate)  \
             { displayClock = clock(); DISPLAY(__VA_ARGS__); \
@@ -706,7 +710,7 @@
 
 #define OFFCODE_MAX 30  /* only applicable to first block */
 static size_t ZDICT_analyzeEntropy(void*  dstBuffer, size_t maxDstSize,
-                                   unsigned compressionLevel,
+                                   int compressionLevel,
                              const void*  srcBuffer, const size_t* fileSizes, unsigned nbFiles,
                              const void* dictBuffer, size_t  dictBufferSize,
                                    unsigned notificationLevel)
@@ -741,7 +745,7 @@
     memset(repOffset, 0, sizeof(repOffset));
     repOffset[1] = repOffset[4] = repOffset[8] = 1;
     memset(bestRepOffset, 0, sizeof(bestRepOffset));
-    if (compressionLevel==0) compressionLevel = g_compressionLevel_default;
+    if (compressionLevel==0) compressionLevel = ZSTD_CLEVEL_DEFAULT;
     params = ZSTD_getParams(compressionLevel, averageSampleSize, dictBufferSize);
 
     esr.dict = ZSTD_createCDict_advanced(dictBuffer, dictBufferSize, ZSTD_dlm_byRef, ZSTD_dct_rawContent, params.cParams, ZSTD_defaultCMem);
@@ -786,7 +790,7 @@
     /* note : the result of this phase should be used to better appreciate the impact on statistics */
 
     total=0; for (u=0; u<=offcodeMax; u++) total+=offcodeCount[u];
-    errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, offcodeMax);
+    errorCode = FSE_normalizeCount(offcodeNCount, Offlog, offcodeCount, total, offcodeMax, /* useLowProbCount */ 1);
     if (FSE_isError(errorCode)) {
         eSize = errorCode;
         DISPLAYLEVEL(1, "FSE_normalizeCount error with offcodeCount \n");
@@ -795,7 +799,7 @@
     Offlog = (U32)errorCode;
 
     total=0; for (u=0; u<=MaxML; u++) total+=matchLengthCount[u];
-    errorCode = FSE_normalizeCount(matchLengthNCount, mlLog, matchLengthCount, total, MaxML);
+    errorCode = FSE_normalizeCount(matchLengthNCount, mlLog, matchLengthCount, total, MaxML, /* useLowProbCount */ 1);
     if (FSE_isError(errorCode)) {
         eSize = errorCode;
         DISPLAYLEVEL(1, "FSE_normalizeCount error with matchLengthCount \n");
@@ -804,7 +808,7 @@
     mlLog = (U32)errorCode;
 
     total=0; for (u=0; u<=MaxLL; u++) total+=litLengthCount[u];
-    errorCode = FSE_normalizeCount(litLengthNCount, llLog, litLengthCount, total, MaxLL);
+    errorCode = FSE_normalizeCount(litLengthNCount, llLog, litLengthCount, total, MaxLL, /* useLowProbCount */ 1);
     if (FSE_isError(errorCode)) {
         eSize = errorCode;
         DISPLAYLEVEL(1, "FSE_normalizeCount error with litLengthCount \n");
@@ -893,7 +897,7 @@
     size_t hSize;
 #define HBUFFSIZE 256   /* should prove large enough for all entropy headers */
     BYTE header[HBUFFSIZE];
-    int const compressionLevel = (params.compressionLevel == 0) ? g_compressionLevel_default : params.compressionLevel;
+    int const compressionLevel = (params.compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : params.compressionLevel;
     U32 const notificationLevel = params.notificationLevel;
 
     /* check conditions */
@@ -939,7 +943,7 @@
         const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
         ZDICT_params_t params)
 {
-    int const compressionLevel = (params.compressionLevel == 0) ? g_compressionLevel_default : params.compressionLevel;
+    int const compressionLevel = (params.compressionLevel == 0) ? ZSTD_CLEVEL_DEFAULT : params.compressionLevel;
     U32 const notificationLevel = params.notificationLevel;
     size_t hSize = 8;
 
@@ -968,16 +972,11 @@
     return MIN(dictBufferCapacity, hSize+dictContentSize);
 }
 
-/* Hidden declaration for dbio.c */
-size_t ZDICT_trainFromBuffer_unsafe_legacy(
-                            void* dictBuffer, size_t maxDictSize,
-                            const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
-                            ZDICT_legacy_params_t params);
 /*! ZDICT_trainFromBuffer_unsafe_legacy() :
-*   Warning : `samplesBuffer` must be followed by noisy guard band.
+*   Warning : `samplesBuffer` must be followed by noisy guard band !!!
 *   @return : size of dictionary, or an error code which can be tested with ZDICT_isError()
 */
-size_t ZDICT_trainFromBuffer_unsafe_legacy(
+static size_t ZDICT_trainFromBuffer_unsafe_legacy(
                             void* dictBuffer, size_t maxDictSize,
                             const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
                             ZDICT_legacy_params_t params)
@@ -1114,8 +1113,8 @@
     memset(&params, 0, sizeof(params));
     params.d = 8;
     params.steps = 4;
-    /* Default to level 6 since no compression level information is available */
-    params.zParams.compressionLevel = 3;
+    /* Use default level since no compression level information is available */
+    params.zParams.compressionLevel = ZSTD_CLEVEL_DEFAULT;
 #if defined(DEBUGLEVEL) && (DEBUGLEVEL>=1)
     params.zParams.notificationLevel = DEBUGLEVEL;
 #endif
diff --git a/Utilities/cmzstd/lib/dictBuilder/zdict.h b/Utilities/cmzstd/lib/zdict.h
similarity index 70%
rename from Utilities/cmzstd/lib/dictBuilder/zdict.h
rename to Utilities/cmzstd/lib/zdict.h
index ff2e77f..75b05db 100644
--- a/Utilities/cmzstd/lib/dictBuilder/zdict.h
+++ b/Utilities/cmzstd/lib/zdict.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -36,6 +36,145 @@
 #  define ZDICTLIB_API ZDICTLIB_VISIBILITY
 #endif
 
+/*******************************************************************************
+ * Zstd dictionary builder
+ *
+ * FAQ
+ * ===
+ * Why should I use a dictionary?
+ * ------------------------------
+ *
+ * Zstd can use dictionaries to improve compression ratio of small data.
+ * Traditionally small files don't compress well because there is very little
+ * repetion in a single sample, since it is small. But, if you are compressing
+ * many similar files, like a bunch of JSON records that share the same
+ * structure, you can train a dictionary on ahead of time on some samples of
+ * these files. Then, zstd can use the dictionary to find repetitions that are
+ * present across samples. This can vastly improve compression ratio.
+ *
+ * When is a dictionary useful?
+ * ----------------------------
+ *
+ * Dictionaries are useful when compressing many small files that are similar.
+ * The larger a file is, the less benefit a dictionary will have. Generally,
+ * we don't expect dictionary compression to be effective past 100KB. And the
+ * smaller a file is, the more we would expect the dictionary to help.
+ *
+ * How do I use a dictionary?
+ * --------------------------
+ *
+ * Simply pass the dictionary to the zstd compressor with
+ * `ZSTD_CCtx_loadDictionary()`. The same dictionary must then be passed to
+ * the decompressor, using `ZSTD_DCtx_loadDictionary()`. There are other
+ * more advanced functions that allow selecting some options, see zstd.h for
+ * complete documentation.
+ *
+ * What is a zstd dictionary?
+ * --------------------------
+ *
+ * A zstd dictionary has two pieces: Its header, and its content. The header
+ * contains a magic number, the dictionary ID, and entropy tables. These
+ * entropy tables allow zstd to save on header costs in the compressed file,
+ * which really matters for small data. The content is just bytes, which are
+ * repeated content that is common across many samples.
+ *
+ * What is a raw content dictionary?
+ * ---------------------------------
+ *
+ * A raw content dictionary is just bytes. It doesn't have a zstd dictionary
+ * header, a dictionary ID, or entropy tables. Any buffer is a valid raw
+ * content dictionary.
+ *
+ * How do I train a dictionary?
+ * ----------------------------
+ *
+ * Gather samples from your use case. These samples should be similar to each
+ * other. If you have several use cases, you could try to train one dictionary
+ * per use case.
+ *
+ * Pass those samples to `ZDICT_trainFromBuffer()` and that will train your
+ * dictionary. There are a few advanced versions of this function, but this
+ * is a great starting point. If you want to further tune your dictionary
+ * you could try `ZDICT_optimizeTrainFromBuffer_cover()`. If that is too slow
+ * you can try `ZDICT_optimizeTrainFromBuffer_fastCover()`.
+ *
+ * If the dictionary training function fails, that is likely because you
+ * either passed too few samples, or a dictionary would not be effective
+ * for your data. Look at the messages that the dictionary trainer printed,
+ * if it doesn't say too few samples, then a dictionary would not be effective.
+ *
+ * How large should my dictionary be?
+ * ----------------------------------
+ *
+ * A reasonable dictionary size, the `dictBufferCapacity`, is about 100KB.
+ * The zstd CLI defaults to a 110KB dictionary. You likely don't need a
+ * dictionary larger than that. But, most use cases can get away with a
+ * smaller dictionary. The advanced dictionary builders can automatically
+ * shrink the dictionary for you, and select a the smallest size that
+ * doesn't hurt compression ratio too much. See the `shrinkDict` parameter.
+ * A smaller dictionary can save memory, and potentially speed up
+ * compression.
+ *
+ * How many samples should I provide to the dictionary builder?
+ * ------------------------------------------------------------
+ *
+ * We generally recommend passing ~100x the size of the dictionary
+ * in samples. A few thousand should suffice. Having too few samples
+ * can hurt the dictionaries effectiveness. Having more samples will
+ * only improve the dictionaries effectiveness. But having too many
+ * samples can slow down the dictionary builder.
+ *
+ * How do I determine if a dictionary will be effective?
+ * -----------------------------------------------------
+ *
+ * Simply train a dictionary and try it out. You can use zstd's built in
+ * benchmarking tool to test the dictionary effectiveness.
+ *
+ *   # Benchmark levels 1-3 without a dictionary
+ *   zstd -b1e3 -r /path/to/my/files
+ *   # Benchmark levels 1-3 with a dictioanry
+ *   zstd -b1e3 -r /path/to/my/files -D /path/to/my/dictionary
+ *
+ * When should I retrain a dictionary?
+ * -----------------------------------
+ *
+ * You should retrain a dictionary when its effectiveness drops. Dictionary
+ * effectiveness drops as the data you are compressing changes. Generally, we do
+ * expect dictionaries to "decay" over time, as your data changes, but the rate
+ * at which they decay depends on your use case. Internally, we regularly
+ * retrain dictionaries, and if the new dictionary performs significantly
+ * better than the old dictionary, we will ship the new dictionary.
+ *
+ * I have a raw content dictionary, how do I turn it into a zstd dictionary?
+ * -------------------------------------------------------------------------
+ *
+ * If you have a raw content dictionary, e.g. by manually constructing it, or
+ * using a third-party dictionary builder, you can turn it into a zstd
+ * dictionary by using `ZDICT_finalizeDictionary()`. You'll also have to
+ * provide some samples of the data. It will add the zstd header to the
+ * raw content, which contains a dictionary ID and entropy tables, which
+ * will improve compression ratio, and allow zstd to write the dictionary ID
+ * into the frame, if you so choose.
+ *
+ * Do I have to use zstd's dictionary builder?
+ * -------------------------------------------
+ *
+ * No! You can construct dictionary content however you please, it is just
+ * bytes. It will always be valid as a raw content dictionary. If you want
+ * a zstd dictionary, which can improve compression ratio, use
+ * `ZDICT_finalizeDictionary()`.
+ *
+ * What is the attack surface of a zstd dictionary?
+ * ------------------------------------------------
+ *
+ * Zstd is heavily fuzz tested, including loading fuzzed dictionaries, so
+ * zstd should never crash, or access out-of-bounds memory no matter what
+ * the dictionary is. However, if an attacker can control the dictionary
+ * during decompression, they can cause zstd to generate arbitrary bytes,
+ * just like if they controlled the compressed data.
+ *
+ ******************************************************************************/
+
 
 /*! ZDICT_trainFromBuffer():
  *  Train a dictionary from an array of samples.
@@ -64,7 +203,14 @@
 typedef struct {
     int      compressionLevel;   /*< optimize for a specific zstd compression level; 0 means default */
     unsigned notificationLevel;  /*< Write log to stderr; 0 = none (default); 1 = errors; 2 = progression; 3 = details; 4 = debug; */
-    unsigned dictID;             /*< force dictID value; 0 means auto mode (32-bits random value) */
+    unsigned dictID;             /*< force dictID value; 0 means auto mode (32-bits random value)
+                                  *   NOTE: The zstd format reserves some dictionary IDs for future use.
+                                  *         You may use them in private settings, but be warned that they
+                                  *         may be used by zstd in a public dictionary registry in the future.
+                                  *         These dictionary IDs are:
+                                  *           - low range  : <= 32767
+                                  *           - high range : >= (2^31)
+                                  */
 } ZDICT_params_t;
 
 /*! ZDICT_finalizeDictionary():
@@ -264,10 +410,11 @@
  *  Note: ZDICT_trainFromBuffer_legacy() will send notifications into stderr if instructed to, using notificationLevel>0.
  */
 ZDICTLIB_API size_t ZDICT_trainFromBuffer_legacy(
-    void *dictBuffer, size_t dictBufferCapacity,
-    const void *samplesBuffer, const size_t *samplesSizes, unsigned nbSamples,
+    void* dictBuffer, size_t dictBufferCapacity,
+    const void* samplesBuffer, const size_t* samplesSizes, unsigned nbSamples,
     ZDICT_legacy_params_t parameters);
 
+
 /* Deprecation warnings */
 /* It is generally possible to disable deprecation warnings from compiler,
    for example with -Wno-deprecated-declarations for gcc
@@ -279,7 +426,7 @@
 #  define ZDICT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
 #  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
 #    define ZDICT_DEPRECATED(message) [[deprecated(message)]] ZDICTLIB_API
-#  elif (ZDICT_GCC_VERSION >= 405) || defined(__clang__)
+#  elif defined(__clang__) || (ZDICT_GCC_VERSION >= 405)
 #    define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated(message)))
 #  elif (ZDICT_GCC_VERSION >= 301)
 #    define ZDICT_DEPRECATED(message) ZDICTLIB_API __attribute__((deprecated))
diff --git a/Utilities/cmzstd/lib/zstd.h b/Utilities/cmzstd/lib/zstd.h
index 8c6fc6a..4651e6c 100644
--- a/Utilities/cmzstd/lib/zstd.h
+++ b/Utilities/cmzstd/lib/zstd.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -71,17 +71,22 @@
 
 /*------   Version   ------*/
 #define ZSTD_VERSION_MAJOR    1
-#define ZSTD_VERSION_MINOR    4
-#define ZSTD_VERSION_RELEASE  5
-
+#define ZSTD_VERSION_MINOR    5
+#define ZSTD_VERSION_RELEASE  0
 #define ZSTD_VERSION_NUMBER  (ZSTD_VERSION_MAJOR *100*100 + ZSTD_VERSION_MINOR *100 + ZSTD_VERSION_RELEASE)
-ZSTDLIB_API unsigned ZSTD_versionNumber(void);   /**< to check runtime library version */
+
+/*! ZSTD_versionNumber() :
+ *  Return runtime library version, the value is (MAJOR*100*100 + MINOR*100 + RELEASE). */
+ZSTDLIB_API unsigned ZSTD_versionNumber(void);
 
 #define ZSTD_LIB_VERSION ZSTD_VERSION_MAJOR.ZSTD_VERSION_MINOR.ZSTD_VERSION_RELEASE
 #define ZSTD_QUOTE(str) #str
 #define ZSTD_EXPAND_AND_QUOTE(str) ZSTD_QUOTE(str)
 #define ZSTD_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LIB_VERSION)
-ZSTDLIB_API const char* ZSTD_versionString(void);   /* requires v1.3.0+ */
+
+/*! ZSTD_versionString() :
+ *  Return runtime library version, like "1.4.5". Requires v1.3.0+. */
+ZSTDLIB_API const char* ZSTD_versionString(void);
 
 /* *************************************
  *  Default constant
@@ -104,7 +109,6 @@
 #define ZSTD_BLOCKSIZE_MAX     (1<<ZSTD_BLOCKSIZELOG_MAX)
 
 
-
 /***************************************
 *  Simple API
 ***************************************/
@@ -161,7 +165,7 @@
  * @return : decompressed size of `src` frame content _if known and not empty_, 0 otherwise. */
 ZSTDLIB_API unsigned long long ZSTD_getDecompressedSize(const void* src, size_t srcSize);
 
-/*! ZSTD_findFrameCompressedSize() :
+/*! ZSTD_findFrameCompressedSize() : Requires v1.4.0+
  * `src` should point to the start of a ZSTD frame or skippable frame.
  * `srcSize` must be >= first frame size
  * @return : the compressed size of the first frame starting at `src`,
@@ -175,8 +179,9 @@
 ZSTDLIB_API size_t      ZSTD_compressBound(size_t srcSize); /*!< maximum compressed size in worst case single-pass scenario */
 ZSTDLIB_API unsigned    ZSTD_isError(size_t code);          /*!< tells if a `size_t` function result is an error code */
 ZSTDLIB_API const char* ZSTD_getErrorName(size_t code);     /*!< provides readable string from an error code */
-ZSTDLIB_API int         ZSTD_minCLevel(void);               /*!< minimum negative compression level allowed */
+ZSTDLIB_API int         ZSTD_minCLevel(void);               /*!< minimum negative compression level allowed, requires v1.4.0+ */
 ZSTDLIB_API int         ZSTD_maxCLevel(void);               /*!< maximum compression level available */
+ZSTDLIB_API int         ZSTD_defaultCLevel(void);           /*!< default compression level, specified by ZSTD_CLEVEL_DEFAULT, requires v1.5.0+ */
 
 
 /***************************************
@@ -194,7 +199,7 @@
  */
 typedef struct ZSTD_CCtx_s ZSTD_CCtx;
 ZSTDLIB_API ZSTD_CCtx* ZSTD_createCCtx(void);
-ZSTDLIB_API size_t     ZSTD_freeCCtx(ZSTD_CCtx* cctx);
+ZSTDLIB_API size_t     ZSTD_freeCCtx(ZSTD_CCtx* cctx);  /* accept NULL pointer */
 
 /*! ZSTD_compressCCtx() :
  *  Same as ZSTD_compress(), using an explicit ZSTD_CCtx.
@@ -217,7 +222,7 @@
  *  Use one context per thread for parallel execution. */
 typedef struct ZSTD_DCtx_s ZSTD_DCtx;
 ZSTDLIB_API ZSTD_DCtx* ZSTD_createDCtx(void);
-ZSTDLIB_API size_t     ZSTD_freeDCtx(ZSTD_DCtx* dctx);
+ZSTDLIB_API size_t     ZSTD_freeDCtx(ZSTD_DCtx* dctx);  /* accept NULL pointer */
 
 /*! ZSTD_decompressDCtx() :
  *  Same as ZSTD_decompress(),
@@ -229,9 +234,9 @@
                                  const void* src, size_t srcSize);
 
 
-/***************************************
-*  Advanced compression API
-***************************************/
+/*********************************************
+*  Advanced compression API (Requires v1.4.0+)
+**********************************************/
 
 /* API design :
  *   Parameters are pushed one by one into an existing context,
@@ -261,7 +266,6 @@
                          Only the order (from fast to strong) is guaranteed */
 } ZSTD_strategy;
 
-
 typedef enum {
 
     /* compression parameters
@@ -327,14 +331,15 @@
                               * The higher the value of selected strategy, the more complex it is,
                               * resulting in stronger and slower compression.
                               * Special: value 0 means "use default strategy". */
-
     /* LDM mode parameters */
     ZSTD_c_enableLongDistanceMatching=160, /* Enable long distance matching.
                                      * This parameter is designed to improve compression ratio
                                      * for large inputs, by finding large matches at long distance.
                                      * It increases memory usage and window size.
                                      * Note: enabling this parameter increases default ZSTD_c_windowLog to 128 MB
-                                     * except when expressly set to a different value. */
+                                     * except when expressly set to a different value.
+                                     * Note: will be enabled by default if ZSTD_c_windowLog >= 128 MB and
+                                     * compression strategy >= ZSTD_btopt (== compression level 16+) */
     ZSTD_c_ldmHashLog=161,   /* Size of the table for long distance matching, as a power of 2.
                               * Larger values increase memory usage and compression ratio,
                               * but decrease compression speed.
@@ -365,20 +370,24 @@
     ZSTD_c_dictIDFlag=202,   /* When applicable, dictionary's ID is written into frame header (default:1) */
 
     /* multi-threading parameters */
-    /* These parameters are only useful if multi-threading is enabled (compiled with build macro ZSTD_MULTITHREAD).
-     * They return an error otherwise. */
+    /* These parameters are only active if multi-threading is enabled (compiled with build macro ZSTD_MULTITHREAD).
+     * Otherwise, trying to set any other value than default (0) will be a no-op and return an error.
+     * In a situation where it's unknown if the linked library supports multi-threading or not,
+     * setting ZSTD_c_nbWorkers to any value >= 1 and consulting the return value provides a quick way to check this property.
+     */
     ZSTD_c_nbWorkers=400,    /* Select how many threads will be spawned to compress in parallel.
-                              * When nbWorkers >= 1, triggers asynchronous mode when used with ZSTD_compressStream*() :
+                              * When nbWorkers >= 1, triggers asynchronous mode when invoking ZSTD_compressStream*() :
                               * ZSTD_compressStream*() consumes input and flush output if possible, but immediately gives back control to caller,
-                              * while compression work is performed in parallel, within worker threads.
+                              * while compression is performed in parallel, within worker thread(s).
                               * (note : a strong exception to this rule is when first invocation of ZSTD_compressStream2() sets ZSTD_e_end :
                               *  in which case, ZSTD_compressStream2() delegates to ZSTD_compress2(), which is always a blocking call).
                               * More workers improve speed, but also increase memory usage.
-                              * Default value is `0`, aka "single-threaded mode" : no worker is spawned, compression is performed inside Caller's thread, all invocations are blocking */
+                              * Default value is `0`, aka "single-threaded mode" : no worker is spawned,
+                              * compression is performed inside Caller's thread, and all invocations are blocking */
     ZSTD_c_jobSize=401,      /* Size of a compression job. This value is enforced only when nbWorkers >= 1.
                               * Each compression job is completed in parallel, so this value can indirectly impact the nb of active threads.
                               * 0 means default, which is dynamically determined based on compression parameters.
-                              * Job size must be a minimum of overlap size, or 1 MB, whichever is largest.
+                              * Job size must be a minimum of overlap size, or ZSTDMT_JOBSIZE_MIN (= 512 KB), whichever is largest.
                               * The minimum size is automatically and transparently enforced. */
     ZSTD_c_overlapLog=402,   /* Control the overlap size, as a fraction of window size.
                               * The overlap size is an amount of data reloaded from previous job at the beginning of a new job.
@@ -403,6 +412,13 @@
      * ZSTD_c_literalCompressionMode
      * ZSTD_c_targetCBlockSize
      * ZSTD_c_srcSizeHint
+     * ZSTD_c_enableDedicatedDictSearch
+     * ZSTD_c_stableInBuffer
+     * ZSTD_c_stableOutBuffer
+     * ZSTD_c_blockDelimiters
+     * ZSTD_c_validateSequences
+     * ZSTD_c_splitBlocks
+     * ZSTD_c_useRowMatchFinder
      * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
      * note : never ever use experimentalParam? names directly;
      *        also, the enums values themselves are unstable and can still change.
@@ -413,7 +429,15 @@
      ZSTD_c_experimentalParam4=1001,
      ZSTD_c_experimentalParam5=1002,
      ZSTD_c_experimentalParam6=1003,
-     ZSTD_c_experimentalParam7=1004
+     ZSTD_c_experimentalParam7=1004,
+     ZSTD_c_experimentalParam8=1005,
+     ZSTD_c_experimentalParam9=1006,
+     ZSTD_c_experimentalParam10=1007,
+     ZSTD_c_experimentalParam11=1008,
+     ZSTD_c_experimentalParam12=1009,
+     ZSTD_c_experimentalParam13=1010,
+     ZSTD_c_experimentalParam14=1011,
+     ZSTD_c_experimentalParam15=1012
 } ZSTD_cParameter;
 
 typedef struct {
@@ -498,9 +522,9 @@
                              const void* src, size_t srcSize);
 
 
-/***************************************
-*  Advanced decompression API
-***************************************/
+/***********************************************
+*  Advanced decompression API (Requires v1.4.0+)
+************************************************/
 
 /* The advanced API pushes parameters one by one into an existing DCtx context.
  * Parameters are sticky, and remain valid for all following frames
@@ -524,11 +548,15 @@
      * At the time of this writing, they include :
      * ZSTD_d_format
      * ZSTD_d_stableOutBuffer
+     * ZSTD_d_forceIgnoreChecksum
+     * ZSTD_d_refMultipleDDicts
      * Because they are not stable, it's necessary to define ZSTD_STATIC_LINKING_ONLY to access them.
      * note : never ever use experimentalParam? names directly
      */
      ZSTD_d_experimentalParam1=1000,
-     ZSTD_d_experimentalParam2=1001
+     ZSTD_d_experimentalParam2=1001,
+     ZSTD_d_experimentalParam3=1002,
+     ZSTD_d_experimentalParam4=1003
 
 } ZSTD_dParameter;
 
@@ -642,7 +670,7 @@
                                  /* Continue to distinguish them for compatibility with older versions <= v1.2.0 */
 /*===== ZSTD_CStream management functions =====*/
 ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream(void);
-ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs);
+ZSTDLIB_API size_t ZSTD_freeCStream(ZSTD_CStream* zcs);  /* accept NULL pointer */
 
 /*===== Streaming compression functions =====*/
 typedef enum {
@@ -658,14 +686,15 @@
                         : note : multithreaded compression will block to flush as much output as possible. */
 } ZSTD_EndDirective;
 
-/*! ZSTD_compressStream2() :
+/*! ZSTD_compressStream2() : Requires v1.4.0+
  *  Behaves about the same as ZSTD_compressStream, with additional control on end directive.
  *  - Compression parameters are pushed into CCtx before starting compression, using ZSTD_CCtx_set*()
  *  - Compression parameters cannot be changed once compression is started (save a list of exceptions in multi-threading mode)
  *  - output->pos must be <= dstCapacity, input->pos must be <= srcSize
  *  - output->pos and input->pos will be updated. They are guaranteed to remain below their respective limit.
+ *  - endOp must be a valid directive
  *  - When nbWorkers==0 (default), function is blocking : it completes its job before returning to caller.
- *  - When nbWorkers>=1, function is non-blocking : it just acquires a copy of input, and distributes jobs to internal worker threads, flush whatever is available,
+ *  - When nbWorkers>=1, function is non-blocking : it copies a portion of input, distributes jobs to internal worker threads, flush to output whatever is available,
  *                                                  and then immediately returns, just indicating that there is some data remaining to be flushed.
  *                                                  The function nonetheless guarantees forward progress : it will return only after it reads or write at least 1+ byte.
  *  - Exception : if the first call requests a ZSTD_e_end directive and provides enough dstCapacity, the function delegates to ZSTD_compress2() which is always blocking.
@@ -703,11 +732,11 @@
 
 
 /* *****************************************************************************
- * This following is a legacy streaming API.
+ * This following is a legacy streaming API, available since v1.0+ .
  * It can be replaced by ZSTD_CCtx_reset() and ZSTD_compressStream2().
  * It is redundant, but remains fully supported.
- * Advanced parameters and dictionary compression can only be used through the
- * new API.
+ * Streaming in combination with advanced parameters and dictionary compression
+ * can only be used through the new API.
  ******************************************************************************/
 
 /*!
@@ -762,7 +791,7 @@
                                  /* For compatibility with versions <= v1.2.0, prefer differentiating them. */
 /*===== ZSTD_DStream management functions =====*/
 ZSTDLIB_API ZSTD_DStream* ZSTD_createDStream(void);
-ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds);
+ZSTDLIB_API size_t ZSTD_freeDStream(ZSTD_DStream* zds);  /* accept NULL pointer */
 
 /*===== Streaming decompression functions =====*/
 
@@ -785,7 +814,7 @@
 /*! ZSTD_compress_usingDict() :
  *  Compression at an explicit compression level using a Dictionary.
  *  A dictionary can be any arbitrary data segment (also called a prefix),
- *  or a buffer with specified information (see dictBuilder/zdict.h).
+ *  or a buffer with specified information (see zdict.h).
  *  Note : This function loads the dictionary, resulting in significant startup delay.
  *         It's intended for a dictionary used only once.
  *  Note 2 : When `dict == NULL || dictSize < 8` no dictionary is used. */
@@ -828,7 +857,8 @@
                                          int compressionLevel);
 
 /*! ZSTD_freeCDict() :
- *  Function frees memory allocated by ZSTD_createCDict(). */
+ *  Function frees memory allocated by ZSTD_createCDict().
+ *  If a NULL pointer is passed, no operation is performed. */
 ZSTDLIB_API size_t      ZSTD_freeCDict(ZSTD_CDict* CDict);
 
 /*! ZSTD_compress_usingCDict() :
@@ -850,7 +880,8 @@
 ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict(const void* dictBuffer, size_t dictSize);
 
 /*! ZSTD_freeDDict() :
- *  Function frees memory allocated with ZSTD_createDDict() */
+ *  Function frees memory allocated with ZSTD_createDDict()
+ *  If a NULL pointer is passed, no operation is performed. */
 ZSTDLIB_API size_t      ZSTD_freeDDict(ZSTD_DDict* ddict);
 
 /*! ZSTD_decompress_usingDDict() :
@@ -866,19 +897,25 @@
  *  Dictionary helper functions
  *******************************/
 
-/*! ZSTD_getDictID_fromDict() :
+/*! ZSTD_getDictID_fromDict() : Requires v1.4.0+
  *  Provides the dictID stored within dictionary.
  *  if @return == 0, the dictionary is not conformant with Zstandard specification.
  *  It can still be loaded, but as a content-only dictionary. */
 ZSTDLIB_API unsigned ZSTD_getDictID_fromDict(const void* dict, size_t dictSize);
 
-/*! ZSTD_getDictID_fromDDict() :
+/*! ZSTD_getDictID_fromCDict() : Requires v1.5.0+
+ *  Provides the dictID of the dictionary loaded into `cdict`.
+ *  If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
+ *  Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
+ZSTDLIB_API unsigned ZSTD_getDictID_fromCDict(const ZSTD_CDict* cdict);
+
+/*! ZSTD_getDictID_fromDDict() : Requires v1.4.0+
  *  Provides the dictID of the dictionary loaded into `ddict`.
  *  If @return == 0, the dictionary is not conformant to Zstandard specification, or empty.
  *  Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */
 ZSTDLIB_API unsigned ZSTD_getDictID_fromDDict(const ZSTD_DDict* ddict);
 
-/*! ZSTD_getDictID_fromFrame() :
+/*! ZSTD_getDictID_fromFrame() : Requires v1.4.0+
  *  Provides the dictID required to decompressed the frame stored within `src`.
  *  If @return == 0, the dictID could not be decoded.
  *  This could for one of the following reasons :
@@ -892,7 +929,7 @@
 
 
 /*******************************************************************************
- * Advanced dictionary and prefix API
+ * Advanced dictionary and prefix API (Requires v1.4.0+)
  *
  * This API allows dictionaries to be used with ZSTD_compress2(),
  * ZSTD_compressStream2(), and ZSTD_decompress(). Dictionaries are sticky, and
@@ -901,7 +938,7 @@
  ******************************************************************************/
 
 
-/*! ZSTD_CCtx_loadDictionary() :
+/*! ZSTD_CCtx_loadDictionary() : Requires v1.4.0+
  *  Create an internal CDict from `dict` buffer.
  *  Decompression will have to use same dictionary.
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
@@ -920,11 +957,11 @@
  *           to precisely select how dictionary content must be interpreted. */
 ZSTDLIB_API size_t ZSTD_CCtx_loadDictionary(ZSTD_CCtx* cctx, const void* dict, size_t dictSize);
 
-/*! ZSTD_CCtx_refCDict() :
+/*! ZSTD_CCtx_refCDict() : Requires v1.4.0+
  *  Reference a prepared dictionary, to be used for all next compressed frames.
  *  Note that compression parameters are enforced from within CDict,
  *  and supersede any compression parameter previously set within CCtx.
- *  The parameters ignored are labled as "superseded-by-cdict" in the ZSTD_cParameter enum docs.
+ *  The parameters ignored are labelled as "superseded-by-cdict" in the ZSTD_cParameter enum docs.
  *  The ignored parameters will be used again if the CCtx is returned to no-dictionary mode.
  *  The dictionary will remain valid for future compressed frames using same CCtx.
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
@@ -934,7 +971,7 @@
  *  Note 2 : CDict is just referenced, its lifetime must outlive its usage within CCtx. */
 ZSTDLIB_API size_t ZSTD_CCtx_refCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict);
 
-/*! ZSTD_CCtx_refPrefix() :
+/*! ZSTD_CCtx_refPrefix() : Requires v1.4.0+
  *  Reference a prefix (single-usage dictionary) for next compressed frame.
  *  A prefix is **only used once**. Tables are discarded at end of frame (ZSTD_e_end).
  *  Decompression will need same prefix to properly regenerate data.
@@ -955,7 +992,7 @@
 ZSTDLIB_API size_t ZSTD_CCtx_refPrefix(ZSTD_CCtx* cctx,
                                  const void* prefix, size_t prefixSize);
 
-/*! ZSTD_DCtx_loadDictionary() :
+/*! ZSTD_DCtx_loadDictionary() : Requires v1.4.0+
  *  Create an internal DDict from dict buffer,
  *  to be used to decompress next frames.
  *  The dictionary remains valid for all future frames, until explicitly invalidated.
@@ -972,9 +1009,16 @@
  */
 ZSTDLIB_API size_t ZSTD_DCtx_loadDictionary(ZSTD_DCtx* dctx, const void* dict, size_t dictSize);
 
-/*! ZSTD_DCtx_refDDict() :
+/*! ZSTD_DCtx_refDDict() : Requires v1.4.0+
  *  Reference a prepared dictionary, to be used to decompress next frames.
  *  The dictionary remains active for decompression of future frames using same DCtx.
+ *
+ *  If called with ZSTD_d_refMultipleDDicts enabled, repeated calls of this function
+ *  will store the DDict references in a table, and the DDict used for decompression
+ *  will be determined at decompression time, as per the dict ID in the frame.
+ *  The memory for the table is allocated on the first call to refDDict, and can be
+ *  freed with ZSTD_freeDCtx().
+ *
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
  *  Note 1 : Currently, only one dictionary can be managed.
  *           Referencing a new dictionary effectively "discards" any previous one.
@@ -983,7 +1027,7 @@
  */
 ZSTDLIB_API size_t ZSTD_DCtx_refDDict(ZSTD_DCtx* dctx, const ZSTD_DDict* ddict);
 
-/*! ZSTD_DCtx_refPrefix() :
+/*! ZSTD_DCtx_refPrefix() : Requires v1.4.0+
  *  Reference a prefix (single-usage dictionary) to decompress next frame.
  *  This is the reverse operation of ZSTD_CCtx_refPrefix(),
  *  and must use the same prefix as the one used during compression.
@@ -1004,7 +1048,7 @@
 
 /* ===   Memory management   === */
 
-/*! ZSTD_sizeof_*() :
+/*! ZSTD_sizeof_*() : Requires v1.4.0+
  *  These functions give the _current_ memory usage of selected object.
  *  Note that object memory usage can evolve (increase or decrease) over time. */
 ZSTDLIB_API size_t ZSTD_sizeof_CCtx(const ZSTD_CCtx* cctx);
@@ -1029,6 +1073,28 @@
 #if defined(ZSTD_STATIC_LINKING_ONLY) && !defined(ZSTD_H_ZSTD_STATIC_LINKING_ONLY)
 #define ZSTD_H_ZSTD_STATIC_LINKING_ONLY
 
+/* Deprecation warnings :
+ * Should these warnings be a problem, it is generally possible to disable them,
+ * typically with -Wno-deprecated-declarations for gcc or _CRT_SECURE_NO_WARNINGS in Visual.
+ * Otherwise, it's also possible to define ZSTD_DISABLE_DEPRECATE_WARNINGS.
+ */
+#ifdef ZSTD_DISABLE_DEPRECATE_WARNINGS
+#  define ZSTD_DEPRECATED(message) ZSTDLIB_API  /* disable deprecation warnings */
+#else
+#  if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
+#    define ZSTD_DEPRECATED(message) [[deprecated(message)]] ZSTDLIB_API
+#  elif (defined(GNUC) && (GNUC > 4 || (GNUC == 4 && GNUC_MINOR >= 5))) || defined(__clang__)
+#    define ZSTD_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated(message)))
+#  elif defined(__GNUC__) && (__GNUC__ >= 3)
+#    define ZSTD_DEPRECATED(message) ZSTDLIB_API __attribute__((deprecated))
+#  elif defined(_MSC_VER)
+#    define ZSTD_DEPRECATED(message) ZSTDLIB_API __declspec(deprecated(message))
+#  else
+#    pragma message("WARNING: You need to implement ZSTD_DEPRECATED for this compiler")
+#    define ZSTD_DEPRECATED(message) ZSTDLIB_API
+#  endif
+#endif /* ZSTD_DISABLE_DEPRECATE_WARNINGS */
+
 /****************************************************************************************
  *   experimental API (static linking only)
  ****************************************************************************************
@@ -1100,21 +1166,40 @@
 typedef struct ZSTD_CCtx_params_s ZSTD_CCtx_params;
 
 typedef struct {
-    unsigned int matchPos; /* Match pos in dst */
-    /* If seqDef.offset > 3, then this is seqDef.offset - 3
-     * If seqDef.offset < 3, then this is the corresponding repeat offset
-     * But if seqDef.offset < 3 and litLength == 0, this is the
-     *   repeat offset before the corresponding repeat offset
-     * And if seqDef.offset == 3 and litLength == 0, this is the
-     *   most recent repeat offset - 1
-     */
-    unsigned int offset;
-    unsigned int litLength; /* Literal length */
-    unsigned int matchLength; /* Match length */
-    /* 0 when seq not rep and seqDef.offset otherwise
-     * when litLength == 0 this will be <= 4, otherwise <= 3 like normal
-     */
-    unsigned int rep;
+    unsigned int offset;      /* The offset of the match. (NOT the same as the offset code)
+                               * If offset == 0 and matchLength == 0, this sequence represents the last
+                               * literals in the block of litLength size.
+                               */
+
+    unsigned int litLength;   /* Literal length of the sequence. */
+    unsigned int matchLength; /* Match length of the sequence. */
+
+                              /* Note: Users of this API may provide a sequence with matchLength == litLength == offset == 0.
+                               * In this case, we will treat the sequence as a marker for a block boundary.
+                               */
+
+    unsigned int rep;         /* Represents which repeat offset is represented by the field 'offset'.
+                               * Ranges from [0, 3].
+                               *
+                               * Repeat offsets are essentially previous offsets from previous sequences sorted in
+                               * recency order. For more detail, see doc/zstd_compression_format.md
+                               *
+                               * If rep == 0, then 'offset' does not contain a repeat offset.
+                               * If rep > 0:
+                               *  If litLength != 0:
+                               *      rep == 1 --> offset == repeat_offset_1
+                               *      rep == 2 --> offset == repeat_offset_2
+                               *      rep == 3 --> offset == repeat_offset_3
+                               *  If litLength == 0:
+                               *      rep == 1 --> offset == repeat_offset_2
+                               *      rep == 2 --> offset == repeat_offset_3
+                               *      rep == 3 --> offset == repeat_offset_1 - 1
+                               *
+                               * Note: This field is optional. ZSTD_generateSequences() will calculate the value of
+                               * 'rep', but repeat offsets do not necessarily need to be calculated from an external
+                               * sequence provider's perspective. For example, ZSTD_compressSequences() does not
+                               * use this 'rep' field at all (as of now).
+                               */
 } ZSTD_Sequence;
 
 typedef struct {
@@ -1157,6 +1242,18 @@
 } ZSTD_format_e;
 
 typedef enum {
+    /* Note: this enum controls ZSTD_d_forceIgnoreChecksum */
+    ZSTD_d_validateChecksum = 0,
+    ZSTD_d_ignoreChecksum = 1
+} ZSTD_forceIgnoreChecksum_e;
+
+typedef enum {
+    /* Note: this enum controls ZSTD_d_refMultipleDDicts */
+    ZSTD_rmd_refSingleDDict = 0,
+    ZSTD_rmd_refMultipleDDicts = 1
+} ZSTD_refMultipleDDicts_e;
+
+typedef enum {
     /* Note: this enum and the behavior it controls are effectively internal
      * implementation details of the compressor. They are expected to continue
      * to evolve and should be considered only in the context of extremely
@@ -1204,6 +1301,11 @@
   ZSTD_lcm_uncompressed = 2   /**< Always emit uncompressed literals. */
 } ZSTD_literalCompressionMode_e;
 
+typedef enum {
+  ZSTD_urm_auto = 0,                   /* Automatically determine whether or not we use row matchfinder */
+  ZSTD_urm_disableRowMatchFinder = 1,  /* Never use row matchfinder */
+  ZSTD_urm_enableRowMatchFinder = 2    /* Always use row matchfinder when applicable */
+} ZSTD_useRowMatchFinderMode_e;
 
 /***************************************
 *  Frame size functions
@@ -1237,7 +1339,7 @@
  *  `srcSize` must be the _exact_ size of this series
  *       (i.e. there should be a frame boundary at `src + srcSize`)
  *  @return : - upper-bound for the decompressed size of all data in all successive frames
- *            - if an error occured: ZSTD_CONTENTSIZE_ERROR
+ *            - if an error occurred: ZSTD_CONTENTSIZE_ERROR
  *
  *  note 1  : an error can occur if `src` contains an invalid or incorrectly formatted frame.
  *  note 2  : the upper-bound is exact when the decompressed size field is available in every ZSTD encoded frame of `src`.
@@ -1253,14 +1355,91 @@
  *           or an error code (if srcSize is too small) */
 ZSTDLIB_API size_t ZSTD_frameHeaderSize(const void* src, size_t srcSize);
 
-/*! ZSTD_getSequences() :
- * Extract sequences from the sequence store
+typedef enum {
+  ZSTD_sf_noBlockDelimiters = 0,         /* Representation of ZSTD_Sequence has no block delimiters, sequences only */
+  ZSTD_sf_explicitBlockDelimiters = 1    /* Representation of ZSTD_Sequence contains explicit block delimiters */
+} ZSTD_sequenceFormat_e;
+
+/*! ZSTD_generateSequences() :
+ * Generate sequences using ZSTD_compress2, given a source buffer.
+ *
+ * Each block will end with a dummy sequence
+ * with offset == 0, matchLength == 0, and litLength == length of last literals.
+ * litLength may be == 0, and if so, then the sequence of (of: 0 ml: 0 ll: 0)
+ * simply acts as a block delimiter.
+ *
  * zc can be used to insert custom compression params.
  * This function invokes ZSTD_compress2
- * @return : number of sequences extracted
+ *
+ * The output of this function can be fed into ZSTD_compressSequences() with CCtx
+ * setting of ZSTD_c_blockDelimiters as ZSTD_sf_explicitBlockDelimiters
+ * @return : number of sequences generated
  */
-ZSTDLIB_API size_t ZSTD_getSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
-    size_t outSeqsSize, const void* src, size_t srcSize);
+
+ZSTDLIB_API size_t ZSTD_generateSequences(ZSTD_CCtx* zc, ZSTD_Sequence* outSeqs,
+                                          size_t outSeqsSize, const void* src, size_t srcSize);
+
+/*! ZSTD_mergeBlockDelimiters() :
+ * Given an array of ZSTD_Sequence, remove all sequences that represent block delimiters/last literals
+ * by merging them into into the literals of the next sequence.
+ *
+ * As such, the final generated result has no explicit representation of block boundaries,
+ * and the final last literals segment is not represented in the sequences.
+ *
+ * The output of this function can be fed into ZSTD_compressSequences() with CCtx
+ * setting of ZSTD_c_blockDelimiters as ZSTD_sf_noBlockDelimiters
+ * @return : number of sequences left after merging
+ */
+ZSTDLIB_API size_t ZSTD_mergeBlockDelimiters(ZSTD_Sequence* sequences, size_t seqsSize);
+
+/*! ZSTD_compressSequences() :
+ * Compress an array of ZSTD_Sequence, generated from the original source buffer, into dst.
+ * If a dictionary is included, then the cctx should reference the dict. (see: ZSTD_CCtx_refCDict(), ZSTD_CCtx_loadDictionary(), etc.)
+ * The entire source is compressed into a single frame.
+ *
+ * The compression behavior changes based on cctx params. In particular:
+ *    If ZSTD_c_blockDelimiters == ZSTD_sf_noBlockDelimiters, the array of ZSTD_Sequence is expected to contain
+ *    no block delimiters (defined in ZSTD_Sequence). Block boundaries are roughly determined based on
+ *    the block size derived from the cctx, and sequences may be split. This is the default setting.
+ *
+ *    If ZSTD_c_blockDelimiters == ZSTD_sf_explicitBlockDelimiters, the array of ZSTD_Sequence is expected to contain
+ *    block delimiters (defined in ZSTD_Sequence). Behavior is undefined if no block delimiters are provided.
+ *
+ *    If ZSTD_c_validateSequences == 0, this function will blindly accept the sequences provided. Invalid sequences cause undefined
+ *    behavior. If ZSTD_c_validateSequences == 1, then if sequence is invalid (see doc/zstd_compression_format.md for
+ *    specifics regarding offset/matchlength requirements) then the function will bail out and return an error.
+ *
+ *    In addition to the two adjustable experimental params, there are other important cctx params.
+ *    - ZSTD_c_minMatch MUST be set as less than or equal to the smallest match generated by the match finder. It has a minimum value of ZSTD_MINMATCH_MIN.
+ *    - ZSTD_c_compressionLevel accordingly adjusts the strength of the entropy coder, as it would in typical compression.
+ *    - ZSTD_c_windowLog affects offset validation: this function will return an error at higher debug levels if a provided offset
+ *      is larger than what the spec allows for a given window log and dictionary (if present). See: doc/zstd_compression_format.md
+ *
+ * Note: Repcodes are, as of now, always re-calculated within this function, so ZSTD_Sequence::rep is unused.
+ * Note 2: Once we integrate ability to ingest repcodes, the explicit block delims mode must respect those repcodes exactly,
+ *         and cannot emit an RLE block that disagrees with the repcode history
+ * @return : final compressed size or a ZSTD error.
+ */
+ZSTDLIB_API size_t ZSTD_compressSequences(ZSTD_CCtx* const cctx, void* dst, size_t dstSize,
+                                  const ZSTD_Sequence* inSeqs, size_t inSeqsSize,
+                                  const void* src, size_t srcSize);
+
+
+/*! ZSTD_writeSkippableFrame() :
+ * Generates a zstd skippable frame containing data given by src, and writes it to dst buffer.
+ *
+ * Skippable frames begin with a a 4-byte magic number. There are 16 possible choices of magic number,
+ * ranging from ZSTD_MAGIC_SKIPPABLE_START to ZSTD_MAGIC_SKIPPABLE_START+15.
+ * As such, the parameter magicVariant controls the exact skippable frame magic number variant used, so
+ * the magic number used will be ZSTD_MAGIC_SKIPPABLE_START + magicVariant.
+ *
+ * Returns an error if destination buffer is not large enough, if the source size is not representable
+ * with a 4-byte unsigned int, or if the parameter magicVariant is greater than 15 (and therefore invalid).
+ *
+ * @return : number of bytes written or a ZSTD error.
+ */
+ZSTDLIB_API size_t ZSTD_writeSkippableFrame(void* dst, size_t dstCapacity,
+                                            const void* src, size_t srcSize, unsigned magicVariant);
 
 
 /***************************************
@@ -1372,7 +1551,11 @@
 typedef void* (*ZSTD_allocFunction) (void* opaque, size_t size);
 typedef void  (*ZSTD_freeFunction) (void* opaque, void* address);
 typedef struct { ZSTD_allocFunction customAlloc; ZSTD_freeFunction customFree; void* opaque; } ZSTD_customMem;
-static ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */
+static
+#ifdef __GNUC__
+__attribute__((__unused__))
+#endif
+ZSTD_customMem const ZSTD_defaultCMem = { NULL, NULL, NULL };  /**< this constant defers to stdlib's functions */
 
 ZSTDLIB_API ZSTD_CCtx*    ZSTD_createCCtx_advanced(ZSTD_customMem customMem);
 ZSTDLIB_API ZSTD_CStream* ZSTD_createCStream_advanced(ZSTD_customMem customMem);
@@ -1385,12 +1568,38 @@
                                                   ZSTD_compressionParameters cParams,
                                                   ZSTD_customMem customMem);
 
-ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(const void* dict, size_t dictSize,
-                                                  ZSTD_dictLoadMethod_e dictLoadMethod,
-                                                  ZSTD_dictContentType_e dictContentType,
-                                                  ZSTD_customMem customMem);
+/* ! Thread pool :
+ * These prototypes make it possible to share a thread pool among multiple compression contexts.
+ * This can limit resources for applications with multiple threads where each one uses
+ * a threaded compression mode (via ZSTD_c_nbWorkers parameter).
+ * ZSTD_createThreadPool creates a new thread pool with a given number of threads.
+ * Note that the lifetime of such pool must exist while being used.
+ * ZSTD_CCtx_refThreadPool assigns a thread pool to a context (use NULL argument value
+ * to use an internal thread pool).
+ * ZSTD_freeThreadPool frees a thread pool, accepts NULL pointer.
+ */
+typedef struct POOL_ctx_s ZSTD_threadPool;
+ZSTDLIB_API ZSTD_threadPool* ZSTD_createThreadPool(size_t numThreads);
+ZSTDLIB_API void ZSTD_freeThreadPool (ZSTD_threadPool* pool);  /* accept NULL pointer */
+ZSTDLIB_API size_t ZSTD_CCtx_refThreadPool(ZSTD_CCtx* cctx, ZSTD_threadPool* pool);
 
 
+/*
+ * This API is temporary and is expected to change or disappear in the future!
+ */
+ZSTDLIB_API ZSTD_CDict* ZSTD_createCDict_advanced2(
+    const void* dict, size_t dictSize,
+    ZSTD_dictLoadMethod_e dictLoadMethod,
+    ZSTD_dictContentType_e dictContentType,
+    const ZSTD_CCtx_params* cctxParams,
+    ZSTD_customMem customMem);
+
+ZSTDLIB_API ZSTD_DDict* ZSTD_createDDict_advanced(
+    const void* dict, size_t dictSize,
+    ZSTD_dictLoadMethod_e dictLoadMethod,
+    ZSTD_dictContentType_e dictContentType,
+    ZSTD_customMem customMem);
+
 
 /***************************************
 *  Advanced compression functions
@@ -1430,18 +1639,20 @@
 /*! ZSTD_compress_advanced() :
  *  Note : this function is now DEPRECATED.
  *         It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_setParameter() and other parameter setters.
- *  This prototype will be marked as deprecated and generate compilation warning on reaching v1.5.x */
-ZSTDLIB_API size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
+ *  This prototype will generate compilation warnings. */
+ZSTD_DEPRECATED("use ZSTD_compress2")
+size_t ZSTD_compress_advanced(ZSTD_CCtx* cctx,
                                           void* dst, size_t dstCapacity,
                                     const void* src, size_t srcSize,
                                     const void* dict,size_t dictSize,
                                           ZSTD_parameters params);
 
 /*! ZSTD_compress_usingCDict_advanced() :
- *  Note : this function is now REDUNDANT.
+ *  Note : this function is now DEPRECATED.
  *         It can be replaced by ZSTD_compress2(), in combination with ZSTD_CCtx_loadDictionary() and other parameter setters.
- *  This prototype will be marked as deprecated and generate compilation warning in some future version */
-ZSTDLIB_API size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
+ *  This prototype will generate compilation warnings. */
+ZSTD_DEPRECATED("use ZSTD_compress2 with ZSTD_CCtx_loadDictionary")
+size_t ZSTD_compress_usingCDict_advanced(ZSTD_CCtx* cctx,
                                               void* dst, size_t dstCapacity,
                                         const void* src, size_t srcSize,
                                         const ZSTD_CDict* cdict,
@@ -1503,7 +1714,7 @@
 
 /* Controls how the literals are compressed (default is auto).
  * The value must be of type ZSTD_literalCompressionMode_e.
- * See ZSTD_literalCompressionMode_t enum definition for details.
+ * See ZSTD_literalCompressionMode_e enum definition for details.
  */
 #define ZSTD_c_literalCompressionMode ZSTD_c_experimentalParam5
 
@@ -1518,12 +1729,189 @@
  * but compression ratio may regress significantly if guess considerably underestimates */
 #define ZSTD_c_srcSizeHint ZSTD_c_experimentalParam7
 
+/* Controls whether the new and experimental "dedicated dictionary search
+ * structure" can be used. This feature is still rough around the edges, be
+ * prepared for surprising behavior!
+ *
+ * How to use it:
+ *
+ * When using a CDict, whether to use this feature or not is controlled at
+ * CDict creation, and it must be set in a CCtxParams set passed into that
+ * construction (via ZSTD_createCDict_advanced2()). A compression will then
+ * use the feature or not based on how the CDict was constructed; the value of
+ * this param, set in the CCtx, will have no effect.
+ *
+ * However, when a dictionary buffer is passed into a CCtx, such as via
+ * ZSTD_CCtx_loadDictionary(), this param can be set on the CCtx to control
+ * whether the CDict that is created internally can use the feature or not.
+ *
+ * What it does:
+ *
+ * Normally, the internal data structures of the CDict are analogous to what
+ * would be stored in a CCtx after compressing the contents of a dictionary.
+ * To an approximation, a compression using a dictionary can then use those
+ * data structures to simply continue what is effectively a streaming
+ * compression where the simulated compression of the dictionary left off.
+ * Which is to say, the search structures in the CDict are normally the same
+ * format as in the CCtx.
+ *
+ * It is possible to do better, since the CDict is not like a CCtx: the search
+ * structures are written once during CDict creation, and then are only read
+ * after that, while the search structures in the CCtx are both read and
+ * written as the compression goes along. This means we can choose a search
+ * structure for the dictionary that is read-optimized.
+ *
+ * This feature enables the use of that different structure.
+ *
+ * Note that some of the members of the ZSTD_compressionParameters struct have
+ * different semantics and constraints in the dedicated search structure. It is
+ * highly recommended that you simply set a compression level in the CCtxParams
+ * you pass into the CDict creation call, and avoid messing with the cParams
+ * directly.
+ *
+ * Effects:
+ *
+ * This will only have any effect when the selected ZSTD_strategy
+ * implementation supports this feature. Currently, that's limited to
+ * ZSTD_greedy, ZSTD_lazy, and ZSTD_lazy2.
+ *
+ * Note that this means that the CDict tables can no longer be copied into the
+ * CCtx, so the dict attachment mode ZSTD_dictForceCopy will no longer be
+ * useable. The dictionary can only be attached or reloaded.
+ *
+ * In general, you should expect compression to be faster--sometimes very much
+ * so--and CDict creation to be slightly slower. Eventually, we will probably
+ * make this mode the default.
+ */
+#define ZSTD_c_enableDedicatedDictSearch ZSTD_c_experimentalParam8
+
+/* ZSTD_c_stableInBuffer
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Tells the compressor that the ZSTD_inBuffer will ALWAYS be the same
+ * between calls, except for the modifications that zstd makes to pos (the
+ * caller must not modify pos). This is checked by the compressor, and
+ * compression will fail if it ever changes. This means the only flush
+ * mode that makes sense is ZSTD_e_end, so zstd will error if ZSTD_e_end
+ * is not used. The data in the ZSTD_inBuffer in the range [src, src + pos)
+ * MUST not be modified during compression or you will get data corruption.
+ *
+ * When this flag is enabled zstd won't allocate an input window buffer,
+ * because the user guarantees it can reference the ZSTD_inBuffer until
+ * the frame is complete. But, it will still allocate an output buffer
+ * large enough to fit a block (see ZSTD_c_stableOutBuffer). This will also
+ * avoid the memcpy() from the input buffer to the input window buffer.
+ *
+ * NOTE: ZSTD_compressStream2() will error if ZSTD_e_end is not used.
+ * That means this flag cannot be used with ZSTD_compressStream().
+ *
+ * NOTE: So long as the ZSTD_inBuffer always points to valid memory, using
+ * this flag is ALWAYS memory safe, and will never access out-of-bounds
+ * memory. However, compression WILL fail if you violate the preconditions.
+ *
+ * WARNING: The data in the ZSTD_inBuffer in the range [dst, dst + pos) MUST
+ * not be modified during compression or you will get data corruption. This
+ * is because zstd needs to reference data in the ZSTD_inBuffer to find
+ * matches. Normally zstd maintains its own window buffer for this purpose,
+ * but passing this flag tells zstd to use the user provided buffer.
+ */
+#define ZSTD_c_stableInBuffer ZSTD_c_experimentalParam9
+
+/* ZSTD_c_stableOutBuffer
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Tells he compressor that the ZSTD_outBuffer will not be resized between
+ * calls. Specifically: (out.size - out.pos) will never grow. This gives the
+ * compressor the freedom to say: If the compressed data doesn't fit in the
+ * output buffer then return ZSTD_error_dstSizeTooSmall. This allows us to
+ * always decompress directly into the output buffer, instead of decompressing
+ * into an internal buffer and copying to the output buffer.
+ *
+ * When this flag is enabled zstd won't allocate an output buffer, because
+ * it can write directly to the ZSTD_outBuffer. It will still allocate the
+ * input window buffer (see ZSTD_c_stableInBuffer).
+ *
+ * Zstd will check that (out.size - out.pos) never grows and return an error
+ * if it does. While not strictly necessary, this should prevent surprises.
+ */
+#define ZSTD_c_stableOutBuffer ZSTD_c_experimentalParam10
+
+/* ZSTD_c_blockDelimiters
+ * Default is 0 == ZSTD_sf_noBlockDelimiters.
+ *
+ * For use with sequence compression API: ZSTD_compressSequences().
+ *
+ * Designates whether or not the given array of ZSTD_Sequence contains block delimiters
+ * and last literals, which are defined as sequences with offset == 0 and matchLength == 0.
+ * See the definition of ZSTD_Sequence for more specifics.
+ */
+#define ZSTD_c_blockDelimiters ZSTD_c_experimentalParam11
+
+/* ZSTD_c_validateSequences
+ * Default is 0 == disabled. Set to 1 to enable sequence validation.
+ *
+ * For use with sequence compression API: ZSTD_compressSequences().
+ * Designates whether or not we validate sequences provided to ZSTD_compressSequences()
+ * during function execution.
+ *
+ * Without validation, providing a sequence that does not conform to the zstd spec will cause
+ * undefined behavior, and may produce a corrupted block.
+ *
+ * With validation enabled, a if sequence is invalid (see doc/zstd_compression_format.md for
+ * specifics regarding offset/matchlength requirements) then the function will bail out and
+ * return an error.
+ *
+ */
+#define ZSTD_c_validateSequences ZSTD_c_experimentalParam12
+
+/* ZSTD_c_splitBlocks
+ * Default is 0 == disabled. Set to 1 to enable block splitting.
+ *
+ * Will attempt to split blocks in order to improve compression ratio at the cost of speed.
+ */
+#define ZSTD_c_splitBlocks ZSTD_c_experimentalParam13
+
+/* ZSTD_c_useRowMatchFinder
+ * Default is ZSTD_urm_auto.
+ * Controlled with ZSTD_useRowMatchFinderMode_e enum.
+ *
+ * By default, in ZSTD_urm_auto, when finalizing the compression parameters, the library
+ * will decide at runtime whether to use the row-based matchfinder based on support for SIMD
+ * instructions as well as the windowLog.
+ *
+ * Set to ZSTD_urm_disableRowMatchFinder to never use row-based matchfinder.
+ * Set to ZSTD_urm_enableRowMatchFinder to force usage of row-based matchfinder.
+ */
+#define ZSTD_c_useRowMatchFinder ZSTD_c_experimentalParam14
+
+/* ZSTD_c_deterministicRefPrefix
+ * Default is 0 == disabled. Set to 1 to enable.
+ *
+ * Zstd produces different results for prefix compression when the prefix is
+ * directly adjacent to the data about to be compressed vs. when it isn't.
+ * This is because zstd detects that the two buffers are contiguous and it can
+ * use a more efficient match finding algorithm. However, this produces different
+ * results than when the two buffers are non-contiguous. This flag forces zstd
+ * to always load the prefix in non-contiguous mode, even if it happens to be
+ * adjacent to the data, to guarantee determinism.
+ *
+ * If you really care about determinism when using a dictionary or prefix,
+ * like when doing delta compression, you should select this option. It comes
+ * at a speed penalty of about ~2.5% if the dictionary and data happened to be
+ * contiguous, and is free if they weren't contiguous. We don't expect that
+ * intentionally making the dictionary and data contiguous will be worth the
+ * cost to memcpy() the data.
+ */
+#define ZSTD_c_deterministicRefPrefix ZSTD_c_experimentalParam15
+
 /*! ZSTD_CCtx_getParameter() :
  *  Get the requested compression parameter value, selected by enum ZSTD_cParameter,
  *  and store it into int* value.
  * @return : 0, or an error code (which can be tested with ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_CCtx_getParameter(ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
+ZSTDLIB_API size_t ZSTD_CCtx_getParameter(const ZSTD_CCtx* cctx, ZSTD_cParameter param, int* value);
 
 
 /*! ZSTD_CCtx_params :
@@ -1538,13 +1926,13 @@
  *                                    These parameters will be applied to
  *                                    all subsequent frames.
  *  - ZSTD_compressStream2() : Do compression using the CCtx.
- *  - ZSTD_freeCCtxParams() : Free the memory.
+ *  - ZSTD_freeCCtxParams() : Free the memory, accept NULL pointer.
  *
  *  This can be used with ZSTD_estimateCCtxSize_advanced_usingCCtxParams()
  *  for static allocation of CCtx for single-threaded compression.
  */
 ZSTDLIB_API ZSTD_CCtx_params* ZSTD_createCCtxParams(void);
-ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);
+ZSTDLIB_API size_t ZSTD_freeCCtxParams(ZSTD_CCtx_params* params);  /* accept NULL pointer */
 
 /*! ZSTD_CCtxParams_reset() :
  *  Reset params to default values.
@@ -1563,11 +1951,13 @@
  */
 ZSTDLIB_API size_t ZSTD_CCtxParams_init_advanced(ZSTD_CCtx_params* cctxParams, ZSTD_parameters params);
 
-/*! ZSTD_CCtxParams_setParameter() :
+/*! ZSTD_CCtxParams_setParameter() : Requires v1.4.0+
  *  Similar to ZSTD_CCtx_setParameter.
  *  Set one compression parameter, selected by enum ZSTD_cParameter.
- *  Parameters must be applied to a ZSTD_CCtx using ZSTD_CCtx_setParametersUsingCCtxParams().
- * @result : 0, or an error code (which can be tested with ZSTD_isError()).
+ *  Parameters must be applied to a ZSTD_CCtx using
+ *  ZSTD_CCtx_setParametersUsingCCtxParams().
+ * @result : a code representing success or failure (which can be tested with
+ *           ZSTD_isError()).
  */
 ZSTDLIB_API size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int value);
 
@@ -1576,7 +1966,7 @@
  * Get the requested value of one compression parameter, selected by enum ZSTD_cParameter.
  * @result : 0, or an error code (which can be tested with ZSTD_isError()).
  */
-ZSTDLIB_API size_t ZSTD_CCtxParams_getParameter(ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
+ZSTDLIB_API size_t ZSTD_CCtxParams_getParameter(const ZSTD_CCtx_params* params, ZSTD_cParameter param, int* value);
 
 /*! ZSTD_CCtx_setParametersUsingCCtxParams() :
  *  Apply a set of ZSTD_CCtx_params to the compression context.
@@ -1647,6 +2037,13 @@
  */
 ZSTDLIB_API size_t ZSTD_DCtx_setMaxWindowSize(ZSTD_DCtx* dctx, size_t maxWindowSize);
 
+/*! ZSTD_DCtx_getParameter() :
+ *  Get the requested decompression parameter value, selected by enum ZSTD_dParameter,
+ *  and store it into int* value.
+ * @return : 0, or an error code (which can be tested with ZSTD_isError()).
+ */
+ZSTDLIB_API size_t ZSTD_DCtx_getParameter(ZSTD_DCtx* dctx, ZSTD_dParameter param, int* value);
+
 /* ZSTD_d_format
  * experimental parameter,
  * allowing selection between ZSTD_format_e input compression formats
@@ -1684,12 +2081,49 @@
  */
 #define ZSTD_d_stableOutBuffer ZSTD_d_experimentalParam2
 
+/* ZSTD_d_forceIgnoreChecksum
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable
+ *
+ * Tells the decompressor to skip checksum validation during decompression, regardless
+ * of whether checksumming was specified during compression. This offers some
+ * slight performance benefits, and may be useful for debugging.
+ * Param has values of type ZSTD_forceIgnoreChecksum_e
+ */
+#define ZSTD_d_forceIgnoreChecksum ZSTD_d_experimentalParam3
+
+/* ZSTD_d_refMultipleDDicts
+ * Experimental parameter.
+ * Default is 0 == disabled. Set to 1 to enable
+ *
+ * If enabled and dctx is allocated on the heap, then additional memory will be allocated
+ * to store references to multiple ZSTD_DDict. That is, multiple calls of ZSTD_refDDict()
+ * using a given ZSTD_DCtx, rather than overwriting the previous DDict reference, will instead
+ * store all references. At decompression time, the appropriate dictID is selected
+ * from the set of DDicts based on the dictID in the frame.
+ *
+ * Usage is simply calling ZSTD_refDDict() on multiple dict buffers.
+ *
+ * Param has values of byte ZSTD_refMultipleDDicts_e
+ *
+ * WARNING: Enabling this parameter and calling ZSTD_DCtx_refDDict(), will trigger memory
+ * allocation for the hash table. ZSTD_freeDCtx() also frees this memory.
+ * Memory is allocated as per ZSTD_DCtx::customMem.
+ *
+ * Although this function allocates memory for the table, the user is still responsible for
+ * memory management of the underlying ZSTD_DDict* themselves.
+ */
+#define ZSTD_d_refMultipleDDicts ZSTD_d_experimentalParam4
+
+
 /*! ZSTD_DCtx_setFormat() :
+ *  This function is REDUNDANT. Prefer ZSTD_DCtx_setParameter().
  *  Instruct the decoder context about what kind of data to decode next.
  *  This instruction is mandatory to decode data without a fully-formed header,
  *  such ZSTD_f_zstd1_magicless for example.
  * @return : 0, or an error code (which can be tested using ZSTD_isError()). */
-ZSTDLIB_API size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
+ZSTD_DEPRECATED("use ZSTD_DCtx_setParameter() instead")
+size_t ZSTD_DCtx_setFormat(ZSTD_DCtx* dctx, ZSTD_format_e format);
 
 /*! ZSTD_decompressStream_simpleArgs() :
  *  Same as ZSTD_decompressStream(),
@@ -1711,8 +2145,9 @@
 ********************************************************************/
 
 /*=====   Advanced Streaming compression functions  =====*/
-/**! ZSTD_initCStream_srcSize() :
- * This function is deprecated, and equivalent to:
+
+/*! ZSTD_initCStream_srcSize() :
+ * This function is DEPRECATED, and equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     ZSTD_CCtx_refCDict(zcs, NULL); // clear the dictionary (if any)
  *     ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
@@ -1721,15 +2156,15 @@
  * pledgedSrcSize must be correct. If it is not known at init time, use
  * ZSTD_CONTENTSIZE_UNKNOWN. Note that, for compatibility with older programs,
  * "0" also disables frame content size field. It may be enabled in the future.
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
+ * This prototype will generate compilation warnings.
  */
-ZSTDLIB_API size_t
-ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+size_t ZSTD_initCStream_srcSize(ZSTD_CStream* zcs,
                          int compressionLevel,
                          unsigned long long pledgedSrcSize);
 
-/**! ZSTD_initCStream_usingDict() :
- * This function is deprecated, and is equivalent to:
+/*! ZSTD_initCStream_usingDict() :
+ * This function is DEPRECATED, and is equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     ZSTD_CCtx_setParameter(zcs, ZSTD_c_compressionLevel, compressionLevel);
  *     ZSTD_CCtx_loadDictionary(zcs, dict, dictSize);
@@ -1738,15 +2173,15 @@
  * dict == NULL or dictSize < 8, in which case no dict is used.
  * Note: dict is loaded with ZSTD_dct_auto (treated as a full zstd dictionary if
  * it begins with ZSTD_MAGIC_DICTIONARY, else as raw content) and ZSTD_dlm_byCopy.
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
+ * This prototype will generate compilation warnings.
  */
-ZSTDLIB_API size_t
-ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+size_t ZSTD_initCStream_usingDict(ZSTD_CStream* zcs,
                      const void* dict, size_t dictSize,
                            int compressionLevel);
 
-/**! ZSTD_initCStream_advanced() :
- * This function is deprecated, and is approximately equivalent to:
+/*! ZSTD_initCStream_advanced() :
+ * This function is DEPRECATED, and is approximately equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     // Pseudocode: Set each zstd parameter and leave the rest as-is.
  *     for ((param, value) : params) {
@@ -1758,25 +2193,26 @@
  * dict is loaded with ZSTD_dct_auto and ZSTD_dlm_byCopy.
  * pledgedSrcSize must be correct.
  * If srcSize is not known at init time, use value ZSTD_CONTENTSIZE_UNKNOWN.
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
+ * This prototype will generate compilation warnings.
  */
-ZSTDLIB_API size_t
-ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+size_t ZSTD_initCStream_advanced(ZSTD_CStream* zcs,
                     const void* dict, size_t dictSize,
                           ZSTD_parameters params,
                           unsigned long long pledgedSrcSize);
 
-/**! ZSTD_initCStream_usingCDict() :
- * This function is deprecated, and equivalent to:
+/*! ZSTD_initCStream_usingCDict() :
+ * This function is DEPRECATED, and equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     ZSTD_CCtx_refCDict(zcs, cdict);
- *
+ * 
  * note : cdict will just be referenced, and must outlive compression session
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
+ * This prototype will generate compilation warnings.
  */
-ZSTDLIB_API size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+size_t ZSTD_initCStream_usingCDict(ZSTD_CStream* zcs, const ZSTD_CDict* cdict);
 
-/**! ZSTD_initCStream_usingCDict_advanced() :
+/*! ZSTD_initCStream_usingCDict_advanced() :
  *   This function is DEPRECATED, and is approximately equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     // Pseudocode: Set each zstd frame parameter and leave the rest as-is.
@@ -1789,18 +2225,21 @@
  * same as ZSTD_initCStream_usingCDict(), with control over frame parameters.
  * pledgedSrcSize must be correct. If srcSize is not known at init time, use
  * value ZSTD_CONTENTSIZE_UNKNOWN.
- * Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
+ * This prototype will generate compilation warnings.
  */
-ZSTDLIB_API size_t
-ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset and ZSTD_CCtx_refCDict, see zstd.h for detailed instructions")
+size_t ZSTD_initCStream_usingCDict_advanced(ZSTD_CStream* zcs,
                                const ZSTD_CDict* cdict,
                                      ZSTD_frameParameters fParams,
                                      unsigned long long pledgedSrcSize);
 
 /*! ZSTD_resetCStream() :
- * This function is deprecated, and is equivalent to:
+ * This function is DEPRECATED, and is equivalent to:
  *     ZSTD_CCtx_reset(zcs, ZSTD_reset_session_only);
  *     ZSTD_CCtx_setPledgedSrcSize(zcs, pledgedSrcSize);
+ * Note: ZSTD_resetCStream() interprets pledgedSrcSize == 0 as ZSTD_CONTENTSIZE_UNKNOWN, but
+ *       ZSTD_CCtx_setPledgedSrcSize() does not do the same, so ZSTD_CONTENTSIZE_UNKNOWN must be
+ *       explicitly specified.
  *
  *  start a new frame, using same parameters from previous frame.
  *  This is typically useful to skip dictionary loading stage, since it will re-use it in-place.
@@ -1810,9 +2249,10 @@
  *  For the time being, pledgedSrcSize==0 is interpreted as "srcSize unknown" for compatibility with older programs,
  *  but it will change to mean "empty" in future version, so use macro ZSTD_CONTENTSIZE_UNKNOWN instead.
  * @return : 0, or an error code (which can be tested using ZSTD_isError())
- *  Note : this prototype will be marked as deprecated and generate compilation warnings on reaching v1.5.x
+ *  This prototype will generate compilation warnings.
  */
-ZSTDLIB_API size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
+ZSTD_DEPRECATED("use ZSTD_CCtx_reset, see zstd.h for detailed instructions")
+size_t ZSTD_resetCStream(ZSTD_CStream* zcs, unsigned long long pledgedSrcSize);
 
 
 typedef struct {
@@ -1849,7 +2289,8 @@
 
 
 /*=====   Advanced Streaming decompression functions  =====*/
-/**
+
+/*!
  * This function is deprecated, and is equivalent to:
  *
  *     ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
@@ -1860,7 +2301,7 @@
  */
 ZSTDLIB_API size_t ZSTD_initDStream_usingDict(ZSTD_DStream* zds, const void* dict, size_t dictSize);
 
-/**
+/*!
  * This function is deprecated, and is equivalent to:
  *
  *     ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
@@ -1871,7 +2312,7 @@
  */
 ZSTDLIB_API size_t ZSTD_initDStream_usingDDict(ZSTD_DStream* zds, const ZSTD_DDict* ddict);
 
-/**
+/*!
  * This function is deprecated, and is equivalent to:
  *
  *     ZSTD_DCtx_reset(zds, ZSTD_reset_session_only);
@@ -1898,8 +2339,7 @@
   ZSTD_CCtx object can be re-used multiple times within successive compression operations.
 
   Start by initializing a context.
-  Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression,
-  or ZSTD_compressBegin_advanced(), for finer parameter control.
+  Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary compression.
   It's also possible to duplicate a reference context which has already been initialized, using ZSTD_copyCCtx()
 
   Then, consume your input using ZSTD_compressContinue().
@@ -1924,16 +2364,18 @@
 /*=====   Buffer-less streaming compression functions  =====*/
 ZSTDLIB_API size_t ZSTD_compressBegin(ZSTD_CCtx* cctx, int compressionLevel);
 ZSTDLIB_API size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, int compressionLevel);
-ZSTDLIB_API size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
 ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx* cctx, const ZSTD_CDict* cdict); /**< note: fails if cdict==NULL */
-ZSTDLIB_API size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
 ZSTDLIB_API size_t ZSTD_copyCCtx(ZSTD_CCtx* cctx, const ZSTD_CCtx* preparedCCtx, unsigned long long pledgedSrcSize); /**<  note: if pledgedSrcSize is not known, use ZSTD_CONTENTSIZE_UNKNOWN */
 
 ZSTDLIB_API size_t ZSTD_compressContinue(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
 ZSTDLIB_API size_t ZSTD_compressEnd(ZSTD_CCtx* cctx, void* dst, size_t dstCapacity, const void* src, size_t srcSize);
 
-
-/*-
+/* The ZSTD_compressBegin_advanced() and ZSTD_compressBegin_usingCDict_advanced() are now DEPRECATED and will generate a compiler warning */
+ZSTD_DEPRECATED("use advanced API to access custom parameters")
+size_t ZSTD_compressBegin_advanced(ZSTD_CCtx* cctx, const void* dict, size_t dictSize, ZSTD_parameters params, unsigned long long pledgedSrcSize); /**< pledgedSrcSize : If srcSize is not known at init time, use ZSTD_CONTENTSIZE_UNKNOWN */
+ZSTD_DEPRECATED("use advanced API to access custom parameters")
+size_t ZSTD_compressBegin_usingCDict_advanced(ZSTD_CCtx* const cctx, const ZSTD_CDict* const cdict, ZSTD_frameParameters const fParams, unsigned long long const pledgedSrcSize);   /* compression parameters are already set within cdict. pledgedSrcSize must be correct. If srcSize is not known, use macro ZSTD_CONTENTSIZE_UNKNOWN */
+/**
   Buffer-less streaming decompression (synchronous mode)
 
   A ZSTD_DCtx object is required to track streaming operations.
diff --git a/Utilities/cmzstd/lib/common/zstd_errors.h b/Utilities/cmzstd/lib/zstd_errors.h
similarity index 97%
rename from Utilities/cmzstd/lib/common/zstd_errors.h
rename to Utilities/cmzstd/lib/zstd_errors.h
index 998398e..fa3686b 100644
--- a/Utilities/cmzstd/lib/common/zstd_errors.h
+++ b/Utilities/cmzstd/lib/zstd_errors.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.
+ * Copyright (c) Yann Collet, Facebook, Inc.
  * All rights reserved.
  *
  * This source code is licensed under both the BSD-style license (found in the
@@ -77,6 +77,7 @@
   ZSTD_error_frameIndex_tooLarge = 100,
   ZSTD_error_seekableIO          = 102,
   ZSTD_error_dstBuffer_wrong     = 104,
+  ZSTD_error_srcBuffer_wrong     = 105,
   ZSTD_error_maxCode = 120  /* never EVER use this value directly, it can change in future versions! Use ZSTD_isError() instead */
 } ZSTD_ErrorCode;
 
diff --git a/bootstrap b/bootstrap
index c7fbed1..a487375 100755
--- a/bootstrap
+++ b/bootstrap
@@ -325,6 +325,7 @@
   cmDefinePropertyCommand \
   cmDefinitions \
   cmDocumentationFormatter \
+  cmELF \
   cmEnableLanguageCommand \
   cmEnableTestingCommand \
   cmExecProgramCommand \
@@ -426,6 +427,7 @@
   cmPolicies \
   cmProcessOutput \
   cmProjectCommand \
+  cmValue \
   cmPropertyDefinition \
   cmPropertyMap \
   cmGccDepfileLexerHelper \