b83debefc678713eeeba10d9d4887b575c4712fe
[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     return true;
85
86   return false;
87 }
88
89 bool
90 white_space (char ch)
91 {
92   return (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r');
93 }
94
95 bool
96 MicroConf::tokenize()
97 {
98   enum { BLANK, STRING, QUOTED_STRING, QUOTED_STRING_ESCAPED, COMMENT } state = BLANK;
99   string s;
100
101   string xline = current_line + '\n';
102   tokens.clear();
103   for (string::const_iterator i = xline.begin(); i != xline.end(); i++)
104     {
105       if (state == BLANK && string_chars (*i))
106         {
107           state = STRING;
108           s += *i;
109         }
110       else if (state == BLANK && *i == '"')
111         {
112           state = QUOTED_STRING;
113         }
114       else if (state == BLANK && white_space (*i))
115         {
116           // ignore more whitespaces if we've already seen one
117         }
118       else if (state == STRING && string_chars (*i))
119         {
120           s += *i;
121         }
122       else if (state == STRING && white_space (*i)
123            ||  state == QUOTED_STRING && *i == '"')
124         {
125           tokens.push_back (s);
126           s = "";
127           state = BLANK;
128         }
129       else if (state == QUOTED_STRING && *i == '\\')
130         {
131           state = QUOTED_STRING_ESCAPED;
132         }
133       else if (state == QUOTED_STRING)
134         {
135           s += *i;
136         }
137       else if (state == QUOTED_STRING_ESCAPED)
138         {
139           s += *i;
140           state = QUOTED_STRING;
141         }
142       else if (*i == '#')
143         {
144           state = COMMENT;
145         }
146       else if (state == COMMENT)
147         {
148           // ignore comments
149         }
150       else
151         {
152           return false;
153         }
154     }
155   return state == BLANK;
156 }
157
158 bool
159 MicroConf::convert (const std::string& token, int& arg)
160 {
161   arg = atoi (token.c_str());
162   return true;
163 }
164
165 bool
166 MicroConf::convert (const std::string& token, double& arg)
167 {
168   arg = atof (token.c_str());
169   return true;
170 }
171
172 bool
173 MicroConf::convert (const std::string& token, std::string& arg)
174 {
175   arg = token;
176   return true;
177 }
178
179 void
180 MicroConf::die_if_unknown()
181 {
182   if (tokens.size())
183     {
184       fprintf (stderr, "configuration file %s: bad line number %d: %s\n",
185                current_file.c_str(), current_no, current_line.c_str());
186       exit (1);
187     }
188 }