1 /* MicroConf - minimal configuration framework
2 * Copyright (C) 2010 Stefan Westerfeld
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19 #include <stdlib.h>
20 #include <assert.h>
21 #include "microconf.h"
22
23 using std::string;
24 using std::vector;
25
MicroConf(const string & filename)26 MicroConf::MicroConf (const string& filename)
27 {
28 cfg_file = fopen (filename.c_str(), "r");
29
30 current_file = filename;
31 current_no = 0;
32 }
33
~MicroConf()34 MicroConf::~MicroConf()
35 {
36 if (cfg_file)
37 {
38 fclose (cfg_file);
39 cfg_file = NULL;
40 }
41 }
42
43 bool
open_ok()44 MicroConf::open_ok()
45 {
46 return cfg_file != NULL;
47 }
48
49 static bool
is_newline(char ch)50 is_newline (char ch)
51 {
52 return (ch == '\n' || ch == '\r');
53 }
54
55 bool
next()56 MicroConf::next()
57 {
58 assert (cfg_file != NULL);
59
60 char s[1024];
61
62 if (!fgets (s, 1024, cfg_file))
63 return false; // eof
64
65 current_line = s;
66 current_no++;
67
68 // strip newline at end of input
69 while (!current_line.empty() && is_newline (current_line[current_line.size() - 1]))
70 current_line.resize (current_line.size() - 1);
71
72 tokenizer_error = !tokenize();
73
74 return true;
75 }
76
77 string
line()78 MicroConf::line()
79 {
80 return current_line;
81 }
82
83 bool
string_chars(char ch)84 string_chars (char ch)
85 {
86 if ((ch >= 'A' && ch <= 'Z')
87 || (ch >= '0' && ch <= '9')
88 || (ch >= 'a' && ch <= 'z')
89 || (ch == '.')
90 || (ch == ':')
91 || (ch == '=')
92 || (ch == '/')
93 || (ch == '-')
94 || (ch == '_'))
95 return true;
96
97 return false;
98 }
99
100 bool
white_space(char ch)101 white_space (char ch)
102 {
103 return (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r');
104 }
105
106 bool
tokenize()107 MicroConf::tokenize()
108 {
109 enum { BLANK, STRING, QUOTED_STRING, QUOTED_STRING_ESCAPED, COMMENT } state = BLANK;
110 string s;
111
112 string xline = current_line + '\n';
113 tokens.clear();
114 for (string::const_iterator i = xline.begin(); i != xline.end(); i++)
115 {
116 if (state == BLANK && string_chars (*i))
117 {
118 state = STRING;
119 s += *i;
120 }
121 else if (state == BLANK && *i == '"')
122 {
123 state = QUOTED_STRING;
124 }
125 else if (state == BLANK && white_space (*i))
126 {
127 // ignore more whitespaces if we've already seen one
128 }
129 else if (state == STRING && string_chars (*i))
130 {
131 s += *i;
132 }
133 else if ((state == STRING && white_space (*i))
134 || (state == QUOTED_STRING && *i == '"'))
135 {
136 tokens.push_back (s);
137 s = "";
138 state = BLANK;
139 }
140 else if (state == QUOTED_STRING && *i == '\\')
141 {
142 state = QUOTED_STRING_ESCAPED;
143 }
144 else if (state == QUOTED_STRING)
145 {
146 s += *i;
147 }
148 else if (state == QUOTED_STRING_ESCAPED)
149 {
150 s += *i;
151 state = QUOTED_STRING;
152 }
153 else if (*i == '#')
154 {
155 state = COMMENT;
156 }
157 else if (state == COMMENT)
158 {
159 // ignore comments
160 }
161 else
162 {
163 return false;
164 }
165 }
166 return state == BLANK;
167 }
168
169 bool
convert(const std::string & token,int & arg)170 MicroConf::convert (const std::string& token, int& arg)
171 {
172 arg = atoi (token.c_str());
173 return true;
174 }
175
176 bool
convert(const std::string & token,double & arg)177 MicroConf::convert (const std::string& token, double& arg)
178 {
179 arg = atof (token.c_str());
180 return true;
181 }
182
183 bool
convert(const std::string & token,std::string & arg)184 MicroConf::convert (const std::string& token, std::string& arg)
185 {
186 arg = token;
187 return true;
188 }
189
190 void
die_if_unknown()191 MicroConf::die_if_unknown()
192 {
193 if (tokens.size())
194 {
195 fprintf (stderr, "configuration file %s: bad line number %d: %s\n",
196 current_file.c_str(), current_no, current_line.c_str());
197 exit (1);
198 }
199 }
200