2022-12-30 08:56:55 +01:00
|
|
|
/*
|
2023-01-28 17:46:00 +01:00
|
|
|
** Copyright (C) 2022-2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
2022-12-30 08:56:55 +01:00
|
|
|
**
|
|
|
|
** This program is free software; you can redistribute it and/or modify it
|
|
|
|
** under the terms of the GNU General Public License as published by the
|
|
|
|
** Free Software Foundation; either version 3, or (at your option) any
|
|
|
|
** later version.
|
|
|
|
**
|
|
|
|
** This program is distributed in the hope that it will be useful,
|
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
** GNU General Public License for more details.
|
|
|
|
**
|
|
|
|
** You should have received a copy of the GNU General Public License
|
|
|
|
** along with this program; if not, write to the Free Software Foundation,
|
|
|
|
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
**
|
|
|
|
*/
|
|
|
|
#ifndef MU_REGEX_HH__
|
|
|
|
#define MU_REGEX_HH__
|
|
|
|
|
|
|
|
#include <glib.h>
|
2023-07-10 23:30:23 +02:00
|
|
|
#
|
2022-12-30 08:56:55 +01:00
|
|
|
#include <utils/mu-result.hh>
|
2023-07-10 23:30:23 +02:00
|
|
|
#include <utils/mu-utils.hh>
|
2022-12-30 08:56:55 +01:00
|
|
|
|
|
|
|
namespace Mu {
|
|
|
|
/**
|
|
|
|
* RAII wrapper around a GRegex which in itself is a wrapper around PCRE. We use
|
|
|
|
* PCRE rather than std::regex because it is much faster.
|
|
|
|
*/
|
|
|
|
struct Regex {
|
2022-12-30 23:10:24 +01:00
|
|
|
#if !GLIB_CHECK_VERSION(2,74,0) /* backward compat */
|
|
|
|
#define G_REGEX_DEFAULT (static_cast<GRegexCompileFlags>(0))
|
|
|
|
#define G_REGEX_MATCH_DEFAULT (static_cast<GRegexMatchFlags>(0))
|
|
|
|
#endif
|
2022-12-30 08:56:55 +01:00
|
|
|
/**
|
|
|
|
* Trivial constructor
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
Regex() noexcept: rx_{} {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new Regex object
|
|
|
|
*
|
|
|
|
* @param ptrn PRRE regular expression pattern
|
|
|
|
* @param cflags compile flags
|
|
|
|
* @param mflags match flags
|
|
|
|
*
|
|
|
|
* @return a Regex object or an error.
|
|
|
|
*/
|
|
|
|
static Result<Regex> make(const std::string& ptrn,
|
|
|
|
GRegexCompileFlags cflags = G_REGEX_DEFAULT,
|
2023-07-10 23:30:23 +02:00
|
|
|
GRegexMatchFlags mflags = G_REGEX_MATCH_DEFAULT)
|
|
|
|
noexcept try {
|
2022-12-30 08:56:55 +01:00
|
|
|
return Regex(ptrn.c_str(), cflags, mflags);
|
|
|
|
} catch (const Error& err) {
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
|
2023-07-10 23:30:23 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy CTOR
|
|
|
|
*
|
|
|
|
* @param other some other Regex
|
|
|
|
*/
|
|
|
|
Regex(const Regex& other) noexcept: rx_{} { *this = other; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Move CTOR
|
|
|
|
*
|
|
|
|
* @param other some other Regex
|
|
|
|
*/
|
|
|
|
Regex(Regex&& other) noexcept: rx_{} { *this = std::move(other); }
|
|
|
|
|
|
|
|
|
2022-12-30 08:56:55 +01:00
|
|
|
/**
|
|
|
|
* DTOR
|
|
|
|
*/
|
2023-07-10 23:30:23 +02:00
|
|
|
~Regex() noexcept { g_clear_pointer(&rx_, g_regex_unref); }
|
2022-12-30 08:56:55 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Cast to the the underlying GRegex*
|
|
|
|
*
|
|
|
|
* @return a GRegex*
|
|
|
|
*/
|
|
|
|
operator const GRegex*() const noexcept { return rx_; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Doe this object contain a valid GRegex*?
|
|
|
|
*
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
operator bool() const noexcept { return !!rx_; }
|
|
|
|
|
|
|
|
/**
|
2023-07-10 23:30:23 +02:00
|
|
|
* operator=
|
2022-12-30 08:56:55 +01:00
|
|
|
*
|
2023-07-10 23:30:23 +02:00
|
|
|
* @param other copy some other object to this one
|
|
|
|
*
|
|
|
|
* @return *this
|
2022-12-30 08:56:55 +01:00
|
|
|
*/
|
2023-07-10 23:30:23 +02:00
|
|
|
Regex& operator=(const Regex& other) noexcept {
|
|
|
|
if (this != &other) {
|
|
|
|
g_clear_pointer(&rx_, g_regex_unref);
|
|
|
|
if (other.rx_)
|
|
|
|
rx_ = g_regex_ref(other.rx_);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
2022-12-30 08:56:55 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* operator=
|
|
|
|
*
|
2023-07-10 23:30:23 +02:00
|
|
|
* @param other move some other object to this one
|
2022-12-30 08:56:55 +01:00
|
|
|
*
|
|
|
|
* @return *this
|
|
|
|
*/
|
2023-07-10 23:30:23 +02:00
|
|
|
Regex& operator=(Regex&& other) noexcept {
|
2022-12-30 08:56:55 +01:00
|
|
|
if (this != &other) {
|
2023-07-10 23:30:23 +02:00
|
|
|
g_clear_pointer(&rx_, g_regex_unref);
|
|
|
|
rx_ = other.rx_;
|
|
|
|
other.rx_ = nullptr;
|
2022-12-30 08:56:55 +01:00
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does this regexp match the given string? An unset Regex matches
|
|
|
|
* nothing.
|
|
|
|
*
|
2023-07-10 23:30:23 +02:00
|
|
|
* @param str string to test
|
|
|
|
* @param mflags match flags
|
2022-12-30 08:56:55 +01:00
|
|
|
*
|
|
|
|
* @return true or false
|
|
|
|
*/
|
|
|
|
bool matches(const std::string& str,
|
|
|
|
GRegexMatchFlags mflags=G_REGEX_MATCH_DEFAULT) const noexcept {
|
2023-07-10 23:30:23 +02:00
|
|
|
if (!rx_)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return g_regex_match(rx_, str.c_str(), mflags, nullptr);
|
|
|
|
// strangely, valgrind reports some memory error related to
|
|
|
|
// the str.c_str(). It *seems* like a false alarm.
|
2022-12-30 08:56:55 +01:00
|
|
|
}
|
|
|
|
|
2023-01-28 17:46:00 +01:00
|
|
|
/**
|
|
|
|
* Replace all occurences of @this regexp in some string with a
|
|
|
|
* replacement string
|
|
|
|
*
|
|
|
|
* @param str some string
|
|
|
|
* @param repl replacement string
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
std::string replace(const std::string& str, const std::string& repl) {
|
|
|
|
char *s{g_regex_replace(rx_, str.c_str(), str.length(), 0,
|
|
|
|
repl.c_str(), G_REGEX_MATCH_DEFAULT, {})};
|
|
|
|
if (!s)
|
|
|
|
throw Err(Error::Code::InvalidArgument, "error in Regex::replace");
|
2023-07-10 23:30:23 +02:00
|
|
|
return to_string_gchar(std::move(s));
|
2023-01-28 17:46:00 +01:00
|
|
|
}
|
|
|
|
|
2023-07-10 23:30:23 +02:00
|
|
|
const GRegex* g_regex() const { return rx_; }
|
|
|
|
|
2022-12-30 08:56:55 +01:00
|
|
|
private:
|
|
|
|
Regex(const char *ptrn, GRegexCompileFlags cflags, GRegexMatchFlags mflags) {
|
|
|
|
GError *err{};
|
2023-07-10 23:30:23 +02:00
|
|
|
if (rx_ = g_regex_new(ptrn, cflags, mflags, &err); !rx_)
|
|
|
|
throw Err(Error::Code::InvalidArgument, &err,
|
|
|
|
"invalid regexp: '{}'", ptrn);
|
2022-12-30 08:56:55 +01:00
|
|
|
}
|
2023-07-10 23:30:23 +02:00
|
|
|
|
|
|
|
GRegex *rx_;
|
2022-12-30 08:56:55 +01:00
|
|
|
};
|
|
|
|
|
2023-07-10 23:30:23 +02:00
|
|
|
static inline std::string format_as(const Regex& rx) {
|
|
|
|
if (auto&& grx{rx.g_regex()}; !grx)
|
|
|
|
return "//";
|
|
|
|
else
|
|
|
|
return mu_format("/{}/", g_regex_get_pattern(grx));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-30 08:56:55 +01:00
|
|
|
} // namespace Mu
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* MU_REGEX_HH__ */
|