8#include "algorithm.hpp"
16enum class glob_token_type_t {
29 case glob_token_type_t::String: lhs <<
"String";
break;
30 case glob_token_type_t::StringList: lhs <<
"StringList";
break;
31 case glob_token_type_t::CharacterList: lhs <<
"CharacterList";
break;
32 case glob_token_type_t::InverseCharacterList: lhs <<
"InverseCharacterList";
break;
33 case glob_token_type_t::Separator: lhs <<
"Separator";
break;
34 case glob_token_type_t::AnyString: lhs <<
"AnyString";
break;
35 case glob_token_type_t::AnyCharacter: lhs <<
"AnyCharacter";
break;
36 case glob_token_type_t::AnyDirectory: lhs <<
"AnyDirectory";
break;
37 default: tt_no_default();
43 glob_token_type_t type;
47 glob_token_t(glob_token_type_t type) : type(type), value(), values() {}
53using glob_token_iterator = glob_token_list_t::iterator;
54using glob_token_const_iterator = glob_token_list_t::const_iterator;
58 return lhs.type == rhs.type && lhs.value == rhs.value && lhs.values == rhs.values;
63 if (rhs.value.size() > 0) {
64 lhs <<
":" << rhs.value;
65 }
else if (rhs.values.size() > 0) {
67 for (
size_t i = 0; i < rhs.values.size(); i++) {
97inline glob_token_list_t parseGlob(std::string_view glob)
105 FoundSlashDoubleStar,
109 state_t state = state_t::Idle;
114 bool isInverse =
false;
115 bool isFirstCharacter =
false;
116 bool isRange =
false;
118 auto i = glob.begin();
120 auto c = (i != glob.end()) ? *i :
'\0';
125 case '/': state = state_t::FoundSlash;
break;
126 case '?': r.emplace_back(glob_token_type_t::AnyCharacter);
break;
127 case '*': r.emplace_back(glob_token_type_t::AnyString);
break;
130 isFirstCharacter =
true;
132 state = state_t::FoundBracket;
134 case '{': state = state_t::FoundBrace;
break;
135 case '\\': state = state_t::FoundEscape;
break;
137 default: state = state_t::FoundText;
continue;
141 case state_t::FoundText:
142 if (c ==
'/' || c ==
'?' || c ==
'*' || c ==
'[' || c ==
'{' || c ==
'\0') {
143 r.emplace_back(glob_token_type_t::String, tmpString);
145 state = state_t::Idle;
147 }
else if (c ==
'\\') {
148 state = state_t::FoundEscape;
154 case state_t::FoundEscape:
156 r.emplace_back(glob_token_type_t::String, tmpString);
157 state = state_t::Idle;
161 state = state_t::FoundText;
165 case state_t::FoundSlash:
167 state = state_t::FoundSlashStar;
169 r.emplace_back(glob_token_type_t::Separator);
170 state = state_t::Idle;
175 case state_t::FoundSlashStar:
177 state = state_t::FoundSlashDoubleStar;
179 r.emplace_back(glob_token_type_t::Separator);
180 r.emplace_back(glob_token_type_t::AnyString);
181 state = state_t::Idle;
186 case state_t::FoundSlashDoubleStar:
188 r.emplace_back(glob_token_type_t::AnyDirectory);
189 r.emplace_back(glob_token_type_t::Separator);
190 state = state_t::Idle;
194 r.emplace_back(glob_token_type_t::Separator);
195 r.emplace_back(glob_token_type_t::AnyString);
196 state = state_t::Idle;
201 case state_t::FoundBracket:
204 if (isFirstCharacter) {
213 if (isFirstCharacter) {
221 r.emplace_back(glob_token_type_t::InverseCharacterList, tmpString);
223 r.emplace_back(glob_token_type_t::CharacterList, tmpString);
227 state = state_t::Idle;
229 isFirstCharacter =
false;
233 if (isFirstCharacter) {
238 isFirstCharacter =
false;
247 r.emplace_back(glob_token_type_t::InverseCharacterList, tmpString);
249 r.emplace_back(glob_token_type_t::CharacterList, tmpString);
251 state = state_t::Idle;
256 ttlet firstCharacter =
static_cast<uint8_t
>(tmpString.
back());
257 ttlet lastCharacter =
static_cast<uint8_t
>(c);
258 for (uint8_t character = firstCharacter + 1; character <= lastCharacter; character++) {
259 tmpString +=
static_cast<char>(character);
265 isFirstCharacter =
false;
270 case state_t::FoundBrace:
275 r.emplace_back(glob_token_type_t::StringList, tmpStringList);
276 tmpStringList.
clear();
277 state = state_t::Idle;
285 r.emplace_back(glob_token_type_t::StringList, tmpStringList);
286 state = state_t::Idle;
302enum class glob_match_result_t {
308inline glob_match_result_t matchGlob(glob_token_const_iterator index, glob_token_const_iterator end, std::string_view str)
311 return (str.size() == 0) ?
312 glob_match_result_t::Match :
313 glob_match_result_t::No;
315 }
else if (str.size() == 0) {
316 switch (index->type) {
317 case glob_token_type_t::Separator:
318 return glob_match_result_t::Partial;
319 case glob_token_type_t::AnyDirectory:
320 return glob_match_result_t::Partial;
321 case glob_token_type_t::AnyString:
322 return matchGlob(index + 1, end, str);
324 return glob_match_result_t::No;
328#define MATCH_GLOB_RECURSE(out, next, end, str)\
329 switch (ttlet tmp = matchGlob(next, end, str)) {\
330 case glob_match_result_t::No: break;\
331 case glob_match_result_t::Match: return tmp;\
332 case glob_match_result_t::Partial: out = tmp; break;\
333 default: tt_no_default();\
337 auto result = glob_match_result_t::No;
338 bool found_slash =
false;
339 ttlet next_index = index + 1;
341 switch (index->type) {
342 case glob_token_type_t::String:
343 if (str.starts_with(index->value)) {
344 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(index->value.size()));
348 case glob_token_type_t::StringList:
349 for (ttlet &value: index->values) {
350 if (str.starts_with(value)) {
351 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(value.size()));
356 case glob_token_type_t::CharacterList:
357 if (index->value.find(str.front()) != std::string::npos) {
358 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(1));
362 case glob_token_type_t::InverseCharacterList:
363 if (index->value.find(str.front()) == std::string::npos) {
364 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(1));
368 case glob_token_type_t::Separator:
369 if (str.front() ==
'/') {
370 return matchGlob(next_index, end, str.substr(1));
372 return glob_match_result_t::No;
375 case glob_token_type_t::AnyCharacter:
376 if (str.front() !=
'/') {
377 return matchGlob(next_index, end, str.substr(1));
379 return glob_match_result_t::No;
382 case glob_token_type_t::AnyString:
384 for (
size_t i = 0; i <= str.size(); i++) {
385 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(i));
388 if (i < str.size() && str[i] ==
'/') {
394 case glob_token_type_t::AnyDirectory:
397 for (
size_t i = 0; i <= str.size(); i++) {
398 if (i == str.size() || str[i] ==
'/') {
399 MATCH_GLOB_RECURSE(result, next_index, end, str.substr(i));
408#undef MATCH_GLOB_RECURSE
411inline glob_match_result_t matchGlob(glob_token_list_t
const &glob, std::string_view str)
413 return matchGlob(glob.begin(), glob.end(), str);
416inline glob_match_result_t matchGlob(std::string_view glob, std::string_view str)
418 return matchGlob(parseGlob(glob), str);
421inline std::string basePathOfGlob(glob_token_const_iterator first, glob_token_const_iterator last) {
428 return x.type == glob_token_type_t::String || x.type == glob_token_type_t::Separator;
431 if (endOfBase != last) {
435 endOfBase = rfind_if(first, endOfBase, [](ttlet &x) {
436 return x.type == glob_token_type_t::Separator;
441 if (endOfBase == first && first->type == glob_token_type_t::Separator) {
446 for (
auto index = first; index != endOfBase; index++) {
447 switch (index->type) {
448 case glob_token_type_t::String:
451 case glob_token_type_t::Separator:
461inline std::string basePathOfGlob(glob_token_list_t
const &glob)
463 return basePathOfGlob(glob.begin(), glob.end());
466inline std::string basePathOfGlob(std::string_view glob)
468 return basePathOfGlob(parseGlob(glob));