1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <algorithm>
6 #include "common/logging/backend.h"
7 #include "common/logging/filter.h"
8 #include "common/string_util.h"
9 
10 namespace Log {
11 namespace {
12 template <typename It>
GetLevelByName(const It begin,const It end)13 Level GetLevelByName(const It begin, const It end) {
14     for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
15         const char* level_name = GetLevelName(static_cast<Level>(i));
16         if (Common::ComparePartialString(begin, end, level_name)) {
17             return static_cast<Level>(i);
18         }
19     }
20     return Level::Count;
21 }
22 
23 template <typename It>
GetClassByName(const It begin,const It end)24 Class GetClassByName(const It begin, const It end) {
25     for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
26         const char* level_name = GetLogClassName(static_cast<Class>(i));
27         if (Common::ComparePartialString(begin, end, level_name)) {
28             return static_cast<Class>(i);
29         }
30     }
31     return Class::Count;
32 }
33 
34 template <typename Iterator>
ParseFilterRule(Filter & instance,Iterator begin,Iterator end)35 bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
36     auto level_separator = std::find(begin, end, ':');
37     if (level_separator == end) {
38         LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
39                   std::string(begin, end));
40         return false;
41     }
42 
43     const Level level = GetLevelByName(level_separator + 1, end);
44     if (level == Level::Count) {
45         LOG_ERROR(Log, "Unknown log level in filter: {}", std::string(begin, end));
46         return false;
47     }
48 
49     if (Common::ComparePartialString(begin, level_separator, "*")) {
50         instance.ResetAll(level);
51         return true;
52     }
53 
54     const Class log_class = GetClassByName(begin, level_separator);
55     if (log_class == Class::Count) {
56         LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end));
57         return false;
58     }
59 
60     instance.SetClassLevel(log_class, level);
61     return true;
62 }
63 } // Anonymous namespace
64 
Filter(Level default_level)65 Filter::Filter(Level default_level) {
66     ResetAll(default_level);
67 }
68 
ResetAll(Level level)69 void Filter::ResetAll(Level level) {
70     class_levels.fill(level);
71 }
72 
SetClassLevel(Class log_class,Level level)73 void Filter::SetClassLevel(Class log_class, Level level) {
74     class_levels[static_cast<std::size_t>(log_class)] = level;
75 }
76 
ParseFilterString(std::string_view filter_view)77 void Filter::ParseFilterString(std::string_view filter_view) {
78     auto clause_begin = filter_view.cbegin();
79     while (clause_begin != filter_view.cend()) {
80         auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
81 
82         // If clause isn't empty
83         if (clause_end != clause_begin) {
84             ParseFilterRule(*this, clause_begin, clause_end);
85         }
86 
87         if (clause_end != filter_view.cend()) {
88             // Skip over the whitespace
89             ++clause_end;
90         }
91         clause_begin = clause_end;
92     }
93 }
94 
CheckMessage(Class log_class,Level level) const95 bool Filter::CheckMessage(Class log_class, Level level) const {
96     return static_cast<u8>(level) >=
97            static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
98 }
99 } // namespace Log
100