cmake: Add flag to list cache entries matching a regex

Add a `-LR[A][H] <regex>` flag with similar functionality to `-L[A][H]`,
but instead of listing all cached variables, it show only specific
variables that match the name regex.
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index 48a9219..fa4d809 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -232,6 +232,17 @@
  will display also advanced variables.  If ``H`` is specified, it will also
  display help for each variable.
 
+.. option:: -LR[A][H] <regex>
+
+ .. versionadded:: 3.31
+
+ Show specific non-advanced cached variables
+
+ Show non-``INTERNAL`` nor :prop_cache:`ADVANCED` variables from the CMake
+ ``CACHE`` that match the given regex. If ``A`` is specified, then it
+ will also show advanced variables.  If ``H`` is specified, it will also
+ display help for each variable.
+
 .. option:: -N
 
  View mode only.
diff --git a/Help/release/dev/cmake-list-cached-variables.rst b/Help/release/dev/cmake-list-cached-variables.rst
new file mode 100644
index 0000000..983925d
--- /dev/null
+++ b/Help/release/dev/cmake-list-cached-variables.rst
@@ -0,0 +1,5 @@
+cmake-list-cached-variables
+---------------------------
+
+* The :option:`cmake -LR[A][H]` option was added to list cache entries
+  whose names match a regular expression.
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index e142fc9..a630cae 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -43,6 +43,7 @@
 #endif
 
 #include "cmsys/Encoding.hxx"
+#include "cmsys/RegularExpression.hxx"
 #include "cmsys/Terminal.h"
 
 namespace {
@@ -68,12 +69,13 @@
   "Run 'cmake --help' for more information."
 };
 
-const cmDocumentationEntry cmDocumentationOptions[32] = {
+const cmDocumentationEntry cmDocumentationOptions[33] = {
   { "--preset <preset>,--preset=<preset>", "Specify a configure preset." },
   { "--list-presets[=<type>]", "List available presets." },
   { "--workflow [<options>]", "Run a workflow preset." },
   { "-E", "CMake command mode." },
   { "-L[A][H]", "List non-advanced cached variables." },
+  { "-LR[A][H] <regex>", "Show cached variables that match the regex." },
   { "--fresh",
     "Configure a fresh build tree, removing any existing cache file." },
   { "--build <dir>", "Build a CMake-generated project binary tree." },
@@ -192,6 +194,21 @@
   }
 }
 
