1 #include "config_parser.hpp"
2 
3 #include <algorithm>
4 #include <fstream>
5 #include <list>
6 #include <phosphor-logging/log.hpp>
7 #include <regex>
8 #include <string>
9 #include <unordered_map>
10 
11 namespace phosphor
12 {
13 namespace network
14 {
15 namespace config
16 {
17 
18 using namespace phosphor::logging;
19 
20 Parser::Parser(const fs::path& filePath)
21 {
22     setFile(filePath);
23 }
24 
25 std::tuple<ReturnCode, KeyValueMap>
26     Parser::getSection(const std::string& section)
27 {
28     auto it = sections.find(section);
29     if (it == sections.end())
30     {
31         KeyValueMap keyValues;
32         return std::make_tuple(ReturnCode::SECTION_NOT_FOUND,
33                                std::move(keyValues));
34     }
35 
36     return std::make_tuple(ReturnCode::SUCCESS, it->second);
37 }
38 
39 std::tuple<ReturnCode, ValueList> Parser::getValues(const std::string& section,
40                                                     const std::string& key)
41 {
42     ValueList values;
43     KeyValueMap keyValues{};
44     auto rc = ReturnCode::SUCCESS;
45 
46     std::tie(rc, keyValues) = getSection(section);
47     if (rc != ReturnCode::SUCCESS)
48     {
49         return std::make_tuple(rc, std::move(values));
50     }
51 
52     auto it = keyValues.find(key);
53     if (it == keyValues.end())
54     {
55         return std::make_tuple(ReturnCode::KEY_NOT_FOUND, std::move(values));
56     }
57 
58     for (; it != keyValues.end() && key == it->first; it++)
59     {
60         values.push_back(it->second);
61     }
62 
63     return std::make_tuple(ReturnCode::SUCCESS, std::move(values));
64 }
65 
66 bool Parser::isValueExist(const std::string& section, const std::string& key,
67                           const std::string& value)
68 {
69     auto rc = ReturnCode::SUCCESS;
70     ValueList values;
71     std::tie(rc, values) = getValues(section, key);
72 
73     if (rc != ReturnCode::SUCCESS)
74     {
75         return false;
76     }
77     auto it = std::find(values.begin(), values.end(), value);
78     return it != std::end(values) ? true : false;
79 }
80 
81 void Parser::setValue(const std::string& section, const std::string& key,
82                       const std::string& value)
83 {
84     KeyValueMap values;
85     auto it = sections.find(section);
86     if (it != sections.end())
87     {
88         values = std::move(it->second);
89     }
90     values.insert(std::make_pair(key, value));
91 
92     if (it != sections.end())
93     {
94         it->second = std::move(values);
95     }
96     else
97     {
98         sections.insert(std::make_pair(section, std::move(values)));
99     }
100 }
101 
102 #if 0
103 void Parser::print()
104 {
105     for (auto section : sections)
106     {
107         std::cout << "[" << section.first << "]\n\n";
108         for (auto keyValue : section.second)
109         {
110             std::cout << keyValue.first << "=" << keyValue.second << "\n";
111         }
112     }
113 }
114 #endif
115 
116 void Parser::setFile(const fs::path& filePath)
117 {
118     this->filePath = filePath;
119     std::fstream stream;
120     stream.open(filePath.string(), std::fstream::in);
121 
122     if (!stream.is_open())
123     {
124         return;
125     }
126     // clear all the section data.
127     sections.clear();
128     parse(stream);
129     stream.close();
130 }
131 
132 void Parser::parse(std::istream& in)
133 {
134     static const std::regex commentRegex{R"x(\s*[;#])x"};
135     static const std::regex sectionRegex{R"x(\s*\[([^\]]+)\])x"};
136     static const std::regex valueRegex{R"x(\s*(\S[^ \t=]*)\s*=\s*(\S+)\s*$)x"};
137     std::string section;
138     std::smatch pieces;
139     for (std::string line; std::getline(in, line);)
140     {
141         if (line.empty() || std::regex_match(line, pieces, commentRegex))
142         {
143             // skip comment lines and blank lines
144         }
145         else if (std::regex_match(line, pieces, sectionRegex))
146         {
147             if (pieces.size() == 2)
148             {
149                 section = pieces[1].str();
150             }
151         }
152         else if (std::regex_match(line, pieces, valueRegex))
153         {
154             if (pieces.size() == 3)
155             {
156                 setValue(section, pieces[1].str(), pieces[2].str());
157             }
158         }
159     }
160 }
161 
162 } // namespace config
163 } // namespace network
164 } // namespace phosphor
165