00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "pent_include.h"
00020 #include "INIFile.h"
00021
00022 #include "FileSystem.h"
00023 #include "IDataSource.h"
00024
00025 using Pentagram::istring;
00026 using std::string;
00027
00028 INIFile::INIFile()
00029 : is_file(false), readonly(false)
00030 {
00031
00032 }
00033
00034 INIFile::INIFile(string fname, istring root_)
00035 : root(root_), is_file(false), readonly(false)
00036 {
00037 readConfigFile(fname);
00038 }
00039
00040 INIFile::~INIFile()
00041 {
00042
00043 }
00044
00045 bool INIFile::Section::hasKey(istring key)
00046 {
00047 return (getKey(key) != 0);
00048 }
00049
00050 INIFile::KeyValue* INIFile::Section::getKey(istring key)
00051 {
00052 std::list<KeyValue>::iterator i;
00053 for (i = keys.begin(); i != keys.end(); ++i) {
00054 if (i->key == key) {
00055 return &(*i);
00056 }
00057 }
00058 return 0;
00059 }
00060
00061 void INIFile::Section::setKey(istring key, string value)
00062 {
00063 KeyValue* kv = getKey(key);
00064 if (kv) {
00065 kv->value = value;
00066 return;
00067 }
00068
00069 KeyValue newkey;
00070 newkey.key = key;
00071 newkey.value = value;
00072 newkey.comment = "";
00073 keys.push_back(newkey);
00074 }
00075
00076 void INIFile::Section::unsetKey(istring key)
00077 {
00078 std::list<KeyValue>::iterator i;
00079 for (i = keys.begin(); i != keys.end(); ++i) {
00080 if (i->key == key) {
00081 i = keys.erase(i);
00082 }
00083 }
00084 }
00085
00086 string INIFile::Section::dump()
00087 {
00088 string s = comment;
00089 s += "[" + name + "]\n";
00090 std::list<KeyValue>::iterator i;
00091 for (i = keys.begin(); i != keys.end(); ++i) {
00092 s += i->comment;
00093 s += i->key + "=" + i->value + "\n";
00094 }
00095
00096 return s;
00097 }
00098
00099 bool INIFile::readConfigFile(string fname)
00100 {
00101 IDataSource *f = FileSystem::get_instance()->ReadFile(fname, true);
00102 if (!f) return false;
00103
00104 string sbuf, line;
00105 while (!f->eof()) {
00106 f->readline(line);
00107 string::size_type pos = line.find_first_of("\n\r");
00108 if (pos != string::npos) {
00109 sbuf += line.substr(0, pos) + "\n";
00110 } else {
00111 sbuf += line + "\n";
00112 }
00113 }
00114
00115 delete f;
00116
00117 if (!readConfigString(sbuf))
00118 return false;
00119
00120 is_file = true;
00121 filename = fname;
00122 return true;
00123 }
00124
00125
00126 static void rtrim(string& s)
00127 {
00128 string::size_type pos = s.find_last_not_of(" \t");
00129 if (pos != string::npos) {
00130 if (pos+1 < s.size())
00131 s.erase(pos+1);
00132 } else {
00133 s.clear();
00134 }
00135 }
00136
00137 static void ltrim(string& s)
00138 {
00139 string::size_type pos = s.find_first_not_of(" \t");
00140 if (pos != string::npos) {
00141 if (pos > 0)
00142 s.erase(0, pos-1);
00143 } else {
00144 s.clear();
00145 }
00146 }
00147
00148
00149
00150
00151
00152
00153 bool INIFile::readConfigString(string config)
00154 {
00155 is_file = false;
00156
00157 string line;
00158 string comment;
00159 unsigned int lineno = 0;
00160 Section section;
00161
00162 while (!config.empty())
00163 {
00164 lineno++;
00165
00166 string::size_type pos = config.find('\n');
00167 if (pos != string::npos) {
00168 line = config.substr(0, pos);
00169 config.erase(0, pos+1);
00170 } else {
00171 line = config;
00172 config.clear();
00173 }
00174
00175 if (line[0] == '#') {
00176
00177
00178
00179 comment += line + "\n";
00180 } else if (line[0] == '[') {
00181
00182 unsigned int p = 1;
00183
00184 if (line[p] == ']') {
00185 perr << "Config file buggy: empty section name in line "
00186 << lineno << std::endl;
00187 return false;
00188 }
00189
00190
00191
00192
00193 while (p < line.size() && (isalnum(line[p]) || line[p] == '-' ||
00194 line[p] == '_' || line[p] == ':'))
00195 p++;
00196
00197 if (p >= line.size()) {
00198 perr << "Config file buggy: missing ] in line " << lineno
00199 << ": '" << line << "'" << std::endl;
00200 return false;
00201 }
00202 if (line[p] != ']') {
00203 perr << "Config file buggy: Invalid character '" << line[p]
00204 << "' occured in section name in line " << lineno
00205 << std::endl;
00206 return false;
00207 }
00208
00209 if (!section.name.empty()) {
00210
00211 sections.push_back(section);
00212 }
00213 section.name.clear();
00214 section.comment.clear();
00215 section.keys.clear();
00216
00217 section.name = line.substr(1, p-1);
00218 section.comment = comment;
00219 comment.clear();
00220
00221 } else {
00222
00223 rtrim(line);
00224 ltrim(line);
00225
00226
00227 if (line.empty())
00228 continue;
00229
00230
00231 if (section.name.empty()) {
00232 perr << "Config file buggy: Key/value pair found outside "
00233 << "a section in line " << lineno << std::endl;
00234 return false;
00235 }
00236
00237
00238 string::size_type p = line.find('=');
00239 if (p == string::npos || p == 0) {
00240 perr << "Config file buggy: Junk found in line " << lineno
00241 << ": '" << line << "'" << std::endl;
00242 return false;
00243 }
00244
00245 KeyValue v;
00246
00247 string t = line.substr(0, p);
00248 rtrim(t);
00249 v.key = t;
00250
00251 if (p+1 < line.size())
00252 t = line.substr(p+1);
00253 else
00254 t = "";
00255 ltrim(t);
00256 v.value = t;
00257
00258 v.comment = comment;
00259 comment.clear();
00260
00261 #if 0
00262 pout << "section: " << section.name << ", key: " << v.key
00263 << ", value: " << v.value << std::endl;
00264 #endif
00265
00266 section.keys.push_back(v);
00267 }
00268 }
00269
00270 if (!section.name.empty()) {
00271
00272 sections.push_back(section);
00273 }
00274
00275 return true;
00276 }
00277
00278 void INIFile::clear(istring root_)
00279 {
00280 sections.clear();
00281 root = root_;
00282 is_file = false;
00283 readonly = false;
00284 filename = "";
00285 }
00286
00287 string INIFile::dump()
00288 {
00289 string s;
00290
00291 std::list<Section>::iterator i;
00292 for (i = sections.begin(); i != sections.end(); ++i) {
00293 if (i != sections.begin())
00294 s += "\n";
00295
00296 s += i->dump();
00297 }
00298
00299 return s;
00300 }
00301
00302 void INIFile::write()
00303 {
00304 if (!is_file || readonly)
00305 return;
00306
00307 ODataSource *f = FileSystem::get_instance()->WriteFile(filename, true);
00308 if (!f) return;
00309
00310 std::string s = dump();
00311 const char *cstr = s.c_str();
00312 f->write(cstr,strlen(cstr));
00313
00314 delete f;
00315 }
00316
00317 bool INIFile::stripRoot(istring& key)
00318 {
00319 string::size_type pos = key.find('/');
00320 if (pos == istring::npos) return false;
00321
00322 istring keyroot = key.substr(0, pos);
00323 if (keyroot != root) return false;
00324
00325 key.erase(0, pos+1);
00326
00327 return true;
00328 }
00329
00330 INIFile::Section* INIFile::getSection(istring section)
00331 {
00332 std::list<Section>::iterator i;
00333 for (i = sections.begin(); i != sections.end(); ++i) {
00334 if (i->name == section) {
00335 return &(*i);
00336 }
00337 }
00338 return 0;
00339 }
00340
00341 bool INIFile::splitKey(istring key, istring& section, istring& sectionkey)
00342 {
00343
00344
00345 string::size_type pos = key.find('/');
00346 if (pos == istring::npos || pos+1 >= key.size()) return false;
00347
00348 section = key.substr(0, pos);
00349 sectionkey = key.substr(pos+1);
00350
00351 return true;
00352 }
00353
00354 bool INIFile::hasSection(istring section)
00355 {
00356 if (!stripRoot(section)) return false;
00357
00358 return (getSection(section) != 0);
00359 }
00360
00361 bool INIFile::hasKey(istring key)
00362 {
00363 if (!stripRoot(key)) return false;
00364 istring s, k;
00365 splitKey(key, s, k);
00366
00367 Section* section = getSection(s);
00368 if (!section) return false;
00369
00370 return section->hasKey(k);
00371 }
00372
00373 bool INIFile::checkRoot(istring key)
00374 {
00375 return (root == key || stripRoot(key));
00376 }
00377
00378 bool INIFile::value(istring key, string& ret)
00379 {
00380 if (!stripRoot(key)) return false;
00381 istring s, k;
00382 splitKey(key, s, k);
00383
00384 Section* section = getSection(s);
00385 if (!section) return false;
00386
00387 KeyValue* kv = section->getKey(k);
00388 if (!kv) return false;
00389
00390 ret = kv->value;
00391 return true;
00392 }
00393
00394 bool INIFile::value(istring key, int& ret)
00395 {
00396 string stringval;
00397 bool found = value(key, stringval);
00398
00399 if (!found) return false;
00400
00401 ret = std::strtol(stringval.c_str(), 0, 0);
00402 return true;
00403 }
00404
00405 bool INIFile::value(istring key, bool& ret)
00406 {
00407 istring stringval;
00408 bool found = value(key, stringval);
00409
00410 if (!found) return false;
00411
00412 ret = (stringval == "yes" || stringval == "true");
00413 return true;
00414 }
00415
00416 void INIFile::set(istring key, string value)
00417 {
00418 if (!stripRoot(key)) return;
00419 istring s, k;
00420 splitKey(key, s, k);
00421
00422 Section* section = getSection(s);
00423 if (!section) {
00424 Section newsec;
00425 newsec.name = s;
00426 newsec.comment = "";
00427 sections.push_back(newsec);
00428 section = getSection(s);
00429 assert(section);
00430 }
00431
00432 section->setKey(k, value);
00433 }
00434
00435 void INIFile::set(istring key, const char* value)
00436 {
00437 string v = value;
00438 set(key, v);
00439 }
00440
00441 void INIFile::set(istring key, int value)
00442 {
00443 char buf[32];
00444 snprintf(buf, 32, "%d", value);
00445 set(key, buf);
00446 }
00447
00448 void INIFile::set(istring key, bool value)
00449 {
00450 if (value)
00451 set(key, "true");
00452 else
00453 set(key, "false");
00454 }
00455
00456 void INIFile::unset(istring key)
00457 {
00458 if (!stripRoot(key)) return;
00459 istring s, k;
00460 splitKey(key, s, k);
00461
00462 Section* section = getSection(s);
00463 if (section) {
00464 section->unsetKey(k);
00465 }
00466 }
00467
00468 void INIFile::listKeys(std::set<istring>& keys, istring section_,
00469 bool longformat)
00470 {
00471 if (!stripRoot(section_)) return;
00472
00473 Section* section = getSection(section_);
00474 if (!section) return;
00475
00476 std::list<KeyValue>::iterator i;
00477 for (i = section->keys.begin(); i != section->keys.end(); ++i) {
00478 istring k;
00479 if (longformat)
00480 k = root + "/" + section->name + "/" + i->key;
00481 else
00482 k = i->key;
00483
00484 keys.insert(k);
00485 }
00486 }
00487
00488 void INIFile::listSections(std::set<istring>& sections_, bool longformat)
00489 {
00490 std::list<Section>::iterator i;
00491 for (i = sections.begin(); i != sections.end(); ++i) {
00492 istring s;
00493 if (longformat)
00494 s = root + "/" + i->name;
00495 else
00496 s = i->name;
00497
00498 sections_.insert(s);
00499 }
00500 }
00501
00502 void INIFile::listKeyValues(std::map<istring,string>& keyvalues,
00503 istring section_, bool longformat)
00504 {
00505 if (!stripRoot(section_)) return;
00506
00507 Section* section = getSection(section_);
00508 if (!section) return;
00509
00510 std::list<KeyValue>::iterator i;
00511 for (i = section->keys.begin(); i != section->keys.end(); ++i) {
00512 istring k;
00513 if (longformat)
00514 k = root + "/" + section->name + "/" + i->key;
00515 else
00516 k = i->key;
00517
00518 keyvalues[k] = i->value;
00519 }
00520 }