ソースを参照

string: Add REGEX QUOTE sub-command

Add a command to generate a regular expression that matches an
input string literally by escaping special characters.

Fixes: #18580
Timo Röhling 3 ヶ月 前
コミット
d94d79a909

+ 11 - 0
Help/command/string.rst

@@ -27,6 +27,7 @@ Synopsis
     string(`STRIP`_ <string> <out-var>)
     string(`GENEX_STRIP`_ <string> <out-var>)
     string(`REPEAT`_ <string> <count> <out-var>)
+    string(`REGEX QUOTE`_ <out-var> <input>...)
 
   `Comparison`_
     string(`COMPARE`_ <op> <string1> <string2> <out-var>)
@@ -290,6 +291,16 @@ Manipulation
   Produce the output string as the input ``<string>``
   repeated ``<count>`` times.
 
+.. signature::
+  string(REGEX QUOTE <out-var> <input>...)
+
+  .. versionadded:: 4.2
+
+  Store in an ``<out-var>`` a regular expression matching the ``<input>``.
+  All characters that have special meaning in a regular expressions are
+  escaped, such that the output string can be used as part of a regular
+  expression to match the input literally.
+
 Comparison
 ^^^^^^^^^^
 

+ 5 - 0
Help/release/dev/string-regex-quote.rst

@@ -0,0 +1,5 @@
+string-regex-quote
+------------------
+
+* The :command:`string(REGEX QUOTE)` command was added to
+  generate a regular expression exactly matching a string.

+ 32 - 0
Source/cmStringCommand.cxx

@@ -47,6 +47,8 @@ bool RegexMatchAll(std::vector<std::string> const& args,
                    cmExecutionStatus& status);
 bool RegexReplace(std::vector<std::string> const& args,
                   cmExecutionStatus& status);
+bool RegexQuote(std::vector<std::string> const& args,
+                cmExecutionStatus& status);
 
 bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
               size_t varIdx, cmMakefile& makefile);
@@ -218,6 +220,14 @@ bool HandleRegexCommand(std::vector<std::string> const& args,
     }
     return RegexReplace(args, status);
   }
+  if (mode == "QUOTE") {
+    if (args.size() < 4) {
+      status.SetError("sub-command REGEX, mode QUOTE needs "
+                      "at least 4 arguments total to command.");
+      return false;
+    }
+    return RegexQuote(args, status);
+  }
 
   std::string e = "sub-command REGEX does not recognize mode " + mode;
   status.SetError(e);
@@ -356,6 +366,28 @@ bool RegexReplace(std::vector<std::string> const& args,
   return true;
 }
 
+bool RegexQuote(std::vector<std::string> const& args,
+                cmExecutionStatus& status)
+{
+  //"STRING(REGEX QUOTE <output variable> <input> [<input>...]\n"
+  std::string const& outvar = args[2];
+  std::string const input =
+    cmJoin(cmMakeRange(args).advance(3), std::string());
+  std::string output;
+
+  // Escape all regex special characters
+  cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)");
+  if (!replaceHelper.Replace(input, output)) {
+    status.SetError(
+      "sub-command REGEX, mode QUOTE: " + replaceHelper.GetError() + ".");
+    return false;
+  }
+
+  // Store the output in the provided variable.
+  status.GetMakefile().AddDefinition(outvar, output);
+  return true;
+}
+
 bool HandleFindCommand(std::vector<std::string> const& args,
                        cmExecutionStatus& status)
 {

+ 13 - 0
Tests/StringFileTest/CMakeLists.txt

@@ -115,6 +115,19 @@ if(NOT "${CMAKE_MATCH_2}" STREQUAL "")
   message(SEND_ERROR "CMAKE_MATCH_2 wrong: \"${CMAKE_MATCH_2}\", expected empty string")
 endif()
 
+set(regex_quote "ab|c+12?3[x-z]$(y)\\t\\r\\n.cma*ke^[:alpha:]")
+string(REGEX QUOTE regex_quote_output "${regex_quote}")
+if(NOT regex_quote MATCHES "^${regex_quote_output}$")
+  message(SEND_ERROR "string(REGEX QUOTE) problem: \"${regex_quote}\" does not match against \"^${regex_quote_output}$\"")
+endif()
+string(REPLACE "." "a" nomatch_regex_quote "${regex_quote}")
+if(nomatch_regex_quote MATCHES "^${regex_quote_output}$")
+  message(SEND_ERROR "string(REGEX QUOTE) problem: \"${nomatch_regex_quote}\" matches against \"^${regex_quote_output}$\"")
+endif()
+string(REGEX QUOTE multi_regex_quote ${regex_quote} ${regex_quote})
+if(NOT "${regex_quote}${regex_quote}" MATCHES "^${multi_regex_quote}$")
+  message(SEND_ERROR "string(REGEX QUOTE) problem: \"${regex_quote}${regex_quote}\" does not match against \"^${multi_regex_quote}$\"")
+endif()
 
 string(STRIP "
   ST1