source: trash/old-modules/transport/protlib/configuration.cpp@ 5887

Last change on this file since 5887 was 5641, checked in by Christoph Mayer, 15 years ago
File size: 13.2 KB
RevLine 
[5641]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.