diff options
-rw-r--r-- | KernelConfigParser.cpp | 54 | ||||
-rw-r--r-- | test/main.cpp | 60 |
2 files changed, 91 insertions, 23 deletions
diff --git a/KernelConfigParser.cpp b/KernelConfigParser.cpp index 1d08fca..44cbf07 100644 --- a/KernelConfigParser.cpp +++ b/KernelConfigParser.cpp | |||
@@ -18,6 +18,13 @@ | |||
18 | 18 | ||
19 | #include <regex> | 19 | #include <regex> |
20 | 20 | ||
21 | #define KEY "(CONFIG[\\w_]+)" | ||
22 | #define COMMENT "(?:#.*)" | ||
23 | |||
24 | static const std::regex sKeyValuePattern("^\\s*" KEY "\\s*=\\s*([^#]+)" COMMENT "?$"); | ||
25 | static const std::regex sNotSetPattern("^\\s*#\\s*" KEY " is not set\\s*$"); | ||
26 | static const std::regex sCommentPattern("^\\s*" COMMENT "$"); | ||
27 | |||
21 | namespace android { | 28 | namespace android { |
22 | namespace vintf { | 29 | namespace vintf { |
23 | 30 | ||
@@ -39,43 +46,44 @@ const std::map<std::string, std::string>& KernelConfigParser::configs() const { | |||
39 | return mConfigs; | 46 | return mConfigs; |
40 | } | 47 | } |
41 | 48 | ||
49 | // trim spaces between value and #, value and end of line | ||
50 | std::string trimTrailingSpaces(const std::string& s) { | ||
51 | auto r = s.rbegin(); | ||
52 | for (; r != s.rend() && std::isspace(*r); ++r) | ||
53 | ; | ||
54 | return std::string{s.begin(), r.base()}; | ||
55 | } | ||
56 | |||
42 | status_t KernelConfigParser::processRemaining() { | 57 | status_t KernelConfigParser::processRemaining() { |
43 | static std::regex sCommentPattern("^# (CONFIG[\\w_]+) is not set$"); | ||
44 | 58 | ||
45 | if (mRemaining.empty()) { | 59 | if (mRemaining.empty()) { |
46 | return OK; | 60 | return OK; |
47 | } | 61 | } |
48 | 62 | ||
49 | if (mRemaining[0] == '#') { | 63 | std::smatch match; |
50 | if (!mProcessComments) { | 64 | if (std::regex_match(mRemaining, match, sKeyValuePattern)) { |
65 | if (mConfigs.emplace(match[1], trimTrailingSpaces(match[2])).second) { | ||
51 | return OK; | 66 | return OK; |
52 | } | 67 | } |
53 | std::smatch sm; | 68 | mError << "Duplicated key in configs: " << match[1] << "\n"; |
54 | if (!std::regex_match(mRemaining, sm, sCommentPattern)) { | 69 | return UNKNOWN_ERROR; |
55 | return OK; // ignore this comment; | ||
56 | } | ||
57 | if (!mConfigs.emplace(sm[1], "n").second) { | ||
58 | mError << "Key " << sm[1] << " is set but commented as not set" | ||
59 | << "\n"; | ||
60 | return UNKNOWN_ERROR; | ||
61 | } | ||
62 | |||
63 | return OK; | ||
64 | } | 70 | } |
65 | 71 | ||
66 | size_t equalPos = mRemaining.find('='); | 72 | if (mProcessComments && std::regex_match(mRemaining, match, sNotSetPattern)) { |
67 | if (equalPos == std::string::npos) { | 73 | if (mConfigs.emplace(match[1], "n").second) { |
68 | mError << "Unrecognized line in configs: " << mRemaining << "\n"; | 74 | return OK; |
75 | } | ||
76 | mError << "Key " << match[1] << " is set but commented as not set" | ||
77 | << "\n"; | ||
69 | return UNKNOWN_ERROR; | 78 | return UNKNOWN_ERROR; |
70 | } | 79 | } |
71 | std::string key = mRemaining.substr(0, equalPos); | 80 | |
72 | std::string value = mRemaining.substr(equalPos + 1); | 81 | if (std::regex_match(mRemaining, match, sCommentPattern)) { |
73 | if (!mConfigs.emplace(std::move(key), std::move(value)).second) { | 82 | return OK; |
74 | mError << "Duplicated key in configs: " << mRemaining.substr(0, equalPos) << "\n"; | ||
75 | return UNKNOWN_ERROR; | ||
76 | } | 83 | } |
77 | 84 | ||
78 | return OK; | 85 | mError << "Unrecognized line in configs: " << mRemaining << "\n"; |
86 | return UNKNOWN_ERROR; | ||
79 | } | 87 | } |
80 | 88 | ||
81 | status_t KernelConfigParser::process(const char* buf, size_t len) { | 89 | status_t KernelConfigParser::process(const char* buf, size_t len) { |
diff --git a/test/main.cpp b/test/main.cpp index 179407f..b3b8a59 100644 --- a/test/main.cpp +++ b/test/main.cpp | |||
@@ -1362,6 +1362,66 @@ TEST_F(LibVintfTest, KernelConfigParser2) { | |||
1362 | EXPECT_EQ(configs.find("CONFIG_NOT_SET2")->second, "n"); | 1362 | EXPECT_EQ(configs.find("CONFIG_NOT_SET2")->second, "n"); |
1363 | } | 1363 | } |
1364 | 1364 | ||
1365 | TEST_F(LibVintfTest, KernelConfigParserSpace) { | ||
1366 | // usage in android-base.cfg | ||
1367 | const std::string data = | ||
1368 | " # CONFIG_NOT_SET is not set \n" | ||
1369 | " CONFIG_ONE=1 # 'tis a one!\n" | ||
1370 | " CONFIG_TWO=2 #'tis a two! \n" | ||
1371 | " CONFIG_THREE=3#'tis a three! \n" | ||
1372 | " CONFIG_233=233#'tis a three! \n" | ||
1373 | "#yey! random comments\n" | ||
1374 | "CONFIG_Y=y \n" | ||
1375 | " CONFIG_YES=y#YES! \n" | ||
1376 | "CONFIG_STR=string\n" | ||
1377 | "CONFIG_HELLO=hello world! #still works\n" | ||
1378 | "CONFIG_WORLD=hello world! \n" | ||
1379 | "CONFIG_GOOD = good morning! #comments here\n" | ||
1380 | " CONFIG_MORNING = good morning! \n"; | ||
1381 | auto pair = processData(data, true /* processComments */); | ||
1382 | ASSERT_EQ(OK, pair.second) << pair.first.error(); | ||
1383 | const auto& configs = pair.first.configs(); | ||
1384 | |||
1385 | EXPECT_EQ(configs.find("CONFIG_ONE")->second, "1"); | ||
1386 | EXPECT_EQ(configs.find("CONFIG_TWO")->second, "2"); | ||
1387 | EXPECT_EQ(configs.find("CONFIG_THREE")->second, "3"); | ||
1388 | EXPECT_EQ(configs.find("CONFIG_Y")->second, "y"); | ||
1389 | EXPECT_EQ(configs.find("CONFIG_STR")->second, "string"); | ||
1390 | EXPECT_EQ(configs.find("CONFIG_HELLO")->second, "hello world!") | ||
1391 | << "Value should be \"hello world!\" without trailing spaces"; | ||
1392 | EXPECT_EQ(configs.find("CONFIG_WORLD")->second, "hello world!") | ||
1393 | << "Value should be \"hello world!\" without trailing spaces"; | ||
1394 | EXPECT_EQ(configs.find("CONFIG_GOOD")->second, "good morning!") | ||
1395 | << "Value should be \"good morning!\" without leading or trailing spaces"; | ||
1396 | EXPECT_EQ(configs.find("CONFIG_MORNING")->second, "good morning!") | ||
1397 | << "Value should be \"good morning!\" without leading or trailing spaces"; | ||
1398 | EXPECT_EQ(configs.find("CONFIG_NOT_SET")->second, "n"); | ||
1399 | } | ||
1400 | |||
1401 | // Run KernelConfigParserInvalidTest on processComments = {true, false} | ||
1402 | class KernelConfigParserInvalidTest : public ::testing::TestWithParam<bool> {}; | ||
1403 | |||
1404 | TEST_P(KernelConfigParserInvalidTest, NonSet1) { | ||
1405 | const std::string data = "# CONFIG_NOT_EXIST is not sat\n"; | ||
1406 | auto pair = processData(data, GetParam() /* processComments */); | ||
1407 | ASSERT_EQ(OK, pair.second) << pair.first.error(); | ||
1408 | const auto& configs = pair.first.configs(); | ||
1409 | EXPECT_EQ(configs.find("CONFIG_NOT_EXIST"), configs.end()) | ||
1410 | << "CONFIG_NOT_EXIST should not exist because of typo"; | ||
1411 | } | ||
1412 | |||
1413 | TEST_P(KernelConfigParserInvalidTest, InvalidLine1) { | ||
1414 | const std::string data = "FOO_CONFIG=foo\n"; | ||
1415 | ASSERT_NE(OK, processData(data, GetParam() /* processComments */).second); | ||
1416 | } | ||
1417 | |||
1418 | TEST_P(KernelConfigParserInvalidTest, InvalidLine2) { | ||
1419 | const std::string data = "CONFIG_BAR-BAZ=foo\n"; | ||
1420 | ASSERT_NE(OK, processData(data, GetParam() /* processComments */).second); | ||
1421 | } | ||
1422 | |||
1423 | INSTANTIATE_TEST_CASE_P(KernelConfigParser, KernelConfigParserInvalidTest, ::testing::Bool()); | ||
1424 | |||
1365 | } // namespace vintf | 1425 | } // namespace vintf |
1366 | } // namespace android | 1426 | } // namespace android |
1367 | 1427 | ||