1 //===-- LogFilterRegex.cpp --------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "LogFilterRegex.h"
10 
11 #include "DNBLog.h"
12 #include "LogMessage.h"
13 
14 // Enable enhanced mode if it is available. This allows for things like
15 // \d for digit, \s for space, and many more, but it isn't available
16 // everywhere.
17 #if defined(REG_ENHANCED)
18 #define DEFAULT_COMPILE_FLAGS (REG_ENHANCED | REG_EXTENDED)
19 #else
20 #define DEFAULT_COMPILE_FLAGS (REG_EXTENDED)
21 #endif
22 
LogFilterRegex(bool match_accepts,FilterTarget filter_target,const std::string & regex)23 LogFilterRegex::LogFilterRegex(bool match_accepts, FilterTarget filter_target,
24                                const std::string &regex)
25     : LogFilter(match_accepts), m_filter_target(filter_target),
26       m_regex_text(regex), m_regex(), m_is_valid(false), m_error_text() {
27   // Clear it.
28   memset(&m_regex, 0, sizeof(m_regex));
29 
30   // Compile it.
31   if (!regex.empty()) {
32     auto comp_err = ::regcomp(&m_regex, regex.c_str(), DEFAULT_COMPILE_FLAGS);
33     m_is_valid = (comp_err == 0);
34     if (!m_is_valid) {
35       char buffer[256];
36       buffer[0] = '\0';
37       ::regerror(comp_err, &m_regex, buffer, sizeof(buffer));
38       m_error_text = buffer;
39     }
40   }
41 }
42 
~LogFilterRegex()43 LogFilterRegex::~LogFilterRegex() {
44   if (m_is_valid) {
45     // Free the regex internals.
46     regfree(&m_regex);
47   }
48 }
49 
DoesMatch(const LogMessage & message) const50 bool LogFilterRegex::DoesMatch(const LogMessage &message) const {
51   switch (m_filter_target) {
52   case eFilterTargetActivity:
53     // Empty fields never match a condition.
54     if (!message.HasActivity())
55       return false;
56     return ::regexec(&m_regex, message.GetActivity(), 0, nullptr, 0) == 0;
57   case eFilterTargetActivityChain:
58     // Empty fields never match a condition.
59     if (!message.HasActivity())
60       return false;
61     return ::regexec(&m_regex, message.GetActivityChain().c_str(), 0, nullptr,
62                      0) == 0;
63   case eFilterTargetCategory:
64     // Empty fields never match a condition.
65     if (!message.HasCategory())
66       return false;
67     return ::regexec(&m_regex, message.GetCategory(), 0, nullptr, 0) == 0;
68   case eFilterTargetMessage: {
69     const char *message_text = message.GetMessage();
70     if (!message_text) {
71       DNBLogThreadedIf(LOG_DARWIN_LOG,
72                        "LogFilterRegex: regex "
73                        "\"%s\" no match due to nullptr message.",
74                        m_regex_text.c_str());
75       return false;
76     }
77 
78     bool match = ::regexec(&m_regex, message_text, 0, nullptr, 0) == 0;
79     DNBLogThreadedIf(LOG_DARWIN_LOG, "LogFilterRegex: regex "
80                                      "\"%s\" %s message \"%s\".",
81                      m_regex_text.c_str(), match ? "matches" : "does not match",
82                      message_text);
83     return match;
84   }
85   case eFilterTargetSubsystem:
86     // Empty fields never match a condition.
87     if (!message.HasSubsystem())
88       return false;
89     return ::regexec(&m_regex, message.GetSubsystem(), 0, nullptr, 0) == 0;
90   default:
91     // We don't know this type.
92     return false;
93   }
94 }
95