00001 00002 // 00003 // Copyright (C) 2000 00004 // Ralf Westram 00005 // (Coded@ReallySoft.de) 00006 // 00007 // Permission to use, copy, modify, distribute and sell this software 00008 // and its documentation for any purpose is hereby granted without fee, 00009 // provided that the above copyright notice appear in all copies and 00010 // that both that copyright notice and this permission notice appear 00011 // in supporting documentation. Ralf Westram makes no 00012 // representations about the suitability of this software for any 00013 // purpose. It is provided "as is" without express or implied warranty. 00014 // 00015 // This code is part of my library. 00016 // You may find a more recent version at http://www.reallysoft.de/ 00017 // 00019 00020 #include "inifile.h" 00021 #include "InputFile.h" 00022 00023 #ifndef __FSTREAM__ 00024 #include <fstream> 00025 #endif 00026 00027 using namespace std; 00028 using namespace rs; 00029 using namespace rs::err; 00030 using namespace rs::file; 00031 using namespace rs::ini; 00032 using namespace rs::str; 00033 00034 namespace rs { 00035 namespace ini { 00036 00037 bool need_check_for_changes = false; 00038 00039 }; 00040 }; 00041 00042 // ---------------------------------------- 00043 // void Comment::split_multiline() 00044 // ---------------------------------------- 00045 void Comment::split_multiline() { 00046 size_t cr_pos = text.find('\n'); 00047 while (cr_pos!=string::npos) { 00048 text.insert(cr_pos+1, 1, comment_char); 00049 cr_pos = text.find('\n', cr_pos+1); 00050 } 00051 } 00052 // ----------------------------------------------- 00053 // void Comment::append(Comment *comment) 00054 // ----------------------------------------------- 00055 void Comment::append(Comment *comment) { 00056 Comment *cmt = this; 00057 while (cmt->next) cmt = cmt->next; 00058 assert(cmt->next==0); 00059 assert(comment->next==0); 00060 cmt->next = comment; 00061 } 00062 // ----------------------------------------------- 00063 // void Comment::dump(ostream& out) const 00064 // ----------------------------------------------- 00065 void Comment::dump(ostream& out) const { 00066 const Comment *cmt = this; 00067 while (cmt) { 00068 out << comment_char << cmt->text << '\n'; 00069 cmt = cmt->next; 00070 } 00071 } 00072 00073 // start of implementation of class Entry: 00074 00075 // ------------------------------------------------ 00076 // double Entry::getNumericContent() const 00077 // ------------------------------------------------ 00078 double Entry::getNumericContent() const 00079 { 00080 const char *c = content.c_str(); 00081 const char *e = 0; 00082 double value = strtod(c, const_cast<char**>(&e)); 00083 00084 if (e && e[0]) { // sth behind value 00085 while (e[0] == ' ') ++e; // skip spaces 00086 if (e[0]) throw_Error(strf("Unexpected content (%s) found behind/instead of numeric value", e)); 00087 } 00088 00089 return value; 00090 } 00091 00092 // ---------------------------------------------- 00093 // long Entry::getIntegerContent() const 00094 // ---------------------------------------------- 00095 long Entry::getIntegerContent() const 00096 { 00097 const char *c = content.c_str(); 00098 const char *e = 0; 00099 long value = strtol(c, &const_cast<char*>(e), 10); 00100 00101 if (e && e[0]) { // sth behind value 00102 while (e[0] == ' ') ++e; // skip spaces 00103 if (e[0]) throw_Error(strf("Unexpected content (%s) found behind/instead of numeric value", e)); 00104 } 00105 00106 return value; 00107 } 00108 00109 00110 // -------------------------------------------------------------- 00111 // void Entry::throw_Error(const string& message) const 00112 // -------------------------------------------------------------- 00113 void Entry::throw_Error(const string& message) const 00114 { 00115 string mess2 = strf("In [%s]/%s: %s", mySection()->getName().c_str(), getName().c_str(), message.c_str()); 00116 FilePosition::throw_Error(mySection()->myInifile()->loadedFrom(), mess2); 00117 } 00118 00119 // -end- of implementation of class Entry. 00120 00121 // start of implementation of class Section: 00122 00123 00124 // --------------------------------------------------------------------------------------------------------------------------------- 00125 // EntryPtr Section::findOrCreateEntry(const string& name_, long default_value, ChangesState mode, const char *add_comment) 00126 // --------------------------------------------------------------------------------------------------------------------------------- 00127 EntryPtr Section::findOrCreateEntry(const string& name_, long default_value, ChangesState mode, const char *add_comment) 00128 { 00129 EntryPtr entry = findEntry(name_); 00130 if (entry.Null()) { 00131 string default_string_value = strf("%li", default_value); 00132 insertEntry(new Entry(name_, default_string_value, UNDEFINED_FILEPOSITION), mode); 00133 entry = findEntry(name_); 00134 if (add_comment) entry->append(add_comment); 00135 } 00136 return entry; 00137 } 00138 00139 // -------------------------------------------------- 00140 // ChangesState Section::changesMade() const 00141 // -------------------------------------------------- 00142 ChangesState Section::changesMade() const { 00143 if (changes_made == NO_CHANGES && need_check_for_changes) { 00144 for (Entries::const_iterator e = begin(); changes_made == NO_CHANGES && e != end(); ++e) { 00145 if (e->second->changesMade()) changes_made = MINOR_CHANGES; 00146 } 00147 } 00148 00149 return changes_made; 00150 } 00151 00152 // ------------------------------------------------------------------------------------------------------------------------------------ 00153 // EntryPtr Section::findOrCreateEntry(const string& name_, const string& content, ChangesState mode, const char *add_comment) 00154 // ------------------------------------------------------------------------------------------------------------------------------------ 00155 EntryPtr Section::findOrCreateEntry(const string& name_, const string& content, ChangesState mode, const char *add_comment) 00156 { 00157 EntryPtr entry = findEntry(name_); 00158 if (entry.Null()) { 00159 insertEntry(new Entry(name_, content, UNDEFINED_FILEPOSITION), mode); 00160 entry = findEntry(name_); 00161 if (add_comment) entry->append(add_comment); 00162 } 00163 return entry; 00164 } 00165 00166 // -------------------------------------------------------------------------------------------------------------------------------------- 00167 // EntryPtr Section::findOrCreateEntry(const string& name_, string (*get_content)(), ChangesState mode, const char *add_comment) 00168 // -------------------------------------------------------------------------------------------------------------------------------------- 00169 EntryPtr Section::findOrCreateEntry(const string& name_, string (*get_content)(), ChangesState mode, const char *add_comment) 00170 { 00171 EntryPtr entry = findEntry(name_); 00172 if (entry.Null()) { 00173 insertEntry(new Entry(name_, get_content(), UNDEFINED_FILEPOSITION), mode); 00174 entry = findEntry(name_); 00175 if (add_comment) entry->append(add_comment); 00176 } 00177 return entry; 00178 } 00179 00180 // -------------------------------------------------------------------------- 00181 // const string& Section::findStringEntry(const string& name_) const 00182 // -------------------------------------------------------------------------- 00183 const string& Section::findStringEntry(const string& name_) const 00184 { 00185 return expectEntry(name_)->getContent(); 00186 } 00187 00188 // -------------------------------------------------------------------- 00189 // double Section::findNumericEntry(const string& name_) const 00190 // -------------------------------------------------------------------- 00191 double Section::findNumericEntry(const string& name_) const 00192 { 00193 return expectEntry(name_)->getNumericContent(); 00194 } 00195 00196 // ------------------------------------------------------------------- 00197 // long Section::findIntegerEntry(const string& name_) const 00198 // ------------------------------------------------------------------- 00199 long Section::findIntegerEntry(const string& name_) const { 00200 return expectEntry(name_)->getIntegerContent(); 00201 } 00202 00203 // --------------------------------------------------------------- 00204 // void Section::throw_Error(const string& message) const 00205 // --------------------------------------------------------------- 00206 void Section::throw_Error(const string& message) const 00207 { 00208 FilePosition::throw_Error(myInifile()->loadedFrom(), strf("In section [%s]: %s", getName().c_str(), message.c_str())); 00209 } 00210 00211 // ----------------------------------------------------------------- 00212 // EntryPtr Section::expectEntry(const string& name_) const 00213 // ----------------------------------------------------------------- 00214 EntryPtr Section::expectEntry(const string& name_) const 00215 { 00216 EntryPtr entry = findEntry(name_); 00217 if (entry.Null()) throw_Error(string("No such entry: '")+name_+'\''); 00218 return entry; 00219 } 00220 00221 // -end- of implementation of class Section. 00222 00223 // start of implementation of class Inifile: 00224 00225 // ---------------------------------------------------------------------------------------- 00226 // SectionPtr Inifile::findOrCreateSection(const string& name_, ChangesState mode) 00227 // ---------------------------------------------------------------------------------------- 00228 SectionPtr Inifile::findOrCreateSection(const string& name_, ChangesState mode) 00229 { 00230 SectionPtr section = findSection(name_); 00231 if (section.Null()) { 00232 insertSection(new Section(name_, UNDEFINED_FILEPOSITION), mode); 00233 section = findSection(name_); 00234 assert(!section.Null()); 00235 } 00236 return section; 00237 } 00238 00239 // -------------------------------------------------- 00240 // ChangesState Inifile::changesMade() const 00241 // -------------------------------------------------- 00242 ChangesState Inifile::changesMade() const { 00243 for (Sections::const_iterator s = sections.begin(); changes_made!=MAJOR_CHANGES && s!=sections.end(); ++s) { 00244 ChangesState section_state = s->second->changesMade(); 00245 if (section_state>changes_made) changes_made = section_state; 00246 } 00247 return changes_made; 00248 } 00249 // --------------------------------- 00250 // void Inifile::fixState() 00251 // --------------------------------- 00252 void Inifile::fixState() { 00253 for (Sections::iterator s = sections.begin(); s!=sections.end(); ++s) { 00254 s->second->fixState(); 00255 } 00256 changes_made = NO_CHANGES; 00257 } 00258 00259 // ------------------------------------------------------------------------------------------------- 00260 // const string& Inifile::findStringEntry(const string& section, const string& entry) const 00261 // ------------------------------------------------------------------------------------------------- 00262 const string& Inifile::findStringEntry(const string& section, const string& entry) const 00263 { 00264 return expectSection(section)->findStringEntry(entry); 00265 } 00266 00267 // ------------------------------------------------------------------------------------------- 00268 // double Inifile::findNumericEntry(const string& section, const string& entry) const 00269 // ------------------------------------------------------------------------------------------- 00270 double Inifile::findNumericEntry(const string& section, const string& entry) const 00271 { 00272 return expectSection(section)->findNumericEntry(entry); 00273 } 00274 // ----------------------------------------------------------------------------------------- 00275 // long Inifile::findIntegerEntry(const string& section, const string& entry) const 00276 // ----------------------------------------------------------------------------------------- 00277 long Inifile::findIntegerEntry(const string& section, const string& entry) const 00278 { 00279 return expectSection(section)->findIntegerEntry(entry); 00280 } 00281 00282 // -------------------------------------------------------------------------------------------- 00283 // const EntryPtr Inifile::findEntry(const string& section, const string& entry) const 00284 // -------------------------------------------------------------------------------------------- 00285 const EntryPtr Inifile::findEntry(const string& section, const string& entry) const 00286 { 00287 return expectSection(section)->expectEntry(entry); 00288 } 00289 00290 // ---------------------------------------------------------------------- 00291 // SectionPtr Inifile::expectSection(const string& name_) const 00292 // ---------------------------------------------------------------------- 00293 SectionPtr Inifile::expectSection(const string& name_) const 00294 { 00295 SectionPtr s = findSection(name_); 00296 if (s.Null()) throw Error(strf("In %s: No such section: [%s]", loadedFrom().c_str(), name_.c_str())); 00297 return s; 00298 } 00299 00300 // ------------------------------------------------------------- 00301 // SectionPtr Inifile::findSection(const string& name_) 00302 // ------------------------------------------------------------- 00303 SectionPtr Inifile::findSection(const string& name_) 00304 { 00305 Sections::iterator s = sections.find(name_); 00306 if (s==sections.end()) return SectionPtr(); 00307 return s->second; 00308 } 00309 00310 // -end- of implementation of class Inifile. 00311 00312 // start of implementation of class FilePosition: 00313 00314 // -------------------------------------------------------------------------------------------- 00315 // void FilePosition::throw_Error(const string& filename, const string& message) const 00316 // -------------------------------------------------------------------------------------------- 00317 void FilePosition::throw_Error(const string& filename, const string& message) const 00318 { 00319 throw Error(strf("In %s (#%lu): %s", filename.c_str(), lineNumber, message.c_str())); 00320 } 00321 00322 // -end- of implementation of class FilePosition. 00323 00324 00325 namespace rs { 00326 namespace ini { 00327 // -------------------------------------------------------------------------------- 00328 // InifilePtr readInifile(const string& filename) 00329 // -------------------------------------------------------------------------------- 00330 InifilePtr readInifile(const string& filename) { 00331 InifilePtr ini(new Inifile(filename)); 00332 SectionPtr currentSection; 00333 EntryPtr currentEntry; 00334 00335 InputFile in(filename); 00336 string line; 00337 Comment *comment = 0; 00338 00339 while (in.getLine(line)) { 00340 const char *linestart = line.c_str(); 00341 while (linestart[0]==' ') ++linestart; 00342 00343 switch (linestart[0]) { 00344 case 0: break; // eol 00345 00346 // comments 00347 case ';': 00348 case '#': { 00349 if (comment) comment->append(linestart+1); 00350 else comment = new Comment(linestart+1); 00351 break; 00352 } 00353 00354 case '[': { // section 00355 const char *kl_zu = strchr(linestart, ']'); 00356 if (!kl_zu) in.throw_Error("Missing ']'"); 00357 00358 string sectionName(linestart+1, 0, kl_zu-linestart-1); 00359 currentSection = new Section(sectionName, in.getLineNumber()); 00360 if (comment) { 00361 currentSection->append(comment); 00362 comment = 0; 00363 } 00364 ini->insertSection(currentSection, NO_CHANGES); 00365 break; 00366 } 00367 default: { // entry 00368 const char *gleich = strchr(linestart, '='); 00369 if (!gleich) in.throw_Error("Missing '='"); 00370 00371 if (currentSection.Null()) in.throw_Error(string("Entry without section")); 00372 00373 string entryName(linestart, 0, gleich-linestart); 00374 string content(gleich+1); 00375 00376 currentEntry = new Entry(entryName, content, in.getLineNumber()); 00377 if (comment) { 00378 currentEntry->append(comment); 00379 comment = 0; 00380 } 00381 currentSection->insertEntry(currentEntry, NO_CHANGES); 00382 00383 break; 00384 } 00385 } 00386 } 00387 00388 ini->fixState(); 00389 return ini; 00390 } 00391 00392 // ------------------------------------------------------------------- 00393 // void writeInifile(const string& filename, InifilePtr ini) 00394 // ------------------------------------------------------------------- 00395 void writeInifile(const string& filename, InifilePtr ini) { 00396 ofstream out(filename.c_str()); 00397 if (!out.good()) throw IOError(filename); 00398 out << *ini; 00399 ini->fixState(); // "state in file == state in memory" 00400 } 00401 00402 // -------------------------------------------------------------------------------------------------- 00403 // InifilePtr readOrCreateInifile(const string& inifilename, InifileCompleter complete_ini) 00404 // -------------------------------------------------------------------------------------------------- 00405 InifilePtr readOrCreateInifile(const string& inifilename, InifileCompleter complete_ini) { 00406 InifilePtr ini; 00407 00408 try { 00409 ini = readInifile(inifilename); 00410 ini->fixState(); // "state in file == state in memory" 00411 } 00412 catch (Error &err) { // if file not found = > start with empty ini 00413 cerr << err.get_print_message() << "\n"; 00414 cerr << "Creating new inifile '" << inifilename << "'\n"; 00415 ini = new Inifile("<new inifile>"); 00416 } 00417 00418 complete_ini(ini); 00419 00420 ChangesState changes = ini->changesMade(); 00421 if (changes != NO_CHANGES) { 00422 writeInifile(inifilename, ini); 00423 if (changes==MAJOR_CHANGES) { 00424 throw Error(false, string("'")+inifilename+"' was created or extended.\n" 00425 "It's really recommended that you edit it now."); 00426 } 00427 } 00428 00429 return ini; 00430 } 00431 }; 00432 }; 00433 00434 00435
Contact me in case of errors or questions. This documentation is powered by . |
(C) 2000-2002 |