cmStringReplaceHelper.cxx 3.9 KB

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