#include "sedpp/Regex.h" #include namespace sedpp { Regex::Regex(const std::string &pattern, const Options &options, bool ignoreCase, bool multiline) { int flags = 0; // Keep regex flavor decisions here so compiler-normalized patterns can be // reused by addresses and substitutions without duplicating regcomp flags. if (options.extendedRegex) { flags |= REG_EXTENDED; } if (ignoreCase) { flags |= REG_ICASE; } if (multiline) { flags |= REG_NEWLINE; } const int rc = ::regcomp(®ex_, pattern.c_str(), flags); if (rc != 0) { char message[256]; ::regerror(rc, ®ex_, message, sizeof(message)); throw std::runtime_error(message); } compiled_ = true; } Regex::Regex(Regex &&other) noexcept { // regex_t is a C resource; moving transfers the compiled object and prevents // the source wrapper from freeing it. regex_ = other.regex_; compiled_ = other.compiled_; other.compiled_ = false; } Regex &Regex::operator=(Regex &&other) noexcept { if (this != &other) { if (compiled_) { ::regfree(®ex_); } regex_ = other.regex_; compiled_ = other.compiled_; other.compiled_ = false; } return *this; } Regex::~Regex() { if (compiled_) { ::regfree(®ex_); } } std::optional Regex::search(const std::string &text, std::size_t start) const { constexpr std::size_t kMaxGroups = 10; Match match; match.groups.resize(kMaxGroups); const char *base = text.c_str(); // Searches after byte 0 should not let ^ match the original beginning again. const int eflags = start == 0 ? 0 : REG_NOTBOL; const int rc = ::regexec(®ex_, base + start, match.groups.size(), match.groups.data(), eflags); if (rc == REG_NOMATCH) { return std::nullopt; } if (rc != 0) { char message[256]; ::regerror(rc, ®ex_, message, sizeof(message)); throw std::runtime_error(message); } for (regmatch_t &group : match.groups) { if (group.rm_so >= 0) { // regexec saw base + start, so convert offsets back into the full string. group.rm_so += static_cast(start); group.rm_eo += static_cast(start); } } return match; } } // namespace sedpp