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