source: source/ariba/communication/modules/transport/protlib/configuration.cpp@ 5638

Last change on this file since 5638 was 5638, checked in by Christoph Mayer, 15 years ago

adress detection aufgeräumt, network info für bleutooth, data stream (hopeful crash fix), logging auf maemo nur warn, ...

File size: 13.2 KB
Line 
1/// ----------------------------------------*- mode: C++; -*--
2/// @file configuration.cpp
3/// A configuration file parser
4/// ----------------------------------------------------------
5/// $Id: configuration.cpp 2549 2007-04-02 22:17:37Z bless $
6/// $HeadURL: https://svn.ipv6.tm.uka.de/nsis/protlib/trunk/src/configuration.cpp $
7// ===========================================================
8//
9// Copyright (C) 2005-2007, all rights reserved by
10// - Institute of Telematics, Universitaet Karlsruhe (TH)
11//
12// More information and contact:
13// https://projekte.tm.uka.de/trac/NSIS
14//
15// This program is free software; you can redistribute it and/or modify
16// it under the terms of the GNU General Public License as published by
17// the Free Software Foundation; version 2 of the License
18//
19// This program is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22// GNU General Public License for more details.
23//
24// You should have received a copy of the GNU General Public License along
25// with this program; if not, write to the Free Software Foundation, Inc.,
26// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27//
28// ===========================================================
29#include <iostream>
30#include <sstream>
31
32#include "configuration.h"
33
34
35using namespace natfw;
36
37
38// only used internally
39class parse_error : public std::exception {
40 public:
41 parse_error(const std::string &msg) throw () : msg(msg) { }
42 virtual ~parse_error() throw () { }
43
44 std::string get_msg() const throw () { return msg; }
45
46 private:
47 std::string msg;
48};
49
50
51/**
52 * Constructor.
53 */
54configuration::configuration(config_entry rules[]) {
55 for (unsigned i=0; rules[i].type != config_entry::T_END; i++)
56 values[ rules[i].key ] = rules[i];
57}
58
59
60/**
61 * Load a configuration file.
62 *
63 * If there's a parse error or the file can't be opened, a config_error
64 * exception is thrown.
65 *
66 * @param filename the file to load
67 */
68void configuration::load(const std::string &filename) throw (config_error) {
69
70 std::ifstream in(filename.c_str());
71
72 if ( ! in )
73 throw config_error("cannot open file `" + filename + "'");
74
75 try {
76 this->load(in);
77 }
78 catch ( config_error &e ) {
79 in.close();
80 throw;
81 }
82 catch ( ... ) {
83 in.close();
84 throw config_error("unknown exception thrown");
85 }
86
87 in.close();
88}
89
90
91/**
92 * Load configuration data from a stream.
93 *
94 * If there is a parse error, a config_error exception is thrown. This method
95 * will read until end of file. It is up to the caller to close the stream.
96 *
97 * @param in_stream the input stream to read data from
98 */
99void configuration::load(std::istream &in_stream) throw (config_error) {
100 using namespace std;
101
102 for (int line = 1; in_stream; line++) {
103 string buf;
104 string key;
105
106 getline(in_stream, buf);
107
108 stringstream in(buf);
109
110 // skip leading whitespace
111 strip_leading_space(in);
112
113 // skip empty lines and comments
114 if ( in.peek() == -1 || in.peek() == '#' )
115 continue;
116
117 // read the key
118 in >> key;
119 if ( key == "")
120 throw config_error("parse error", line);
121
122 if ( values.find(key) == values.end() )
123 throw config_error("invalid key `" + key + "'", line);
124
125 // skip space between key and '='
126 strip_leading_space(in);
127
128 char c = in.get();
129 if ( c != '=' )
130 throw config_error("parse error", line);
131
132 // skip space between '=' and value
133 strip_leading_space(in);
134
135 // no value for this key, we ignore it altogether
136 if ( in.peek() == -1 )
137 continue;
138
139 try {
140 parse_and_assign(key, in);
141 }
142 catch ( parse_error &e ) {
143 throw config_error(e.get_msg(), line);
144 }
145 }
146
147 if ( ! in_stream.eof() )
148 throw config_error("stream error");
149
150 // check if all required config settings are set.
151 for ( c_iter i = values.begin(); i != values.end(); i++ ) {
152 const config_entry &e = i->second;
153
154 if ( e.required && ! e.defined )
155 throw config_error(
156 "key `" + e.key + "' required but not set");
157 }
158}
159
160
161/**
162 * Write the configuration data to a stream.
163 *
164 * If there is a write error, a config_error exception is thrown. This method
165 * doesn't close the stream after writing. It is up to the caller to do that.
166 *
167 * @param out the output stream to read data from
168 */
169void configuration::dump(std::ostream &out) throw (config_error) {
170 using namespace std;
171
172 out << "# Configuration dump" << endl;
173
174 for ( c_iter i = values.begin(); i != values.end(); i++ ) {
175 const config_entry &e = i->second;
176
177 out << e.key << " = ";
178
179 if ( ! e.defined ) {
180 out << endl;
181 continue;
182 }
183
184 switch ( e.type ) {
185 case config_entry::T_BOOL:
186 out << e.bool_value << endl;
187 break;
188 case config_entry::T_INT:
189 out << e.int_value << endl;
190 break;
191 case config_entry::T_FLOAT:
192 out << e.float_value << endl;
193 break;
194 case config_entry::T_STR:
195 write_string(out, e.str_value);
196 out << endl;
197 break;
198 case config_entry::T_IPv4:
199 out << e.ipv4_value << endl;
200 break;
201 case config_entry::T_IPv6:
202 out << e.ipv6_value << endl;
203 break;
204 case config_entry::T_IPv4_LIST: // fall-through
205 case config_entry::T_IPv6_LIST:
206 dump_address_list(out, e.address_list);
207 out << endl;
208 break;
209 default:
210 assert( false );
211 }
212 }
213}
214
215
216void configuration::dump_address_list(std::ostream &out,
217 const std::list<hostaddress> &addresses) const {
218
219 typedef std::list<hostaddress>::const_iterator addr_iter;
220
221 for ( addr_iter i = addresses.begin(); i != addresses.end(); i++ )
222 out << *i << " ";
223}
224
225
226/**
227 * Test if the configuration contains the given value.
228 *
229 * @param key the name of the key
230 * @return true, if the configuraiton has that key
231 */
232bool configuration::is_defined(const std::string &key) const throw () {
233 c_iter i = values.find(key);
234
235 if ( values.find(key) == values.end() )
236 return false;
237 else
238 return i->second.defined;
239}
240
241
242/**
243 * Get a string configuration value.
244 *
245 * @param key the name of the key
246 * @return the value from the configuration
247 */
248std::string configuration::get_string(const std::string &key) const throw () {
249 c_iter i = values.find(key);
250 assert( i != values.end() );
251 assert( i->second.type == config_entry::T_STR );
252
253 return i->second.str_value;
254}
255
256
257/**
258 * Get a boolean configuration value.
259 *
260 * @param key the name of the key
261 * @return the value from the configuration
262 */
263bool configuration::get_bool(const std::string &key) const throw () {
264 c_iter i = values.find(key);
265 assert( i != values.end() );
266 assert( i->second.type == config_entry::T_BOOL );
267
268 return i->second.bool_value;
269}
270
271
272/**
273 * Get an integer configuration value.
274 *
275 * @param key the name of the key
276 * @return the value from the configuration
277 */
278int configuration::get_int(const std::string &key) const throw () {
279 c_iter i = values.find(key);
280 assert( i != values.end() );
281 assert( i->second.type == config_entry::T_INT );
282
283 return i->second.int_value;
284}
285
286
287/**
288 * Get a floating point configuration value.
289 *
290 * @param key the name of the key
291 * @return the value from the configuration
292 */
293float configuration::get_float(const std::string &key) const throw () {
294 c_iter i = values.find(key);
295 assert( i != values.end() );
296 assert( i->second.type == config_entry::T_FLOAT );
297
298 return i->second.float_value;
299}
300
301
302/**
303 * Get an IPv4 hostaddress configuration value.
304 *
305 * @param key the name of the key
306 * @return the value from the configuration
307 */
308hostaddress configuration::get_ipv4_address(
309 const std::string &key) const throw () {
310
311 c_iter i = values.find(key);
312 assert( i != values.end() );
313 assert( i->second.type == config_entry::T_IPv4 );
314
315 return i->second.ipv4_value;
316}
317
318
319/**
320 * Get an IPv6 hostaddress configuration value.
321 *
322 * @param key the name of the key
323 * @return the value from the configuration
324 */
325hostaddress configuration::get_ipv6_address(
326 const std::string &key) const throw () {
327
328 c_iter i = values.find(key);
329 assert( i != values.end() );
330 assert( i->second.type == config_entry::T_IPv6 );
331
332 return i->second.ipv6_value;
333}
334
335
336/**
337 * Get a list of IPv4 hostaddress objects.
338 *
339 * @param key the name of the key
340 * @return the list of values from the configuration
341 */
342std::list<hostaddress> configuration::get_ipv4_address_list(
343 const std::string &key) const throw () {
344
345 c_iter i = values.find(key);
346 assert( i != values.end() );
347 assert( i->second.type == config_entry::T_IPv4_LIST );
348
349 return i->second.address_list;
350}
351
352
353/**
354 * Get a list of IPv6 hostaddress objects.
355 *
356 * @param key the name of the key
357 * @return the list of values from the configuration
358 */
359std::list<hostaddress> configuration::get_ipv6_address_list(
360 const std::string &key) const throw () {
361
362 c_iter i = values.find(key);
363 assert( i != values.end() );
364 assert( i->second.type == config_entry::T_IPv6_LIST );
365
366 return i->second.address_list;
367}
368
369
370void configuration::strip_leading_space(std::istream &in) const {
371 while ( in && ( in.peek() == ' ' || in.peek() == '\t' ) )
372 in.get();
373}
374
375
376// Parse the given buffer and assign the value to the config entry
377void configuration::parse_and_assign(const std::string &key, std::istream &in) {
378
379 switch ( values[key].type ) {
380 case config_entry::T_BOOL:
381 values[key].bool_value = parse_bool(in);
382 break;
383 case config_entry::T_INT:
384 values[key].int_value = parse_int(in);
385 break;
386 case config_entry::T_FLOAT:
387 values[key].float_value = parse_float(in);
388 break;
389 case config_entry::T_STR:
390 values[key].str_value = parse_string(in);
391 break;
392 case config_entry::T_IPv4:
393 values[key].ipv4_value = parse_ipv4_address(in);
394 break;
395 case config_entry::T_IPv6:
396 values[key].ipv6_value = parse_ipv6_address(in);
397 break;
398 case config_entry::T_IPv4_LIST:
399 values[key].address_list = parse_ipv4_address_list(in);
400 break;
401 case config_entry::T_IPv6_LIST:
402 values[key].address_list = parse_ipv6_address_list(in);
403 break;
404 default:
405 assert( false );
406 throw parse_error("invalid value"); // not reached
407 }
408
409 // no exception thrown until now, so parsing was successful
410 values[key].defined = true;
411}
412
413
414// Write the string to the stream, adding quotation marks and escape sequences
415void configuration::write_string(
416 std::ostream &out, const std::string &str) const {
417
418 std::stringstream stream(str);
419
420 out << '"';
421
422 char c;
423 while ( stream.get(c) ) {
424 switch ( c ) {
425 case '\\': // fallthrough
426 case '"': out << '\\' << c; break;
427 default: out << c;
428 }
429 }
430
431 out << '"';
432}
433
434
435// Matches pattern "[^"]*"\s*
436std::string configuration::parse_string(std::istream &in) const {
437
438 if ( in.get() != '"' )
439 throw parse_error("string doesn't start with a quotation mark");
440
441 bool escape = false;
442 std::string tmp;
443 char c;
444
445 while ( in.get(c) ) {
446 if ( escape ) {
447 if ( c == '\\' || c == '"' )
448 tmp += c;
449 else
450 throw parse_error("invalid escape sequence");
451
452 escape = false;
453 }
454 else {
455 if ( c == '"' )
456 break;
457 else if ( c == '\\' )
458 escape = true;
459 else
460 tmp += c;
461 }
462 }
463
464 // we should be on the closing quotation mark
465 if ( c != '"' )
466 throw parse_error("unterminated string");
467
468 skip_rest_of_line(in);
469
470 return tmp;
471}
472
473
474hostaddress configuration::parse_ipv4_address(std::istream &in) const {
475 std::string word;
476 in >> word;
477
478 bool success;
479 hostaddress addr(word.c_str(), &success);
480
481 if ( success || ! addr.is_ipv4() )
482 return addr;
483 else
484 throw parse_error("invalid IPv4 address");
485
486 skip_rest_of_line(in);
487
488 return addr;
489}
490
491
492hostaddress configuration::parse_ipv6_address(std::istream &in) const {
493 std::string word;
494 in >> word;
495
496 bool success;
497 hostaddress addr(word.c_str(), &success);
498
499 if ( success || ! addr.is_ipv6() )
500 return addr;
501 else
502 throw parse_error("invalid IPv6 address");
503
504 skip_rest_of_line(in);
505
506 return addr;
507}
508
509std::list<hostaddress> configuration::parse_ipv4_address_list(
510 std::istream &in) const {
511
512 std::list<hostaddress> result;
513
514 std::string tmp;
515 while ( in >> tmp ) {
516 bool success;
517 hostaddress addr(tmp.c_str(), &success);
518
519 if ( success && addr.is_ipv4() )
520 result.push_back(addr);
521 else
522 throw parse_error("invalid IPv4 address `" + tmp + "'");
523 }
524
525 return result;
526}
527
528std::list<hostaddress> configuration::parse_ipv6_address_list(
529 std::istream &in) const {
530
531 std::list<hostaddress> result;
532
533 std::string tmp;
534 while ( in >> tmp ) {
535 bool success;
536 hostaddress addr(tmp.c_str(), &success);
537
538 if ( success && addr.is_ipv6() )
539 result.push_back(addr);
540 else
541 throw parse_error("invalid IPv6 address `" + tmp + "'");
542 }
543
544 return result;
545}
546
547int configuration::parse_int(std::istream &in) const {
548 int tmp = -1;
549
550 in >> tmp;
551
552 if ( ! in.good() && ! in.eof() )
553 throw parse_error("parsing integer failed");
554
555 skip_rest_of_line(in);
556
557 return tmp;
558}
559
560
561float configuration::parse_float(std::istream &in) const {
562 float tmp = 0.0;
563
564 in >> tmp;
565
566 if ( ! in.good() && ! in.eof() )
567 throw parse_error("parsing float failed");
568
569 skip_rest_of_line(in);
570
571 return tmp;
572}
573
574
575bool configuration::parse_bool(std::istream &in) const {
576 std::string tmp;
577 bool value;
578
579 in >> tmp;
580
581 if ( tmp == "true" )
582 value = true;
583 else if ( tmp == "false" )
584 value = false;
585 else
586 throw parse_error("parsing boolean failed");
587
588 skip_rest_of_line(in);
589
590 return value;
591}
592
593
594// throw an exception if the rest of the line doesn't only contain whitespace
595void configuration::skip_rest_of_line(std::istream &in) const {
596 char c;
597 while ( in.get(c) ) {
598 if ( c != ' ' && c != '\t' )
599 throw parse_error("junk after value");
600 }
601}
602
603
604// EOF
Note: See TracBrowser for help on using the repository browser.