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