diff options
author | Brad King <brad.king@kitware.com> | 2022-05-03 15:30:57 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2022-05-03 15:31:04 (GMT) |
commit | e0dbca93aae6b01f8b239d346a0bc99d2ca2473e (patch) | |
tree | 3c3a5d15c907c72c6e9d3700601156ba31cab8fb /Source | |
parent | 252fdfe6e400f9316911c9fffca7e420258a892c (diff) | |
parent | 8d7e80cf3d39ae24a6c88ee07492b9cfe40defd5 (diff) | |
download | CMake-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.txt | 2 | ||||
-rw-r--r-- | Source/cmCMakeHostSystemInformationCommand.cxx | 18 | ||||
-rw-r--r-- | Source/cmFindBase.cxx | 15 | ||||
-rw-r--r-- | Source/cmFindCommon.cxx | 12 | ||||
-rw-r--r-- | Source/cmFindCommon.h | 2 | ||||
-rw-r--r-- | Source/cmFindPackageCommand.cxx | 21 | ||||
-rw-r--r-- | Source/cmFindPackageCommand.h | 1 | ||||
-rw-r--r-- | Source/cmFindProgramCommand.cxx | 14 | ||||
-rw-r--r-- | Source/cmPolicies.h | 4 | ||||
-rw-r--r-- | Source/cmSearchPath.cxx | 30 | ||||
-rw-r--r-- | Source/cmWindowsRegistry.cxx | 400 | ||||
-rw-r--r-- | Source/cmWindowsRegistry.h | 34 |
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; }; |