cmStringReplaceHelper.cxx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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.push_back(this->ReplaceExpression.substr(l, r - l));
  77. } else {
  78. if (r - l > 0) {
  79. this->Replacements.push_back(this->ReplaceExpression.substr(l, r - l));
  80. }
  81. if (r == (this->ReplaceExpression.length() - 1)) {
  82. this->ValidReplaceExpression = false;
  83. this->ErrorString = "replace-expression ends in a backslash";
  84. return;
  85. }
  86. if ((this->ReplaceExpression[r + 1] >= '0') &&
  87. (this->ReplaceExpression[r + 1] <= '9')) {
  88. this->Replacements.push_back(this->ReplaceExpression[r + 1] - '0');
  89. } else if (this->ReplaceExpression[r + 1] == 'n') {
  90. this->Replacements.push_back("\n");
  91. } else if (this->ReplaceExpression[r + 1] == '\\') {
  92. this->Replacements.push_back("\\");
  93. } else {
  94. this->ValidReplaceExpression = false;
  95. std::ostringstream error;
  96. error << "Unknown escape \"" << this->ReplaceExpression.substr(r, 2)
  97. << "\" in replace-expression";
  98. this->ErrorString = error.str();
  99. return;
  100. }
  101. r += 2;
  102. }
  103. l = r;
  104. }
  105. }