#include #include #include #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 IntegerFilter::clone() const { return std::make_unique(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 StringFilter::clone() const { return std::make_unique(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(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(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 BufferFilter::clone() const { return std::make_unique(this->wanted, this->_disabled); } const std::string* BufferFilter::error() const { return nullptr; } bool BufferFilter::match(const LogcatEntry& entry) const { return this->wanted & static_cast(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 PriorityFilter::clone() const { return std::make_unique(this->wanted, this->_disabled); } const std::string* PriorityFilter::error() const { return nullptr; } bool PriorityFilter::match(const LogcatEntry& entry) const { return this->wanted & static_cast(entry.priority) || this->wanted == 0; } GroupFilter::GroupFilter(std::vector> 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 GroupFilter::clone() const { std::vector> new_filters; new_filters.reserve(this->filters.size()); for (const std::unique_ptr& filter : this->filters) { new_filters.push_back(filter->clone()); } return std::make_unique(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 : 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(); 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(); 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) { const std::string& filter_type = j.at("type").get_ref(); bool disabled = j.at("disabled").get(); if (filter_type == "INTEGER") { filter = std::make_unique(j.at("key").get(), j.at("other").get(), j.at("inverted").get(), disabled); } else if (filter_type == "STRING") { filter = std::make_unique( j.at("key").get(), j.at("other").get(), j.at("inverted").get(), j.at("exact_match").get(), 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(it->get()); } filter = std::make_unique(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(it->get()); } filter = std::make_unique(wanted, disabled); } else if (filter_type == "GROUP") { filter = std::make_unique( j.at("filters").get>>(), j.at("group_type").get(), disabled); } else { throw std::invalid_argument(std::string("Unknown filter type: ") + filter_type); } } void to_json(nlohmann::json& j, const std::unique_ptr& filter) { j["disabled"] = filter->disabled(); if (const IntegerFilter* ifilter = dynamic_cast(filter.get())) { j["type"] = "INTEGER"; j["key"] = ifilter->key; j["other"] = ifilter->other; j["inverted"] = ifilter->inverted; } else if (const StringFilter* sfilter = dynamic_cast(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(filter.get())) { j["type"] = "BUFFER"; j["wanted"] = nlohmann::json::array(); if (bfilter->wanted & static_cast(Buffer::Unknown)) { j["wanted"].emplace_back(Buffer::Unknown); } if (bfilter->wanted & static_cast(Buffer::Main)) { j["wanted"].emplace_back(Buffer::Main); } if (bfilter->wanted & static_cast(Buffer::System)) { j["wanted"].emplace_back(Buffer::System); } if (bfilter->wanted & static_cast(Buffer::Radio)) { j["wanted"].emplace_back(Buffer::Radio); } if (bfilter->wanted & static_cast(Buffer::Events)) { j["wanted"].emplace_back(Buffer::Events); } if (bfilter->wanted & static_cast(Buffer::Crash)) { j["wanted"].emplace_back(Buffer::Crash); } } else if (const PriorityFilter* pfilter = dynamic_cast(filter.get())) { j["type"] = "PRIORITY"; j["wanted"] = nlohmann::json::array(); if (pfilter->wanted & static_cast(Priority::Unknown)) { j["wanted"].emplace_back(Priority::Unknown); } if (pfilter->wanted & static_cast(Priority::Verbose)) { j["wanted"].emplace_back(Priority::Verbose); } if (pfilter->wanted & static_cast(Priority::Debug)) { j["wanted"].emplace_back(Priority::Debug); } if (pfilter->wanted & static_cast(Priority::Info)) { j["wanted"].emplace_back(Priority::Info); } if (pfilter->wanted & static_cast(Priority::Warn)) { j["wanted"].emplace_back(Priority::Warn); } if (pfilter->wanted & static_cast(Priority::Error)) { j["wanted"].emplace_back(Priority::Error); } if (pfilter->wanted & static_cast(Priority::Fatal)) { j["wanted"].emplace_back(Priority::Fatal); } } else if (const GroupFilter* gfilter = dynamic_cast(filter.get())) { j["type"] = "GROUP"; j["group_type"] = gfilter->type; j["filters"] = nlohmann::json::array(); for (const std::unique_ptr& i : gfilter->filters) { j["filters"].emplace_back(i); } } else { throw std::invalid_argument("Cannot serialize unknown filter"); } }