summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2022-05-03 15:30:57 (GMT)
committerKitware Robot <kwrobot@kitware.com>2022-05-03 15:31:04 (GMT)
commite0dbca93aae6b01f8b239d346a0bc99d2ca2473e (patch)
tree3c3a5d15c907c72c6e9d3700601156ba31cab8fb /Source
parent252fdfe6e400f9316911c9fffca7e420258a892c (diff)
parent8d7e80cf3d39ae24a6c88ee07492b9cfe40defd5 (diff)
downloadCMake-e0dbca93aae6b01f8b239d346a0bc99d2ca2473e.zip
CMake-e0dbca93aae6b01f8b239d346a0bc99d2ca2473e.tar.gz
CMake-e0dbca93aae6b01f8b239d346a0bc99d2ca2473e.tar.bz2
Merge topic 'find_item-query-windows-registry'
8d7e80cf3d find_* commands: add control over Windows registry views 08941a9a40 cmWindowsRegistry: Add helper for conversion between string and enum View 769f25aa3c cmWindowsRegistry: enhance unicode conversions Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !7211
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt2
-rw-r--r--Source/cmCMakeHostSystemInformationCommand.cxx18
-rw-r--r--Source/cmFindBase.cxx15
-rw-r--r--Source/cmFindCommon.cxx12
-rw-r--r--Source/cmFindCommon.h2
-rw-r--r--Source/cmFindPackageCommand.cxx21
-rw-r--r--Source/cmFindPackageCommand.h1
-rw-r--r--Source/cmFindProgramCommand.cxx14
-rw-r--r--Source/cmPolicies.h4
-rw-r--r--Source/cmSearchPath.cxx30
-rw-r--r--Source/cmWindowsRegistry.cxx400
-rw-r--r--Source/cmWindowsRegistry.h34
12 files changed, 481 insertions, 72 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index c245f68..a988bf1 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -734,7 +734,7 @@ set(SRCS
bindexplib.cxx
)
-SET_PROPERTY(SOURCE cmProcessOutput.cxx APPEND PROPERTY COMPILE_DEFINITIONS
+SET_PROPERTY(SOURCE cmProcessOutput.cxx cmWindowsRegistry.cxx APPEND PROPERTY COMPILE_DEFINITIONS
KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE})
# Xcode only works on Apple
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 0c41c68..2a019b1 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -9,7 +9,6 @@
#include <map>
#include <string>
#include <type_traits>
-#include <unordered_map>
#include <utility>
#include <cm/optional>
@@ -469,14 +468,6 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
std::string const& variable)
{
using View = cmWindowsRegistry::View;
- static std::unordered_map<cm::string_view, cmWindowsRegistry::View>
- ViewDefinitions{
- { "BOTH"_s, View::Both }, { "HOST"_s, View::Host },
- { "TARGET"_s, View::Target }, { "32"_s, View::Reg32 },
- { "64"_s, View::Reg64 }, { "32_64"_s, View::Reg32_64 },
- { "64_32"_s, View::Reg64_32 }
- };
-
if (args.empty()) {
status.SetError("missing <key> specification.");
return false;
@@ -522,8 +513,8 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
"\"VALUE_NAMES\" or \"SUBKEYS\".");
return false;
}
- if (!arguments.View.empty() &&
- ViewDefinitions.find(arguments.View) == ViewDefinitions.end()) {
+
+ if (!arguments.View.empty() && !cmWindowsRegistry::ToView(arguments.View)) {
status.SetError(
cmStrCat("given invalid value for \"VIEW\": ", arguments.View, '.'));
return false;
@@ -533,8 +524,9 @@ bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
makefile.AddDefinition(variable, ""_s);
- auto view =
- arguments.View.empty() ? View::Both : ViewDefinitions[arguments.View];
+ auto view = arguments.View.empty()
+ ? View::Both
+ : *cmWindowsRegistry::ToView(arguments.View);
cmWindowsRegistry registry(makefile);
if (arguments.ValueNames) {
auto result = registry.GetValueNames(key, view);
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index a8db63d..702d9fe 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -7,6 +7,7 @@
#include <map>
#include <utility>
+#include <cm/optional>
#include <cmext/algorithm>
#include "cmCMakePath.h"
@@ -20,6 +21,7 @@
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
+#include "cmWindowsRegistry.h"
#include "cmake.h"
class cmExecutionStatus;
@@ -123,6 +125,19 @@ bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn)
doing = DoingNone;
this->Required = true;
newStyle = true;
+ } else if (args[j] == "REGISTRY_VIEW") {
+ if (++j == args.size()) {
+ this->SetError("missing required argument for \"REGISTRY_VIEW\"");
+ return false;
+ }
+ auto view = cmWindowsRegistry::ToView(args[j]);
+ if (view) {
+ this->RegistryView = *view;
+ } else {
+ this->SetError(
+ cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[j]));
+ return false;
+ }
} else if (this->CheckCommonArgument(args[j])) {
doing = DoingNone;
} else {
diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx
index 9fd712a..c3fb907 100644
--- a/Source/cmFindCommon.cxx
+++ b/Source/cmFindCommon.cxx
@@ -11,6 +11,7 @@
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
+#include "cmPolicies.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
@@ -58,6 +59,17 @@ cmFindCommon::cmFindCommon(cmExecutionStatus& status)
this->InitializeSearchPathGroups();
this->DebugMode = false;
+
+ // Windows Registry views
+ // When policy CMP0134 is not NEW, rely on previous behavior:
+ if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0134) !=
+ cmPolicies::NEW) {
+ if (this->Makefile->GetDefinition("CMAKE_SIZEOF_VOID_P") == "8") {
+ this->RegistryView = cmWindowsRegistry::View::Reg64;
+ } else {
+ this->RegistryView = cmWindowsRegistry::View::Reg32;
+ }
+ }
}
void cmFindCommon::SetError(std::string const& e)
diff --git a/Source/cmFindCommon.h b/Source/cmFindCommon.h
index 4c02df0..41de797 100644
--- a/Source/cmFindCommon.h
+++ b/Source/cmFindCommon.h
@@ -11,6 +11,7 @@
#include "cmPathLabel.h"
#include "cmSearchPath.h"
+#include "cmWindowsRegistry.h"
class cmExecutionStatus;
class cmMakefile;
@@ -131,6 +132,7 @@ protected:
bool NoSystemEnvironmentPath;
bool NoCMakeSystemPath;
bool NoCMakeInstallPath;
+ cmWindowsRegistry::View RegistryView = cmWindowsRegistry::View::Target;
std::vector<std::string> SearchPathSuffixes;
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 9a89935..e23ed0b 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -13,6 +13,7 @@
#include <utility>
#include <cm/memory>
+#include <cm/optional>
#include <cmext/string_view>
#include "cmsys/Directory.hxx"
@@ -33,6 +34,7 @@
#include "cmSystemTools.h"
#include "cmValue.h"
#include "cmVersion.h"
+#include "cmWindowsRegistry.h"
#if defined(__HAIKU__)
# include <FindDirectory.h>
@@ -317,6 +319,20 @@ bool cmFindPackageCommand::InitialPass(std::vector<std::string> const& args)
// Ignore legacy option.
configArgs.insert(i);
doing = DoingNone;
+ } else if (args[i] == "REGISTRY_VIEW") {
+ if (++i == args.size()) {
+ this->SetError("missing required argument for \"REGISTRY_VIEW\"");
+ return false;
+ }
+ auto view = cmWindowsRegistry::ToView(args[i]);
+ if (view) {
+ this->RegistryView = *view;
+ this->RegistryViewDefined = true;
+ } else {
+ this->SetError(
+ cmStrCat("given invalid value for \"REGISTRY_VIEW\": ", args[i]));
+ return false;
+ }
} else if (this->CheckCommonArgument(args[i])) {
configArgs.insert(i);
doing = DoingNone;
@@ -767,6 +783,11 @@ void cmFindPackageCommand::SetModuleVariables(const std::string& components)
id = cmStrCat(this->Name, "_FIND_VERSION_RANGE_MAX");
this->AddFindDefinition(id, this->VersionRangeMax);
}
+
+ if (this->RegistryViewDefined) {
+ this->AddFindDefinition(cmStrCat(this->Name, "_FIND_REGISTRY_VIEW"),
+ cmWindowsRegistry::FromView(this->RegistryView));
+ }
}
void cmFindPackageCommand::AddFindDefinition(const std::string& var,
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index b9f19e4..03f29b6 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -200,6 +200,7 @@ private:
bool UseRealPath = false;
bool PolicyScope = true;
bool GlobalScope = false;
+ bool RegistryViewDefined = false;
std::string LibraryArchitecture;
std::vector<std::string> Names;
std::vector<std::string> Configs;
diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx
index 780b256..a64e0e4 100644
--- a/Source/cmFindProgramCommand.cxx
+++ b/Source/cmFindProgramCommand.cxx
@@ -12,6 +12,8 @@
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
+#include "cmValue.h"
+#include "cmWindowsRegistry.h"
class cmExecutionStatus;
@@ -172,6 +174,18 @@ cmFindProgramCommand::cmFindProgramCommand(cmExecutionStatus& status)
this->NamesPerDirAllowed = true;
this->VariableDocumentation = "Path to a program.";
this->VariableType = cmStateEnums::FILEPATH;
+ // Windows Registry views
+ // When policy CMP0134 is not NEW, rely on previous behavior:
+ if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0134) !=
+ cmPolicies::NEW) {
+ if (this->Makefile->GetDefinition("CMAKE_SIZEOF_VOID_P") == "8") {
+ this->RegistryView = cmWindowsRegistry::View::Reg64_32;
+ } else {
+ this->RegistryView = cmWindowsRegistry::View::Reg32_64;
+ }
+ } else {
+ this->RegistryView = cmWindowsRegistry::View::Both;
+ }
}
// cmFindProgramCommand
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 5393747..8739c55 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -400,6 +400,10 @@ class cmMakefile;
SELECT(POLICY, CMP0133, \
"The CPack module disables SLA by default in the CPack DragNDrop " \
"Generator.", \
+ 3, 24, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0134, \
+ "Fallback to \"HOST\" Windows registry view when \"TARGET\" view " \
+ "is not usable.", \
3, 24, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
diff --git a/Source/cmSearchPath.cxx b/Source/cmSearchPath.cxx
index bfee64c..6c53b85 100644
--- a/Source/cmSearchPath.cxx
+++ b/Source/cmSearchPath.cxx
@@ -6,11 +6,14 @@
#include <cassert>
#include <utility>
+#include <cm/optional>
+
#include "cmFindCommon.h"
#include "cmMakefile.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
+#include "cmWindowsRegistry.h"
cmSearchPath::cmSearchPath(cmFindCommon* findCmd)
: FC(findCmd)
@@ -46,26 +49,13 @@ void cmSearchPath::AddUserPath(const std::string& path)
std::vector<std::string> outPaths;
- // We should view the registry as the target application would view
- // it.
- cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
- cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
- if (this->FC->Makefile->PlatformIs64Bit()) {
- view = cmSystemTools::KeyWOW64_64;
- other_view = cmSystemTools::KeyWOW64_32;
- }
-
- // Expand using the view of the target application.
- std::string expanded = path;
- cmSystemTools::ExpandRegistryValues(expanded, view);
- cmSystemTools::GlobDirs(expanded, outPaths);
-
- // Executables can be either 32-bit or 64-bit, so expand using the
- // alternative view.
- if (expanded != path && this->FC->CMakePathName == "PROGRAM") {
- expanded = path;
- cmSystemTools::ExpandRegistryValues(expanded, other_view);
- cmSystemTools::GlobDirs(expanded, outPaths);
+ cmWindowsRegistry registry(*this->FC->Makefile,
+ cmWindowsRegistry::SimpleTypes);
+ auto expandedPaths = registry.ExpandExpression(path, this->FC->RegistryView);
+ if (expandedPaths) {
+ for (const auto& expandedPath : expandedPaths.value()) {
+ cmSystemTools::GlobDirs(expandedPath, outPaths);
+ }
}
// Process them all from the current directory
diff --git a/Source/cmWindowsRegistry.cxx b/Source/cmWindowsRegistry.cxx
index c857a3b..6dba863 100644
--- a/Source/cmWindowsRegistry.cxx
+++ b/Source/cmWindowsRegistry.cxx
@@ -1,31 +1,61 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmConfigure.h" // IWYU pragma: keep
#include "cmWindowsRegistry.h"
+#include <cctype>
+#include <cstddef>
+#include <functional>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+
+#include <cmext/string_view>
+
+#include "cmsys/RegularExpression.hxx"
+
#if defined(_WIN32) && !defined(__CYGWIN__)
# include <algorithm>
-# include <cstdint>
+# include <cstring>
# include <exception>
# include <iterator>
-# include <utility>
# include <vector>
# include <cm/memory>
-# include <cmext/string_view>
# include <windows.h>
-# include "cmsys/Encoding.hxx"
-# include "cmsys/SystemTools.hxx"
-
# include "cmMakefile.h"
# include "cmStringAlgorithms.h"
# include "cmValue.h"
#endif
-#if defined(_WIN32) && !defined(__CYGWIN__)
namespace {
+// Case-independent string comparison
+int Strucmp(cm::string_view l, cm::string_view r)
+{
+ if (l.empty() && r.empty()) {
+ return 0;
+ }
+ if (l.empty() || r.empty()) {
+ return static_cast<int>(l.size() - r.size());
+ }
+
+ int lc;
+ int rc;
+ cm::string_view::size_type li = 0;
+ cm::string_view::size_type ri = 0;
+
+ do {
+ lc = std::tolower(l[li++]);
+ rc = std::tolower(r[ri++]);
+ } while (lc == rc && li < l.size() && ri < r.size());
+
+ return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc;
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
bool Is64BitWindows()
{
# if defined(_WIN64)
@@ -38,6 +68,26 @@ bool Is64BitWindows()
# endif
}
+// Helper to translate Windows registry value type to enum ValueType
+cm::optional<cmWindowsRegistry::ValueType> ToValueType(DWORD type)
+{
+ using ValueType = cmWindowsRegistry::ValueType;
+
+ static std::unordered_map<DWORD, ValueType> ValueTypes{
+ { REG_SZ, ValueType::Reg_SZ },
+ { REG_EXPAND_SZ, ValueType::Reg_EXPAND_SZ },
+ { REG_MULTI_SZ, ValueType::Reg_MULTI_SZ },
+ { REG_DWORD, ValueType::Reg_DWORD },
+ { REG_QWORD, ValueType::Reg_QWORD }
+ };
+
+ auto it = ValueTypes.find(type);
+
+ return it == ValueTypes.end()
+ ? cm::nullopt
+ : cm::optional<cmWindowsRegistry::ValueType>{ it->second };
+}
+
// class registry_exception
class registry_error : public std::exception
{
@@ -59,6 +109,7 @@ class KeyHandler
{
public:
using View = cmWindowsRegistry::View;
+ using ValueTypeSet = cmWindowsRegistry::ValueTypeSet;
KeyHandler(HKEY hkey)
: Handler(hkey)
@@ -66,29 +117,34 @@ public:
}
~KeyHandler() { RegCloseKey(this->Handler); }
+ static KeyHandler OpenKey(cm::string_view rootKey, cm::string_view subKey,
+ View view);
static KeyHandler OpenKey(cm::string_view key, View view);
- std::string ReadValue(cm::string_view name, cm::string_view separator);
+ std::string ReadValue(
+ cm::string_view name,
+ ValueTypeSet supportedTypes = cmWindowsRegistry::AllTypes,
+ cm::string_view separator = "\0"_s);
std::vector<std::string> GetValueNames();
std::vector<std::string> GetSubKeys();
private:
static std::string FormatSystemError(LSTATUS status);
+ static std::wstring ToWide(cm::string_view str);
+ static std::string ToNarrow(const wchar_t* str, int size = -1);
HKEY Handler;
};
-KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
+KeyHandler KeyHandler::OpenKey(cm::string_view rootKey, cm::string_view subKey,
+ View view)
{
if (view == View::Reg64 && !Is64BitWindows()) {
throw registry_error("No 64bit registry on Windows32.");
}
- auto start = key.find_first_of("\\/"_s);
- auto rootKey = key.substr(0, start);
HKEY hRootKey;
-
if (rootKey == "HKCU"_s || rootKey == "HKEY_CURRENT_USER"_s) {
hRootKey = HKEY_CURRENT_USER;
} else if (rootKey == "HKLM"_s || rootKey == "HKEY_LOCAL_MACHINE"_s) {
@@ -102,12 +158,9 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
} else {
throw registry_error(cmStrCat(rootKey, ": invalid root key."));
}
- std::wstring subKey;
- if (start != cm::string_view::npos) {
- subKey = cmsys::Encoding::ToWide(key.substr(start + 1).data());
- }
// Update path format
- std::replace(subKey.begin(), subKey.end(), L'/', L'\\');
+ auto key = ToWide(subKey);
+ std::replace(key.begin(), key.end(), L'/', L'\\');
REGSAM options = KEY_READ;
if (Is64BitWindows()) {
@@ -115,32 +168,107 @@ KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
}
HKEY hKey;
- if (LSTATUS status = RegOpenKeyExW(hRootKey, subKey.c_str(), 0, options,
- &hKey) != ERROR_SUCCESS) {
+ LSTATUS status;
+ if ((status = RegOpenKeyExW(hRootKey, key.c_str(), 0, options, &hKey)) !=
+ ERROR_SUCCESS) {
throw registry_error(FormatSystemError(status));
}
return KeyHandler(hKey);
}
+KeyHandler KeyHandler::OpenKey(cm::string_view key, View view)
+{
+ auto start = key.find_first_of("\\/"_s);
+
+ return OpenKey(key.substr(0, start),
+ start == cm::string_view::npos ? cm::string_view{ ""_s }
+ : key.substr(start + 1),
+ view);
+}
+
std::string KeyHandler::FormatSystemError(LSTATUS status)
{
- std::string formattedMessage;
+ std::string formattedMessage{ "Windows Registry: unexpected error." };
LPWSTR message = nullptr;
DWORD size = 1024;
if (FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr,
- status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) == 0) {
- formattedMessage = "Windows Registry: unexpected error.";
- } else {
- formattedMessage = cmTrimWhitespace(cmsys::Encoding::ToNarrow(message));
+ status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) != 0) {
+ try {
+ formattedMessage = cmTrimWhitespace(ToNarrow(message));
+ } catch (...) {
+ // ignore any exception because this method can be called
+ // as part of the raise of an exception
+ }
}
LocalFree(message);
return formattedMessage;
}
+std::wstring KeyHandler::ToWide(cm::string_view str)
+{
+ std::wstring wstr;
+
+ if (str.empty()) {
+ return wstr;
+ }
+
+ const auto wlength =
+ MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
+ int(str.size()), nullptr, 0);
+ if (wlength > 0) {
+ auto wdata = cm::make_unique<wchar_t[]>(wlength);
+ const auto r =
+ MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(),
+ int(str.size()), wdata.get(), wlength);
+ if (r > 0) {
+ wstr = std::wstring(wdata.get(), wlength);
+ } else {
+ throw registry_error(FormatSystemError(GetLastError()));
+ }
+ } else {
+ throw registry_error(FormatSystemError(GetLastError()));
+ }
+
+ return wstr;
+}
+
+std::string KeyHandler::ToNarrow(const wchar_t* wstr, int size)
+{
+ std::string str;
+
+ if (size == 0 || (size == -1 && wstr[0] == L'\0')) {
+ return str;
+ }
+
+ const auto length =
+ WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
+ nullptr, 0, nullptr, nullptr);
+ if (length > 0) {
+ auto data = cm::make_unique<char[]>(length);
+ const auto r =
+ WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size,
+ data.get(), length, nullptr, nullptr);
+ if (r > 0) {
+ if (size == -1) {
+ str = std::string(data.get());
+ } else {
+ str = std::string(data.get(), length);
+ }
+ } else {
+ throw registry_error(FormatSystemError(GetLastError()));
+ }
+ } else {
+ throw registry_error(FormatSystemError(GetLastError()));
+ }
+
+ return str;
+}
+
std::string KeyHandler::ReadValue(cm::string_view name,
+ ValueTypeSet supportedTypes,
cm::string_view separator)
{
LSTATUS status;
@@ -153,14 +281,20 @@ std::string KeyHandler::ReadValue(cm::string_view name,
}
auto data = cm::make_unique<BYTE[]>(size);
DWORD type;
- auto valueName = cmsys::Encoding::ToWide(name.data());
+ auto valueName = this->ToWide(name);
if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr,
&type, data.get(), &size)) != ERROR_SUCCESS) {
throw registry_error(this->FormatSystemError(status));
}
+
+ auto valueType = ToValueType(type);
+ if (!valueType || !supportedTypes.contains(*valueType)) {
+ throw registry_error(cmStrCat(type, ": unsupported type."));
+ }
+
switch (type) {
case REG_SZ:
- return cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
+ return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
break;
case REG_EXPAND_SZ: {
auto expandSize = ExpandEnvironmentStringsW(
@@ -170,7 +304,7 @@ std::string KeyHandler::ReadValue(cm::string_view name,
expandData.get(), expandSize + 1) == 0) {
throw registry_error(this->FormatSystemError(GetLastError()));
} else {
- return cmsys::Encoding::ToNarrow(expandData.get());
+ return this->ToNarrow(expandData.get());
}
} break;
case REG_DWORD:
@@ -181,12 +315,12 @@ std::string KeyHandler::ReadValue(cm::string_view name,
break;
case REG_MULTI_SZ: {
// replace separator with semicolon
- auto sep = cmsys::Encoding::ToWide(separator.data())[0];
+ auto sep = this->ToWide(separator)[0];
std::replace(reinterpret_cast<wchar_t*>(data.get()),
reinterpret_cast<wchar_t*>(data.get()) +
(size / sizeof(wchar_t)) - 1,
sep, L';');
- return cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
+ return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get()));
} break;
default:
throw registry_error(cmStrCat(type, ": unsupported type."));
@@ -213,7 +347,7 @@ std::vector<std::string> KeyHandler::GetValueNames()
while ((status = RegEnumValueW(this->Handler, index, data.get(), &size,
nullptr, nullptr, nullptr, nullptr)) ==
ERROR_SUCCESS) {
- auto name = cmsys::Encoding::ToNarrow(data.get());
+ auto name = this->ToNarrow(data.get());
valueNames.push_back(name.empty() ? "(default)" : name);
size = maxSize;
++index;
@@ -243,7 +377,7 @@ std::vector<std::string> KeyHandler::GetSubKeys()
while ((status = RegEnumKeyW(this->Handler, index, data.get(), size)) ==
ERROR_SUCCESS) {
- subKeys.push_back(cmsys::Encoding::ToNarrow(data.get()));
+ subKeys.push_back(this->ToNarrow(data.get()));
++index;
}
if (status != ERROR_NO_MORE_ITEMS) {
@@ -252,12 +386,109 @@ std::vector<std::string> KeyHandler::GetSubKeys()
return subKeys;
}
-}
#endif
+// ExpressionParser: Helper to parse expression holding multiple
+// registry specifications
+class ExpressionParser
+{
+public:
+ ExpressionParser(cm::string_view expression)
+ : Expression(expression)
+ , Separator(";"_s)
+ , RegistryFormat{
+ "\\[({.+})?(HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE|HKCR|HKEY_"
+ "CLASSES_"
+ "ROOT|HKCC|HKEY_CURRENT_CONFIG|HKU|HKEY_USERS)[/\\]?([^]]*)\\]"
+ }
+ {
+ }
+
+ bool Find()
+ {
+ // reset result members
+ this->RootKey = cm::string_view{};
+ this->SubKey = cm::string_view{};
+ this->ValueName = cm::string_view{};
+
+ auto result = this->RegistryFormat.find(this->Expression);
+
+ if (result) {
+ auto separator = cm::string_view{
+ this->Expression.data() + this->RegistryFormat.start(1),
+ this->RegistryFormat.end(1) - this->RegistryFormat.start(1)
+ };
+ if (separator.empty()) {
+ separator = this->Separator;
+ } else {
+ separator = separator.substr(1, separator.length() - 2);
+ }
+
+ this->RootKey = cm::string_view{
+ this->Expression.data() + this->RegistryFormat.start(2),
+ this->RegistryFormat.end(2) - this->RegistryFormat.start(2)
+ };
+ this->SubKey = cm::string_view{
+ this->Expression.data() + this->RegistryFormat.start(3),
+ this->RegistryFormat.end(3) - this->RegistryFormat.start(3)
+ };
+
+ auto pos = this->SubKey.find(separator);
+ if (pos != cm::string_view::npos) {
+ // split in ValueName and SubKey
+ this->ValueName = this->SubKey.substr(pos + separator.size());
+ if (Strucmp(this->ValueName, "(default)") == 0) {
+ // handle magic name for default value
+ this->ValueName = ""_s;
+ }
+ this->SubKey = this->SubKey.substr(0, pos);
+ } else {
+ this->ValueName = ""_s;
+ }
+ }
+ return result;
+ }
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ void Replace(const std::string& value)
+ {
+ this->Expression.replace(
+ this->RegistryFormat.start(),
+ this->RegistryFormat.end() - this->RegistryFormat.start(), value);
+ }
+
+ cm::string_view GetRootKey() const { return this->RootKey; }
+
+ cm::string_view GetSubKey() const { return this->SubKey; }
+ cm::string_view GetValueName() const { return this->ValueName; }
+
+ const std::string& GetExpression() const { return this->Expression; }
+#endif
+
+private:
+ std::string Expression;
+ cm::string_view Separator;
+ cmsys::RegularExpression RegistryFormat;
+ cm::string_view RootKey;
+ cm::string_view SubKey;
+ cm::string_view ValueName;
+};
+}
+
// class cmWindowsRegistry
-cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
-#if !defined(_WIN32) || defined(__CYGWIN__)
+const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::SimpleTypes =
+ cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ,
+ cmWindowsRegistry::ValueType::Reg_EXPAND_SZ,
+ cmWindowsRegistry::ValueType::Reg_DWORD,
+ cmWindowsRegistry::ValueType::Reg_QWORD };
+const cmWindowsRegistry::ValueTypeSet cmWindowsRegistry::AllTypes =
+ cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ;
+
+cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile,
+ const ValueTypeSet& supportedTypes)
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ : SupportedTypes(supportedTypes)
+#else
: LastError("No Registry on this platform.")
#endif
{
@@ -267,9 +498,56 @@ cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile)
}
#else
(void)makefile;
+ (void)supportedTypes;
#endif
}
+cm::optional<cmWindowsRegistry::View> cmWindowsRegistry::ToView(
+ cm::string_view name)
+{
+ static std::unordered_map<cm::string_view, cmWindowsRegistry::View>
+ ViewDefinitions{
+ { "BOTH"_s, View::Both }, { "HOST"_s, View::Host },
+ { "TARGET"_s, View::Target }, { "32"_s, View::Reg32 },
+ { "64"_s, View::Reg64 }, { "32_64"_s, View::Reg32_64 },
+ { "64_32"_s, View::Reg64_32 }
+ };
+
+ auto it = ViewDefinitions.find(name);
+
+ return it == ViewDefinitions.end()
+ ? cm::nullopt
+ : cm::optional<cmWindowsRegistry::View>{ it->second };
+}
+
+// define hash structure required by std::unordered_map
+namespace std {
+template <>
+struct hash<cmWindowsRegistry::View>
+{
+ size_t operator()(cmWindowsRegistry::View const& v) const noexcept
+ {
+ return static_cast<
+ typename underlying_type<cmWindowsRegistry::View>::type>(v);
+ }
+};
+}
+
+cm::string_view cmWindowsRegistry::FromView(View view)
+{
+ static std::unordered_map<cmWindowsRegistry::View, cm::string_view>
+ ViewDefinitions{
+ { View::Both, "BOTH"_s }, { View::Host, "HOST"_s },
+ { View::Target, "TARGET"_s }, { View::Reg32, "32"_s },
+ { View::Reg64, "64"_s }, { View::Reg32_64, "32_64"_s },
+ { View::Reg64_32, "64_32"_s }
+ };
+
+ auto it = ViewDefinitions.find(view);
+
+ return it == ViewDefinitions.end() ? ""_s : it->second;
+}
+
cm::string_view cmWindowsRegistry::GetLastError() const
{
return this->LastError;
@@ -335,7 +613,7 @@ cm::optional<std::string> cmWindowsRegistry::ReadValue(
// compute list of registry views
auto views = this->ComputeViews(view);
- if (cmsys::SystemTools::Strucmp(name.data(), "(default)") == 0) {
+ if (Strucmp(name, "(default)") == 0) {
// handle magic name for default value
name = ""_s;
}
@@ -347,7 +625,7 @@ cm::optional<std::string> cmWindowsRegistry::ReadValue(
try {
this->LastError.clear();
auto handler = KeyHandler::OpenKey(key, v);
- return handler.ReadValue(name, separator);
+ return handler.ReadValue(name, this->SupportedTypes, separator);
} catch (const registry_error& e) {
this->LastError = e.what();
continue;
@@ -440,3 +718,53 @@ cm::optional<std::vector<std::string>> cmWindowsRegistry::GetSubKeys(
#endif
return cm::nullopt;
}
+
+cm::optional<std::vector<std::string>> cmWindowsRegistry::ExpandExpression(
+ cm::string_view expression, View view, cm::string_view separator)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ static std::string NOTFOUND{ "/REGISTRY-NOTFOUND" };
+
+ this->LastError.clear();
+
+ // compute list of registry views
+ auto views = this->ComputeViews(view);
+ std::vector<std::string> result;
+
+ for (auto v : views) {
+ ExpressionParser parser(expression);
+
+ while (parser.Find()) {
+ try {
+ auto handler =
+ KeyHandler::OpenKey(parser.GetRootKey(), parser.GetSubKey(), v);
+ auto data = handler.ReadValue(parser.GetValueName(),
+ this->SupportedTypes, separator);
+ parser.Replace(data);
+ } catch (const registry_error& e) {
+ this->LastError = e.what();
+ parser.Replace(NOTFOUND);
+ continue;
+ }
+ }
+ result.emplace_back(parser.GetExpression());
+ if (expression == parser.GetExpression()) {
+ // there no substitutions, so can ignore other views
+ break;
+ }
+ }
+
+ return result;
+#else
+ (void)view;
+ (void)separator;
+
+ ExpressionParser parser(expression);
+ if (parser.Find()) {
+ // expression holds unsupported registry access
+ // so the expression cannot be used on this platform
+ return cm::nullopt;
+ }
+ return std::vector<std::string>{ std::string{ expression } };
+#endif
+}
diff --git a/Source/cmWindowsRegistry.h b/Source/cmWindowsRegistry.h
index 6f10b3a..bb9090e 100644
--- a/Source/cmWindowsRegistry.h
+++ b/Source/cmWindowsRegistry.h
@@ -7,6 +7,7 @@
#include <cm/optional>
#include <cm/string_view>
+#include <cmext/enum_set>
#include <cmext/string_view>
class cmMakefile;
@@ -14,8 +15,6 @@ class cmMakefile;
class cmWindowsRegistry
{
public:
- cmWindowsRegistry(cmMakefile&);
-
enum class View
{
Both,
@@ -27,6 +26,30 @@ public:
Reg64
};
+ // Registry supported types
+ enum class ValueType : std::uint8_t
+ {
+ Reg_SZ,
+ Reg_EXPAND_SZ,
+ Reg_MULTI_SZ,
+ Reg_DWORD,
+ Reg_QWORD
+ };
+ using ValueTypeSet = cm::enum_set<ValueType>;
+
+ // All types as defined by enum ValueType
+ static const ValueTypeSet AllTypes;
+ // same as AllTYpes but without type REG_MULTI_SZ
+ static const ValueTypeSet SimpleTypes;
+
+ cmWindowsRegistry(cmMakefile&,
+ const ValueTypeSet& supportedTypes = AllTypes);
+
+ // Helper routine to convert string to enum value
+ static cm::optional<View> ToView(cm::string_view name);
+ // Helper routine to convert enum to string
+ static cm::string_view FromView(View view);
+
cm::optional<std::string> ReadValue(cm::string_view key,
View view = View::Both,
cm::string_view separator = "\0"_s)
@@ -43,6 +66,12 @@ public:
cm::optional<std::vector<std::string>> GetSubKeys(cm::string_view key,
View view = View::Both);
+ // Expand an expression which may contains multiple references
+ // to registry keys.
+ // Depending of the view specified, one or two expansions can be done.
+ cm::optional<std::vector<std::string>> ExpandExpression(
+ cm::string_view expression, View view, cm::string_view separator = "\0"_s);
+
cm::string_view GetLastError() const;
private:
@@ -50,6 +79,7 @@ private:
std::vector<View> ComputeViews(View view);
int TargetSize = 0;
+ ValueTypeSet SupportedTypes = AllTypes;
#endif
std::string LastError;
};