Branch data Line data Source code
1 : : /*
2 : : ** Copyright (C) 2021 Sylvain Fargier
3 : : **
4 : : ** This software is provided 'as-is', without any express or implied
5 : : ** warranty. In no event will the authors be held liable for any damages
6 : : ** arising from the use of this software.
7 : : **
8 : : ** Permission is granted to anyone to use this software for any purpose,
9 : : ** including commercial applications, and to alter it and redistribute it
10 : : ** freely, subject to the following restrictions:
11 : : **
12 : : ** 1. The origin of this software must not be misrepresented; you must not
13 : : ** claim that you wrote the original software. If you use this software
14 : : ** in a product, an acknowledgment in the product documentation would be
15 : : ** appreciated but is not required.
16 : : ** 2. Altered source versions must be plainly marked as such, and must not be
17 : : ** misrepresented as being the original software.
18 : : ** 3. This notice may not be removed or altered from any source distribution.
19 : : **
20 : : ** Created on: 2021-10-18T18:44:27+02:00
21 : : ** Author: Sylvain Fargier <fargie_s> <fargier.sylvain@free.fr>
22 : : **
23 : : */
24 : :
25 : : #include "Regex.hpp"
26 : :
27 : : #include <stdexcept>
28 : :
29 : : namespace ccut {
30 : :
31 : 20 : Regex::Regex(const std::string &re, int flags)
32 : : {
33 : 20 : int code = regcomp(&m_regex, re.c_str(), flags);
34 : 20 : if (code != 0)
35 : : {
36 : 2 : size_t sz = regerror(code, &m_regex, nullptr, 0);
37 : 2 : std::vector<char> err(sz + 1);
38 : :
39 : 2 : regerror(code, &m_regex, err.data(), err.size());
40 : 2 : throw std::invalid_argument(std::string("invalid regex: ") + err.data());
41 : 2 : }
42 : 18 : }
43 : :
44 : 18 : Regex::~Regex()
45 : : {
46 : 18 : ::regfree(&m_regex);
47 : 18 : }
48 : :
49 : 20 : bool Regex::match(const std::string &str,
50 : : size_t &offset,
51 : : MatchGroup &matches) const
52 : : {
53 : 38 : std::vector<regmatch_t> pmatch(matches.capacity() ? matches.capacity() :
54 : 58 : Regex::MaxMatchDefault);
55 : 20 : matches.resize(0);
56 : 20 : if (regexec(&m_regex, str.c_str() + offset, pmatch.size(), pmatch.data(),
57 : 40 : offset ? REG_NOTBOL : 0) != 0)
58 : 3 : return false;
59 : :
60 : 223 : for (const regmatch_t &m : pmatch)
61 : : {
62 : 206 : if (m.rm_so >= 0 && m.rm_eo >= 0)
63 : : {
64 : 26 : matches.push_back(str.substr(m.rm_so + offset, m.rm_eo - m.rm_so));
65 : : }
66 : : }
67 : 17 : offset = pmatch[0].rm_so + offset;
68 : 17 : return true;
69 : 20 : }
70 : :
71 : 8 : std::string Regex::replace(
72 : : const std::string &str,
73 : : std::function<std::string(size_t start, size_t end)> fun) const
74 : : {
75 : : regmatch_t matched;
76 : 8 : std::string res;
77 : 8 : res.reserve(str.size());
78 : :
79 : 30 : for (size_t off = 0; off < str.size();)
80 : : {
81 : 22 : if (regexec(&m_regex, str.c_str() + off, 1, &matched,
82 : 22 : (off != 0) ? REG_NOTBOL : 0) == 0)
83 : : {
84 : 19 : if (matched.rm_so != 0)
85 : 7 : res.insert(res.end(), str.begin() + off,
86 : 14 : str.begin() + off + matched.rm_so);
87 : 19 : res += fun(matched.rm_so + off, matched.rm_eo + off);
88 : 19 : if (matched.rm_eo == 0) // empty match, consume a character
89 : : {
90 : 8 : res += str[off];
91 : 8 : ++off;
92 : : }
93 : : else
94 : 11 : off += matched.rm_eo;
95 : : }
96 : : else
97 : : {
98 : 3 : res.insert(res.end(), str.begin() + off, str.end());
99 : 3 : off = str.size();
100 : : }
101 : : }
102 : :
103 : 16 : return res;
104 : 0 : }
105 : :
106 : 3 : std::string Regex::replace(
107 : : const std::string &str,
108 : : std::function<std::string(const MatchGroup &matches)> fun,
109 : : size_t maxMatch) const
110 : : {
111 : 3 : MatchGroup matches(maxMatch ? maxMatch : Regex::MaxMatchDefault);
112 : 3 : size_t prev = 0;
113 : 3 : std::string res;
114 : 3 : res.reserve(str.size());
115 : :
116 : 10 : for (size_t offset = 0; offset < str.size(); prev = offset)
117 : : {
118 : 7 : if (match(str, offset, matches))
119 : : {
120 : 6 : if (offset != prev)
121 : 2 : res.insert(res.end(), str.begin() + prev, str.begin() + offset);
122 : 6 : res += fun(matches);
123 : 6 : if (matches[0].empty())
124 : : {
125 : 0 : res += str[offset];
126 : 0 : ++offset;
127 : : }
128 : : else
129 : 6 : offset += matches[0].size();
130 : : }
131 : : else
132 : : {
133 : 1 : res.insert(res.end(), str.begin() + offset, str.end());
134 : 1 : offset = str.size();
135 : : }
136 : : }
137 : :
138 : 6 : return res;
139 : 3 : }
140 : :
141 : 6 : bool Regex::search(const std::string &str) const
142 : : {
143 : 6 : return regexec(&m_regex, str.c_str(), 0, nullptr, 0) == 0;
144 : : }
145 : :
146 : : } /* namespace ccut */
|