Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}