Updated MicroConf.
[jacksampler.git] / src / microconf.cc
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.hh"
22
23 using std::string;
24 using std::vector;
25
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
34 bool
35 MicroConf::open_ok()
36 {
37   return cfg_file != NULL;
38 }
39
40 static bool
41 is_newline (char ch)
42 {
43   return (ch == '\n' || ch == '\r');
44 }
45
46 bool
47 MicroConf::next()
48 {
49   assert (cfg_file != NULL);
50
51   char s[1024];
52
53   if (!fgets (s, 1024, cfg_file))
54     return false; // eof
55
56   current_line = s;
57   current_no++;
58
59   // strip newline at end of input
60   while (!current_line.empty() && is_newline (current_line[current_line.size() - 1]))
61     current_line.resize (current_line.size() - 1);
62
63   tokenizer_error = !tokenize();
64
65   return true;
66 }
67
68 string
69 MicroConf::line()
70 {
71   return current_line;
72 }
73
74 bool
75 string_chars (char ch)
76 {
77   if ((ch >= 'A' && ch <= 'Z')
78   ||  (ch >= '0' && ch <= '9')
79   ||  (ch >= 'a' && ch <= 'z')
80   ||  (ch == '.')
81   ||  (ch == ':')
82   ||  (ch == '=')
83   ||  (ch == '/')
84   ||  (ch == '-')
85   ||  (ch == '_'))
86     return true;
87
88   return false;
89 }
90
91 bool
92 white_space (char ch)
93 {
94   return (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r');
95 }
96
97 bool
98 MicroConf::tokenize()
99 {
100   enum { BLANK, STRING, QUOTED_STRING, QUOTED_STRING_ESCAPED, COMMENT } state = BLANK;
101   string s;
102
103   string xline = current_line + '\n';
104   tokens.clear();
105   for (string::const_iterator i = xline.begin(); i != xline.end(); i++)
106     {
107       if (state == BLANK && string_chars (*i))
108         {
109           state = STRING;
110           s += *i;
111         }
112       else if (state == BLANK && *i == '"')
113         {
114           state = QUOTED_STRING;
115         }
116       else if (state == BLANK && white_space (*i))
117         {
118           // ignore more whitespaces if we've already seen one
119         }
120       else if (state == STRING && string_chars (*i))
121         {
122           s += *i;
123         }
124       else if (state == STRING && white_space (*i)
125            ||  state == QUOTED_STRING && *i == '"')
126         {
127           tokens.push_back (s);
128           s = "";
129           state = BLANK;
130         }
131       else if (state == QUOTED_STRING && *i == '\\')
132         {
133           state = QUOTED_STRING_ESCAPED;
134         }
135       else if (state == QUOTED_STRING)
136         {
137           s += *i;
138         }
139       else if (state == QUOTED_STRING_ESCAPED)
140         {
141           s += *i;
142           state = QUOTED_STRING;
143         }
144       else if (*i == '#')
145         {
146           state = COMMENT;
147         }
148       else if (state == COMMENT)
149         {
150           // ignore comments
151         }
152       else
153         {
154           return false;
155         }
156     }
157   return state == BLANK;
158 }
159
160 bool
161 MicroConf::convert (const std::string& token, int& arg)
162 {
163   arg = atoi (token.c_str());
164   return true;
165 }
166
167 bool
168 MicroConf::convert (const std::string& token, double& arg)
169 {
170   arg = atof (token.c_str());
171   return true;
172 }
173
174 bool
175 MicroConf::convert (const std::string& token, std::string& arg)
176 {
177   arg = token;
178   return true;
179 }
180
181 void
182 MicroConf::die_if_unknown()
183 {
184   if (tokens.size())
185     {
186       fprintf (stderr, "configuration file %s: bad line number %d: %s\n",
187                current_file.c_str(), current_no, current_line.c_str());
188       exit (1);
189     }
190 }