+std::function<bool(std::string const& value)> getShowCachedCallback(
+  bool& show_flag, bool* help_flag = nullptr, std::string* filter = nullptr)
+{
+  return [=, &show_flag](std::string const& value) -> bool {
+    show_flag = true;
+    if (help_flag) {
+      *help_flag = true;
+    }
+    if (filter) {
+      *filter = value;
+    }
+    return true;
+  };
+}
+
 int do_cmake(int ac, char const* const* av)
 {
   if (cmSystemTools::GetCurrentWorkingDirectory().empty()) {
@@ -243,6 +260,8 @@
   bool list_cached = false;
   bool list_all_cached = false;
   bool list_help = false;
+  // (Regex) Filter on the cached variable(s) to print.
+  std::string filter_var_name;
   bool view_only = false;
   cmake::WorkingMode workingMode = cmake::NORMAL_MODE;
   std::vector<std::string> parsedArgs;
@@ -267,13 +286,25 @@
     CommandArgument{ "-N", CommandArgument::Values::Zero,
                      CommandArgument::setToTrue(view_only) },
     CommandArgument{ "-LAH", CommandArgument::Values::Zero,
-                     CommandArgument::setToTrue(list_all_cached, list_help) },
+                     getShowCachedCallback(list_all_cached, &list_help) },
     CommandArgument{ "-LA", CommandArgument::Values::Zero,
-                     CommandArgument::setToTrue(list_all_cached) },
+                     getShowCachedCallback(list_all_cached) },
     CommandArgument{ "-LH", CommandArgument::Values::Zero,
-                     CommandArgument::setToTrue(list_cached, list_help) },
+                     getShowCachedCallback(list_cached, &list_help) },
     CommandArgument{ "-L", CommandArgument::Values::Zero,
-                     CommandArgument::setToTrue(list_cached) },
+                     getShowCachedCallback(list_cached) },
+    CommandArgument{
+      "-LRAH", CommandArgument::Values::One,
+      getShowCachedCallback(list_all_cached, &list_help, &filter_var_name) },
+    CommandArgument{
+      "-LRA", CommandArgument::Values::One,
+      getShowCachedCallback(list_all_cached, nullptr, &filter_var_name) },
+    CommandArgument{
+      "-LRH", CommandArgument::Values::One,
+      getShowCachedCallback(list_cached, &list_help, &filter_var_name) },
+    CommandArgument{
+      "-LR", CommandArgument::Values::One,
+      getShowCachedCallback(list_cached, nullptr, &filter_var_name) },
     CommandArgument{ "-P", "No script specified for argument -P",
                      CommandArgument::Values::One,
                      CommandArgument::RequiresSeparator::No,
@@ -368,7 +399,15 @@
   if (list_cached || list_all_cached) {
     std::cout << "-- Cache values" << std::endl;
     std::vector<std::string> keys = cm.GetState()->GetCacheEntryKeys();
+    cmsys::RegularExpression regex_var_name;
+    if (!filter_var_name.empty()) {
+      regex_var_name.compile(filter_var_name);
+    }
     for (std::string const& k : keys) {
+      if (regex_var_name.is_valid() && !regex_var_name.find(k)) {
+        continue;
+      }
+
       cmStateEnums::CacheEntryType t = cm.GetState()->GetCacheEntryType(k);
       if (t != cmStateEnums::INTERNAL && t != cmStateEnums::STATIC &&
           t != cmStateEnums::UNINITIALIZED) {
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 7a5a334..22ee2bd 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -49,6 +49,16 @@
 run_cmake_command(E___run_co_compile-no-cc ${CMAKE_COMMAND} -E __run_co_compile --iwyu=iwyu-does-not-exist --)
 run_cmake_command(E___run_co_compile-tidy-remove-fixes ${CMAKE_COMMAND} -E __run_co_compile "--tidy=${CMAKE_COMMAND}\\;-E\\;true\\;--export-fixes=${RunCMake_BINARY_DIR}/tidy-fixes.yaml" -- ${CMAKE_COMMAND} -E true)
 
+block()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/list-cache-build)
+  run_cmake(list-cache)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(list-cache-LR ${CMAKE_COMMAND} . -LR MIDDLE)
+  run_cmake_command(list-cache-LRA ${CMAKE_COMMAND} . -LRA MIDDLE)
+  run_cmake_command(list-cache-LRH ${CMAKE_COMMAND} . -LRH MIDDLE)
+  run_cmake_command(list-cache-LRAH ${CMAKE_COMMAND} . -LRAH MIDDLE)
+endblock()
+
 run_cmake_command(G_no-arg ${CMAKE_COMMAND} -B DummyBuildDir -G)
 run_cmake_command(G_bad-arg ${CMAKE_COMMAND} -B DummyBuildDir -G NoSuchGenerator)
 run_cmake_command(P_no-arg ${CMAKE_COMMAND} -P)
diff --git a/Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt
new file mode 100644
index 0000000..9a4e0db
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/list-cache-LR-stdout.txt
@@ -0,0 +1,3 @@
+-- Cache values
+MIDDLE_ENTRY_1:STRING=1
+MIDDLE_ENTRY_2:STRING=2$
diff --git a/Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt
new file mode 100644
index 0000000..a452355
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/list-cache-LRA-stdout.txt
@@ -0,0 +1,4 @@
+-- Cache values
+MIDDLE_ENTRY_1:STRING=1
+MIDDLE_ENTRY_2:STRING=2
+MIDDLE_ENTRY_3:STRING=3$
diff --git a/Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt
new file mode 100644
index 0000000..cf502c1
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/list-cache-LRAH-stdout.txt
@@ -0,0 +1,9 @@
+-- Cache values
+// mid 1
+MIDDLE_ENTRY_1:STRING=1
+
+// mid 2
+MIDDLE_ENTRY_2:STRING=2
+
+// mid 3
+MIDDLE_ENTRY_3:STRING=3$
diff --git a/Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt b/Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt
new file mode 100644
index 0000000..0f6b3a0d
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/list-cache-LRH-stdout.txt
@@ -0,0 +1,6 @@
+-- Cache values
+// mid 1
+MIDDLE_ENTRY_1:STRING=1
+
+// mid 2
+MIDDLE_ENTRY_2:STRING=2$
diff --git a/Tests/RunCMake/CommandLine/list-cache.cmake b/Tests/RunCMake/CommandLine/list-cache.cmake
new file mode 100644
index 0000000..5617054
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/list-cache.cmake
@@ -0,0 +1,6 @@
+set(EARLY_ENTRY_1 "1" CACHE STRING "early")
+set(MIDDLE_ENTRY_1 "1" CACHE STRING "mid 1")
+set(MIDDLE_ENTRY_2 "2" CACHE STRING "mid 2")
+set(MIDDLE_ENTRY_3 "3" CACHE STRING "mid 3")
+mark_as_advanced(MIDDLE_ENTRY_3)
+set(LATER_ENTRY_1 "1" CACHE STRING "later")