blob: b684fa293374c03aee7445f29886bbce86a30349 [file] [log] [blame]
Ratan Guptaed123a32017-06-15 09:07:31 +05301#include "config_parser.hpp"
Ratan Guptaed123a32017-06-15 09:07:31 +05302
William A. Kennington IIIbc52d932022-08-18 16:34:02 -07003#include <functional>
4#include <iterator>
William A. Kennington III61ef4f22022-08-18 16:29:09 -07005#include <stdplus/exception.hpp>
6#include <stdplus/fd/create.hpp>
7#include <stdplus/fd/line.hpp>
8#include <utility>
Ratan Guptaed123a32017-06-15 09:07:31 +05309
10namespace phosphor
11{
12namespace network
13{
14namespace config
15{
16
William A. Kennington III150753f2022-08-05 15:20:54 -070017using std::literals::string_view_literals::operator""sv;
18
19bool icaseeq(std::string_view in, std::string_view expected) noexcept
20{
21 return std::equal(in.begin(), in.end(), expected.begin(), expected.end(),
22 [](auto a, auto b) { return tolower(a) == b; });
23}
24
25std::optional<bool> parseBool(std::string_view in) noexcept
26{
27 if (in == "1"sv || icaseeq(in, "yes"sv) || icaseeq(in, "y"sv) ||
28 icaseeq(in, "true"sv) || icaseeq(in, "t"sv) || icaseeq(in, "on"sv))
29 {
30 return true;
31 }
32 if (in == "0"sv || icaseeq(in, "no"sv) || icaseeq(in, "n"sv) ||
33 icaseeq(in, "false"sv) || icaseeq(in, "f"sv) || icaseeq(in, "off"sv))
34 {
35 return false;
36 }
37 return std::nullopt;
38}
39
William A. Kennington III25511a12022-08-04 16:32:28 -070040Parser::Parser(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +053041{
William A. Kennington III25511a12022-08-04 16:32:28 -070042 setFile(filename);
Ratan Guptaed123a32017-06-15 09:07:31 +053043}
44
William A. Kennington III25511a12022-08-04 16:32:28 -070045const ValueList& Parser::getValues(std::string_view section,
46 std::string_view key) const noexcept
Ratan Guptaed123a32017-06-15 09:07:31 +053047{
William A. Kennington III25511a12022-08-04 16:32:28 -070048 static const ValueList empty;
49 auto sit = sections.find(section);
50 if (sit == sections.end())
Ratan Guptaed123a32017-06-15 09:07:31 +053051 {
William A. Kennington III25511a12022-08-04 16:32:28 -070052 return empty;
Ratan Guptaed123a32017-06-15 09:07:31 +053053 }
Ratan Guptac27170a2017-11-22 15:44:42 +053054
William A. Kennington III25511a12022-08-04 16:32:28 -070055 auto kit = sit->second.find(key);
56 if (kit == sit->second.end())
Ratan Guptac27170a2017-11-22 15:44:42 +053057 {
William A. Kennington III25511a12022-08-04 16:32:28 -070058 return empty;
Ratan Guptac27170a2017-11-22 15:44:42 +053059 }
60
William A. Kennington III25511a12022-08-04 16:32:28 -070061 return kit->second;
Ratan Guptaed123a32017-06-15 09:07:31 +053062}
63
William A. Kennington III61ef4f22022-08-18 16:29:09 -070064inline bool isspace(char c) noexcept
Ratan Guptaed123a32017-06-15 09:07:31 +053065{
William A. Kennington III61ef4f22022-08-18 16:29:09 -070066 return c == ' ' || c == '\t';
Ratan Guptaed123a32017-06-15 09:07:31 +053067}
68
William A. Kennington III61ef4f22022-08-18 16:29:09 -070069inline bool iscomment(char c) noexcept
70{
71 return c == '#' || c == ';';
72}
73
74static void removePadding(std::string_view& str) noexcept
75{
76 size_t idx = str.size();
77 for (; idx > 0 && isspace(str[idx - 1]); idx--)
78 ;
79 str.remove_suffix(str.size() - idx);
80
81 idx = 0;
82 for (; idx < str.size() && isspace(str[idx]); idx++)
83 ;
84 str.remove_prefix(idx);
85}
86
87struct Parse
88{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -070089 std::reference_wrapper<const fs::path> filename;
William A. Kennington III61ef4f22022-08-18 16:29:09 -070090 SectionMap sections;
William A. Kennington IIIbc52d932022-08-18 16:34:02 -070091 KeyValuesMap* section;
92 std::vector<std::string> warnings;
93 size_t lineno;
94
95 inline Parse(const fs::path& filename) :
96 filename(filename), section(nullptr), lineno(0)
97 {
98 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -070099
100 void pumpSection(std::string_view line)
101 {
102 auto cpos = line.find(']');
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700103 if (cpos == line.npos)
104 {
105 warnings.emplace_back(fmt::format("{}:{}: Section missing ]",
106 filename.get().native(), lineno));
107 }
108 else
109 {
110 for (auto c : line.substr(cpos + 1))
111 {
112 if (!isspace(c))
113 {
114 warnings.emplace_back(
115 fmt::format("{}:{}: Characters outside section name",
116 filename.get().native(), lineno));
117 break;
118 }
119 }
120 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700121 auto s = line.substr(0, cpos);
122 auto it = sections.find(s);
123 if (it == sections.end())
124 {
125 std::tie(it, std::ignore) =
126 sections.emplace(Section(s), KeyValuesMap{});
127 }
128 section = &it->second;
129 }
130
131 void pumpKV(std::string_view line)
132 {
133 auto epos = line.find('=');
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700134 std::vector<std::string> new_warnings;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700135 if (epos == line.npos)
136 {
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700137 new_warnings.emplace_back(fmt::format(
138 "{}:{}: KV missing `=`", filename.get().native(), lineno));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700139 }
140 auto k = line.substr(0, epos);
141 removePadding(k);
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700142 if (section == nullptr)
143 {
144 new_warnings.emplace_back(
145 fmt::format("{}:{}: Key `{}` missing section",
146 filename.get().native(), lineno, k));
147 }
148 if (!new_warnings.empty())
149 {
150 warnings.insert(warnings.end(),
151 std::make_move_iterator(new_warnings.begin()),
152 std::make_move_iterator(new_warnings.end()));
153 return;
154 }
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700155 auto v = line.substr(epos + 1);
156 removePadding(v);
157
158 auto it = section->find(k);
159 if (it == section->end())
160 {
161 std::tie(it, std::ignore) = section->emplace(Key(k), ValueList{});
162 }
163 it->second.emplace_back(v);
164 }
165
166 void pump(std::string_view line)
167 {
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700168 lineno++;
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700169 for (size_t i = 0; i < line.size(); ++i)
170 {
171 auto c = line[i];
172 if (iscomment(c))
173 {
174 return;
175 }
176 else if (c == '[')
177 {
178 return pumpSection(line.substr(i + 1));
179 }
180 else if (!isspace(c))
181 {
182 return pumpKV(line.substr(i));
183 }
184 }
185 }
186};
187
William A. Kennington III25511a12022-08-04 16:32:28 -0700188void Parser::setFile(const fs::path& filename)
Ratan Guptaed123a32017-06-15 09:07:31 +0530189{
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700190 Parse parse(filename);
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700191
192 try
Ratan Guptaed123a32017-06-15 09:07:31 +0530193 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700194 auto fd = stdplus::fd::open(filename.c_str(),
195 stdplus::fd::OpenAccess::ReadOnly);
196 stdplus::fd::LineReader reader(fd);
197 while (true)
Ratan Guptaed123a32017-06-15 09:07:31 +0530198 {
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700199 parse.pump(*reader.readLine());
Ratan Guptaed123a32017-06-15 09:07:31 +0530200 }
201 }
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700202 catch (const stdplus::exception::Eof&)
203 {
204 }
205 catch (const std::exception& e)
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700206 {
207 // TODO: Pass exceptions once callers can handle them
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700208 parse.warnings.emplace_back(
209 fmt::format("{}: Read error: {}", filename.native(), e.what()));
William A. Kennington III61ef4f22022-08-18 16:29:09 -0700210 }
211
212 this->sections = std::move(parse.sections);
William A. Kennington IIIbc52d932022-08-18 16:34:02 -0700213 this->warnings = std::move(parse.warnings);
Ratan Guptaed123a32017-06-15 09:07:31 +0530214}
215
Gunnar Mills57d9c502018-09-14 14:42:34 -0500216} // namespace config
217} // namespace network
218} // namespace phosphor