regex-instance: HalInterface stores regex instances.

Parse <regex-instance> regular expression patterns using
Extended Regular Expression syntax, and store it in
HalInterface object.

Bug: 73738616
Test: libvintf_test
Test: vintf_object_test
Test: vts_treble_vintf_test

Change-Id: If0b8e3bc053a6c2ecd9048092071f52f7896bba0
diff --git a/Android.bp b/Android.bp
index 03267a5..83a3586 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,6 +42,7 @@
         "MatrixHal.cpp",
         "MatrixInstance.cpp",
         "MatrixKernel.cpp",
+        "Regex.cpp",
         "SystemSdk.cpp",
         "TransportArch.cpp",
         "VintfObject.cpp",
diff --git a/HalInterface.cpp b/HalInterface.cpp
index cec33af..b5cbd6b 100644
--- a/HalInterface.cpp
+++ b/HalInterface.cpp
@@ -35,6 +35,11 @@
             return false;
         }
     }
+    for (const auto& instance : mRegexes) {
+        if (!func(mName, instance, true /* isRegex */)) {
+            return false;
+        }
+    }
     return true;
 }
 
@@ -47,12 +52,20 @@
     return found;
 }
 
-bool HalInterface::insertInstance(const std::string& instanceOrPattern, bool /*isRegex*/) {
-    return mInstances.insert(instanceOrPattern).second;
+bool HalInterface::insertInstance(const std::string& instanceOrPattern, bool isRegex) {
+    if (isRegex) {
+        return mRegexes.insert(instanceOrPattern).second;
+    } else {
+        return mInstances.insert(instanceOrPattern).second;
+    }
 }
 
-bool HalInterface::removeInstance(const std::string& instanceOrPattern, bool /*isRegex*/) {
-    return mInstances.erase(instanceOrPattern) > 0;
+bool HalInterface::removeInstance(const std::string& instanceOrPattern, bool isRegex) {
+    if (isRegex) {
+        return mRegexes.erase(instanceOrPattern) > 0;
+    } else {
+        return mInstances.erase(instanceOrPattern) > 0;
+    }
 }
 
 } // namespace vintf
diff --git a/Regex.cpp b/Regex.cpp
new file mode 100644
index 0000000..c343398
--- /dev/null
+++ b/Regex.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Regex.h"
+
+namespace android {
+namespace vintf {
+namespace details {
+
+Regex::~Regex() {
+    clear();
+}
+
+void Regex::clear() {
+    if (mImpl != nullptr) {
+        regfree(mImpl.get());
+        mImpl = nullptr;
+    }
+}
+
+bool Regex::compile(const std::string& pattern) {
+    clear();
+    mImpl = std::make_unique<regex_t>();
+    int status = regcomp(mImpl.get(), pattern.c_str(), REG_EXTENDED | REG_NEWLINE);
+    return status == 0;
+}
+
+bool Regex::matches(const std::string& s) const {
+    regmatch_t match;
+    int status =
+        regexec(mImpl.get(), s.c_str(), 1 /* nmatch */, &match /* pmatch */, 0 /* flags */);
+    return status == 0 && match.rm_so == 0 && match.rm_eo >= 0 &&
+           static_cast<size_t>(match.rm_eo) == s.length();
+}
+
+}  // namespace details
+}  // namespace vintf
+}  // namespace android
diff --git a/include/vintf/HalInterface.h b/include/vintf/HalInterface.h
index b778a28..12bf79a 100644
--- a/include/vintf/HalInterface.h
+++ b/include/vintf/HalInterface.h
@@ -51,6 +51,7 @@
 
     std::string mName;
     std::set<std::string> mInstances;
+    std::set<std::string> mRegexes;
 };
 
 } // namespace vintf
diff --git a/include/vintf/Regex.h b/include/vintf/Regex.h
new file mode 100644
index 0000000..86c66f2
--- /dev/null
+++ b/include/vintf/Regex.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VINTF_REGEX_H_
+#define ANDROID_VINTF_REGEX_H_
+
+#include <regex.h>
+#include <string>
+
+namespace android {
+namespace vintf {
+namespace details {
+
+// A wrapper class around regex.h. This is used instead of C++ <regex> library because
+// C++ regex library throws exceptions when an invalid regular expression is compiled.
+// Use Extended Regular Expression (ERE) syntax.
+class Regex {
+   public:
+    Regex() = default;
+    ~Regex();
+
+    Regex& operator=(const Regex&) = delete;
+    Regex(const Regex&) = delete;
+
+    __attribute__((warn_unused_result)) bool compile(const std::string& pattern);
+
+    bool matches(const std::string& s) const;
+
+    /**
+     * Return nullptr if not a valid regex pattern, else the Regex object.
+     */
+    static const Regex* Get(const std::string& pattern);
+
+   private:
+    std::unique_ptr<regex_t> mImpl;
+
+    void clear();
+};
+
+}  // namespace details
+}  // namespace vintf
+}  // namespace android
+
+#endif  // ANDROID_VINTF_REGEX_H_
diff --git a/parse_xml.cpp b/parse_xml.cpp
index 3d67e1c..27f31c9 100644
--- a/parse_xml.cpp
+++ b/parse_xml.cpp
@@ -25,6 +25,7 @@
 
 #include <tinyxml2.h>
 
+#include "Regex.h"
 #include "constants.h"
 #include "parse_string.h"
 
@@ -475,11 +476,14 @@
     void mutateNode(const HalInterface &intf, NodeType *root, DocType *d) const override {
         appendTextElement(root, "name", intf.name(), d);
         appendTextElements(root, "instance", intf.mInstances, d);
+        appendTextElements(root, "regex-instance", intf.mRegexes, d);
     }
     bool buildObject(HalInterface* intf, NodeType* root, std::string* error) const override {
         std::vector<std::string> instances;
+        std::vector<std::string> regexes;
         if (!parseTextElement(root, "name", &intf->mName, error) ||
-            !parseTextElements(root, "instance", &instances, error)) {
+            !parseTextElements(root, "instance", &instances, error) ||
+            !parseTextElements(root, "regex-instance", &regexes, error)) {
             return false;
         }
         bool success = true;
@@ -490,6 +494,19 @@
                 success = false;
             }
         }
+        for (const auto& e : regexes) {
+            details::Regex regex;
+            if (!regex.compile(e)) {
+                if (!error->empty()) *error += "\n";
+                *error += "Invalid regular expression '" + e + "' in " + intf->name();
+                success = false;
+            }
+            if (!intf->insertInstance(e, true /* isRegex */)) {
+                if (!error->empty()) *error += "\n";
+                *error += "Duplicated regex-instance '" + e + "' in " + intf->name();
+                success = false;
+            }
+        }
         return success;
     }
 };