An Overlay-based
Virtual Network Substrate
SpoVNet

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

Last change on this file since 5641 was 5641, checked in by Christoph Mayer, 14 years ago
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.