/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: Christian Meyer 2005-08-14: complete reimplementation: now the File is parsed at the initialisation, and informations is gathered there. */ #include "ini_parser.h" #if HAVE_CONFIG_H #include #endif #include #include #ifdef DEBUG_LEVEL #include "../../../lib/util/debug.h" #else #define PRINTF(x) printf #define PRINT(x) printf #endif /// /// /// /// /// /// /// INI-PARSER NODE /// /// /// /// /// /// /// IniParser::Node::Node(const std::string& name, const std::string& comment) { this->_name = name; this->_comment = comment; } /// /// /// /// /// //// /// INI-PARSER ENTRY /// /// /// /// /// /// //// IniParser::Entry::Entry(const std::string& name, const std::string& value, const std::string& comment) : IniParser::Node(name, comment), _value(value) {} void IniParser::Entry::debug() const { printf(" %s = %s\n", this->name().c_str(), this->_value.c_str()); } /// /// /// /// /// /// /// /// INI-PARSER SECTION /// /// /// /// /// /// /// /// IniParser::Section::Section(const std::string& sectionName, const std::string& comment) : IniParser::Node(sectionName, comment) {} IniParser::Entry& IniParser::Section::addEntry(const std::string& entryName, const std::string& value, const std::string& comment) { Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName); if (entry == this->_entries.end()) { this->_entries.push_back(Entry(entryName, value, comment)); entry = --this->_entries.end(); } return *entry; } bool IniParser::Section::editEntry(const std::string& entryName, const std::string& value, bool createMissing) { Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName); if (entry == this->_entries.end()) { if (createMissing) { this->addEntry(entryName, value); return true; } else return false; } (*entry).setValue(value); return true; } const std::string& IniParser::Section::getValue(const std::string& entryName, const std::string& defaultValue) const { Entry::const_iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName); if (entry != this->_entries.end()) return (*entry).value(); else return defaultValue; } bool IniParser::Section::setEntryComment(const std::string& entryName, const std::string& comment) { Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName); if (entry != this->_entries.end()) { (*entry).setComment(comment); return true; } return false; } const std::string& IniParser::Section::getEntryComment(const std::string& entryName) const { Entry::const_iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName); if (entry != this->_entries.end()) { return (*entry).comment(); } return IniParser::_emptyString; } IniParser::Entry* IniParser::Section::getEntry(const std::string& entryName) { Entry::iterator entry = std::find(this->_entries.begin(), this->_entries.end(), entryName); if (entry != this->_entries.end()) return &(*entry); else return NULL; } IniParser::Entry::const_iterator IniParser::Section::getEntryIt(const std::string& entryName) const { return std::find(this->_entries.begin(), this->_entries.end(), entryName); } void IniParser::Section::clear() { this->_entries.clear(); } void IniParser::Section::debug() const { printf(" [%s]\n", this->name().c_str()); for(Entry::const_iterator entry = this->_entries.begin(); entry != this->_entries.end(); ++entry) (*entry).debug(); } /// /// /// /// /// /// /// /// INI-PARSER DOCUMENT /// /// /// /// /// /// /// /// IniParser::Document::Document(const std::string& fileName, const std::string& comment) : IniParser::Node(fileName, comment) {} IniParser::Section& IniParser::Document::addSection(const std::string& sectionName, const std::string& comment) { Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section == this->_sections.end()) { this->_sections.push_back(Section(sectionName, comment)); return *(--_sections.end()); } else return *section; } bool IniParser::Document::removeSection(const std::string& sectionName) { Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section != this->_sections.end()) { this->_sections.erase(section); return true; } else return false; } bool IniParser::Document::setSectionComment(const std::string& sectionName, const std::string& comment) { Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section != this->_sections.end()) { (*section).setComment(comment); return true; } else return false; } IniParser::Section* IniParser::Document::getSection(const std::string& sectionName) { Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section != this->_sections.end()) { return &(*section); } else return NULL; } IniParser::Section::const_iterator IniParser::Document::getSectionIt(const std::string& sectionName) const { Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); return section; } bool IniParser::Document::addEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, const std::string& comment) { // locating section Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section == this->_sections.end()) { // creating section if not found!! this->_sections.push_back(Section(sectionName)); section = --_sections.end(); } (*section).addEntry(entryName, value, comment); return true; } bool IniParser::Document::editEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, bool createMissing) { // locating section Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section == this->_sections.end()) { // creating section if not found!! if (createMissing) { this->_sections.push_back(Section(sectionName)); section = --_sections.end(); } else return false; } return (*section).editEntry(entryName, value, createMissing); } const std::string& IniParser::Document::getValue(const std::string& sectionName, const std::string& entryName, const std::string& defaultValue) const { // locating section Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section != this->_sections.end()) return (*section).getValue(entryName, defaultValue); return defaultValue; } bool IniParser::Document::setEntryComment(const std::string& sectionName, const std::string& entryName, const std::string& comment) { // locating section Section::iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section != this->_sections.end()) return (*section).setEntryComment(entryName, comment); else return false; } const std::string& IniParser::Document::getEntryComment(const std::string& sectionName, const std::string& entryName) const { Section::const_iterator section = std::find(this->_sections.begin(), this->_sections.end(), sectionName); if (section != this->_sections.end()) return (*section).getEntryComment(entryName); else return IniParser::_emptyString; } void IniParser::Document::clear() { this->_sections.clear(); } void IniParser::Document::debug() const { for(Section::const_iterator section = this->_sections.begin(); section != this->_sections.end(); ++section) (*section).debug(); } /// /// /// /// /// /// // /// INI-PARSER ITSELF //// /// /// /// /// /// /// // const std::string IniParser::_emptyString = ""; /** * @brief constructs an IniParser using a file * @param fileName: the path and name of the file to parse */ IniParser::IniParser (const std::string& fileName) : _document(fileName) { this->_fileName = fileName; if (!fileName.empty()) this->readFile(fileName); } /** * @brief removes the IniParser from memory */ IniParser::~IniParser () {} /** * @brief opens a file to parse * @param fileName: path and name of the new file to parse * @param keepSettings if the Settings (if already some are set) should be kept (false by default) * @return true on success false otherwise; * * If there was already an opened file, the file will be closed, * and the new one will be opened. */ bool IniParser::readFile(const std::string& fileName, bool keepSettings) { FILE* stream; //< The stream we use to read the file. int lineCount = 0; //< The Count of lines read. std::list commentList; //< A list of Comments. Section* currentSection = NULL; if (!keepSettings) this->_document.clear(); if( (stream = fopen (fileName.c_str(), "r")) == NULL) { PRINTF(1)("IniParser could not open %s for reading\n", fileName.c_str()); return false; } else { this->_fileName = fileName; ///////////////////////////// // READING IN THE INI-FILE // ///////////////////////////// char lineBuffer[PARSELINELENGHT+1]; char buffer[PARSELINELENGHT+1]; const char* lineBegin; char* ptr; while( fgets (lineBuffer, PARSELINELENGHT, stream)) { lineBegin = lineBuffer; // remove newline char, and \0-terminate if( (ptr = strchr( lineBuffer, '\n')) != NULL) *ptr = 0; else lineBuffer[PARSELINELENGHT] = 0; // cut up to the beginning of the line. while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer)) ++lineBegin; if ( !strcmp( lineBegin, "" ) ) continue; // check if we have a FileComment if ( (*lineBegin == '#' || *lineBegin == ';')) { std::string newCommenLine = lineBegin; commentList.push_back(newCommenLine); continue; } if (lineCount == 0 && !commentList.empty()) { this->setNodeComment(&this->_document, &commentList); lineCount++; } // check for section identifyer else if( sscanf (lineBegin, "[%s", buffer) == 1) { if( (ptr = strchr( buffer, ']')) != NULL) { *ptr = 0; Section& node = this->_document.addSection(buffer); setNodeComment(&node, &commentList); currentSection = &node; } } // check for Entry identifier (Entry = Value) else if( (ptr = strchr( lineBegin, '=')) != NULL) { if (currentSection == NULL) { PRINTF(2)("Not in a Section yet for %s\n", lineBegin); lineCount++; continue; } if( ptr == lineBegin) { lineCount++; continue; } char* valueBegin = ptr+1; while ((*valueBegin == ' ' || *valueBegin == '\t') && valueBegin <= lineBegin + strlen(lineBegin)) ++valueBegin; char* valueEnd = valueBegin + strlen(valueBegin)-1; while ((*valueEnd == ' ' || *valueEnd == '\t') && valueEnd >= valueBegin) --valueEnd; valueEnd[1] = '\0'; char* nameEnd = ptr-1; while ((*nameEnd == ' ' || *nameEnd == '\t' ) && nameEnd >= lineBegin) --nameEnd; nameEnd[1] = '\0'; Entry& node = currentSection->addEntry(lineBegin, valueBegin); this->setNodeComment(&node, &commentList); lineCount++; } } } fclose(stream); return true; } /** * @brief opens a file and writes to it * @param fileName: path and name of the new file to write to * @return true on success false otherwise */ bool IniParser::writeFile(const std::string& fileName) const { FILE* stream; //!< The stream we use to read the file. if( fileName.empty()) return false; if( (stream = fopen (fileName.c_str(), "w")) == NULL) { PRINTF(1)("IniParser could not open %s for writing\n", fileName.c_str()); return false; } else { if (!this->_document.comment().empty()) fprintf(stream, "%s\n\n", this->_document.comment().c_str()); Section::const_iterator section; for (section = this->_document.sections().begin(); section != this->_document.sections().end(); ++section) { if (!(*section).comment().empty()) fprintf(stream, "%s", (*section).comment().c_str()); fprintf(stream, "\n [%s]\n", (*section).name().c_str()); Entry::const_iterator entry; for (entry = (*section).entries().begin(); entry != (*section).entries().end(); ++entry) { if (!(*entry).comment().empty()) fprintf(stream, "%s", (*entry).comment().c_str()); fprintf(stream, " %s = %s\n", (*entry).name().c_str(), (*entry).value().c_str()); } } } fclose(stream); return true; } void IniParser::setFileComment(const std::string& fileComment) { this->_document.setComment(fileComment); } /** * @brief adds a section to the list of Sections, * if no Section list is availiable, it will create it * @param sectionName the Name of the section to add * @return true on success... there is only success or segfault :) */ IniParser::Section& IniParser::addSection(const std::string& sectionName) { return this->_document.addSection(sectionName); } /** * @brief adds a new Entry to either the currentSection or the section called by sectionName * @param entryName the Name of the Entry to add * @param value the value to assign to this entry * @param sectionName if NULL then this entry will be set to the currentSection * otherwise to the section refered to by sectionName. * If both are NULL no entry will be added * @return The Entry, that was added. */ bool IniParser::addEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, const std::string& comment) { return this->_document.addEntry(sectionName, entryName, value, comment); } /** * @brief edits the entry speciefied by entryName in sectionName/currentSection or creates it if it doesn't exist * @param entryName the Name of the Entry to add * @param value the value to assign to this entry * @param sectionName if NULL then this entry will be set to the currentSection * otherwise to the section refered to by sectionName. * If both are NULL no entry will be added * @return true if everything is ok false on error */ bool IniParser::editEntry(const std::string& sectionName, const std::string& entryName, const std::string& value, bool createMissing) { return this->_document.editEntry(sectionName, entryName, value, createMissing); } /** * @brief directly acesses an entry in a section * @param sectionName: the section where the entry is to be found * @param entryName: the name of the entry to find * @param defaultValue: what should be returned in case the entry cannot be found * @return The Value of the Entry. */ const std::string& IniParser::getValue(const std::string& sectionName, const std::string& entryName, const std::string& defaultValue) const { return this->_document.getValue(sectionName, entryName, defaultValue); } /** * Set the Comment of a specified Entry. * @param comment the Comment to set * @param entryName the Name of the Entry * @param sectionName the Name of the Section */ void IniParser::setEntryComment(const std::string& sectionName, const std::string& entryName, const std::string& comment) { this->_document.setEntryComment(sectionName, entryName, comment); } /** * @param entryName the Entry to query for * @param sectionName the Section to Query for * @returns the queried Comment. */ const std::string& IniParser::getEntryComment(const std::string& sectionName, const std::string& entryName) const { return this->_document.getEntryComment(sectionName, entryName); } /** * @brief output the whole tree in a nice and easy way. */ void IniParser::debug() const { PRINT(0)("Iniparser '%s' - debug\n", this->_fileName.c_str()); if (!this->_document.comment().empty()) PRINT(0)("FileComment:\n '%s'\n\n", this->_document.comment().c_str()); if (!this->_document.sections().empty()) this->_document.debug(); else PRINTF(0)("no Sections Defined in this ini-file (%s).\n", _fileName.c_str()); } /** * takes lines together to form one NodeComment, ereasing the commentList */ void IniParser::setNodeComment(Node* node, std::list* comments) { assert(node != NULL); assert(comments != NULL); std::string comment; if (comments->empty()) { comment = ""; } else { while (!comments->empty()) { if (!comment.empty()) comment += "\n"; comment += comments->front(); comments->pop_front(); } } node->setComment(comment); }