Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

inifile.cpp

Go to the documentation of this file.
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 Doxygen.
(C) 2000-2002 Doxygen