1 #include "config_parser.hpp"
2 
3 #include <stdplus/exception.hpp>
4 #include <stdplus/fd/create.hpp>
5 #include <stdplus/fd/line.hpp>
6 #include <utility>
7 
8 namespace phosphor
9 {
10 namespace network
11 {
12 namespace config
13 {
14 
15 Parser::Parser(const fs::path& filename)
16 {
17     setFile(filename);
18 }
19 
20 const ValueList& Parser::getValues(std::string_view section,
21                                    std::string_view key) const noexcept
22 {
23     static const ValueList empty;
24     auto sit = sections.find(section);
25     if (sit == sections.end())
26     {
27         return empty;
28     }
29 
30     auto kit = sit->second.find(key);
31     if (kit == sit->second.end())
32     {
33         return empty;
34     }
35 
36     return kit->second;
37 }
38 
39 inline bool isspace(char c) noexcept
40 {
41     return c == ' ' || c == '\t';
42 }
43 
44 inline bool iscomment(char c) noexcept
45 {
46     return c == '#' || c == ';';
47 }
48 
49 static void removePadding(std::string_view& str) noexcept
50 {
51     size_t idx = str.size();
52     for (; idx > 0 && isspace(str[idx - 1]); idx--)
53         ;
54     str.remove_suffix(str.size() - idx);
55 
56     idx = 0;
57     for (; idx < str.size() && isspace(str[idx]); idx++)
58         ;
59     str.remove_prefix(idx);
60 }
61 
62 struct Parse
63 {
64     SectionMap sections;
65     KeyValuesMap* section = nullptr;
66 
67     void pumpSection(std::string_view line)
68     {
69         auto cpos = line.find(']');
70         auto s = line.substr(0, cpos);
71         auto it = sections.find(s);
72         if (it == sections.end())
73         {
74             std::tie(it, std::ignore) =
75                 sections.emplace(Section(s), KeyValuesMap{});
76         }
77         section = &it->second;
78     }
79 
80     void pumpKV(std::string_view line)
81     {
82         auto epos = line.find('=');
83         if (epos == line.npos)
84         {
85             return;
86         }
87         if (section == nullptr)
88         {
89             return;
90         }
91         auto k = line.substr(0, epos);
92         removePadding(k);
93         auto v = line.substr(epos + 1);
94         removePadding(v);
95 
96         auto it = section->find(k);
97         if (it == section->end())
98         {
99             std::tie(it, std::ignore) = section->emplace(Key(k), ValueList{});
100         }
101         it->second.emplace_back(v);
102     }
103 
104     void pump(std::string_view line)
105     {
106         for (size_t i = 0; i < line.size(); ++i)
107         {
108             auto c = line[i];
109             if (iscomment(c))
110             {
111                 return;
112             }
113             else if (c == '[')
114             {
115                 return pumpSection(line.substr(i + 1));
116             }
117             else if (!isspace(c))
118             {
119                 return pumpKV(line.substr(i));
120             }
121         }
122     }
123 };
124 
125 void Parser::setFile(const fs::path& filename)
126 {
127     Parse parse;
128 
129     try
130     {
131         auto fd = stdplus::fd::open(filename.c_str(),
132                                     stdplus::fd::OpenAccess::ReadOnly);
133         stdplus::fd::LineReader reader(fd);
134         while (true)
135         {
136             parse.pump(*reader.readLine());
137         }
138     }
139     catch (...)
140     {
141         // TODO: Pass exceptions once callers can handle them
142     }
143 
144     this->sections = std::move(parse.sections);
145 }
146 
147 } // namespace config
148 } // namespace network
149 } // namespace phosphor
150