276 lines
7.6 KiB
C++
276 lines
7.6 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 disabled)
|
|
: key(key_), other(std::move(other_)), inverted(inverted_), _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->_disabled);
|
|
}
|
|
|
|
const std::string* StringFilter::error() const {
|
|
return !this->_error.empty() ? &this->_error : nullptr;
|
|
}
|
|
|
|
bool StringFilter::match(const LogcatEntry& entry) const {
|
|
bool matched;
|
|
|
|
if (this->error()) {
|
|
log(std::string("StringFilter::match called despite there being an error: ") + *this->error());
|
|
return true;
|
|
}
|
|
if (!this->other.starts_with("regex:")) {
|
|
switch (this->key) {
|
|
case FilterKey::User: matched = entry.user.value_or("") == this->other; break;
|
|
case FilterKey::Tag: matched = entry.tag == this->other; break;
|
|
case FilterKey::Message: matched = entry.message == this->other; break;
|
|
default: throw std::runtime_error("StringFilter::match received unhandled key");
|
|
}
|
|
return !this->inverted ? matched : !matched;
|
|
}
|
|
if (!this->_regex) {
|
|
log("StringFilter::match called with a regex despite there being no regex");
|
|
return true;
|
|
}
|
|
|
|
std::string str;
|
|
regmatch_t pmatch[1];
|
|
switch (this->key) {
|
|
case FilterKey::User: str = entry.user.value_or(""); 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 = static_cast<regoff_t>(str.size());
|
|
|
|
matched = this->_regex->match(str.data(), 1, pmatch, REG_STARTEND)
|
|
&& pmatch[0].rm_so == 0 && static_cast<size_t>(pmatch[0].rm_eo) == str.size();
|
|
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& filters, const Filters& other) {
|
|
filters.clear();
|
|
filters.reserve(other.size());
|
|
|
|
for (const auto &[title, filter] : other) {
|
|
filters.push_back(std::make_pair(title, filter->clone()));
|
|
}
|
|
}
|
|
|
|
bool matches(const Filters& filters, const LogcatEntry& entry) {
|
|
bool ok_filter_exists = false;
|
|
|
|
for (const auto &[title, filter] : filters) {
|
|
if (filter->disabled() || filter->error()) {
|
|
continue;
|
|
}
|
|
ok_filter_exists = true;
|
|
if (filter->match(entry)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return !ok_filter_exists;
|
|
}
|