PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/search-and-replace.cpp

#
C++ | 194 lines | 155 code | 12 blank | 27 comment | 36 complexity | 527bdc589335c3d182cfecd027dc2d39 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. * Copyright (c) 2002-2010 Peter Simons <simons@cryp.to>
  3. * Copyright (c) 2001 The OSSP Project <http://www.ossp.org/>
  4. * Copyright (c) 2001 Cable & Wireless Deutschland <http://www.cw.com/de/>
  5. *
  6. * Permission to use, copy, modify, and distribute this software for
  7. * any purpose with or without fee is hereby granted, provided that
  8. * the above copyright notice and this permission notice appear in all
  9. * copies.
  10. *
  11. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  12. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  13. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  14. * IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
  15. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  16. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  17. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  18. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  19. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  20. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  21. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  22. * SUCH DAMAGE.
  23. */
  24. #include <sys/types.h>
  25. #include <regex.h>
  26. #include "internal.hpp"
  27. namespace varexp
  28. {
  29. namespace internal
  30. {
  31. inline void expand_regex_replace(const char* data, const string& replace,
  32. regmatch_t* pmatch, string& expanded)
  33. {
  34. const char* p = replace.c_str();
  35. size_t i;
  36. while (p != replace.c_str() + replace.size())
  37. {
  38. if (*p == '\\')
  39. {
  40. if (replace.c_str() + replace.size() - p <= 1)
  41. {
  42. throw incomplete_quoted_pair();
  43. }
  44. p++;
  45. if (*p == '\\')
  46. {
  47. expanded.append(p, 1);
  48. ++p;
  49. continue;
  50. }
  51. if (!isdigit((int)*p))
  52. {
  53. throw unknown_quoted_pair_in_replace();
  54. }
  55. i = *p - '0';
  56. p++;
  57. if (pmatch[i].rm_so == -1)
  58. {
  59. throw submatch_out_of_range();
  60. }
  61. expanded.append(data + pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so);
  62. }
  63. else
  64. {
  65. expanded.append(p, 1);
  66. ++p;
  67. }
  68. }
  69. }
  70. void search_and_replace(std::string& data,
  71. const std::string& search,
  72. const std::string& replace,
  73. const std::string& flags)
  74. {
  75. bool case_insensitive = false;
  76. bool global = false;
  77. bool no_regex = false;
  78. if (search.empty())
  79. throw empty_search_string();
  80. for (string::const_iterator p = flags.begin(); p != flags.end(); ++p)
  81. {
  82. switch (tolower(*p))
  83. {
  84. case 'i':
  85. case_insensitive = true;
  86. break;
  87. case 'g':
  88. global = true;
  89. break;
  90. case 't':
  91. no_regex = true;
  92. break;
  93. default:
  94. throw unknown_replace_flag();
  95. }
  96. }
  97. if (no_regex)
  98. {
  99. string tmp;
  100. for (const char* p = data.data(); p != data.data() + data.size();)
  101. {
  102. int rc;
  103. if (case_insensitive)
  104. rc = strncasecmp(p, search.data(), search.size());
  105. else
  106. rc = strncmp(p, search.data(), search.size());
  107. if (rc != 0)
  108. {
  109. // No match, copy character.
  110. tmp.append(p, 1);
  111. ++p;
  112. }
  113. else
  114. {
  115. tmp.append(replace);
  116. p += search.size();
  117. if (!global)
  118. {
  119. tmp.append(p, data.data() + data.size() - p);
  120. break;
  121. }
  122. }
  123. }
  124. data = tmp;
  125. }
  126. else
  127. {
  128. struct sentry
  129. {
  130. sentry() : preg(0) { }
  131. ~sentry() { if (preg) regfree(preg); }
  132. regex_t* preg;
  133. } s;
  134. regex_t preg;
  135. regmatch_t pmatch[10];
  136. int regexec_flag;
  137. // Compile the pattern.
  138. int rc = regcomp(&preg, search.c_str(), REG_NEWLINE | REG_EXTENDED|((case_insensitive)?REG_ICASE:0));
  139. if (rc != 0)
  140. {
  141. throw invalid_regex_in_replace();
  142. }
  143. else
  144. s.preg = &preg;
  145. // Match the pattern and create the result string in
  146. // the tmp buffer.
  147. string tmp;
  148. for (const char* p = data.c_str(); p != data.c_str() + data.size(); )
  149. {
  150. if (p == data.c_str() || p[-1] == '\n')
  151. regexec_flag = 0;
  152. else
  153. regexec_flag = REG_NOTBOL;
  154. if (regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag) == REG_NOMATCH ||
  155. p + pmatch[0].rm_so == data.c_str() + data.size())
  156. {
  157. tmp.append(p, data.c_str() + data.size() - p);
  158. break;
  159. }
  160. else
  161. {
  162. string expanded;
  163. expand_regex_replace(p, replace, pmatch, expanded);
  164. tmp.append(p, pmatch[0].rm_so).append(expanded);
  165. p += pmatch[0].rm_eo;
  166. if (pmatch[0].rm_eo - pmatch[0].rm_so == 0)
  167. {
  168. tmp.append(p, 1);
  169. ++p;
  170. }
  171. if (!global)
  172. {
  173. tmp.append(p, data.c_str() + data.size() - p);
  174. break;
  175. }
  176. }
  177. }
  178. data = tmp;
  179. }
  180. }
  181. }
  182. }