1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "Configurator.hpp"
16 #include "Debug.hpp"
17
18 #include <algorithm>
19 #include <fstream>
20 #include <istream>
21
22 namespace {
trimSpaces(const std::string & str)23 inline std::string trimSpaces(const std::string &str)
24 {
25 std::string trimmed = str;
26 trimmed.erase(trimmed.begin(), std::find_if(trimmed.begin(), trimmed.end(), [](unsigned char c) {
27 return !std::isspace(c);
28 }));
29 trimmed.erase(std::find_if(trimmed.rbegin(), trimmed.rend(), [](unsigned char c) {
30 return !std::isspace(c);
31 }).base(),
32 trimmed.end());
33 return trimmed;
34 }
35 } // namespace
36
37 namespace sw {
38
Configurator(const std::string & filePath)39 Configurator::Configurator(const std::string &filePath)
40 {
41 std::fstream file(filePath, std::ios::in);
42 if(file.fail())
43 {
44 return;
45 }
46 readConfiguration(file);
47 file.close();
48 }
49
Configurator(std::istream & str)50 Configurator::Configurator(std::istream &str)
51 {
52 readConfiguration(str);
53 }
54
readConfiguration(std::istream & str)55 bool Configurator::readConfiguration(std::istream &str)
56 {
57 std::string line;
58 std::string sectionName;
59
60 int lineNumber = 0;
61 while(getline(str, line))
62 {
63 ++lineNumber;
64 if(line.length())
65 {
66 if(line[line.length() - 1] == '\r')
67 {
68 line = line.substr(0, line.length() - 1);
69 }
70
71 if(!isprint(line[0]))
72 {
73 sw::warn("Cannot parse line %d of configuration, skipping line\n", lineNumber);
74 return false;
75 }
76
77 std::string::size_type pLeft = line.find_first_of(";#[=");
78
79 if(pLeft != std::string::npos)
80 {
81 switch(line[pLeft])
82 {
83 case '[':
84 {
85 std::string::size_type pRight = line.find_last_of("]");
86
87 if(pRight != std::string::npos && pRight > pLeft)
88 {
89 sectionName = trimSpaces(line.substr(pLeft + 1, pRight - pLeft - 1));
90 if(!sectionName.length())
91 {
92 sw::warn("Found empty section name at line %d of configuration\n", lineNumber);
93 }
94 }
95 }
96 break;
97 case '=':
98 {
99 std::string key = trimSpaces(line.substr(0, pLeft));
100 std::string value = trimSpaces(line.substr(pLeft + 1));
101 if(!key.length() || !value.length())
102 {
103 sw::warn("Cannot parse key-value pair at line %d of configuration (key or value is empty), skipping key-value pair\n", lineNumber);
104 }
105 else
106 {
107 sections[sectionName].keyValuePairs[key] = value;
108 }
109 }
110 break;
111 case ';':
112 case '#':
113 // Ignore comments
114 break;
115 }
116 }
117 }
118 }
119
120 return sections.size() > 0;
121 }
122
writeFile(const std::string & filePath,const std::string & title)123 void Configurator::writeFile(const std::string &filePath, const std::string &title)
124 {
125 std::fstream file(filePath, std::ios::out);
126 if(file.fail())
127 {
128 return;
129 }
130
131 file << "; " << title << std::endl
132 << std::endl;
133
134 for(const auto &[sectionName, section] : sections)
135 {
136 file << "[" << sectionName << "]" << std::endl;
137 for(const auto &[key, value] : section.keyValuePairs)
138 {
139 file << key << "=" << value << std::endl;
140 }
141 file << std::endl;
142 }
143
144 file.close();
145 }
146
getValueIfExists(const std::string & sectionName,const std::string & keyName) const147 std::optional<std::string> Configurator::getValueIfExists(const std::string §ionName, const std::string &keyName) const
148 {
149 const auto section = sections.find(sectionName);
150 if(section == sections.end())
151 {
152 return std::nullopt;
153 }
154
155 const auto keyValue = section->second.keyValuePairs.find(keyName);
156 if(keyValue == section->second.keyValuePairs.end())
157 {
158 return std::nullopt;
159 }
160
161 return keyValue->second;
162 }
163
getValue(const std::string & sectionName,const std::string & keyName,const std::string & defaultValue) const164 std::string Configurator::getValue(const std::string §ionName, const std::string &keyName, const std::string &defaultValue) const
165 {
166 const auto value = getValueIfExists(sectionName, keyName);
167 if(value)
168 {
169 return *value;
170 }
171 return defaultValue;
172 }
173
addValue(const std::string & sectionName,const std::string & keyName,const std::string & value)174 void Configurator::addValue(const std::string §ionName, const std::string &keyName, const std::string &value)
175 {
176 sections[sectionName].keyValuePairs[keyName] = value;
177 }
178
getBoolean(const std::string & sectionName,const std::string & keyName,bool defaultValue) const179 bool Configurator::getBoolean(const std::string §ionName, const std::string &keyName, bool defaultValue) const
180 {
181 auto strValue = getValueIfExists(sectionName, keyName);
182 if(!strValue)
183 {
184 return defaultValue;
185 }
186
187 std::stringstream ss{ *strValue };
188
189 bool val;
190 ss >> val;
191 if(ss.fail())
192 {
193 // Accept "true" and "false" as well.
194 ss.clear();
195 ss >> std::boolalpha >> val;
196 }
197 return val;
198 }
199
getFloat(const std::string & sectionName,const std::string & keyName,double defaultValue) const200 double Configurator::getFloat(const std::string §ionName, const std::string &keyName, double defaultValue) const
201 {
202 auto strValue = getValueIfExists(sectionName, keyName);
203 if(!strValue)
204 {
205 return defaultValue;
206 }
207
208 std::stringstream ss{ *strValue };
209
210 double val = 0;
211 ss >> val;
212 return val;
213 }
214 } // namespace sw
215