/src/CMake/Source/cmWindowsRegistry.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmConfigure.h" // IWYU pragma: keep |
4 | | |
5 | | #include "cmWindowsRegistry.h" |
6 | | |
7 | | #include <cstddef> |
8 | | #include <type_traits> |
9 | | #include <unordered_map> |
10 | | #include <utility> |
11 | | |
12 | | #include <cmext/string_view> |
13 | | |
14 | | #include "cmsys/RegularExpression.hxx" |
15 | | #include "cmsys/String.h" |
16 | | |
17 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
18 | | # include <algorithm> |
19 | | # include <cstring> |
20 | | # include <exception> |
21 | | # include <iterator> |
22 | | # include <vector> |
23 | | |
24 | | # include <cm/memory> |
25 | | |
26 | | # include <windows.h> |
27 | | |
28 | | # include "cmMakefile.h" |
29 | | # include "cmStringAlgorithms.h" |
30 | | # include "cmValue.h" |
31 | | #endif |
32 | | |
33 | | namespace { |
34 | | // Case-independent string comparison |
35 | | int Strucmp(cm::string_view l, cm::string_view r) |
36 | 0 | { |
37 | 0 | if (l.empty() && r.empty()) { |
38 | 0 | return 0; |
39 | 0 | } |
40 | 0 | if (l.empty() || r.empty()) { |
41 | 0 | return static_cast<int>(l.size() - r.size()); |
42 | 0 | } |
43 | | |
44 | 0 | int lc; |
45 | 0 | int rc; |
46 | 0 | cm::string_view::size_type li = 0; |
47 | 0 | cm::string_view::size_type ri = 0; |
48 | |
|
49 | 0 | do { |
50 | 0 | lc = cmsysString_tolower(l[li++]); |
51 | 0 | rc = cmsysString_tolower(r[ri++]); |
52 | 0 | } while (lc == rc && li < l.size() && ri < r.size()); |
53 | |
|
54 | 0 | return lc == rc ? static_cast<int>(l.size() - r.size()) : lc - rc; |
55 | 0 | } |
56 | | |
57 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
58 | | bool Is64BitWindows() |
59 | | { |
60 | | # if defined(_WIN64) |
61 | | // 64-bit programs run only on Win64 |
62 | | return true; |
63 | | # else |
64 | | // 32-bit programs run on both 32-bit and 64-bit Windows, so we must check. |
65 | | BOOL isWow64 = false; |
66 | | return IsWow64Process(GetCurrentProcess(), &isWow64) && isWow64; |
67 | | # endif |
68 | | } |
69 | | |
70 | | // Helper to translate Windows registry value type to enum ValueType |
71 | | cm::optional<cmWindowsRegistry::ValueType> ToValueType(DWORD type) |
72 | | { |
73 | | using ValueType = cmWindowsRegistry::ValueType; |
74 | | |
75 | | static std::unordered_map<DWORD, ValueType> ValueTypes{ |
76 | | { REG_SZ, ValueType::Reg_SZ }, |
77 | | { REG_EXPAND_SZ, ValueType::Reg_EXPAND_SZ }, |
78 | | { REG_MULTI_SZ, ValueType::Reg_MULTI_SZ }, |
79 | | { REG_DWORD, ValueType::Reg_DWORD }, |
80 | | { REG_QWORD, ValueType::Reg_QWORD } |
81 | | }; |
82 | | |
83 | | auto it = ValueTypes.find(type); |
84 | | |
85 | | return it == ValueTypes.end() |
86 | | ? cm::nullopt |
87 | | : cm::optional<cmWindowsRegistry::ValueType>{ it->second }; |
88 | | } |
89 | | |
90 | | // class registry_exception |
91 | | class registry_error : public std::exception |
92 | | { |
93 | | public: |
94 | | registry_error(std::string msg) |
95 | | : What(std::move(msg)) |
96 | | { |
97 | | } |
98 | | ~registry_error() override = default; |
99 | | |
100 | | char const* what() const noexcept override { return What.c_str(); } |
101 | | |
102 | | private: |
103 | | std::string What; |
104 | | }; |
105 | | |
106 | | // Class KeyHandler |
107 | | class KeyHandler |
108 | | { |
109 | | public: |
110 | | using View = cmWindowsRegistry::View; |
111 | | using ValueTypeSet = cmWindowsRegistry::ValueTypeSet; |
112 | | |
113 | | KeyHandler(HKEY hkey) |
114 | | : Handler(hkey) |
115 | | { |
116 | | } |
117 | | ~KeyHandler() { RegCloseKey(this->Handler); } |
118 | | |
119 | | static KeyHandler OpenKey(cm::string_view rootKey, cm::string_view subKey, |
120 | | View view); |
121 | | static KeyHandler OpenKey(cm::string_view key, View view); |
122 | | |
123 | | std::string ReadValue( |
124 | | cm::string_view name, |
125 | | ValueTypeSet supportedTypes = cmWindowsRegistry::AllTypes, |
126 | | cm::string_view separator = "\0"_s); |
127 | | |
128 | | std::vector<std::string> GetValueNames(); |
129 | | std::vector<std::string> GetSubKeys(); |
130 | | |
131 | | private: |
132 | | static std::string FormatSystemError(LSTATUS status); |
133 | | static std::wstring ToWide(cm::string_view str); |
134 | | static std::string ToNarrow(wchar_t const* str, int size = -1); |
135 | | |
136 | | HKEY Handler; |
137 | | }; |
138 | | |
139 | | KeyHandler KeyHandler::OpenKey(cm::string_view rootKey, cm::string_view subKey, |
140 | | View view) |
141 | | { |
142 | | if (view == View::Reg64 && !Is64BitWindows()) { |
143 | | throw registry_error("No 64bit registry on Windows32."); |
144 | | } |
145 | | |
146 | | HKEY hRootKey; |
147 | | if (rootKey == "HKCU"_s || rootKey == "HKEY_CURRENT_USER"_s) { |
148 | | hRootKey = HKEY_CURRENT_USER; |
149 | | } else if (rootKey == "HKLM"_s || rootKey == "HKEY_LOCAL_MACHINE"_s) { |
150 | | hRootKey = HKEY_LOCAL_MACHINE; |
151 | | } else if (rootKey == "HKCR"_s || rootKey == "HKEY_CLASSES_ROOT"_s) { |
152 | | hRootKey = HKEY_CLASSES_ROOT; |
153 | | } else if (rootKey == "HKCC"_s || rootKey == "HKEY_CURRENT_CONFIG"_s) { |
154 | | hRootKey = HKEY_CURRENT_CONFIG; |
155 | | } else if (rootKey == "HKU"_s || rootKey == "HKEY_USERS"_s) { |
156 | | hRootKey = HKEY_USERS; |
157 | | } else { |
158 | | throw registry_error(cmStrCat(rootKey, ": invalid root key.")); |
159 | | } |
160 | | // Update path format |
161 | | auto key = ToWide(subKey); |
162 | | std::replace(key.begin(), key.end(), L'/', L'\\'); |
163 | | |
164 | | REGSAM options = KEY_READ; |
165 | | if (Is64BitWindows()) { |
166 | | options |= view == View::Reg64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY; |
167 | | } |
168 | | |
169 | | HKEY hKey; |
170 | | LSTATUS status; |
171 | | if ((status = RegOpenKeyExW(hRootKey, key.c_str(), 0, options, &hKey)) != |
172 | | ERROR_SUCCESS) { |
173 | | throw registry_error(FormatSystemError(status)); |
174 | | } |
175 | | |
176 | | return KeyHandler(hKey); |
177 | | } |
178 | | |
179 | | KeyHandler KeyHandler::OpenKey(cm::string_view key, View view) |
180 | | { |
181 | | auto start = key.find_first_of("\\/"_s); |
182 | | |
183 | | return OpenKey(key.substr(0, start), |
184 | | start == cm::string_view::npos ? cm::string_view{ ""_s } |
185 | | : key.substr(start + 1), |
186 | | view); |
187 | | } |
188 | | |
189 | | std::string KeyHandler::FormatSystemError(LSTATUS status) |
190 | | { |
191 | | std::string formattedMessage{ "Windows Registry: unexpected error." }; |
192 | | LPWSTR message = nullptr; |
193 | | DWORD size = 1024; |
194 | | if (FormatMessageW( |
195 | | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, |
196 | | status, 0, reinterpret_cast<LPWSTR>(&message), size, nullptr) != 0) { |
197 | | try { |
198 | | formattedMessage = cmTrimWhitespace(ToNarrow(message)); |
199 | | } catch (...) { |
200 | | // ignore any exception because this method can be called |
201 | | // as part of the raise of an exception |
202 | | } |
203 | | } |
204 | | LocalFree(message); |
205 | | |
206 | | return formattedMessage; |
207 | | } |
208 | | |
209 | | std::wstring KeyHandler::ToWide(cm::string_view str) |
210 | | { |
211 | | std::wstring wstr; |
212 | | |
213 | | if (str.empty()) { |
214 | | return wstr; |
215 | | } |
216 | | |
217 | | auto const wlength = |
218 | | MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), |
219 | | int(str.size()), nullptr, 0); |
220 | | if (wlength > 0) { |
221 | | auto wdata = cm::make_unique<wchar_t[]>(wlength); |
222 | | auto const r = |
223 | | MultiByteToWideChar(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, str.data(), |
224 | | int(str.size()), wdata.get(), wlength); |
225 | | if (r > 0) { |
226 | | wstr = std::wstring(wdata.get(), wlength); |
227 | | } else { |
228 | | throw registry_error(FormatSystemError(GetLastError())); |
229 | | } |
230 | | } else { |
231 | | throw registry_error(FormatSystemError(GetLastError())); |
232 | | } |
233 | | |
234 | | return wstr; |
235 | | } |
236 | | |
237 | | std::string KeyHandler::ToNarrow(wchar_t const* wstr, int size) |
238 | | { |
239 | | std::string str; |
240 | | |
241 | | if (size == 0 || (size == -1 && wstr[0] == L'\0')) { |
242 | | return str; |
243 | | } |
244 | | |
245 | | auto const length = |
246 | | WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size, |
247 | | nullptr, 0, nullptr, nullptr); |
248 | | if (length > 0) { |
249 | | auto data = cm::make_unique<char[]>(length); |
250 | | auto const r = |
251 | | WideCharToMultiByte(KWSYS_ENCODING_DEFAULT_CODEPAGE, 0, wstr, size, |
252 | | data.get(), length, nullptr, nullptr); |
253 | | if (r > 0) { |
254 | | if (size == -1) { |
255 | | str = std::string(data.get()); |
256 | | } else { |
257 | | str = std::string(data.get(), length); |
258 | | } |
259 | | } else { |
260 | | throw registry_error(FormatSystemError(GetLastError())); |
261 | | } |
262 | | } else { |
263 | | throw registry_error(FormatSystemError(GetLastError())); |
264 | | } |
265 | | |
266 | | return str; |
267 | | } |
268 | | |
269 | | std::string KeyHandler::ReadValue(cm::string_view name, |
270 | | ValueTypeSet supportedTypes, |
271 | | cm::string_view separator) |
272 | | { |
273 | | LSTATUS status; |
274 | | DWORD size; |
275 | | // pick-up maximum size for value |
276 | | if ((status = RegQueryInfoKeyW(this->Handler, nullptr, nullptr, nullptr, |
277 | | nullptr, nullptr, nullptr, nullptr, nullptr, |
278 | | &size, nullptr, nullptr)) != ERROR_SUCCESS) { |
279 | | throw registry_error(this->FormatSystemError(status)); |
280 | | } |
281 | | auto data = cm::make_unique<BYTE[]>(size); |
282 | | DWORD type; |
283 | | auto valueName = this->ToWide(name); |
284 | | if ((status = RegQueryValueExW(this->Handler, valueName.c_str(), nullptr, |
285 | | &type, data.get(), &size)) != ERROR_SUCCESS) { |
286 | | throw registry_error(this->FormatSystemError(status)); |
287 | | } |
288 | | |
289 | | auto valueType = ToValueType(type); |
290 | | if (!valueType || !supportedTypes.contains(*valueType)) { |
291 | | throw registry_error(cmStrCat(type, ": unsupported type.")); |
292 | | } |
293 | | |
294 | | switch (type) { |
295 | | case REG_SZ: |
296 | | return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get())); |
297 | | break; |
298 | | case REG_EXPAND_SZ: { |
299 | | auto expandSize = ExpandEnvironmentStringsW( |
300 | | reinterpret_cast<wchar_t*>(data.get()), nullptr, 0); |
301 | | auto expandData = cm::make_unique<wchar_t[]>(expandSize + 1); |
302 | | if (ExpandEnvironmentStringsW(reinterpret_cast<wchar_t*>(data.get()), |
303 | | expandData.get(), expandSize + 1) == 0) { |
304 | | throw registry_error(this->FormatSystemError(GetLastError())); |
305 | | } else { |
306 | | return this->ToNarrow(expandData.get()); |
307 | | } |
308 | | } break; |
309 | | case REG_DWORD: |
310 | | return std::to_string(*reinterpret_cast<std::uint32_t*>(data.get())); |
311 | | break; |
312 | | case REG_QWORD: |
313 | | return std::to_string(*reinterpret_cast<std::uint64_t*>(data.get())); |
314 | | break; |
315 | | case REG_MULTI_SZ: { |
316 | | // replace separator with semicolon |
317 | | auto sep = this->ToWide(separator)[0]; |
318 | | std::replace(reinterpret_cast<wchar_t*>(data.get()), |
319 | | reinterpret_cast<wchar_t*>(data.get()) + |
320 | | (size / sizeof(wchar_t)) - 1, |
321 | | sep, L';'); |
322 | | return this->ToNarrow(reinterpret_cast<wchar_t*>(data.get())); |
323 | | } break; |
324 | | default: |
325 | | throw registry_error(cmStrCat(type, ": unsupported type.")); |
326 | | } |
327 | | } |
328 | | |
329 | | std::vector<std::string> KeyHandler::GetValueNames() |
330 | | { |
331 | | LSTATUS status; |
332 | | DWORD maxSize; |
333 | | // pick-up maximum size for value names |
334 | | if ((status = RegQueryInfoKeyW( |
335 | | this->Handler, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
336 | | nullptr, &maxSize, nullptr, nullptr, nullptr)) != ERROR_SUCCESS) { |
337 | | throw registry_error(this->FormatSystemError(status)); |
338 | | } |
339 | | // increment size for final null |
340 | | auto data = cm::make_unique<wchar_t[]>(++maxSize); |
341 | | DWORD index = 0; |
342 | | DWORD size = maxSize; |
343 | | |
344 | | std::vector<std::string> valueNames; |
345 | | |
346 | | while ((status = RegEnumValueW(this->Handler, index, data.get(), &size, |
347 | | nullptr, nullptr, nullptr, nullptr)) == |
348 | | ERROR_SUCCESS) { |
349 | | auto name = this->ToNarrow(data.get()); |
350 | | valueNames.push_back(name.empty() ? "(default)" : name); |
351 | | size = maxSize; |
352 | | ++index; |
353 | | } |
354 | | |
355 | | if (status != ERROR_NO_MORE_ITEMS) { |
356 | | throw registry_error(this->FormatSystemError(status)); |
357 | | } |
358 | | |
359 | | return valueNames; |
360 | | } |
361 | | |
362 | | std::vector<std::string> KeyHandler::GetSubKeys() |
363 | | { |
364 | | LSTATUS status; |
365 | | DWORD size; |
366 | | // pick-up maximum size for subkeys |
367 | | if ((status = RegQueryInfoKeyW( |
368 | | this->Handler, nullptr, nullptr, nullptr, nullptr, &size, nullptr, |
369 | | nullptr, nullptr, nullptr, nullptr, nullptr)) != ERROR_SUCCESS) { |
370 | | throw registry_error(this->FormatSystemError(status)); |
371 | | } |
372 | | // increment size for final null |
373 | | auto data = cm::make_unique<wchar_t[]>(++size); |
374 | | DWORD index = 0; |
375 | | std::vector<std::string> subKeys; |
376 | | |
377 | | while ((status = RegEnumKeyW(this->Handler, index, data.get(), size)) == |
378 | | ERROR_SUCCESS) { |
379 | | subKeys.push_back(this->ToNarrow(data.get())); |
380 | | ++index; |
381 | | } |
382 | | if (status != ERROR_NO_MORE_ITEMS) { |
383 | | throw registry_error(this->FormatSystemError(status)); |
384 | | } |
385 | | |
386 | | return subKeys; |
387 | | } |
388 | | #endif |
389 | | |
390 | | // ExpressionParser: Helper to parse expression holding multiple |
391 | | // registry specifications |
392 | | class ExpressionParser |
393 | | { |
394 | | public: |
395 | | ExpressionParser(cm::string_view expression) |
396 | 0 | : Expression(expression) |
397 | 0 | , Separator(";"_s) |
398 | 0 | , RegistryFormat{ |
399 | 0 | "\\[({.+})?(HKCU|HKEY_CURRENT_USER|HKLM|HKEY_LOCAL_MACHINE|HKCR|HKEY_" |
400 | 0 | "CLASSES_" |
401 | 0 | "ROOT|HKCC|HKEY_CURRENT_CONFIG|HKU|HKEY_USERS)[/\\]?([^]]*)\\]" |
402 | 0 | } |
403 | 0 | { |
404 | 0 | } |
405 | | |
406 | | bool Find() |
407 | 0 | { |
408 | | // reset result members |
409 | 0 | this->RootKey = cm::string_view{}; |
410 | 0 | this->SubKey = cm::string_view{}; |
411 | 0 | this->ValueName = cm::string_view{}; |
412 | |
|
413 | 0 | auto result = this->RegistryFormat.find(this->Expression); |
414 | |
|
415 | 0 | if (result) { |
416 | 0 | auto separator = cm::string_view{ |
417 | 0 | this->Expression.data() + this->RegistryFormat.start(1), |
418 | 0 | this->RegistryFormat.end(1) - this->RegistryFormat.start(1) |
419 | 0 | }; |
420 | 0 | if (separator.empty()) { |
421 | 0 | separator = this->Separator; |
422 | 0 | } else { |
423 | 0 | separator = separator.substr(1, separator.length() - 2); |
424 | 0 | } |
425 | |
|
426 | 0 | this->RootKey = cm::string_view{ |
427 | 0 | this->Expression.data() + this->RegistryFormat.start(2), |
428 | 0 | this->RegistryFormat.end(2) - this->RegistryFormat.start(2) |
429 | 0 | }; |
430 | 0 | this->SubKey = cm::string_view{ |
431 | 0 | this->Expression.data() + this->RegistryFormat.start(3), |
432 | 0 | this->RegistryFormat.end(3) - this->RegistryFormat.start(3) |
433 | 0 | }; |
434 | |
|
435 | 0 | auto pos = this->SubKey.find(separator); |
436 | 0 | if (pos != cm::string_view::npos) { |
437 | | // split in ValueName and SubKey |
438 | 0 | this->ValueName = this->SubKey.substr(pos + separator.size()); |
439 | 0 | if (Strucmp(this->ValueName, "(default)") == 0) { |
440 | | // handle magic name for default value |
441 | 0 | this->ValueName = ""_s; |
442 | 0 | } |
443 | 0 | this->SubKey = this->SubKey.substr(0, pos); |
444 | 0 | } else { |
445 | 0 | this->ValueName = ""_s; |
446 | 0 | } |
447 | 0 | } |
448 | 0 | return result; |
449 | 0 | } |
450 | | |
451 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
452 | | void Replace(std::string const& value) |
453 | | { |
454 | | this->Expression.replace( |
455 | | this->RegistryFormat.start(), |
456 | | this->RegistryFormat.end() - this->RegistryFormat.start(), value); |
457 | | } |
458 | | |
459 | | cm::string_view GetRootKey() const { return this->RootKey; } |
460 | | |
461 | | cm::string_view GetSubKey() const { return this->SubKey; } |
462 | | cm::string_view GetValueName() const { return this->ValueName; } |
463 | | |
464 | | std::string const& GetExpression() const { return this->Expression; } |
465 | | #endif |
466 | | |
467 | | private: |
468 | | std::string Expression; |
469 | | cm::string_view Separator; |
470 | | cmsys::RegularExpression RegistryFormat; |
471 | | cm::string_view RootKey; |
472 | | cm::string_view SubKey; |
473 | | cm::string_view ValueName; |
474 | | }; |
475 | | } |
476 | | |
477 | | // class cmWindowsRegistry |
478 | | cmWindowsRegistry::ValueTypeSet const cmWindowsRegistry::SimpleTypes = |
479 | | cmWindowsRegistry::ValueTypeSet{ cmWindowsRegistry::ValueType::Reg_SZ, |
480 | | cmWindowsRegistry::ValueType::Reg_EXPAND_SZ, |
481 | | cmWindowsRegistry::ValueType::Reg_DWORD, |
482 | | cmWindowsRegistry::ValueType::Reg_QWORD }; |
483 | | cmWindowsRegistry::ValueTypeSet const cmWindowsRegistry::AllTypes = |
484 | | cmWindowsRegistry::SimpleTypes + cmWindowsRegistry::ValueType::Reg_MULTI_SZ; |
485 | | |
486 | | cmWindowsRegistry::cmWindowsRegistry(cmMakefile& makefile, |
487 | | ValueTypeSet const& supportedTypes) |
488 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
489 | | : SupportedTypes(supportedTypes) |
490 | | #else |
491 | 0 | : LastError("No Registry on this platform.") |
492 | | #endif |
493 | 0 | { |
494 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
495 | | if (cmValue targetSize = makefile.GetDefinition("CMAKE_SIZEOF_VOID_P")) { |
496 | | this->TargetSize = targetSize == "8" ? 64 : 32; |
497 | | } |
498 | | #else |
499 | 0 | (void)makefile; |
500 | 0 | (void)supportedTypes; |
501 | 0 | #endif |
502 | 0 | } |
503 | | |
504 | | cm::optional<cmWindowsRegistry::View> cmWindowsRegistry::ToView( |
505 | | cm::string_view name) |
506 | 0 | { |
507 | 0 | static std::unordered_map<cm::string_view, cmWindowsRegistry::View> |
508 | 0 | ViewDefinitions{ |
509 | 0 | { "BOTH"_s, View::Both }, { "HOST"_s, View::Host }, |
510 | 0 | { "TARGET"_s, View::Target }, { "32"_s, View::Reg32 }, |
511 | 0 | { "64"_s, View::Reg64 }, { "32_64"_s, View::Reg32_64 }, |
512 | 0 | { "64_32"_s, View::Reg64_32 } |
513 | 0 | }; |
514 | |
|
515 | 0 | auto it = ViewDefinitions.find(name); |
516 | |
|
517 | 0 | return it == ViewDefinitions.end() |
518 | 0 | ? cm::nullopt |
519 | 0 | : cm::optional<cmWindowsRegistry::View>{ it->second }; |
520 | 0 | } |
521 | | |
522 | | // define hash structure required by std::unordered_map |
523 | | namespace std { |
524 | | template <> |
525 | | struct hash<cmWindowsRegistry::View> |
526 | | { |
527 | | size_t operator()(cmWindowsRegistry::View v) const noexcept |
528 | 0 | { |
529 | 0 | return static_cast< |
530 | 0 | typename underlying_type<cmWindowsRegistry::View>::type>(v); |
531 | 0 | } |
532 | | }; |
533 | | } |
534 | | |
535 | | cm::string_view cmWindowsRegistry::FromView(View view) |
536 | 0 | { |
537 | 0 | static std::unordered_map<cmWindowsRegistry::View, cm::string_view> |
538 | 0 | ViewDefinitions{ |
539 | 0 | { View::Both, "BOTH"_s }, { View::Host, "HOST"_s }, |
540 | 0 | { View::Target, "TARGET"_s }, { View::Reg32, "32"_s }, |
541 | 0 | { View::Reg64, "64"_s }, { View::Reg32_64, "32_64"_s }, |
542 | 0 | { View::Reg64_32, "64_32"_s } |
543 | 0 | }; |
544 | |
|
545 | 0 | auto it = ViewDefinitions.find(view); |
546 | |
|
547 | 0 | return it == ViewDefinitions.end() ? ""_s : it->second; |
548 | 0 | } |
549 | | |
550 | | cm::string_view cmWindowsRegistry::GetLastError() const |
551 | 0 | { |
552 | 0 | return this->LastError; |
553 | 0 | } |
554 | | |
555 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
556 | | std::vector<cmWindowsRegistry::View> cmWindowsRegistry::ComputeViews(View view) |
557 | | { |
558 | | switch (view) { |
559 | | case View::Both: |
560 | | switch (this->TargetSize) { |
561 | | case 64: |
562 | | return std::vector<View>{ View::Reg64, View::Reg32 }; |
563 | | break; |
564 | | case 32: |
565 | | return Is64BitWindows() |
566 | | ? std::vector<View>{ View::Reg32, View::Reg64 } |
567 | | : std::vector<View>{ View::Reg32 }; |
568 | | break; |
569 | | default: |
570 | | // No language specified, fallback to host architecture |
571 | | return Is64BitWindows() |
572 | | ? std::vector<View>{ View::Reg64, View::Reg32 } |
573 | | : std::vector<View>{ View::Reg32 }; |
574 | | break; |
575 | | } |
576 | | break; |
577 | | case View::Target: |
578 | | switch (this->TargetSize) { |
579 | | case 64: |
580 | | return std::vector<View>{ View::Reg64 }; |
581 | | break; |
582 | | case 32: |
583 | | return std::vector<View>{ View::Reg32 }; |
584 | | break; |
585 | | default: |
586 | | break; |
587 | | } |
588 | | CM_FALLTHROUGH; |
589 | | case View::Host: |
590 | | return std::vector<View>{ Is64BitWindows() ? View::Reg64 : View::Reg32 }; |
591 | | break; |
592 | | case View::Reg64_32: |
593 | | return Is64BitWindows() ? std::vector<View>{ View::Reg64, View::Reg32 } |
594 | | : std::vector<View>{ View::Reg32 }; |
595 | | break; |
596 | | case View::Reg32_64: |
597 | | return Is64BitWindows() ? std::vector<View>{ View::Reg32, View::Reg64 } |
598 | | : std::vector<View>{ View::Reg32 }; |
599 | | break; |
600 | | default: |
601 | | return std::vector<View>{ view }; |
602 | | break; |
603 | | } |
604 | | } |
605 | | #endif |
606 | | |
607 | | cm::optional<std::string> cmWindowsRegistry::ReadValue( |
608 | | cm::string_view key, cm::string_view name, View view, |
609 | | cm::string_view separator) |
610 | 0 | { |
611 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
612 | | // compute list of registry views |
613 | | auto views = this->ComputeViews(view); |
614 | | |
615 | | if (Strucmp(name, "(default)") == 0) { |
616 | | // handle magic name for default value |
617 | | name = ""_s; |
618 | | } |
619 | | if (separator.empty()) { |
620 | | separator = "\0"_s; |
621 | | } |
622 | | |
623 | | for (auto v : views) { |
624 | | try { |
625 | | this->LastError.clear(); |
626 | | auto handler = KeyHandler::OpenKey(key, v); |
627 | | return handler.ReadValue(name, this->SupportedTypes, separator); |
628 | | } catch (registry_error const& e) { |
629 | | this->LastError = e.what(); |
630 | | continue; |
631 | | } |
632 | | } |
633 | | #else |
634 | 0 | (void)key; |
635 | 0 | (void)name; |
636 | 0 | (void)view; |
637 | 0 | (void)separator; |
638 | 0 | #endif |
639 | 0 | return cm::nullopt; |
640 | 0 | } |
641 | | |
642 | | cm::optional<std::vector<std::string>> cmWindowsRegistry::GetValueNames( |
643 | | cm::string_view key, View view) |
644 | 0 | { |
645 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
646 | | this->LastError.clear(); |
647 | | // compute list of registry views |
648 | | auto views = this->ComputeViews(view); |
649 | | std::vector<std::string> valueNames; |
650 | | bool querySuccessful = false; |
651 | | |
652 | | for (auto v : views) { |
653 | | try { |
654 | | auto handler = KeyHandler::OpenKey(key, v); |
655 | | auto list = handler.GetValueNames(); |
656 | | std::move(list.begin(), list.end(), std::back_inserter(valueNames)); |
657 | | querySuccessful = true; |
658 | | } catch (registry_error const& e) { |
659 | | this->LastError = e.what(); |
660 | | continue; |
661 | | } |
662 | | } |
663 | | if (!valueNames.empty()) { |
664 | | // value names must be unique and sorted |
665 | | std::sort(valueNames.begin(), valueNames.end()); |
666 | | valueNames.erase(std::unique(valueNames.begin(), valueNames.end()), |
667 | | valueNames.end()); |
668 | | } |
669 | | |
670 | | if (querySuccessful) { |
671 | | // At least one query was successful, so clean-up any error message |
672 | | this->LastError.clear(); |
673 | | return valueNames; |
674 | | } |
675 | | #else |
676 | 0 | (void)key; |
677 | 0 | (void)view; |
678 | 0 | #endif |
679 | 0 | return cm::nullopt; |
680 | 0 | } |
681 | | |
682 | | cm::optional<std::vector<std::string>> cmWindowsRegistry::GetSubKeys( |
683 | | cm::string_view key, View view) |
684 | 0 | { |
685 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
686 | | this->LastError.clear(); |
687 | | // compute list of registry views |
688 | | auto views = this->ComputeViews(view); |
689 | | std::vector<std::string> subKeys; |
690 | | bool querySuccessful = false; |
691 | | |
692 | | for (auto v : views) { |
693 | | try { |
694 | | auto handler = KeyHandler::OpenKey(key, v); |
695 | | auto list = handler.GetSubKeys(); |
696 | | std::move(list.begin(), list.end(), std::back_inserter(subKeys)); |
697 | | querySuccessful = true; |
698 | | } catch (registry_error const& e) { |
699 | | this->LastError = e.what(); |
700 | | continue; |
701 | | } |
702 | | } |
703 | | if (!subKeys.empty()) { |
704 | | // keys must be unique and sorted |
705 | | std::sort(subKeys.begin(), subKeys.end()); |
706 | | subKeys.erase(std::unique(subKeys.begin(), subKeys.end()), subKeys.end()); |
707 | | } |
708 | | |
709 | | if (querySuccessful) { |
710 | | // At least one query was successful, so clean-up any error message |
711 | | this->LastError.clear(); |
712 | | return subKeys; |
713 | | } |
714 | | #else |
715 | 0 | (void)key; |
716 | 0 | (void)view; |
717 | 0 | #endif |
718 | 0 | return cm::nullopt; |
719 | 0 | } |
720 | | |
721 | | cm::optional<std::vector<std::string>> cmWindowsRegistry::ExpandExpression( |
722 | | cm::string_view expression, View view, cm::string_view separator) |
723 | 0 | { |
724 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
725 | | static std::string NOTFOUND{ "/REGISTRY-NOTFOUND" }; |
726 | | |
727 | | this->LastError.clear(); |
728 | | |
729 | | // compute list of registry views |
730 | | auto views = this->ComputeViews(view); |
731 | | std::vector<std::string> result; |
732 | | |
733 | | for (auto v : views) { |
734 | | ExpressionParser parser(expression); |
735 | | |
736 | | while (parser.Find()) { |
737 | | try { |
738 | | auto handler = |
739 | | KeyHandler::OpenKey(parser.GetRootKey(), parser.GetSubKey(), v); |
740 | | auto data = handler.ReadValue(parser.GetValueName(), |
741 | | this->SupportedTypes, separator); |
742 | | parser.Replace(data); |
743 | | } catch (registry_error const& e) { |
744 | | this->LastError = e.what(); |
745 | | parser.Replace(NOTFOUND); |
746 | | continue; |
747 | | } |
748 | | } |
749 | | result.emplace_back(parser.GetExpression()); |
750 | | if (expression == parser.GetExpression()) { |
751 | | // there no substitutions, so can ignore other views |
752 | | break; |
753 | | } |
754 | | } |
755 | | |
756 | | return result; |
757 | | #else |
758 | 0 | (void)view; |
759 | 0 | (void)separator; |
760 | |
|
761 | 0 | ExpressionParser parser(expression); |
762 | 0 | if (parser.Find()) { |
763 | | // expression holds unsupported registry access |
764 | | // so the expression cannot be used on this platform |
765 | 0 | return cm::nullopt; |
766 | 0 | } |
767 | 0 | return std::vector<std::string>{ std::string{ expression } }; |
768 | 0 | #endif |
769 | 0 | } |