1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "KernelConfigParser.h" 18 19 #include <regex> 20 21 #define KEY "(CONFIG[\\w_]+)" 22 #define COMMENT "(?:#.*)" 23 24 namespace android { 25 namespace vintf { 26 27 KernelConfigParser::KernelConfigParser(bool processComments, bool relaxedFormat) 28 : mProcessComments(processComments), mRelaxedFormat(relaxedFormat) {} 29 30 status_t KernelConfigParser::finish() { 31 return process("\n", 1 /* sizeof "\n" */); 32 } 33 34 std::stringbuf* KernelConfigParser::error() const { 35 return mError.rdbuf(); 36 } 37 38 std::map<std::string, std::string>& KernelConfigParser::configs() { 39 return mConfigs; 40 } 41 42 const std::map<std::string, std::string>& KernelConfigParser::configs() const { 43 return mConfigs; 44 } 45 46 // trim spaces between value and #, value and end of line 47 std::string trimTrailingSpaces(const std::string& s) { 48 auto r = s.rbegin(); 49 for (; r != s.rend() && std::isspace(*r); ++r) 50 ; 51 return std::string{s.begin(), r.base()}; 52 } 53 54 status_t KernelConfigParser::processRemaining() { 55 if (mRemaining.empty()) { 56 return OK; 57 } 58 59 static const std::regex sKeyValuePattern("^\\s*" KEY "\\s*=\\s*([^#]+)" COMMENT "?$"); 60 static const std::regex sNotSetPattern("^\\s*#\\s*" KEY " is not set\\s*$"); 61 static const std::regex sCommentPattern("^\\s*" COMMENT "$"); 62 63 std::smatch match; 64 65 if (mRelaxedFormat) { 66 // Allow free format like " CONFIG_FOO = bar #trailing comments" 67 if (std::regex_match(mRemaining, match, sKeyValuePattern)) { 68 if (mConfigs.emplace(match[1], trimTrailingSpaces(match[2])).second) { 69 return OK; 70 } 71 mError << "Duplicated key in configs: " << match[1] << "\n"; 72 return UNKNOWN_ERROR; 73 } 74 } else { 75 // No spaces. Strictly like "CONFIG_FOO=bar" 76 size_t equalPos = mRemaining.find('='); 77 if (equalPos != std::string::npos) { 78 std::string key = mRemaining.substr(0, equalPos); 79 std::string value = mRemaining.substr(equalPos + 1); 80 if (mConfigs.emplace(std::move(key), std::move(value)).second) { 81 return OK; 82 } 83 mError << "Duplicated key in configs: " << mRemaining.substr(0, equalPos) << "\n"; 84 return UNKNOWN_ERROR; 85 } 86 } 87 88 if (mProcessComments && std::regex_match(mRemaining, match, sNotSetPattern)) { 89 if (mConfigs.emplace(match[1], "n").second) { 90 return OK; 91 } 92 mError << "Key " << match[1] << " is set but commented as not set" 93 << "\n"; 94 return UNKNOWN_ERROR; 95 } 96 97 if (mRelaxedFormat) { 98 // Allow free format like " #comments here" 99 if (std::regex_match(mRemaining, match, sCommentPattern)) { 100 return OK; 101 } 102 } else { 103 // No leading spaces before the comment 104 if (mRemaining.at(0) == '#') { 105 return OK; 106 } 107 } 108 109 mError << "Unrecognized line in configs: " << mRemaining << "\n"; 110 return UNKNOWN_ERROR; 111 } 112 113 status_t KernelConfigParser::process(const char* buf, size_t len) { 114 const char* begin = buf; 115 const char* end = buf; 116 const char* stop = buf + len; 117 status_t err = OK; 118 while (end < stop) { 119 if (*end == '\n') { 120 mRemaining.insert(mRemaining.size(), begin, end - begin); 121 status_t newErr = processRemaining(); 122 if (newErr != OK && err == OK) { 123 err = newErr; 124 // but continue to get more 125 } 126 mRemaining.clear(); 127 begin = end + 1; 128 } 129 end++; 130 } 131 mRemaining.insert(mRemaining.size(), begin, end - begin); 132 return err; 133 } 134 135 status_t KernelConfigParser::processAndFinish(const char* buf, size_t len) { 136 status_t err = process(buf, len); 137 if (err != OK) { 138 return err; 139 } 140 return finish(); 141 } 142 143 status_t KernelConfigParser::processAndFinish(const std::string& content) { 144 return processAndFinish(content.c_str(), content.size()); 145 } 146 147 } // namespace vintf 148 } // namespace android 149