Add KernelConfigParser

KernelConfigParser is a helper class to parse text like follows:

    # CONFIG_FOO is not set
    CONFIG_BAR=y
    CONFIG_BAZ=1

It is used in parsing android-base.cfg and /proc/config.gz.

Test: libvintf_test
Test: device boots

Bug: 38324908
Change-Id: Ic2044a9e8580538fb5bdac0df278b2a65c5b6341
diff --git a/Android.bp b/Android.bp
index 5a67c5f..7203222 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,6 +40,7 @@
         "CompatibilityMatrix.cpp",
         "HalManifest.cpp",
         "HalInterface.cpp",
+        "KernelConfigParser.cpp",
         "KernelConfigTypedValue.cpp",
         "RuntimeInfo.cpp",
         "ManifestHal.cpp",
diff --git a/KernelConfigParser.cpp b/KernelConfigParser.cpp
new file mode 100644
index 0000000..e657a33
--- /dev/null
+++ b/KernelConfigParser.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 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 "KernelConfigParser.h"
+
+#include <regex>
+
+namespace android {
+namespace vintf {
+
+KernelConfigParser::KernelConfigParser(bool processComments) : mProcessComments(processComments) {}
+
+status_t KernelConfigParser::finish() {
+    return process("\n", 1 /* sizeof "\n" */);
+}
+
+const std::stringstream& KernelConfigParser::error() const {
+    return mError;
+}
+
+std::map<std::string, std::string>& KernelConfigParser::configs() {
+    return mConfigs;
+}
+
+const std::map<std::string, std::string>& KernelConfigParser::configs() const {
+    return mConfigs;
+}
+
+status_t KernelConfigParser::processRemaining() {
+    static std::regex sCommentPattern("^# (CONFIG[\\w_]+) is not set$");
+
+    if (mRemaining.empty()) {
+        return OK;
+    }
+
+    if (mRemaining[0] == '#') {
+        if (!mProcessComments) {
+            return OK;
+        }
+        std::smatch sm;
+        if (!std::regex_match(mRemaining, sm, sCommentPattern)) {
+            return OK;  // ignore this comment;
+        }
+        if (!mConfigs.emplace(sm[1], "n").second) {
+            mError << "Key " << sm[1] << " is set but commented as not set"
+                   << "\n";
+            return UNKNOWN_ERROR;
+        }
+
+        return OK;
+    }
+
+    size_t equalPos = mRemaining.find('=');
+    if (equalPos == std::string::npos) {
+        mError << "Unrecognized line in configs: " << mRemaining << "\n";
+        return UNKNOWN_ERROR;
+    }
+    std::string key = mRemaining.substr(0, equalPos);
+    std::string value = mRemaining.substr(equalPos + 1);
+    if (!mConfigs.emplace(std::move(key), std::move(value)).second) {
+        mError << "Duplicated key in configs: " << mRemaining.substr(0, equalPos) << "\n";
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+status_t KernelConfigParser::process(const char* buf, size_t len) {
+    const char* begin = buf;
+    const char* end = buf;
+    const char* stop = buf + len;
+    status_t err = OK;
+    while (end < stop) {
+        if (*end == '\n') {
+            mRemaining.insert(mRemaining.size(), begin, end - begin);
+            status_t newErr = processRemaining();
+            if (newErr != OK && err == OK) {
+                err = newErr;
+                // but continue to get more
+            }
+            mRemaining.clear();
+            begin = end + 1;
+        }
+        end++;
+    }
+    mRemaining.insert(mRemaining.size(), begin, end - begin);
+    return err;
+}
+
+}  // namespace vintf
+}  // namespace android
diff --git a/include/vintf/KernelConfigParser.h b/include/vintf/KernelConfigParser.h
new file mode 100644
index 0000000..0ef7088
--- /dev/null
+++ b/include/vintf/KernelConfigParser.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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_KERNEL_CONFIG_PARSER_H_
+#define ANDROID_VINTF_KERNEL_CONFIG_PARSER_H_
+
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <utils/Errors.h>
+
+namespace android {
+namespace vintf {
+
+class KernelConfigParser {
+   public:
+    KernelConfigParser(bool processComments = false);
+
+    status_t process(const char* buf, size_t len);
+    status_t finish();
+    const std::stringstream& error() const;
+    std::map<std::string, std::string>& configs();
+    const std::map<std::string, std::string>& configs() const;
+
+   private:
+    status_t processRemaining();
+    std::map<std::string, std::string> mConfigs;
+    std::stringstream mError;
+    std::string mRemaining;
+    bool mProcessComments;
+};
+
+}  // namespace vintf
+}  // namespace android
+
+#endif  // ANDROID_VINTF_KERNEL_CONFIG_PARSER_H_