00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "IniFile.h"
00015 #include "Streams/InStreams.h"
00016 #include "Streams/OutStreams.h"
00017 #include <strstream>
00018
00019
00020
00021
00022 IniFile::IniFile()
00023 {
00024 }
00025
00026
00027
00028
00029
00030
00031
00032 IniFile::IniFile(const char *filename)
00033 {
00034 load(filename);
00035 }
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 void IniFile::removeKey(const char *section, const char *key)
00046 {
00047 Sections::iterator s = sections.find(section);
00048 if (s != sections.end())
00049 s->second.erase(key);
00050 }
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067 bool IniFile::load(const char *filename)
00068 {
00069 InBinaryFile ini(filename);
00070 if (!ini.exists())
00071 return false;
00072
00073
00074
00075 enum {
00076 INITIAL, IGNORE,
00077 SECTION, KEY, AFTER_KEY,
00078 BEFORE_VALUE, VALUE
00079 } state = INITIAL;
00080
00081 string s, section, key, value;
00082 bool escape = false, quote = false;
00083
00084 for (;;) {
00085
00086
00087
00088 unsigned char uc;
00089 ini.read(&uc, 1);
00090 int c = ini.getEof() ? EOF : uc;
00091
00092
00093
00094 if (escape) { s+=c; escape=false; continue; }
00095 if (c == '\\') { escape = true; continue; }
00096 if (c == '"') { quote = !quote; continue; }
00097
00098
00099
00100 if (c == '\n' || c == '\r' || c == EOF) {
00101 if (state == VALUE)
00102 value = s;
00103 if (key != "")
00104 sections[section][key] = value;
00105
00106 state = INITIAL;
00107 quote = false;
00108
00109 if (c == EOF)
00110 break;
00111 else
00112 continue;
00113 }
00114
00115
00116
00117 switch (state) {
00118 case INITIAL:
00119 s = key = value = "";
00120 if (c == '[') { state = SECTION; }
00121 else if (c == ';') { state = IGNORE; }
00122 else if (!isspace(c)) { state = KEY; }
00123 break;
00124
00125 case SECTION:
00126 if (quote) break;
00127 if (c == ']') {
00128 section = s.substr(1);
00129 state = IGNORE;
00130 }
00131 break;
00132
00133 case KEY:
00134 if (quote) break;
00135 if (isspace(c)) {
00136 key = s;
00137 s = "";
00138 state = AFTER_KEY;
00139 } else if (c == '=') {
00140 key = s;
00141 s = "";
00142 state = BEFORE_VALUE;
00143 }
00144 break;
00145
00146 case AFTER_KEY:
00147 if (c == '=') {
00148 state = BEFORE_VALUE;
00149 } else if (!isspace(c)) {
00150 state = IGNORE;
00151 }
00152 break;
00153
00154 case BEFORE_VALUE:
00155 if (!(isspace(c))) {
00156 s = "";
00157 state = VALUE;
00158 }
00159 break;
00160
00161 case VALUE:
00162 if (quote) break;
00163 if (isspace(c)) {
00164 value = s;
00165 state = IGNORE;
00166 }
00167 break;
00168
00169 case IGNORE:
00170 break;
00171 }
00172 s += c;
00173 }
00174
00175 return true;
00176 }
00177
00178
00179
00180
00181
00182
00183
00184
00185 static const string esc(const string s)
00186 {
00187 if (s.size() == 0)
00188 return "\"\"";
00189
00190 string t = "";
00191 bool mustQuote = false;
00192
00193 for (unsigned i=0; i<s.size(); i++) {
00194 if (isspace(s[i]) ||
00195 s[i] == '=' || s[i] == ';' ||
00196 s[i] == '[' || s[i] == ']' )
00197 mustQuote = true;
00198
00199 if (!isprint(s[i]) || s[i] == '\\' || s[i] == '"')
00200 t += '\\';
00201
00202 t += s[i];
00203 }
00204
00205 return mustQuote ? "\"" + t + "\"" : t;
00206 }
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218 bool IniFile::save(const char *filename) const
00219 {
00220 OutTextRawFile ini(filename, false);
00221 if (!ini.exists())
00222 return false;
00223
00224 Sections::const_iterator s;
00225 for (s = sections.begin(); s != sections.end(); s++) {
00226 ini << "[" << esc(s->first).c_str() << "]\n";
00227
00228 Keys::const_iterator k;
00229 for (k = s->second.begin(); k != s->second.end(); k++)
00230 ini << esc(k->first).c_str() << " = "
00231 << esc(k->second).c_str() << "\n";
00232
00233 ini << "\n";
00234 }
00235 return true;
00236 }
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247 void IniFile::setString(const char *section, const char *key, const char *val)
00248 {
00249 sections[section][key] = val;
00250 }
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263 const char *IniFile::getString(const char *section, const char *key, const char *def) const
00264 {
00265 Sections::const_iterator s = sections.find(section);
00266 if (s != sections.end()) {
00267 Keys::const_iterator k = s->second.find(key);
00268 if (k != s->second.end())
00269 return k->second.c_str();
00270 }
00271 return def;
00272 }
00273
00274
00275 int IniFile::getInt(const char *section, const char *key, int def) const
00276 {
00277 string s = getString(section, key, "");
00278 return s == "" ? def : strtol(s.c_str(), NULL, 0);
00279 }
00280
00281
00282 double IniFile::getDouble(const char *section, const char *key, double def) const
00283 {
00284 string s = getString(section, key, "");
00285 return s == "" ? def : strtod(s.c_str(), NULL);
00286 }
00287
00288
00289 bool IniFile::getBool(const char *section, const char *key, bool def) const
00290 {
00291 const char *s = getString(section, key, "");
00292 if (*s == '\0')
00293 return def;
00294
00295 if (!stricmp(s, "true") || !stricmp(s, "yes") ||
00296 !stricmp(s, "on") || strtol(s, NULL, 0) != 0)
00297 return true;
00298 else
00299 return false;
00300 }
00301
00302
00303 void IniFile::setInt(const char *section, const char *key, int val)
00304 {
00305 strstream s; s << val;
00306 setString(section, key, s.str());
00307 }
00308
00309
00310 void IniFile::setDouble(const char *section, const char *key, double val)
00311 {
00312 strstream s; s << val;
00313 setString(section, key, s.str());
00314 }
00315
00316
00317 void IniFile::setBool(const char *section, const char *key, bool val)
00318 {
00319 setString(section, key, val ? "true" : "false");
00320 }
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333