logmeow/filters.cpp

458 lines
16 KiB
C++

#include <string>
#include <vector>
#include <stdexcept>
#include "log.h"
#include "filters.h"
Filter::~Filter() {
}
IntegerFilter::IntegerFilter(FilterKey key_, size_t other_, bool inverted_, bool disabled)
: key(key_), other(other_), inverted(inverted_), _disabled(disabled) {
if (key_ != FilterKey::PID && key_ != FilterKey::TID) {
throw std::runtime_error("IntegerFilter is passed a non-integer key");
}
}
void IntegerFilter::updated() {
}
bool IntegerFilter::disabled() const {
return this->_disabled;
}
void IntegerFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> IntegerFilter::clone() const {
return std::make_unique<IntegerFilter>(this->key, this->other, this->inverted, this->_disabled);
}
const std::string* IntegerFilter::error() const {
return nullptr;
}
bool IntegerFilter::match(const LogcatEntry& entry) const {
bool matched;
switch (this->key) {
case FilterKey::PID: matched = entry.pid == this->other; break;
case FilterKey::TID: matched = entry.tid == this->other; break;
default: throw std::runtime_error("IntegerFilter::match received unhandled key");
};
return !this->inverted ? matched : !matched;
}
StringFilter::StringFilter(FilterKey key_, std::string other_, bool inverted_, bool exact_match_, bool disabled)
: key(key_), other(std::move(other_)), inverted(inverted_), exact_match(exact_match_), _disabled(disabled) {
if (key_ != FilterKey::User && key_ != FilterKey::Tag && key_ != FilterKey::Message) {
throw std::runtime_error("StringFilter is passed a non-string key");
}
this->updated();
}
void StringFilter::updated() {
this->_regex.reset();
this->_error.clear();
if (this->other.empty()) {
this->_error = "String must not be empty";
return;
}
if (!this->other.starts_with("regex:")) {
return;
}
if (this->other.size() == 6) {
this->_error = "Regex must not be empty";
return;
}
try {
this->_regex.emplace(&this->other.c_str()[6]);
} catch (const std::exception& e) {
this->_error = e.what();
}
}
bool StringFilter::disabled() const {
return this->_disabled;
}
void StringFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> StringFilter::clone() const {
return std::make_unique<StringFilter>(this->key, this->other, this->inverted, this->exact_match, this->_disabled);
}
const std::string* StringFilter::error() const {
return !this->_error.empty() ? &this->_error : nullptr;
}
bool StringFilter::_match_no_regex(const LogcatEntry& entry) const {
if (this->exact_match) {
switch (this->key) {
case FilterKey::User: return entry.user && *entry.user == this->other;
case FilterKey::Tag: return entry.tag == this->other;
case FilterKey::Message: return entry.message == this->other;
default: throw std::runtime_error("StringFilter::match received unhandled key");
}
} else {
switch (this->key) {
case FilterKey::User: return entry.user && entry.user->find(this->other) != std::string::npos;
case FilterKey::Tag: return entry.tag.find(this->other) != std::string::npos;
case FilterKey::Message: return entry.message.find(this->other) != std::string::npos;
default: throw std::runtime_error("StringFilter::match received unhandled key");
}
}
}
bool StringFilter::_match_regex(const LogcatEntry& entry) const {
if (!this->_regex) {
log("StringFilter::match called with a regex despite there being no regex");
return true;
}
bool matched;
const std::string* str;
regmatch_t pmatch[1];
int eflags = REG_STARTEND;
switch (this->key) {
case FilterKey::User: str = entry.user ? &*entry.user : nullptr; break;
case FilterKey::Tag: str = &entry.tag; break;
case FilterKey::Message: str = &entry.message; break;
default: throw std::runtime_error("StringFilter::match received unhandled key");
}
pmatch[0].rm_so = 0;
pmatch[0].rm_eo = str ? static_cast<regoff_t>(str->size()) : 0;
if (!this->exact_match) {
eflags |= REG_NOSUB;
}
matched = this->_regex->match(str ? str->data() : "", 1, pmatch, eflags);
if (this->exact_match) {
matched = matched && pmatch[0].rm_so == 0 && (str
? static_cast<size_t>(pmatch[0].rm_eo) == str->size()
: pmatch[0].rm_eo == 0);
}
return matched;
}
bool StringFilter::match(const LogcatEntry& entry) const {
if (this->error()) {
log(std::string("StringFilter::match called despite there being an error: ") + *this->error());
return true;
}
bool matched = !this->other.starts_with("regex:")
? this->_match_no_regex(entry)
: this->_match_regex(entry);
return !this->inverted ? matched : !matched;
}
BufferFilter::BufferFilter(unsigned int wanted_, bool disabled) : wanted(wanted_), _disabled(disabled) {
}
void BufferFilter::updated() {
}
bool BufferFilter::disabled() const {
return this->_disabled;
}
void BufferFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> BufferFilter::clone() const {
return std::make_unique<BufferFilter>(this->wanted, this->_disabled);
}
const std::string* BufferFilter::error() const {
return nullptr;
}
bool BufferFilter::match(const LogcatEntry& entry) const {
return this->wanted & static_cast<unsigned int>(entry.buffer) || this->wanted == 0;
}
PriorityFilter::PriorityFilter(unsigned int wanted_, bool disabled) : wanted(wanted_), _disabled(disabled) {
}
void PriorityFilter::updated() {
}
bool PriorityFilter::disabled() const {
return this->_disabled;
}
void PriorityFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> PriorityFilter::clone() const {
return std::make_unique<PriorityFilter>(this->wanted, this->_disabled);
}
const std::string* PriorityFilter::error() const {
return nullptr;
}
bool PriorityFilter::match(const LogcatEntry& entry) const {
return this->wanted & static_cast<unsigned int>(entry.priority) || this->wanted == 0;
}
GroupFilter::GroupFilter(std::vector<std::unique_ptr<Filter>> filters_, GroupFilter::Type type_, bool disabled)
: filters(std::move(filters_)), type(type_), _disabled(disabled) {
}
void GroupFilter::updated() {
}
bool GroupFilter::disabled() const {
return this->_disabled;
}
void GroupFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> GroupFilter::clone() const {
std::vector<std::unique_ptr<Filter>> new_filters;
new_filters.reserve(this->filters.size());
for (const std::unique_ptr<Filter>& filter : this->filters) {
new_filters.push_back(filter->clone());
}
return std::make_unique<GroupFilter>(std::move(new_filters), this->type, this->_disabled);
}
const std::string* GroupFilter::error() const {
return nullptr;
}
bool GroupFilter::match(const LogcatEntry& entry) const {
bool had_matched = false;
for (const std::unique_ptr<Filter>& filter : this->filters) {
if (filter->disabled() || filter->error()) {
continue;
}
bool matched = filter->match(entry);
if (this->type == GroupFilter::Type::Any && matched) {
return true;
} else if (this->type == GroupFilter::Type::None && matched) {
return false;
} else if (this->type == GroupFilter::Type::All && !matched) {
return false;
} else if (this->type == GroupFilter::Type::One) {
if (had_matched && matched) {
return false;
}
if (matched) {
had_matched = true;
}
}
}
if (this->type == GroupFilter::Type::Any) {
return false;
} else if (this->type == GroupFilter::Type::One && !had_matched) {
return false;
}
return true;
}
void copy_filters(Filters& __restrict filters, const Filters& __restrict other) {
filters.clear();
filters.reserve(other.size());
for (const auto &[title, filter] : other) {
filters.push_back(std::make_pair(title, filter->clone()));
}
}
static bool matches(const LogcatEntry& entry, const Filters& filters, bool return_true_if_empty) {
for (const auto &[_, filter] : filters) {
if (filter->disabled() || filter->error()) {
continue;
}
if (filter->match(entry)) {
return true;
}
return_true_if_empty = false;
}
return return_true_if_empty;
}
bool matches(const LogcatEntry& entry, const Filters& __restrict filters, const Filters& __restrict exclusions) {
return !matches(entry, exclusions, false) && matches(entry, filters, true);
}
void from_json(const nlohmann::json& j, FilterKey& key) {
const std::string& str = j.get_ref<const nlohmann::json::string_t&>();
if (str == "BUFFER") key = FilterKey::Buffer;
else if (str == "USER") key = FilterKey::User;
else if (str == "PID") key = FilterKey::PID;
else if (str == "TID") key = FilterKey::TID;
else if (str == "PRIORITY") key = FilterKey::Priority;
else if (str == "TAG") key = FilterKey::Tag;
else if (str == "MESSAGE") key = FilterKey::Message;
else throw std::invalid_argument(std::string("Unknown filter key: ") + str);
}
void to_json(nlohmann::json& j, const FilterKey& key) {
switch (key) {
case FilterKey::Buffer: j = "BUFFER"; break;
case FilterKey::User: j = "USER"; break;
case FilterKey::PID: j = "PID"; break;
case FilterKey::TID: j = "TID"; break;
case FilterKey::Priority: j = "PRIORITY"; break;
case FilterKey::Tag: j = "TAG"; break;
case FilterKey::Message: j = "MESSAGE"; break;
}
}
void from_json(const nlohmann::json& j, GroupFilter::Type& type) {
const std::string& str = j.get_ref<const nlohmann::json::string_t&>();
if (str == "ALL") type = GroupFilter::Type::All;
else if (str == "ANY") type = GroupFilter::Type::Any;
else if (str == "ONE") type = GroupFilter::Type::One;
else if (str == "NONE") type = GroupFilter::Type::None;
else throw std::invalid_argument(std::string("Unknown group filter type: ") + str);
}
void to_json(nlohmann::json& j, const GroupFilter::Type& type) {
switch (type) {
case GroupFilter::Type::All: j = "ALL"; break;
case GroupFilter::Type::Any: j = "ANY"; break;
case GroupFilter::Type::One: j = "ONE"; break;
case GroupFilter::Type::None: j = "NONE"; break;
}
}
void from_json(const nlohmann::json& j, std::unique_ptr<Filter>& filter) {
const std::string& filter_type = j.at("type").get_ref<const nlohmann::json::string_t&>();
bool disabled = j.at("disabled").get<bool>();
if (filter_type == "INTEGER") {
filter = std::make_unique<IntegerFilter>(j.at("key").get<FilterKey>(), j.at("other").get<size_t>(), j.at("inverted").get<bool>(), disabled);
} else if (filter_type == "STRING") {
filter = std::make_unique<StringFilter>(
j.at("key").get<FilterKey>(), j.at("other").get<std::string>(), j.at("inverted").get<bool>(), j.at("exact_match").get<bool>(), disabled);
} else if (filter_type == "BUFFER") {
unsigned int wanted = 0;
nlohmann::json wanted_json = j.at("wanted");
if (!wanted_json.is_array()) {
throw std::invalid_argument("Wanted buffer items is not an array");
}
for (nlohmann::json::const_iterator it = wanted_json.cbegin(); it != wanted_json.cend(); it++) {
wanted |= static_cast<unsigned int>(it->get<Buffer>());
}
filter = std::make_unique<BufferFilter>(wanted, disabled);
} else if (filter_type == "PRIORITY") {
unsigned int wanted = 0;
nlohmann::json wanted_json = j.at("wanted");
if (!wanted_json.is_array()) {
throw std::invalid_argument("Wanted priority items is not an array");
}
for (nlohmann::json::const_iterator it = wanted_json.cbegin(); it != wanted_json.cend(); it++) {
wanted |= static_cast<unsigned int>(it->get<Priority>());
}
filter = std::make_unique<PriorityFilter>(wanted, disabled);
} else if (filter_type == "GROUP") {
filter = std::make_unique<GroupFilter>(
j.at("filters").get<std::vector<std::unique_ptr<Filter>>>(), j.at("group_type").get<GroupFilter::Type>(), disabled);
} else {
throw std::invalid_argument(std::string("Unknown filter type: ") + filter_type);
}
}
void to_json(nlohmann::json& j, const std::unique_ptr<Filter>& filter) {
j["disabled"] = filter->disabled();
if (const IntegerFilter* ifilter = dynamic_cast<const IntegerFilter*>(filter.get())) {
j["type"] = "INTEGER";
j["key"] = ifilter->key;
j["other"] = ifilter->other;
j["inverted"] = ifilter->inverted;
} else if (const StringFilter* sfilter = dynamic_cast<const StringFilter*>(filter.get())) {
j["type"] = "STRING";
j["key"] = sfilter->key;
j["other"] = sfilter->other;
j["inverted"] = sfilter->inverted;
j["exact_match"] = sfilter->exact_match;
} else if (const BufferFilter* bfilter = dynamic_cast<const BufferFilter*>(filter.get())) {
j["type"] = "BUFFER";
j["wanted"] = nlohmann::json::array();
if (bfilter->wanted & static_cast<unsigned int>(Buffer::Unknown)) {
j["wanted"].emplace_back(Buffer::Unknown);
}
if (bfilter->wanted & static_cast<unsigned int>(Buffer::Main)) {
j["wanted"].emplace_back(Buffer::Main);
}
if (bfilter->wanted & static_cast<unsigned int>(Buffer::System)) {
j["wanted"].emplace_back(Buffer::System);
}
if (bfilter->wanted & static_cast<unsigned int>(Buffer::Radio)) {
j["wanted"].emplace_back(Buffer::Radio);
}
if (bfilter->wanted & static_cast<unsigned int>(Buffer::Events)) {
j["wanted"].emplace_back(Buffer::Events);
}
if (bfilter->wanted & static_cast<unsigned int>(Buffer::Crash)) {
j["wanted"].emplace_back(Buffer::Crash);
}
} else if (const PriorityFilter* pfilter = dynamic_cast<const PriorityFilter*>(filter.get())) {
j["type"] = "PRIORITY";
j["wanted"] = nlohmann::json::array();
if (pfilter->wanted & static_cast<unsigned int>(Priority::Unknown)) {
j["wanted"].emplace_back(Priority::Unknown);
}
if (pfilter->wanted & static_cast<unsigned int>(Priority::Verbose)) {
j["wanted"].emplace_back(Priority::Verbose);
}
if (pfilter->wanted & static_cast<unsigned int>(Priority::Debug)) {
j["wanted"].emplace_back(Priority::Debug);
}
if (pfilter->wanted & static_cast<unsigned int>(Priority::Info)) {
j["wanted"].emplace_back(Priority::Info);
}
if (pfilter->wanted & static_cast<unsigned int>(Priority::Warn)) {
j["wanted"].emplace_back(Priority::Warn);
}
if (pfilter->wanted & static_cast<unsigned int>(Priority::Error)) {
j["wanted"].emplace_back(Priority::Error);
}
if (pfilter->wanted & static_cast<unsigned int>(Priority::Fatal)) {
j["wanted"].emplace_back(Priority::Fatal);
}
} else if (const GroupFilter* gfilter = dynamic_cast<const GroupFilter*>(filter.get())) {
j["type"] = "GROUP";
j["group_type"] = gfilter->type;
j["filters"] = nlohmann::json::array();
for (const std::unique_ptr<Filter>& i : gfilter->filters) {
j["filters"].emplace_back(i);
}
} else {
throw std::invalid_argument("Cannot serialize unknown filter");
}
}