begin of config file parsing
Stefan Schuermans authored 7 years ago
|
1) /*
2) * EtherPix simulator
3) *
4) * Copyright 2017 Stefan Schuermans <stefan schuermans info>
5) *
6) * This program is free software: you can redistribute it and/or modify
7) * it under the terms of the GNU General Public License as published by
8) * the Free Software Foundation, version 3 of the License.
9) *
10) *
11) * This program is distributed in the hope that it will be useful,
12) * but WITHOUT ANY WARRANTY; without even the implied warranty of
13) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14) * GNU General Public License for more details.
15) *
16) * You should have received a copy of the GNU Lesser General Public License
17) * along with this program. If not, see <http://www.gnu.org/licenses/>.
18) */
19)
20) #include <algorithm>
21) #include <cctype>
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
22) #include <cmath>
23) #include <cstdlib>
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
24) #include <functional>
25) #include <fstream>
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
26) #include <map>
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
27) #include <stdexcept>
28) #include <sstream>
29) #include <string>
30)
31) #include "config.h"
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
32) #include "distri.h"
33) #include "mapping.h"
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
34)
35) /**
36) * @brief constructor
37) * @param[in] configFile name of config file
38) */
39) Config::Config(std::string const &configFile)
40) {
41) readFile(configFile);
42) }
43)
44) /**
45) * @brief remove whitespace at beging and end of string
46) * @param[in,out] str string to process
47) */
48) void Config::trim(std::string &str)
49) {
50) // remove leading whitespace
51) str.erase(str.begin(),
52) std::find_if(str.begin(), str.end(),
53) std::not1(std::ptr_fun<int, int>(std::isspace))));
54)
55) // remove trailing whitespace
56) str.erase(std::find_if(str.rbegin(), str.rend(),
57) std::not1(std::ptr_fun<int, int>(std::isspace))).base(),
58) str.end());
59) }
60)
61) /**
62) * @brief split string at sequences of whitespace
63) * @param[in] str string to split
64) * @param[out] fields fields of string
65) */
66) void Config::split(std::string const &str, std::vector<std::string> &fields)
67) {
68) std::stringstream strm(str);
69) fields.clear();
70) while (! strm.eof()) {
71) std::string field;
72) strm >> field;
73) if (! field.empty()) {
74) fields.push_back(field);
75) }
76) }
77) }
78)
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
79) /**
80) * @brief split string at specific character
81) * @param[in] str string to split
82) * @param[in] delim delimiter character
83) * @param[out] fields fields of string
84) */
85) void Config::split_chr(std::string const &str, char delim,
86) std::vector<std::string> &fields)
87) {
88) std::string::size_type pos = 0, end;
89) fields.clear();
90) while ((end = str.find(delim, pos)) != std::string::npos) {
91) fields.push_back(str.substr(pos, end - pos));
92) pos = end + 1;
93) }
94) fields.push_back(str.substr(pos));
95) }
96)
97) /**
98) * @brief convert string to unsigned long
99) * @param[in] str string to convert
100) * @return unsigned long parsed from string
101) * @throws std::exception in case of error
102) */
103) unsigned long Config::str2ul(std::string const &str)
104) {
105) char const *sz = str.c_str();
106) char *end;
107) unsigned long ul = strtoul(sz, &end, 0);
108) if (end == sz || *end != 0) {
109) std::stringstream msg;
110) msg << "invalid unsigned integer \"" << str << "\"";
111) throw std::runtime_error(msg.str());
112) }
113) return ul;
114) }
115)
116) /**
117) * @brief convert string to two unsigned longs
118) * @param[in] str string containing two comma-separated unsigned integers
119) * @param[out] ul1 first unsigned long parsed from string
120) * @param[out] ul2 second unsigned long parsed from string
121) * @throws std::exception in case of error
122) */
123) void Config::str2ul2(std::string const &str,
124) unsigned long &ul1, unsigned long &ul2)
125) {
126) std::vector<std::string> fields;
127) split_chr(str, ',', fields);
128) if (fields.size() != 2) {
129) std::stringstream msg;
130) msg << "expected two comma-separated unsigned integers, found \""
131) << str << "\"";
132) throw std::runtime_error(msg.str());
133) }
134) try {
135) ul1 = str2ul(fields[0]);
136) ul2 = str2ul(fields[1]);
137) } catch (std::exception &ex) {
138) std::stringstream msg;
139) msg << "invalid unsigned integer pair \"" << str << "\": " << ex.what();
140) throw std::runtime_error(msg.str());
141) }
142) }
143)
144) /**
145) * @brief convert string to double (using the decimal dot for any locale)
146) * @param[in] str string to convert
147) * @return double parsed from string
148) * @throws std::exception in case of error
149) */
150) double Config::str2d(std::string const &str)
151) {
152) char const *sz = str.c_str();
153)
154) // skip leading whitespace
155) while (isspace(*sz)) {
156) ++sz;
157) }
158)
159) // start processing after leading whitespace
160) char const *ptr = sz;
161)
162) // read optional sign
163) double sign = 1.0;
164) if (*ptr == '+') {
165) sign = 1.0;
166) ++ptr;
167) } else if (*ptr == '-') {
168) sign = -1.0;
169) ++ptr;
170) }
171)
172) // read digits before dot
173) double value = 0.0;
174) while (*ptr >= '0' && *ptr <= '9') {
175) value = value * 10.0 + (double)(*ptr - '0');
176) ++ptr;
177) }
178)
179) // read dot and digits after it
180) double weight = 0.1;
181) if (*ptr == '.') {
182) ++ptr;
183)
184) /* read digits after dot */
185) while (*ptr >= '0' && *ptr <= '9') {
186) value += weight * (double)(*ptr - '0');
187) weight *= 0.1;
188) ++ptr;
189) }
190)
191) } // if (*ptr == '.')
192)
193) // merge sign into value
194) value *= sign;
195)
196) // read exponent
197) if (*ptr == 'E' || *ptr == 'e') {
198) ++ptr;
199)
200) /* read optional sign */
201) int exp_sign = 1;
202) if (*ptr == '+') {
203) exp_sign = 1;
204) ++ptr;
205) } else if (*ptr == '-') {
206) exp_sign = -1;
207) ++ptr;
208) }
209)
210) /* read digits */
211) int exp_value = 0;
212) while (*ptr >= '0' && *ptr <= '9') {
213) exp_value = exp_value * 10 + (int)(*ptr - '0');
214) ++ptr;
215) }
216)
217) /* merge exponent into value */
218) value *= pow(10.0, exp_sign * exp_value);
219)
220) } // if (*nptr == 'E' || *nptr == 'e')
221)
222) // skip trailing whitespace
223) while (isspace(*ptr)) {
224) ++ptr;
225) }
226)
227) // leftover characters -> error */
228) if (ptr == sz || *ptr != 0) {
229) std::stringstream msg;
230) msg << "invalid floating point value \"" << str << "\"";
231) throw std::runtime_error(msg.str());
232) }
233)
234) // return value
235) return value;
236) }
237)
238) /**
239) * @brief convert string to three doubles (decimal dot for any locale)
240) * @param[in] str string containing three comma-separated doubles
241) * @param[out] d1 first double parsed from string
242) * @param[out] d2 second double parsed from string
243) * @param[out] d3 third double parsed from string
244) * @throws std::exception in case of error
245) */
246) void Config::str2d3(std::string const &str,
247) double &d1, double &d2, double &d3)
248) {
249) std::vector<std::string> fields;
250) split_chr(str, ',', fields);
251) if (fields.size() != 3) {
252) std::stringstream msg;
253) msg << "expected three comma-separated floating point values, found \""
254) << str << "\"";
255) throw std::runtime_error(msg.str());
256) }
257) try {
258) d1 = str2d(fields[0]);
259) d2 = str2d(fields[1]);
260) d3 = str2d(fields[2]);
261) } catch (std::exception &ex) {
262) std::stringstream msg;
263) msg << "invalid floating point triplet \"" << str << "\": " << ex.what();
264) throw std::runtime_error(msg.str());
265) }
266) }
267)
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
268) /**
269) * @brief read config file
270) * @param[in] configFile name of config file
271) * @throws std::exception in case of error
272) */
273) void Config::readFile(std::string const &configFile)
274) {
275) // open file
276) std::ifstream cf(configFile.c_str(), std::ios::in);
277) if (! cf.is_open()) {
278) std::stringstream msg;
279) msg << "cannot open config file \"" << configFile << "\" for reading";
280) throw std::runtime_error(msg.str());
281) }
282)
283) // read lines and process them
284) std::string line;
285) size_t no;
286) for (no = 1; std::getline(cf, line); ++no) {
287) // process line
288) try {
289) procLine(line);
290) } catch (std::exception &ex) {
291) std::stringstream msg;
292) msg << "error in line " << no << " of config file \"" << configFile
293) << "\": " << ex.what();
294) throw std::runtime_error(msg.str());
295) }
296) }
297)
298) // close file
299) cf.close();
300) }
301)
302) /**
303) * @brief process line from config file
304) * @param[in] line line to process
305) * @throws std::exception in case of error
306) */
307) void Config::procLine(std::string line)
308) {
309) // remove comment ("# ...")
310) size_t commentStart = line.find("#");
311) if (commentStart != std::string::npos) {
312) line.erase(commentStart);
313) }
314)
315) trim(line);
316)
317) // ignore empty lines
318) if (line.empty()) {
319) return;
320) }
321)
322) // split line at equal sign
323) size_t equalSign = line.find("=");
324) if (equalSign == std::string::npos) {
325) std::stringstream msg;
326) msg << "no equal sign found in \"" << line << "\"";
327) throw std::runtime_error(msg.str());
328) }
329) std::string setting = line.substr(0, equalSign);
330) std::string value = line.substr(equalSign + 1);
331) trim(setting);
332) trim(value);
333)
334) procSetting(setting, value);
335) }
336)
337) /**
338) * @brief process setting from config file
339) * @param[in] setting name of the setting
340) * @param[in] value value for setting
341) * @throws std::exception in case of error
342) */
343) void Config::procSetting(std::string const &setting, std::string const &value)
344) {
345) std::vector<std::string> setFields, valFields;
346) split(setting, setFields);
347) split(value, valFields);
348)
349) // empty setting
350) if (setFields.size() < 1) {
351) throw std::runtime_error("empty setting name");
352) }
353)
354) // distributor
355) if (setFields[0] == "distributor") {
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
356) proc_distributor(setFields, valFields);
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
357) }
358) // distributor address
359) else if (setFields[0] == "distributorAddr") {
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
360) proc_distributorAddr(setFields, valFields);
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
361) }
362) // mapping
363) else if (setFields[0] == "mapping") {
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
364) proc_mapping(setFields, valFields);
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
365) }
366) // output
367) else if (setFields[0] == "output") {
|
continue implementing confi...
Stefan Schuermans authored 7 years ago
|
368) proc_output(setFields, valFields);
|
begin of config file parsing
Stefan Schuermans authored 7 years ago
|
369) }
370) // unknown setting
371) else {
372) std::stringstream msg;
373) msg << "unknown setting \"" << setting << "\"";
374) throw std::runtime_error(msg.str());
375) }
376) }
377)
|