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