653cc0922bab15adb7c7f38912fb6e1a3f994474
Stefan Schuermans 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>
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

22) #include <cmath>
23) #include <cstdlib>
Stefan Schuermans begin of config file parsing

Stefan Schuermans authored 7 years ago

24) #include <functional>
25) #include <fstream>
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

26) #include <map>
Stefan Schuermans 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"
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

32) #include "distri.h"
33) #include "mapping.h"
Stefan Schuermans 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) 
Stefan Schuermans 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) 
Stefan Schuermans 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") {
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

356)     proc_distributor(setFields, valFields);
Stefan Schuermans begin of config file parsing

Stefan Schuermans authored 7 years ago

357)   }
358)   // distributor address
359)   else if (setFields[0] == "distributorAddr") {
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

360)     proc_distributorAddr(setFields, valFields);
Stefan Schuermans begin of config file parsing

Stefan Schuermans authored 7 years ago

361)   }
362)   // mapping
363)   else if (setFields[0] == "mapping") {
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

364)     proc_mapping(setFields, valFields);
Stefan Schuermans begin of config file parsing

Stefan Schuermans authored 7 years ago

365)   }
366)   // output
367)   else if (setFields[0] == "output") {
Stefan Schuermans continue implementing confi...

Stefan Schuermans authored 7 years ago

368)     proc_output(setFields, valFields);
Stefan Schuermans 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)