11bbe3d1eSWilliam A. Kennington III #include "config_parser.hpp"
21bbe3d1eSWilliam A. Kennington III
361ef4f2bSWilliam A. Kennington III #include <stdplus/exception.hpp>
4409f1a66SWilliam A. Kennington III #include <stdplus/fd/atomic.hpp>
561ef4f2bSWilliam A. Kennington III #include <stdplus/fd/create.hpp>
6409f1a66SWilliam A. Kennington III #include <stdplus/fd/fmt.hpp>
761ef4f2bSWilliam A. Kennington III #include <stdplus/fd/line.hpp>
88664252aSWilliam A. Kennington III #include <stdplus/str/cat.hpp>
989d734b9SPatrick Williams
10cafc1512SWilliam A. Kennington III #include <format>
1189d734b9SPatrick Williams #include <functional>
1289d734b9SPatrick Williams #include <iterator>
1389d734b9SPatrick Williams #include <stdexcept>
14e21a5cf9SWilliam A. Kennington III #include <string>
1561ef4f2bSWilliam A. Kennington III #include <utility>
161bbe3d1eSWilliam A. Kennington III
171bbe3d1eSWilliam A. Kennington III namespace phosphor
181bbe3d1eSWilliam A. Kennington III {
191bbe3d1eSWilliam A. Kennington III namespace network
201bbe3d1eSWilliam A. Kennington III {
211bbe3d1eSWilliam A. Kennington III namespace config
221bbe3d1eSWilliam A. Kennington III {
231bbe3d1eSWilliam A. Kennington III
24150753f3SWilliam A. Kennington III using std::literals::string_view_literals::operator""sv;
25150753f3SWilliam A. Kennington III
icaseeq(std::string_view in,std::string_view expected)26150753f3SWilliam A. Kennington III bool icaseeq(std::string_view in, std::string_view expected) noexcept
27150753f3SWilliam A. Kennington III {
28150753f3SWilliam A. Kennington III return std::equal(in.begin(), in.end(), expected.begin(), expected.end(),
29150753f3SWilliam A. Kennington III [](auto a, auto b) { return tolower(a) == b; });
30150753f3SWilliam A. Kennington III }
31150753f3SWilliam A. Kennington III
parseBool(std::string_view in)32150753f3SWilliam A. Kennington III std::optional<bool> parseBool(std::string_view in) noexcept
33150753f3SWilliam A. Kennington III {
34150753f3SWilliam A. Kennington III if (in == "1"sv || icaseeq(in, "yes"sv) || icaseeq(in, "y"sv) ||
35150753f3SWilliam A. Kennington III icaseeq(in, "true"sv) || icaseeq(in, "t"sv) || icaseeq(in, "on"sv))
36150753f3SWilliam A. Kennington III {
37150753f3SWilliam A. Kennington III return true;
38150753f3SWilliam A. Kennington III }
39150753f3SWilliam A. Kennington III if (in == "0"sv || icaseeq(in, "no"sv) || icaseeq(in, "n"sv) ||
40150753f3SWilliam A. Kennington III icaseeq(in, "false"sv) || icaseeq(in, "f"sv) || icaseeq(in, "off"sv))
41150753f3SWilliam A. Kennington III {
42150753f3SWilliam A. Kennington III return false;
43150753f3SWilliam A. Kennington III }
44150753f3SWilliam A. Kennington III return std::nullopt;
45150753f3SWilliam A. Kennington III }
46150753f3SWilliam A. Kennington III
pathForIntfConf(const fs::path & dir,std::string_view intf)47a520a39dSWilliam A. Kennington III fs::path pathForIntfConf(const fs::path& dir, std::string_view intf)
48a520a39dSWilliam A. Kennington III {
498664252aSWilliam A. Kennington III return dir / stdplus::strCat("00-bmc-"sv, intf, ".network"sv);
50a520a39dSWilliam A. Kennington III }
51a520a39dSWilliam A. Kennington III
pathForIntfDev(const fs::path & dir,std::string_view intf)52a520a39dSWilliam A. Kennington III fs::path pathForIntfDev(const fs::path& dir, std::string_view intf)
53a520a39dSWilliam A. Kennington III {
548664252aSWilliam A. Kennington III return dir / stdplus::strCat(intf, ".netdev"sv);
55a520a39dSWilliam A. Kennington III }
56a520a39dSWilliam A. Kennington III
getLastValueString(std::string_view section,std::string_view key) const57*ad205028SPatrick Williams const std::string* SectionMap::getLastValueString(
58*ad205028SPatrick Williams std::string_view section, std::string_view key) const noexcept
59e21a5cf9SWilliam A. Kennington III {
60e21a5cf9SWilliam A. Kennington III auto sit = find(section);
61e21a5cf9SWilliam A. Kennington III if (sit == end())
62e21a5cf9SWilliam A. Kennington III {
63e21a5cf9SWilliam A. Kennington III return nullptr;
64e21a5cf9SWilliam A. Kennington III }
65e21a5cf9SWilliam A. Kennington III for (auto it = sit->second.rbegin(); it != sit->second.rend(); ++it)
66e21a5cf9SWilliam A. Kennington III {
67e21a5cf9SWilliam A. Kennington III auto kit = it->find(key);
68e21a5cf9SWilliam A. Kennington III if (kit == it->end() || kit->second.empty())
69e21a5cf9SWilliam A. Kennington III {
70e21a5cf9SWilliam A. Kennington III continue;
71e21a5cf9SWilliam A. Kennington III }
720dd0937dSWilliam A. Kennington III return &kit->second.back().get();
73e21a5cf9SWilliam A. Kennington III }
74e21a5cf9SWilliam A. Kennington III return nullptr;
75e21a5cf9SWilliam A. Kennington III }
76e21a5cf9SWilliam A. Kennington III
getValueStrings(std::string_view section,std::string_view key) const77e21a5cf9SWilliam A. Kennington III std::vector<std::string> SectionMap::getValueStrings(std::string_view section,
78e21a5cf9SWilliam A. Kennington III std::string_view key) const
79e21a5cf9SWilliam A. Kennington III {
80e21a5cf9SWilliam A. Kennington III return getValues(section, key,
81e21a5cf9SWilliam A. Kennington III [](const Value& v) { return std::string(v); });
82e21a5cf9SWilliam A. Kennington III }
83e21a5cf9SWilliam A. Kennington III
operator ()(std::string_view s)84be3bd2fcSWilliam A. Kennington III void KeyCheck::operator()(std::string_view s)
850dd0937dSWilliam A. Kennington III {
860dd0937dSWilliam A. Kennington III for (auto c : s)
870dd0937dSWilliam A. Kennington III {
880dd0937dSWilliam A. Kennington III if (c == '\n' || c == '=')
890dd0937dSWilliam A. Kennington III {
900dd0937dSWilliam A. Kennington III throw std::invalid_argument(
918664252aSWilliam A. Kennington III stdplus::strCat("Invalid Config Key: "sv, s));
920dd0937dSWilliam A. Kennington III }
930dd0937dSWilliam A. Kennington III }
940dd0937dSWilliam A. Kennington III }
950dd0937dSWilliam A. Kennington III
operator ()(std::string_view s)96be3bd2fcSWilliam A. Kennington III void SectionCheck::operator()(std::string_view s)
970dd0937dSWilliam A. Kennington III {
980dd0937dSWilliam A. Kennington III for (auto c : s)
990dd0937dSWilliam A. Kennington III {
1000dd0937dSWilliam A. Kennington III if (c == '\n' || c == ']')
1010dd0937dSWilliam A. Kennington III {
1020dd0937dSWilliam A. Kennington III throw std::invalid_argument(
1038664252aSWilliam A. Kennington III stdplus::strCat("Invalid Config Section: "sv, s));
1040dd0937dSWilliam A. Kennington III }
1050dd0937dSWilliam A. Kennington III }
1060dd0937dSWilliam A. Kennington III }
1070dd0937dSWilliam A. Kennington III
operator ()(std::string_view s)108be3bd2fcSWilliam A. Kennington III void ValueCheck::operator()(std::string_view s)
1090dd0937dSWilliam A. Kennington III {
1100dd0937dSWilliam A. Kennington III for (auto c : s)
1110dd0937dSWilliam A. Kennington III {
1120dd0937dSWilliam A. Kennington III if (c == '\n')
1130dd0937dSWilliam A. Kennington III {
1140dd0937dSWilliam A. Kennington III throw std::invalid_argument(
1158664252aSWilliam A. Kennington III stdplus::strCat("Invalid Config Value: "sv, s));
1160dd0937dSWilliam A. Kennington III }
1170dd0937dSWilliam A. Kennington III }
1180dd0937dSWilliam A. Kennington III }
1190dd0937dSWilliam A. Kennington III
Parser(const fs::path & filename)12025511a1cSWilliam A. Kennington III Parser::Parser(const fs::path& filename)
1211bbe3d1eSWilliam A. Kennington III {
12225511a1cSWilliam A. Kennington III setFile(filename);
1231bbe3d1eSWilliam A. Kennington III }
1241bbe3d1eSWilliam A. Kennington III
isspace(char c)125f55b7d8eSWilliam A. Kennington III constexpr bool isspace(char c) noexcept
1261bbe3d1eSWilliam A. Kennington III {
12761ef4f2bSWilliam A. Kennington III return c == ' ' || c == '\t';
1281bbe3d1eSWilliam A. Kennington III }
1291bbe3d1eSWilliam A. Kennington III
iscomment(char c)130f55b7d8eSWilliam A. Kennington III constexpr bool iscomment(char c) noexcept
1311bbe3d1eSWilliam A. Kennington III {
13261ef4f2bSWilliam A. Kennington III return c == '#' || c == ';';
13361ef4f2bSWilliam A. Kennington III }
13461ef4f2bSWilliam A. Kennington III
removePadding(std::string_view & str)13561ef4f2bSWilliam A. Kennington III static void removePadding(std::string_view& str) noexcept
13661ef4f2bSWilliam A. Kennington III {
13761ef4f2bSWilliam A. Kennington III size_t idx = str.size();
13861ef4f2bSWilliam A. Kennington III for (; idx > 0 && isspace(str[idx - 1]); idx--)
13961ef4f2bSWilliam A. Kennington III ;
14061ef4f2bSWilliam A. Kennington III str.remove_suffix(str.size() - idx);
14161ef4f2bSWilliam A. Kennington III
14261ef4f2bSWilliam A. Kennington III idx = 0;
14361ef4f2bSWilliam A. Kennington III for (; idx < str.size() && isspace(str[idx]); idx++)
14461ef4f2bSWilliam A. Kennington III ;
14561ef4f2bSWilliam A. Kennington III str.remove_prefix(idx);
14661ef4f2bSWilliam A. Kennington III }
14761ef4f2bSWilliam A. Kennington III
14861ef4f2bSWilliam A. Kennington III struct Parse
14961ef4f2bSWilliam A. Kennington III {
150bc52d93dSWilliam A. Kennington III std::reference_wrapper<const fs::path> filename;
15134bb3e20SWilliam A. Kennington III SectionMap map;
152bc52d93dSWilliam A. Kennington III KeyValuesMap* section;
153bc52d93dSWilliam A. Kennington III std::vector<std::string> warnings;
154bc52d93dSWilliam A. Kennington III size_t lineno;
155bc52d93dSWilliam A. Kennington III
Parsephosphor::network::config::Parse156bc52d93dSWilliam A. Kennington III inline Parse(const fs::path& filename) :
157bc52d93dSWilliam A. Kennington III filename(filename), section(nullptr), lineno(0)
15889d734b9SPatrick Williams {}
15961ef4f2bSWilliam A. Kennington III
pumpSectionphosphor::network::config::Parse16061ef4f2bSWilliam A. Kennington III void pumpSection(std::string_view line)
16161ef4f2bSWilliam A. Kennington III {
16261ef4f2bSWilliam A. Kennington III auto cpos = line.find(']');
163bc52d93dSWilliam A. Kennington III if (cpos == line.npos)
164bc52d93dSWilliam A. Kennington III {
165cafc1512SWilliam A. Kennington III warnings.emplace_back(std::format("{}:{}: Section missing ]",
166bc52d93dSWilliam A. Kennington III filename.get().native(), lineno));
167bc52d93dSWilliam A. Kennington III }
168bc52d93dSWilliam A. Kennington III else
169bc52d93dSWilliam A. Kennington III {
170bc52d93dSWilliam A. Kennington III for (auto c : line.substr(cpos + 1))
171bc52d93dSWilliam A. Kennington III {
172bc52d93dSWilliam A. Kennington III if (!isspace(c))
173bc52d93dSWilliam A. Kennington III {
174bc52d93dSWilliam A. Kennington III warnings.emplace_back(
175cafc1512SWilliam A. Kennington III std::format("{}:{}: Characters outside section name",
176bc52d93dSWilliam A. Kennington III filename.get().native(), lineno));
177bc52d93dSWilliam A. Kennington III break;
178bc52d93dSWilliam A. Kennington III }
179bc52d93dSWilliam A. Kennington III }
180bc52d93dSWilliam A. Kennington III }
18161ef4f2bSWilliam A. Kennington III auto s = line.substr(0, cpos);
18234bb3e20SWilliam A. Kennington III auto it = map.find(s);
18334bb3e20SWilliam A. Kennington III if (it == map.end())
18461ef4f2bSWilliam A. Kennington III {
18534bb3e20SWilliam A. Kennington III std::tie(it, std::ignore) = map.emplace(
1860dd0937dSWilliam A. Kennington III Section(Section::unchecked(), s), KeyValuesMapList{});
18761ef4f2bSWilliam A. Kennington III }
188e21a5cf9SWilliam A. Kennington III section = &it->second.emplace_back();
18961ef4f2bSWilliam A. Kennington III }
19061ef4f2bSWilliam A. Kennington III
pumpKVphosphor::network::config::Parse19161ef4f2bSWilliam A. Kennington III void pumpKV(std::string_view line)
19261ef4f2bSWilliam A. Kennington III {
19361ef4f2bSWilliam A. Kennington III auto epos = line.find('=');
194bc52d93dSWilliam A. Kennington III std::vector<std::string> new_warnings;
19561ef4f2bSWilliam A. Kennington III if (epos == line.npos)
1961bbe3d1eSWilliam A. Kennington III {
197cafc1512SWilliam A. Kennington III new_warnings.emplace_back(std::format(
198bc52d93dSWilliam A. Kennington III "{}:{}: KV missing `=`", filename.get().native(), lineno));
1991bbe3d1eSWilliam A. Kennington III }
20061ef4f2bSWilliam A. Kennington III auto k = line.substr(0, epos);
20161ef4f2bSWilliam A. Kennington III removePadding(k);
202bc52d93dSWilliam A. Kennington III if (section == nullptr)
203bc52d93dSWilliam A. Kennington III {
204bc52d93dSWilliam A. Kennington III new_warnings.emplace_back(
205cafc1512SWilliam A. Kennington III std::format("{}:{}: Key `{}` missing section",
206bc52d93dSWilliam A. Kennington III filename.get().native(), lineno, k));
207bc52d93dSWilliam A. Kennington III }
208bc52d93dSWilliam A. Kennington III if (!new_warnings.empty())
209bc52d93dSWilliam A. Kennington III {
210bc52d93dSWilliam A. Kennington III warnings.insert(warnings.end(),
211bc52d93dSWilliam A. Kennington III std::make_move_iterator(new_warnings.begin()),
212bc52d93dSWilliam A. Kennington III std::make_move_iterator(new_warnings.end()));
213bc52d93dSWilliam A. Kennington III return;
214bc52d93dSWilliam A. Kennington III }
21561ef4f2bSWilliam A. Kennington III auto v = line.substr(epos + 1);
21661ef4f2bSWilliam A. Kennington III removePadding(v);
21761ef4f2bSWilliam A. Kennington III
21861ef4f2bSWilliam A. Kennington III auto it = section->find(k);
21961ef4f2bSWilliam A. Kennington III if (it == section->end())
2201bbe3d1eSWilliam A. Kennington III {
2210dd0937dSWilliam A. Kennington III std::tie(it, std::ignore) =
2220dd0937dSWilliam A. Kennington III section->emplace(Key(Key::unchecked(), k), ValueList{});
2231bbe3d1eSWilliam A. Kennington III }
2240dd0937dSWilliam A. Kennington III it->second.emplace_back(Value::unchecked(), v);
2251bbe3d1eSWilliam A. Kennington III }
22661ef4f2bSWilliam A. Kennington III
pumpphosphor::network::config::Parse22761ef4f2bSWilliam A. Kennington III void pump(std::string_view line)
2281bbe3d1eSWilliam A. Kennington III {
229bc52d93dSWilliam A. Kennington III lineno++;
23061ef4f2bSWilliam A. Kennington III for (size_t i = 0; i < line.size(); ++i)
2311bbe3d1eSWilliam A. Kennington III {
23261ef4f2bSWilliam A. Kennington III auto c = line[i];
23361ef4f2bSWilliam A. Kennington III if (iscomment(c))
23461ef4f2bSWilliam A. Kennington III {
23561ef4f2bSWilliam A. Kennington III return;
23661ef4f2bSWilliam A. Kennington III }
23761ef4f2bSWilliam A. Kennington III else if (c == '[')
23861ef4f2bSWilliam A. Kennington III {
23961ef4f2bSWilliam A. Kennington III return pumpSection(line.substr(i + 1));
24061ef4f2bSWilliam A. Kennington III }
24161ef4f2bSWilliam A. Kennington III else if (!isspace(c))
24261ef4f2bSWilliam A. Kennington III {
24361ef4f2bSWilliam A. Kennington III return pumpKV(line.substr(i));
2441bbe3d1eSWilliam A. Kennington III }
2451bbe3d1eSWilliam A. Kennington III }
2461bbe3d1eSWilliam A. Kennington III }
24761ef4f2bSWilliam A. Kennington III };
24861ef4f2bSWilliam A. Kennington III
setFile(const fs::path & filename)24961ef4f2bSWilliam A. Kennington III void Parser::setFile(const fs::path& filename)
25061ef4f2bSWilliam A. Kennington III {
251bc52d93dSWilliam A. Kennington III Parse parse(filename);
25261ef4f2bSWilliam A. Kennington III
253301e8ad6SWilliam A. Kennington III bool fileExists = true;
25461ef4f2bSWilliam A. Kennington III try
25561ef4f2bSWilliam A. Kennington III {
25661ef4f2bSWilliam A. Kennington III auto fd = stdplus::fd::open(filename.c_str(),
25761ef4f2bSWilliam A. Kennington III stdplus::fd::OpenAccess::ReadOnly);
25861ef4f2bSWilliam A. Kennington III stdplus::fd::LineReader reader(fd);
25961ef4f2bSWilliam A. Kennington III while (true)
26061ef4f2bSWilliam A. Kennington III {
26161ef4f2bSWilliam A. Kennington III parse.pump(*reader.readLine());
26261ef4f2bSWilliam A. Kennington III }
26361ef4f2bSWilliam A. Kennington III }
264bc52d93dSWilliam A. Kennington III catch (const stdplus::exception::Eof&)
26589d734b9SPatrick Williams {}
266301e8ad6SWilliam A. Kennington III catch (const std::system_error& e)
26761ef4f2bSWilliam A. Kennington III {
268301e8ad6SWilliam A. Kennington III fileExists = false;
26961ef4f2bSWilliam A. Kennington III // TODO: Pass exceptions once callers can handle them
270bc52d93dSWilliam A. Kennington III parse.warnings.emplace_back(
271cafc1512SWilliam A. Kennington III std::format("{}: Open error: {}", filename.native(), e.what()));
27261ef4f2bSWilliam A. Kennington III }
27361ef4f2bSWilliam A. Kennington III
27434bb3e20SWilliam A. Kennington III this->map = std::move(parse.map);
275301e8ad6SWilliam A. Kennington III this->fileExists = fileExists;
276a520a39dSWilliam A. Kennington III this->filename = filename;
277bc52d93dSWilliam A. Kennington III this->warnings = std::move(parse.warnings);
2781bbe3d1eSWilliam A. Kennington III }
2791bbe3d1eSWilliam A. Kennington III
writeFileInt(const SectionMap & map,const fs::path & filename)280409f1a66SWilliam A. Kennington III static void writeFileInt(const SectionMap& map, const fs::path& filename)
281409f1a66SWilliam A. Kennington III {
282409f1a66SWilliam A. Kennington III stdplus::fd::AtomicWriter writer(filename, 0644);
283409f1a66SWilliam A. Kennington III stdplus::fd::FormatBuffer out(writer);
284409f1a66SWilliam A. Kennington III for (const auto& [section, maps] : map)
285409f1a66SWilliam A. Kennington III {
286409f1a66SWilliam A. Kennington III for (const auto& map : maps)
287409f1a66SWilliam A. Kennington III {
2888664252aSWilliam A. Kennington III out.appends("["sv, section.get(), "]\n"sv);
289409f1a66SWilliam A. Kennington III for (const auto& [key, vals] : map)
290409f1a66SWilliam A. Kennington III {
291409f1a66SWilliam A. Kennington III for (const auto& val : vals)
292409f1a66SWilliam A. Kennington III {
2938664252aSWilliam A. Kennington III out.appends(key.get(), "="sv, val.get(), "\n"sv);
294409f1a66SWilliam A. Kennington III }
295409f1a66SWilliam A. Kennington III }
296409f1a66SWilliam A. Kennington III }
297409f1a66SWilliam A. Kennington III }
298409f1a66SWilliam A. Kennington III out.flush();
299409f1a66SWilliam A. Kennington III writer.commit();
300409f1a66SWilliam A. Kennington III }
301409f1a66SWilliam A. Kennington III
writeFile() const302409f1a66SWilliam A. Kennington III void Parser::writeFile() const
303409f1a66SWilliam A. Kennington III {
304409f1a66SWilliam A. Kennington III writeFileInt(map, filename);
305409f1a66SWilliam A. Kennington III }
306409f1a66SWilliam A. Kennington III
writeFile(const fs::path & filename)307409f1a66SWilliam A. Kennington III void Parser::writeFile(const fs::path& filename)
308409f1a66SWilliam A. Kennington III {
309409f1a66SWilliam A. Kennington III writeFileInt(map, filename);
310409f1a66SWilliam A. Kennington III this->filename = filename;
311409f1a66SWilliam A. Kennington III }
312409f1a66SWilliam A. Kennington III
3131bbe3d1eSWilliam A. Kennington III } // namespace config
3141bbe3d1eSWilliam A. Kennington III } // namespace network
3151bbe3d1eSWilliam A. Kennington III } // namespace phosphor
316