INIFile.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2004 The Pentagram team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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; // readConfigString sets is_file = false
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 // Large parts of the following function are borrowed from ScummVM's
00150 // config-manager.cpp, copyright (C) 2001-2004 The ScummVM project
00151 // http://www.scummvm.org/
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                         // Accumulate comments here. Once we encounter either the start
00177                         // of a new section, or a key-value-pair, we associate the value
00178                         // of the 'comment' variable with that entity.
00179                         comment += line + "\n";
00180                 } else if (line[0] == '[') {
00181                         // It's a new section which begins here.
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                         // Get the section name, and check whether it's valid (that
00191                         // is, verify that it only consists of alphanumerics,
00192                         // dashes, underscores and colons).
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                                 // save previous section
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                         // Skip leading & trailing whitespaces
00223                         rtrim(line);
00224                         ltrim(line);
00225 
00226                         // Skip empty lines
00227                         if (line.empty())
00228                                 continue;
00229                         
00230                         // If no section has been set, this config file is invalid!
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                         // Split string at '=' into 'key' and 'value'.
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                 // save last section
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         // TODO: more sanity checks might be nice
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 }

Generated on Fri Jul 27 22:27:19 2007 for pentagram by  doxygen 1.4.7