cmStringReplaceHelper.cxx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmStringReplaceHelper.h"
  4. #include <sstream>
  5. #include <utility>
  6. #include "cmMakefile.h"
  7. #include "cmPolicies.h"
  8. cmStringReplaceHelper::cmStringReplaceHelper(std::string const& regex,
  9. std::string replace_expr,
  10. cmMakefile* makefile)
  11. : RegExString(regex)
  12. , RegularExpression(regex)
  13. , ReplaceExpression(std::move(replace_expr))
  14. , Makefile(makefile)
  15. {
  16. this->ParseReplaceExpression();
  17. }
  18. bool cmStringReplaceHelper::Replace(std::string const& input,
  19. std::string& output)
  20. {
  21. output.clear();
  22. unsigned optAnchor = 0;
  23. if (this->Makefile &&
  24. this->Makefile->GetPolicyStatus(cmPolicies::CMP0186) !=
  25. cmPolicies::NEW) {
  26. optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
  27. }
  28. // Scan through the input for all matches.
  29. std::string::size_type base = 0;
  30. while (this->RegularExpression.find(input, base, optAnchor)) {
  31. if (this->Makefile) {
  32. this->Makefile->ClearMatches();
  33. this->Makefile->StoreMatches(this->RegularExpression);
  34. }
  35. auto l2 = this->RegularExpression.start();
  36. auto r = this->RegularExpression.end();
  37. // Concatenate the part of the input that was not matched.
  38. output += input.substr(base, l2 - base);
  39. // Make sure the match had some text.
  40. if (r - l2 == 0) {
  41. std::ostringstream error;
  42. error << "regex \"" << this->RegExString << "\" matched an empty string";
  43. this->ErrorString = error.str();
  44. return false;
  45. }
  46. // Concatenate the replacement for the match.
  47. for (auto const& replacement : this->Replacements) {
  48. if (replacement.Number < 0) {
  49. // This is just a plain-text part of the replacement.
  50. output += replacement.Value;
  51. } else {
  52. // Replace with part of the match.
  53. auto n = replacement.Number;
  54. auto start = this->RegularExpression.start(n);
  55. if (start != std::string::npos) {
  56. output += this->RegularExpression.match(n);
  57. } else {
  58. std::ostringstream error;
  59. error << "replace expression \"" << this->ReplaceExpression
  60. << "\" contains an out-of-range escape for regex \""
  61. << this->RegExString << "\"";
  62. this->ErrorString = error.str();
  63. return false;
  64. }
  65. }
  66. }
  67. // Move past the match.
  68. base = r;
  69. }
  70. // Concatenate the text after the last match.
  71. output += input.substr(base);
  72. return true;
  73. }
  74. void cmStringReplaceHelper::ParseReplaceExpression()
  75. {
  76. std::string::size_type l = 0;
  77. while (l < this->ReplaceExpression.length()) {
  78. auto r = this->ReplaceExpression.find('\\', l);
  79. if (r == std::string::npos) {
  80. r = this->ReplaceExpression.length();
  81. this->Replacements.emplace_back(
  82. this->ReplaceExpression.substr(l, r - l));
  83. } else {
  84. if (r - l > 0) {
  85. this->Replacements.emplace_back(
  86. this->ReplaceExpression.substr(l, r - l));
  87. }
  88. if (r == (this->ReplaceExpression.length() - 1)) {
  89. this->ValidReplaceExpression = false;
  90. this->ErrorString = "replace-expression ends in a backslash";
  91. return;
  92. }
  93. if ((this->ReplaceExpression[r + 1] >= '0') &&
  94. (this->ReplaceExpression[r + 1] <= '9')) {
  95. this->Replacements.emplace_back(this->ReplaceExpression[r + 1] - '0');
  96. } else if (this->ReplaceExpression[r + 1] == 'n') {
  97. this->Replacements.emplace_back("\n");
  98. } else if (this->ReplaceExpression[r + 1] == '\\') {
  99. this->Replacements.emplace_back("\\");
  100. } else {
  101. this->ValidReplaceExpression = false;
  102. std::ostringstream error;
  103. error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2)
  104. << "\" in replace-expression";
  105. this->ErrorString = error.str();
  106. return;
  107. }
  108. r += 2;
  109. }
  110. l = r;
  111. }
  112. }