From 1462561a8cab7cf3cad9979019778f3c13b0bdf9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim=20H=C3=BCtz?= <tim@huetz.biz>
Date: Tue, 15 Feb 2011 11:20:47 +0100
Subject: Add a string(FIND) sub-command (#11795)

---
 Source/cmStringCommand.cxx              | 68 ++++++++++++++++++++++++++++-
 Source/cmStringCommand.h                |  6 +++
 Tests/CMakeTests/StringTest.cmake.in    |  2 +-
 Tests/CMakeTests/StringTestScript.cmake | 77 +++++++++++++++++++++++++++++++++
 4 files changed, 151 insertions(+), 2 deletions(-)

diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 949fcf5..1f873e2 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -72,7 +72,11 @@ bool cmStringCommand
     {
     return this->HandleRandomCommand(args);
     }
-  
+  else if(subCommand == "FIND")
+    {
+    return this->HandleFindCommand(args);
+    }
+
   std::string e = "does not recognize sub-command "+subCommand;
   this->SetError(e.c_str());
   return false;
@@ -499,6 +503,68 @@ void cmStringCommand::StoreMatches(cmMakefile* mf,cmsys::RegularExpression& re)
 }
 
 //----------------------------------------------------------------------------
+bool cmStringCommand::HandleFindCommand(std::vector<std::string> const&
+                                           args)
+{
+  // check if all required parameters were passed
+  if(args.size() < 4 || args.size() > 5)
+    {
+    this->SetError("sub-command FIND requires 3 or 4 parameters.");
+    return false;
+    }
+
+  // check if the reverse flag was set or not
+  bool reverseMode = false;
+  if(args.size() == 5 && args[4] == "REVERSE")
+    {
+    reverseMode = true;
+    }
+
+  // if we have 5 arguments the last one must be REVERSE
+  if(args.size() == 5 && args[4] != "REVERSE")
+    {
+    this->SetError("sub-command FIND: unknown last parameter");
+    return false;
+    }
+
+  // local parameter names.
+  const std::string& sstring = args[1];
+  const std::string& schar = args[2];
+  const std::string& outvar = args[3];
+
+  // ensure that the user cannot accidentally specify REVERSE as a variable
+  if(outvar == "REVERSE")
+    {
+    this->SetError("sub-command FIND does not allow to select REVERSE as "
+                   "the output variable.  "
+                   "Maybe you missed the actual output variable?");
+    return false;
+    }
+
+  // try to find the character and return its position
+  size_t pos;
+  if(!reverseMode)
+    {
+    pos = sstring.find(schar);
+    }
+  else
+    {
+    pos = sstring.rfind(schar);
+    }
+  if(std::string::npos != pos)
+    {
+    std::stringstream s;
+    s << pos;
+    this->Makefile->AddDefinition(outvar.c_str(), s.str().c_str());
+    return true;
+    }
+
+  // the character was not found, but this is not really an error
+  this->Makefile->AddDefinition(outvar.c_str(), "-1");
+  return true;
+}
+
+//----------------------------------------------------------------------------
 bool cmStringCommand::HandleCompareCommand(std::vector<std::string> const&
                                            args)
 {
diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h
index 2a916b4..9586449 100644
--- a/Source/cmStringCommand.h
+++ b/Source/cmStringCommand.h
@@ -90,6 +90,7 @@ public:
       "  string(STRIP <string> <output variable>)\n"
       "  string(RANDOM [LENGTH <length>] [ALPHABET <alphabet>]\n"
       "         [RANDOM_SEED <seed>] <output variable>)\n"
+      "  string(FIND <string> <substring> <output variable> [REVERSE])\n"
       "REGEX MATCH will match the regular expression once and store the "
       "match in the output variable.\n"
       "REGEX MATCHALL will match the regular expression as many times as "
@@ -117,6 +118,10 @@ public:
       "characters and default alphabet is all numbers and upper and "
       "lower case letters.  If an integer RANDOM_SEED is given, its "
       "value will be used to seed the random number generator.\n"
+      "FIND will return the position where the given substring was found "
+      "in the supplied string. If the REVERSE flag was used, the command "
+      "will search for the position of the last occurrence of the "
+      "specified substring.\n"
       "The following characters have special meaning in regular expressions:\n"
       "   ^         Matches at beginning of a line\n"
       "   $         Matches at end of a line\n"
@@ -152,6 +157,7 @@ protected:
   bool HandleSubstringCommand(std::vector<std::string> const& args);
   bool HandleStripCommand(std::vector<std::string> const& args);
   bool HandleRandomCommand(std::vector<std::string> const& args);
+  bool HandleFindCommand(std::vector<std::string> const& args);
 
   class RegexReplacement
   {
diff --git a/Tests/CMakeTests/StringTest.cmake.in b/Tests/CMakeTests/StringTest.cmake.in
index 54fee7f..6bb60f4 100644
--- a/Tests/CMakeTests/StringTest.cmake.in
+++ b/Tests/CMakeTests/StringTest.cmake.in
@@ -1,7 +1,7 @@
 # Execute each test listed in StringTestScript.cmake:
 #
 set(scriptname "@CMAKE_CURRENT_SOURCE_DIR@/StringTestScript.cmake")
-set(number_of_tests_expected 52)
+set(number_of_tests_expected 69)
 
 include("@CMAKE_CURRENT_SOURCE_DIR@/ExecuteScriptTests.cmake")
 execute_all_script_tests(${scriptname} number_of_tests_executed)
diff --git a/Tests/CMakeTests/StringTestScript.cmake b/Tests/CMakeTests/StringTestScript.cmake
index 3703856..8dfdc89 100644
--- a/Tests/CMakeTests/StringTestScript.cmake
+++ b/Tests/CMakeTests/StringTestScript.cmake
@@ -194,6 +194,83 @@ elseif(testname STREQUAL random_with_various_alphabets) # pass
   string(RANDOM LENGTH 78 ALPHABET "~`!@#$%^&*()_-+={}[]\\|:\\;'\",.<>/?" v)
   message(STATUS "v='${v}'")
 
+elseif(testname STREQUAL string_find_with_no_parameter) # fail
+  string(FIND)
+
+elseif(testname STREQUAL string_find_with_one_parameter) # fail
+  string(FIND "CMake is great.")
+
+elseif(testname STREQUAL string_find_with_two_parameters) # fail
+  string(FIND "CMake is great." "a")
+
+elseif(testname STREQUAL string_find_with_three_parameters) # pass
+  string(FIND "CMake is great." "a" v)
+  message(STATUS "v='${v}'")
+
+elseif(testname STREQUAL string_find_with_four_parameters) # fail
+  string(FIND "CMake is great." "a" v v2)
+
+elseif(testname STREQUAL string_find_reverse_with_no_parameter) # fail
+  string(FIND REVERSE)
+
+elseif(testname STREQUAL string_find_reverse_with_one_parameter) # fail
+  string(FIND "CMake is great." REVERSE)
+
+elseif(testname STREQUAL string_find_reverse_with_two_parameters) # fail
+  string(FIND "CMake is great." "a" REVERSE)
+
+elseif(testname STREQUAL string_find_reverse_with_three_parameters) # pass
+  string(FIND "CMake is great." "a" v REVERSE)
+  message(STATUS "v='${v}'")
+
+elseif(testname STREQUAL string_find_reverse_with_four_parameters_part1) # fail
+  string(FIND "CMake is great." "a" v v2 REVERSE)
+
+elseif(testname STREQUAL string_find_reverse_with_four_parameters_part2) # fail
+  string(FIND "CMake is great." "a" v REVERSE v2)
+
+elseif(testname STREQUAL string_find_with_no_possible_result) # pass
+  string(FIND "CMake is a great application." "z" v)
+  message(STATUS "v='${v}'")
+  if(NOT(-1 EQUAL ${v}))
+    message(SEND_ERROR "FIND sub-command should return -1 but returned ${v}.")
+  endif(NOT(-1 EQUAL ${v}))
+
+elseif(testname STREQUAL string_find_reverse_with_no_possible_result) # pass
+  string(FIND "CMake is a great application." "z" v REVERSE)
+  message(STATUS "v='${v}'")
+  if(NOT(-1 EQUAL ${v}))
+    message(SEND_ERROR "FIND REVERSE sub-command should return -1 but returned ${v}.")
+  endif(NOT(-1 EQUAL ${v}))
+
+elseif(testname STREQUAL string_find_with_required_result) # pass
+  string(FIND "CMake is a great application." "g" v)
+  message(STATUS "v='${v}'")
+  if(NOT(11 EQUAL ${v}))
+    message(SEND_ERROR "FIND sub-command should return 11 but returned ${v}.")
+  endif(NOT(11 EQUAL ${v}))
+
+elseif(testname STREQUAL string_find_reverse_with_required_result) # pass
+  string(FIND "CMake is a great application." "e" v REVERSE)
+  message(STATUS "v='${v}'")
+  if(NOT(13 EQUAL ${v}))
+    message(SEND_ERROR "FIND REVERSE sub-command should return 13 but returned ${v}.")
+  endif(NOT(13 EQUAL ${v}))
+
+elseif(testname STREQUAL string_find_word_reverse_with_required_result) # pass
+  string(FIND "The command should find REVERSE in this string. Or maybe this REVERSE?!" "REVERSE" v)
+  message(STATUS "v='${v}'")
+  if(NOT(24 EQUAL ${v}))
+    message(SEND_ERROR "FIND sub-command should return 24 but returned ${v}.")
+  endif(NOT(24 EQUAL ${v}))
+
+elseif(testname STREQUAL string_find_reverse_word_reverse_with_required_result) # pass
+  string(FIND "The command should find REVERSE in this string. Or maybe this REVERSE?!" "REVERSE" v REVERSE)
+  message(STATUS "v='${v}'")
+  if(NOT(62 EQUAL ${v}))
+    message(SEND_ERROR "FIND sub-command should return 62 but returned ${v}.")
+  endif(NOT(62 EQUAL ${v}))
+
 else() # fail
   message(FATAL_ERROR "testname='${testname}' - error: no such test in '${CMAKE_CURRENT_LIST_FILE}'")
 
-- 
cgit v0.12


From 006124b44618db3ed672c7ea069b174b966429a1 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Thu, 17 Feb 2011 08:44:22 -0500
Subject: Avoid direct use of std::stringstream

In method cmStringCommand::HandleFindCommand added by parent commit use
the cmOStringStream compatibility wrapper instead of std::stringstream.
---
 Source/cmStringCommand.cxx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 1f873e2..19d2369 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -553,7 +553,7 @@ bool cmStringCommand::HandleFindCommand(std::vector<std::string> const&
     }
   if(std::string::npos != pos)
     {
-    std::stringstream s;
+    cmOStringStream s;
     s << pos;
     this->Makefile->AddDefinition(outvar.c_str(), s.str().c_str());
     return true;
-- 
cgit v0.12