GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/pathspec/pathspec.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 201 210 95.7%
Branches: 120 174 69.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "pathspec.h"
6
7 #include <cassert>
8
9 #include "util/logging.h"
10 #include "util/smalloc.h"
11
12 165 Pathspec::Pathspec(const std::string &spec) :
13 165 regex_(NULL),
14 165 relaxed_regex_(NULL),
15 165 prefix_regex_(NULL),
16 165 regex_compiled_(false),
17 165 relaxed_regex_compiled_(false),
18 165 prefix_regex_compiled_(false),
19 165 glob_string_compiled_(false),
20 165 glob_string_sequence_compiled_(false),
21 165 valid_(true),
22 165 absolute_(false)
23 {
24
1/2
✓ Branch 1 taken 165 times.
✗ Branch 2 not taken.
165 Parse(spec);
25
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 164 times.
165 if (patterns_.size() == 0) {
26 1 valid_ = false;
27 }
28
29 165 ElementPatterns::const_iterator i = patterns_.begin();
30 165 const ElementPatterns::const_iterator iend = patterns_.end();
31
2/2
✓ Branch 2 taken 363 times.
✓ Branch 3 taken 165 times.
528 for (; i != iend; ++i) {
32
2/2
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 361 times.
363 if (!i->IsValid()) {
33 2 valid_ = false;
34 }
35 }
36 165 }
37
38 // Compiled regex structure cannot be duplicated and needs to be re-compiled
39 // Note: the copy-constructed object will perform a lazy evaluation again
40 181 Pathspec::Pathspec(const Pathspec &other) :
41 181 patterns_(other.patterns_),
42 181 regex_(NULL),
43 181 relaxed_regex_(NULL),
44 181 prefix_regex_(NULL),
45
1/2
✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
181 glob_string_(other.glob_string_),
46
1/2
✓ Branch 1 taken 181 times.
✗ Branch 2 not taken.
181 glob_string_sequence_(other.glob_string_sequence_),
47 181 regex_compiled_(false),
48 181 relaxed_regex_compiled_(false),
49 181 prefix_regex_compiled_(false),
50 181 glob_string_compiled_(other.glob_string_compiled_),
51 181 glob_string_sequence_compiled_(other.glob_string_sequence_compiled_),
52 181 valid_(other.valid_),
53 181 absolute_(other.absolute_) {}
54
55 346 Pathspec::~Pathspec() {
56 346 DestroyRegularExpressions();
57 346 }
58
59 3 Pathspec& Pathspec::operator=(const Pathspec &other) {
60
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (this != &other) {
61 3 DestroyRegularExpressions(); // see: copy c'tor for details
62 3 patterns_ = other.patterns_;
63
64 3 glob_string_compiled_ = other.glob_string_compiled_;
65 3 glob_string_ = other.glob_string_;
66
67 3 glob_string_sequence_compiled_ = other.glob_string_sequence_compiled_;
68 3 glob_string_sequence_ = other.glob_string_sequence_;
69
70 3 valid_ = other.valid_;
71 3 absolute_ = other.absolute_;
72 }
73
74 3 return *this;
75 }
76
77
78 165 void Pathspec::Parse(const std::string &spec) {
79 // parsing is done using std::string iterators to walk through the entire
80 // pathspec parameter. Thus, all parsing methods receive references to these
81 // iterators and increment itr as they pass along.
82 165 std::string::const_iterator itr = spec.begin();
83 165 const std::string::const_iterator end = spec.end();
84
85 165 absolute_ = (*itr == kSeparator);
86
2/2
✓ Branch 1 taken 689 times.
✓ Branch 2 taken 165 times.
854 while (itr != end) {
87
2/2
✓ Branch 1 taken 326 times.
✓ Branch 2 taken 363 times.
689 if (*itr == kSeparator) {
88 326 ++itr;
89 326 continue;
90 }
91
1/2
✓ Branch 1 taken 363 times.
✗ Branch 2 not taken.
363 ParsePathElement(end, &itr);
92 }
93 165 }
94
95 363 void Pathspec::ParsePathElement(
96 const std::string::const_iterator &end,
97 std::string::const_iterator *itr
98 ) {
99 // find the end of the current pattern element (next directory boundary)
100 363 const std::string::const_iterator begin_element = *itr;
101
6/6
✓ Branch 1 taken 1742 times.
✓ Branch 2 taken 157 times.
✓ Branch 4 taken 1536 times.
✓ Branch 5 taken 206 times.
✓ Branch 6 taken 1536 times.
✓ Branch 7 taken 363 times.
1899 while (*itr != end && **itr != kSeparator) {
102 1536 ++(*itr);
103 }
104 363 const std::string::const_iterator end_element = *itr;
105
106 // create a PathspecElementPattern out of this directory description
107
2/4
✓ Branch 1 taken 363 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 363 times.
✗ Branch 5 not taken.
363 patterns_.push_back(PathspecElementPattern(begin_element, end_element));
108 363 }
109
110 407 bool Pathspec::IsMatching(const std::string &query_path) const {
111
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 407 times.
407 assert(IsValid());
112
113
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 390 times.
407 if (query_path.empty()) {
114 17 return false;
115 }
116
117 390 const bool query_is_absolute = (query_path[0] == kSeparator);
118
4/6
✓ Branch 0 taken 301 times.
✓ Branch 1 taken 89 times.
✓ Branch 3 taken 288 times.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
767 return (!query_is_absolute || this->IsAbsolute()) &&
119
2/2
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 228 times.
767 IsPathspecMatching(query_path);
120 }
121
122 12 bool Pathspec::IsPrefixMatching(const std::string &query_path) const {
123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 assert(IsValid());
124
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 assert(IsAbsolute());
125
126
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if (query_path.empty()) {
127 return false;
128 }
129
130 12 const bool query_is_absolute = (query_path[0] == kSeparator);
131
4/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 4 times.
12 return (query_is_absolute && IsPathspecPrefixMatching(query_path));
132 }
133
134 315 bool Pathspec::IsMatchingRelaxed(const std::string &query_path) const {
135
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 315 times.
315 assert(IsValid());
136
137
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 279 times.
315 if (query_path.empty()) {
138 36 return false;
139 }
140
141 279 return IsPathspecMatchingRelaxed(query_path);
142 }
143
144 377 bool Pathspec::IsPathspecMatching(const std::string &query_path) const {
145 377 return ApplyRegularExpression(query_path, GetRegularExpression());
146 }
147
148 11 bool Pathspec::IsPathspecPrefixMatching(const std::string &query_path) const {
149 11 return ApplyRegularExpression(query_path, GetPrefixRegularExpression());
150 }
151
152 279 bool Pathspec::IsPathspecMatchingRelaxed(const std::string &query_path) const {
153 279 return ApplyRegularExpression(query_path, GetRelaxedRegularExpression());
154 }
155
156 667 bool Pathspec::ApplyRegularExpression(const std::string &query_path,
157 regex_t *regex) const {
158 667 const char *path = query_path.c_str();
159 667 const int retval = regexec(regex, path, 0, NULL, 0);
160
161
3/4
✓ Branch 0 taken 459 times.
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 459 times.
667 if (retval != 0 && retval != REG_NOMATCH) {
162 PrintRegularExpressionError(retval);
163 }
164
165 667 return (retval == 0);
166 }
167
168 377 regex_t* Pathspec::GetRegularExpression() const {
169
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 302 times.
377 if (!regex_compiled_) {
170 75 const bool is_relaxed = false;
171
1/2
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
75 const std::string regex = GenerateRegularExpression(is_relaxed);
172
1/2
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
75 LogCvmfs(kLogPathspec, kLogDebug, "compiled regex: %s", regex.c_str());
173
174
1/2
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
75 regex_ = CompileRegularExpression(regex);
175 75 regex_compiled_ = true;
176 75 }
177
178 377 return regex_;
179 }
180
181 11 regex_t* Pathspec::GetPrefixRegularExpression() const {
182
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 9 times.
11 if (!prefix_regex_compiled_) {
183 2 const bool is_relaxed = false;
184 2 const bool is_prefix = true;
185
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 const std::string regex = GenerateRegularExpression(is_relaxed, is_prefix);
186
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 LogCvmfs(kLogPathspec, kLogDebug, "compiled regex: %s", regex.c_str());
187
188
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 prefix_regex_ = CompileRegularExpression(regex);
189 2 prefix_regex_compiled_ = true;
190 2 }
191
192 11 return prefix_regex_;
193 }
194
195 279 regex_t* Pathspec::GetRelaxedRegularExpression() const {
196
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 251 times.
279 if (!relaxed_regex_compiled_) {
197 28 const bool is_relaxed = true;
198
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 const std::string regex = GenerateRegularExpression(is_relaxed);
199
1/2
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
28 LogCvmfs(kLogPathspec, kLogDebug, "compiled relaxed regex: %s",
200 regex.c_str());
201
202
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 relaxed_regex_ = CompileRegularExpression(regex);
203 28 relaxed_regex_compiled_ = true;
204 28 }
205
206 279 return relaxed_regex_;
207 }
208
209 105 std::string Pathspec::GenerateRegularExpression(
210 const bool is_relaxed,
211 const bool is_prefix) const
212 {
213 // start matching at the first character
214
1/2
✓ Branch 2 taken 105 times.
✗ Branch 3 not taken.
105 std::string regex = "^";
215
216 // absolute paths require a / in the beginning
217
2/2
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 19 times.
105 if (IsAbsolute()) {
218
1/2
✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
86 regex += kSeparator;
219 }
220
221 // concatenate the regular expressions of the compiled path elements
222 105 ElementPatterns::const_iterator i = patterns_.begin();
223 105 const ElementPatterns::const_iterator iend = patterns_.end();
224
2/2
✓ Branch 2 taken 227 times.
✓ Branch 3 taken 105 times.
332 for (; i != iend; ++i) {
225
2/4
✓ Branch 2 taken 227 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 227 times.
✗ Branch 6 not taken.
227 regex += i->GenerateRegularExpression(is_relaxed);
226
2/2
✓ Branch 2 taken 122 times.
✓ Branch 3 taken 105 times.
227 if (i + 1 != iend) {
227
1/2
✓ Branch 1 taken 122 times.
✗ Branch 2 not taken.
122 regex += kSeparator;
228 }
229 }
230
231
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 103 times.
105 if (is_prefix) {
232
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 regex += "($|";
233
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 regex += kSeparator;
234
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 regex += ".*$)";
235 } else {
236 // a path might end with a trailing slash
237 // (pathspec does not distinguish files and directories)
238
1/2
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
103 regex += kSeparator;
239
1/2
✓ Branch 1 taken 103 times.
✗ Branch 2 not taken.
103 regex += "?$";
240 }
241
242 210 return regex;
243 }
244
245 105 regex_t* Pathspec::CompileRegularExpression(const std::string &regex) const {
246 105 regex_t *result = reinterpret_cast<regex_t *>(smalloc(sizeof(regex_t)));
247 105 const int flags = REG_NOSUB | REG_NEWLINE | REG_EXTENDED;
248 105 const int retval = regcomp(result, regex.c_str(), flags);
249
250
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 105 times.
105 if (retval != 0) {
251 PrintRegularExpressionError(retval);
252 assert(false && "failed to compile regex");
253 }
254
255 105 return result;
256 }
257
258 349 void Pathspec::DestroyRegularExpressions() {
259
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 274 times.
349 if (regex_compiled_) {
260
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
75 assert(regex_ != NULL);
261 75 regfree(regex_);
262 75 regex_ = NULL;
263 75 regex_compiled_ = false;
264 }
265
266
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 321 times.
349 if (relaxed_regex_compiled_) {
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 assert(relaxed_regex_ != NULL);
268 28 regfree(relaxed_regex_);
269 28 relaxed_regex_ = NULL;
270 28 relaxed_regex_compiled_ = false;
271 }
272 349 }
273
274 90 bool Pathspec::operator==(const Pathspec &other) const {
275
1/2
✓ Branch 2 taken 54 times.
✗ Branch 3 not taken.
144 if (patterns_.size() != other.patterns_.size() ||
276
6/6
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 36 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 49 times.
✓ Branch 6 taken 41 times.
✓ Branch 7 taken 49 times.
144 IsValid() != other.IsValid() ||
277 54 IsAbsolute() != other.IsAbsolute()) {
278 41 return false;
279 }
280
281 49 ElementPatterns::const_iterator i = patterns_.begin();
282 49 const ElementPatterns::const_iterator iend = patterns_.end();
283 49 ElementPatterns::const_iterator j = other.patterns_.begin();
284 49 const ElementPatterns::const_iterator jend = other.patterns_.end();
285
286
5/6
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 16 times.
✓ Branch 6 taken 102 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 102 times.
✓ Branch 9 taken 16 times.
118 for (; i != iend && j != jend; ++i, ++j) {
287
3/4
✓ Branch 3 taken 102 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 33 times.
✓ Branch 6 taken 69 times.
102 if (*i != *j) {
288 33 return false;
289 }
290 }
291
292 16 return true;
293 }
294
295 void Pathspec::PrintRegularExpressionError(const int error_code) const {
296 assert(regex_compiled_);
297 const size_t errbuf_size = 1024;
298 char error[errbuf_size];
299 regerror(error_code, regex_, error, errbuf_size);
300 LogCvmfs(kLogPathspec, kLogStderr, "RegEx Error: %d - %s", error_code, error);
301 }
302
303 17 const Pathspec::GlobStringSequence& Pathspec::GetGlobStringSequence() const {
304
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (!glob_string_sequence_compiled_) {
305 17 GenerateGlobStringSequence();
306 17 glob_string_sequence_compiled_ = true;
307 }
308 17 return glob_string_sequence_;
309 }
310
311
312 17 void Pathspec::GenerateGlobStringSequence() const {
313
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 assert(glob_string_sequence_.empty());
314 17 ElementPatterns::const_iterator i = patterns_.begin();
315 17 const ElementPatterns::const_iterator iend = patterns_.end();
316
2/2
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 17 times.
51 for (; i != iend; ++i) {
317
1/2
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 const std::string glob_string = i->GenerateGlobString();
318
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
34 glob_string_sequence_.push_back(glob_string);
319 34 }
320 17 }
321
322
323 23 const std::string& Pathspec::GetGlobString() const {
324
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 6 times.
23 if (!glob_string_compiled_) {
325 17 GenerateGlobString();
326 17 glob_string_compiled_ = true;
327 }
328 23 return glob_string_;
329 }
330
331
332 17 void Pathspec::GenerateGlobString() const {
333
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
17 assert(glob_string_.empty());
334
335 17 bool is_first = true;
336
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 const GlobStringSequence &seq = GetGlobStringSequence();
337 17 GlobStringSequence::const_iterator i = seq.begin();
338 17 const GlobStringSequence::const_iterator iend = seq.end();
339
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 17 times.
51 for (; i != iend; ++i) {
340
6/6
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 6 times.
✓ Branch 5 taken 28 times.
✓ Branch 6 taken 6 times.
34 if (!is_first || IsAbsolute()) {
341
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 glob_string_ += kSeparator;
342 }
343
1/2
✓ Branch 2 taken 34 times.
✗ Branch 3 not taken.
34 glob_string_ += *i;
344 34 is_first = false;
345 }
346 17 }
347