source: tags/arb_5.0/TEMPLATES/config_parser.h

Last change on this file was 5675, checked in by westram, 15 years ago
  • removed automatic timestamps (the best they were good for, were vc-conflicts)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.3 KB
Line 
1//  ==================================================================== //
2//                                                                       //
3//    File      : config_parser.h                                        //
4//    Purpose   : reads config files                                     //
5//                                                                       //
6//                                                                       //
7//  Coded by Ralf Westram (coder@reallysoft.de) in October 2003          //
8//  Copyright Department of Microbiology (Technical University Munich)   //
9//                                                                       //
10//  Visit our web site at: http://www.arb-home.de/                       //
11//                                                                       //
12//                                                                       //
13//  ==================================================================== //
14
15#ifndef CONFIG_PARSER_H
16#define CONFIG_PARSER_H
17
18// format of config files:
19//
20// # comment
21// key = value
22// # comment
23// key = value
24//
25// # comment
26// key = value
27//
28
29
30#ifndef _CPP_MAP
31#include <map>
32#endif
33#ifndef _CPP_CSTDIO
34#include <cstdio>
35#endif
36#ifndef _CPP_STRING
37#include <string>
38#endif
39
40#define MAXCONFIGLINESIZE 200
41
42namespace {
43
44    class ConfigParser {
45        typedef std::map<std::string, std::string> ConfigMap;
46
47        ConfigMap entries;
48        std::string    filename;
49        GB_ERROR  error;
50
51        ConfigParser(const ConfigParser& other); // copying not allowed
52        ConfigParser& operator = (const ConfigParser& other); // assignment not allowed
53
54        static char *unwhite(char *s) {
55            while (s[0] == ' ') ++s;
56            char *e = strchr(s, 0)-1;
57
58            while (e>s && isspace(e[0])) --e;
59            if (e<s) e = s;
60
61            e[isspace(e[0]) ? 0 : 1] = 0;
62
63            return s;
64        }
65
66    public:
67
68        ConfigParser(const std::string& filename_)
69            : filename(filename_)
70            , error(0)
71        {
72            FILE *in = fopen(filename.c_str(), "rt");
73            if (!in) {
74                error = GBS_global_string("Can't open config '%s'", filename.c_str());
75            }
76            else {
77                char buffer[MAXCONFIGLINESIZE+1];
78                int  lineno = 0;
79                while (!error && fgets(buffer, MAXCONFIGLINESIZE, in) != 0) {
80                    ++lineno;
81                    char *content = unwhite(buffer);
82                    if (content[0] && content[0] != '#') { // skip empty and comment lines
83                        char *key = 0, *value = 0;
84                        error     = splitText(content, '=', key, value);
85                        if (!error && value[0] == 0) {
86                            error = "content missing behind '='";
87                        }
88
89                        if (!error) {
90                            ConfigMap::const_iterator found = entries.find(key);
91                            if (found == entries.end()) {
92                                // fprintf(stderr, "adding value '%s' at key '%s'\n", value, key);
93                                entries[key] = value;
94                            }
95                            else {
96                                error = GBS_global_string("entry '%s' redefined", key);
97                            }
98                        }
99
100                        if (error) error = makeError(lineno, error);
101                    }
102                }
103                fclose(in);
104            }
105        }
106
107        GB_ERROR getError() { return error; }
108
109        GB_ERROR makeError(const std::string& forEntry, const char *msg) {
110            return GBS_global_string("%s (at entry '%s' in %s)", msg, forEntry.c_str(), filename.c_str());
111        }
112        GB_ERROR makeError(int lineno, const char *msg) {
113            return GBS_global_string("%s (at line #%i in %s)", msg, lineno, filename.c_str());
114        }
115        GB_ERROR makeError(const char *msg) {
116            return GBS_global_string("%s (in %s)", msg, filename.c_str());
117        }
118
119        static GB_ERROR splitText(char *text, char separator, char*& lhs, char*& rhs) {
120            text      = unwhite(text);
121            char *sep = strchr(text, separator);
122            if (!sep) return GBS_global_string("'%c' expected in '%s'", separator, text);
123
124            sep[0] = 0;
125            lhs    = unwhite(text);
126            rhs    = unwhite(sep+1);
127
128            return 0;
129        }
130
131        const std::string *getValue(const std::string& key, GB_ERROR& err) {
132            ConfigMap::const_iterator found = entries.find(key);
133            if (found == entries.end()) {
134                err = makeError(GBS_global_string("Entry '%s' expected", key.c_str()));
135                return 0;
136            }
137
138            return &(found->second);
139        }
140    };
141
142    // --------------------------------------------------------------------------------
143
144    class ConfigBase {
145        ConfigBase(const ConfigBase& other); // copying not allowed
146        ConfigBase& operator = (const ConfigBase& other); // assignment not allowed
147
148    protected:
149
150        ConfigParser parser;
151        GB_ERROR     error;
152
153        GB_ERROR parse_double(const char *s, double& d) {
154            if (s[0] == 0) return "floating point number expected";
155
156            char *end = 0;
157            d         = strtod(s, &end);
158            if (end[0] != 0) {
159                return GBS_global_string("Unexpected '%s' behind floating point number", end);
160            }
161            return 0;
162        }
163
164        GB_ERROR check_int_range(int value, int min_value, int max_value) {
165            if (value<min_value || value>max_value) {
166                return GBS_global_string("%i outside allowed range [%i .. %i]", value, min_value, max_value);
167            }
168            return 0;
169        }
170        GB_ERROR check_double_range(double value, double min_value, double max_value) {
171            if (value<min_value || value>max_value) {
172                return GBS_global_string("%f outside allowed range [%f .. %f]", value, min_value, max_value);
173            }
174            return 0;
175        }
176        GB_ERROR check_bool_range(int value) {
177            if (value<0 || value>1) {
178                return GBS_global_string("%i is not boolean (has to be 0 or 1).", value);
179            }
180            return 0;
181        }
182
183        void parseInt(const std::string& key, int& value) {
184            const std::string *val = parser.getValue(key, error);
185            if (val) value = atoi(val->c_str());
186        }
187
188        void parseInt_checked(const std::string& key, int& value, int min_value, int max_value) {
189            parseInt(key, value);
190            if (!error) {
191                error            = check_int_range(value, min_value, max_value);
192                if (error) error = parser.makeError(key, error);
193            }
194        }
195
196        void parseIntRange(const std::string& key, int& low, int& high) {
197            const std::string *val = parser.getValue(key, error);
198            if (val) {
199                char *range = strdup(val->c_str());
200                char *lhs, *rhs;
201
202                error             = ConfigParser::splitText(range, ',', lhs, rhs);
203                if (!error)  {
204                    low  = atoi(lhs);
205                    high = atoi(rhs);
206                    if (low>high) {
207                        error = GBS_global_string("Invalid range (%i has to be smaller than %i)", low, high);
208                    }
209                }
210
211                free(range);
212
213                if (error) error = parser.makeError(key, error);
214            }
215        }
216
217        void parseIntRange_checked(const std::string& key, int& low, int& high, int min_value, int max_value) {
218            parseIntRange(key, low, high);
219            if (!error) {
220                error             = check_int_range(low, min_value, max_value);
221                if (!error) error = check_int_range(high, min_value, max_value);
222                if (error) error  = parser.makeError(key, error);
223            }
224        }
225
226        void parseBool(const std::string& key, bool& boolean) {
227            int b = 0;
228            parseInt(key, b);
229            if (!error) {
230                error            = check_bool_range(b);
231                if (error) error = parser.makeError(key, error);
232                else boolean     = static_cast<bool>(b);
233            }
234        }
235
236        void parseDouble(const std::string& key, double& value) {
237            const std::string *val = parser.getValue(key, error);
238            if (val) {
239                error = parse_double(val->c_str(), value);
240            }
241        }
242
243        void parseDouble_checked(const std::string& key, double& value, double min_value, double max_value) {
244            parseDouble(key, value);
245            if (!error) {
246                error            = check_double_range(value, min_value, max_value);
247                if (error) error = parser.makeError(key, error);
248            }
249        }
250
251        void parseDoubleRange(const std::string& key, double& low, double& high) {
252            const std::string *val = parser.getValue(key, error);
253            if (val) {
254                char *range = strdup(val->c_str());
255                char *lhs, *rhs;
256
257                error             = ConfigParser::splitText(range, ',', lhs, rhs);
258                if (!error) error = parse_double(lhs, low);
259                if (!error) error = parse_double(rhs, high);
260                if (!error && low>high) {
261                    error = GBS_global_string("Invalid range (%f has to be smaller than %f)", low, high);
262                }
263
264                free(range);
265
266                if (error) error = parser.makeError(key, error);
267            }
268        }
269
270        void parseDoubleRange_checked(const std::string& key, double& low, double& high, double min_value, double max_value) {
271            parseDoubleRange(key, low, high);
272            if (!error) {
273                error             = check_double_range(low, min_value, max_value);
274                if (!error) error = check_double_range(high, min_value, max_value);
275                if (error) error  = parser.makeError(key, error);
276            }
277        }
278
279    public:
280        ConfigBase(const std::string &filename)
281            : parser(filename)
282            , error(0)
283        {}
284        virtual ~ConfigBase() {}
285
286        GB_ERROR getError() const { return error; }
287    };
288}
289
290#undef MAXCONFIGLINESIZE
291
292
293#else
294#error config_parser.h included twice
295#endif // CONFIG_PARSER_H
296
Note: See TracBrowser for help on using the repository browser.