cmStringReplaceHelper.cxx 3.9 KB

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