/* 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" #include #include #if HAVE_CONFIG_H #include #endif #ifdef DEBUG #include "../../../defs/debug.h" #else #define PRINTF(x) printf #endif using namespace std; /** * @brief constructs an IniParser using a file * @param fileName: the path and name of the file to parse */ IniParser::IniParser (const char* fileName) { this->fileName = NULL; this->comment = NULL; if (fileName != NULL) this->readFile(fileName); } /** * @brief removes the IniParser from memory */ IniParser::~IniParser () { this->deleteSections(); this->setFileName(NULL); } /** * @brief removes all the sections. This is like delete, but even cooler :) */ void IniParser::deleteSections() { // in all sections while(!this->sections.empty()) { IniSection section = this->sections.front(); // in all entries of the sections while(!section.entries.empty()) { // delete all strings of entries. IniEntry entry = section.entries.front(); delete[] entry.name; delete[] entry.value; delete[] entry.comment; section.entries.pop_front(); } // delete all Sections delete[] section.name; delete[] section.comment; this->sections.pop_front(); } this->currentSection = this->sections.end(); this->setFileName(NULL); } /** * @brief sets the Name of the input-file * @param fileName The new FileName to set to the IniParser * If fileName is NULL the new Name will be set to NULL too. */ void IniParser::setFileName(const char* fileName) { if (this->fileName != NULL) delete[] this->fileName; if (this->comment != NULL) delete[] this->comment; this->comment = NULL; if (fileName != NULL) { this->fileName = new char[strlen(fileName)+1]; strcpy(this->fileName, fileName); } else this->fileName = NULL; } /** * @brief opens a file to parse * @param fileName: path and name of the new file to parse * @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 char* fileName) { FILE* stream; //< The stream we use to read the file. int lineCount = 0; //< The Count of lines. if (this->fileName != NULL) this->deleteSections(); if( fileName == NULL) return false; if( (stream = fopen (fileName, "r")) == NULL) { PRINTF(1)("IniParser could not open %s\n", fileName); return false; } else { this->setFileName(fileName); ///////////////////////////// // READING IN THE INI-FILE // ///////////////////////////// char lineBuffer[PARSELINELENGHT]; char buffer[PARSELINELENGHT]; 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; // cut up to the beginning of the line. while((*lineBegin == ' ' || *lineBegin == '\t') && lineBegin < lineBuffer + strlen(lineBuffer)) ++lineBegin; // check if we have a FileComment if ( (*lineBegin == '#' || *lineBegin == ';')) { char* newCommenLine = new char[strlen(lineBegin)+1]; strcpy(newCommenLine, lineBegin); this->commentList.push_back(newCommenLine); continue; } if (lineCount == 0 && !this->commentList.empty()) { this->setFileComment(); lineCount++; } // check for section identifyer else if( sscanf (lineBegin, "[%s", buffer) == 1) { if( (ptr = strchr( buffer, ']')) != NULL) { *ptr = 0; this->addSection(buffer); this->setSectionComment(); } } // 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'; this->addVar(lineBegin, valueBegin); this->setEntryComment(); lineCount++; } } } this->currentSection = this->sections.begin(); if (!this->sections.empty()) this->currentEntry = (*this->currentSection).entries.begin(); 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 char* fileName) const { FILE* stream; //!< The stream we use to read the file. if( fileName == NULL && (fileName = this->fileName) == NULL ) return false; if( (stream = fopen (fileName, "w")) == NULL) { PRINTF(1)("IniParser could not open %s\n", fileName); return false; } else { if (this->comment != NULL) fprintf(stream, "%s\n\n", this->comment); std::list::const_iterator section; for (section = this->sections.begin(); section != this->sections.end(); section++) { if ((*section).comment != NULL) fprintf(stream, "%s", (*section).comment); fprintf(stream, "\n [%s]\n", (*section).name); std::list::const_iterator entry; for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) { if ((*entry).comment != NULL) fprintf(stream, "%s", (*entry).comment); fprintf(stream, " %s = %s\n", (*entry).name, (*entry).value); } } } fclose(stream); } void IniParser::setFileComment(const char* fileComment) { if (this->comment != NULL) delete this->comment; if (fileComment != NULL) { this->comment = new char[strlen(fileComment)+1]; strcpy(this->comment, fileComment); } else this->comment = NULL; } /** * @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 :) */ bool IniParser::addSection(const char* sectionName) { if (sectionName == NULL) return false; this->sections.push_back(IniSection()); this->sections.back().comment = NULL; this->sections.back().name = new char[strlen(sectionName)+1]; strcpy(this->sections.back().name, sectionName); this->currentSection = --this->sections.end(); if (!this->sections.empty()) this->currentEntry = (*this->currentSection).entries.begin(); PRINTF(5)("Added Section %s\n", sectionName); return true; } /** * @brief Set the parsing cursor to the specified section * @param sectionName: the name of the section to set the cursor to * @return true on success or false if the section could not be found */ bool IniParser::getSection(const char* sectionName) { this->currentSection = this->getSectionIT(sectionName); if (this->currentSection != this->sections.end()) { this->currentEntry = (*this->currentSection).entries.begin(); return true; } else return false; } /** * */ void IniParser::setSectionComment(const char* comment, const char* sectionName) { std::list::iterator section = this->getSectionIT(sectionName); if (section == this->sections.end()) return; if ((*section).comment != NULL) delete[] (*section).comment; if (comment != NULL) { (*section).comment = new char[strlen (comment)+1]; strcpy((*section).comment, comment); } else (*section).comment = NULL; } /** * @param sectionName the Section to query for * @returns the Comment, or NULL on error. */ const char* IniParser::getSectionComment(const char* sectionName) const { std::list::const_iterator section = this->getSectionIT(sectionName); if (section != this->sections.end()) return (*section).comment; else return NULL; } /** * @brief moves to the first section */ void IniParser::firstSection() { this->currentSection = this->sections.begin(); if (!this->sections.empty()) this->currentEntry = (*this->currentSection).entries.begin(); } /** * @brief searches the next section * @returns the name of the section if found, NULL otherwise */ const char* IniParser::nextSection() { if (this->currentSection == this->sections.end()) return NULL; this->currentSection++; if (this->currentSection != this->sections.end()) { this->currentEntry = (*this->currentSection).entries.begin(); return this->currentSection->name; } else return NULL; } /** * @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 true if everything is ok false on error */ bool IniParser::addVar(const char* entryName, const char* value, const char* sectionName) { std::list::iterator section; if (sectionName != NULL) { for (section = this->sections.begin(); section != this->sections.end(); section++) if (!strcmp((*section).name, sectionName)) break; } else section = this->currentSection; if (section == this->sections.end()) return false; if (section == this->sections.end()) { PRINTF(2)("section '%s' not found for value '%s'\n", sectionName, entryName); return false; } else { (*section).entries.push_back(IniEntry()); (*section).entries.back().comment = NULL; (*section).entries.back().name = new char[strlen(entryName)+1]; strcpy((*section).entries.back().name, entryName); (*section).entries.back().value = new char[strlen(value)+1]; strcpy((*section).entries.back().value, value); PRINTF(5)("Added Entry %s with Value '%s' to Section %s\n", (*section).entries.back().name, (*section).entries.back().value, (*section).name); this->currentEntry = --(*section).entries.end(); return true; } } /** * @brief directly acesses an entry in a section * @param entryName: the name of the entry to find * @param sectionName: the section where the entry is to be found * @param defaultValue: what should be returned in case the entry cannot be found * @return a pointer to a buffer conatining the value of the specified entry. This buffer will contain the data specified in defvalue in case the entry wasn't found * * The returned pointer points to an internal buffer, so do not free it on your own. Do not give a NULL pointer to defvalue, this will certainly * lead to unwanted behaviour. */ const char* IniParser::getVar(const char* entryName, const char* sectionName, const char* defaultValue) const { if (this->fileName != NULL) { std::list::const_iterator entry = this->getEntryIT(entryName, sectionName); if (entry != NULL && !strcmp((*entry).name, entryName)) return (*entry).value; PRINTF(2)("Entry '%s' in section '%s' not found.\n", entryName, sectionName); } else PRINTF(2)("no File opened\n"); return 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 char* comment, const char* entryName, const char* sectionName) { std::list::iterator entry = this->getEntryIT(entryName, sectionName); if ((*entry).comment != NULL) delete[] (*entry).comment; if (comment != NULL) { (*entry).comment = new char[strlen (comment)+1]; strcpy((*entry).comment, comment); } else (*entry).comment = NULL; } /** * @param entryName the Entry to query for * @param sectionName the Section to Query for * @returns the queried Comment. */ const char* IniParser::getEntryComment(const char* entryName, const char* sectionName) const { std::list::const_iterator entry = this->getEntryIT(entryName, sectionName); return (*entry).comment; } /** * @brief moves to the first Variable of the current Section */ void IniParser::firstVar() { if (!this->sections.empty() && this->currentSection != this->sections.end()) this->currentEntry = (*this->currentSection).entries.begin(); } /** * @brief gets the next VarName = VarValue pair from the parsing stream * @return true on success, false otherwise (in the latter case name and value will be NULL) */ bool IniParser::nextVar() { if ( this->sections.empty() || this->currentSection == this->sections.end() || this->currentEntry == (*this->currentSection).entries.end()) return false; this->currentEntry++; if (this->currentEntry == (*this->currentSection).entries.end()) return false; else return true; } /** * @returns the name of the Current selected Section */ const char* IniParser::getCurrentSection() const { if (!this->sections.empty() && this->currentSection != this->sections.end()) return this->currentSection->name; else return NULL; } /** * @returns the current entries Name, or NULL if we havn't selected a Entry */ const char* IniParser::getCurrentName() const { if (!this->sections.empty() && this->currentSection != this->sections.end() && this->currentEntry != (*this->currentSection).entries.end()) return (*this->currentEntry).name; else return NULL; } /** * @returns the current entries Value, or NULL if we havn't selected a Entry */ const char* IniParser::getCurrentValue() const { if (!this->sections.empty() && this->currentSection != this->sections.end() && this->currentEntry != (*this->currentSection).entries.end()) return (*this->currentEntry).value; else return NULL; } /** * Finds the Section Iterator of the Section Called sectionName * @param sectionName the Name of the Section to get the Iterator from */ std::list::const_iterator IniParser::getSectionIT(const char* sectionName) const { std::list::const_iterator section = this->currentSection; if (sectionName == NULL) return this->currentSection; else for (section = this->sections.begin(); section != this->sections.end(); section++) if (!strcmp((*section).name, sectionName)) break; return section; } /** * Finds the Section Iterator of the Section Called sectionName * @param sectionName the Name of the Section to get the Iterator from */ std::list::iterator IniParser::getSectionIT(const char* sectionName) { std::list::iterator section = this->currentSection; if (sectionName == NULL) return this->currentSection; else for (section = this->sections.begin(); section != this->sections.end(); section++) if (!strcmp((*section).name, sectionName)) break; return section; } /** * Finds the Entry Iterator of the Section Called sectionName and entry called EntryName * @param entryName the Name of the Entry to get the Iterator from * @param sectionName the Name of the Section to get the Iterator from */ std::list::const_iterator IniParser::getEntryIT(const char* entryName, const char* sectionName) const { if (entryName == NULL) return this->currentEntry; std::list::const_iterator section = this->getSectionIT(sectionName); std::list::const_iterator entry = this->currentEntry; if (section != this->sections.end()) for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) if (!strcmp((*entry).name, entryName)) break; if (entry == (*section).entries.end()) return NULL; else return entry; } /** * Finds the Entry Iterator of the Section Called sectionName and entry called EntryName * @param entryName the Name of the Entry to get the Iterator from * @param sectionName the Name of the Section to get the Iterator from */ std::list::iterator IniParser::getEntryIT(const char* entryName, const char* sectionName) { if (entryName == NULL) return this->currentEntry; std::list::iterator section = this->getSectionIT(sectionName); std::list::iterator entry = this->currentEntry; if (section != this->sections.end()) for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) if (!strcmp((*entry).name, entryName)) break; if (entry == (*section).entries.end()) return NULL; else return entry; } /** * takes lines together to form one FileComment, ereasing the commentList */ void IniParser::setFileComment() { if (this->comment != NULL) delete[] this->comment; if (this->commentList.empty()) { this->comment = NULL; return; } unsigned int stringLength = 1; std::list::iterator comment; for (comment = this->commentList.begin(); comment != this->commentList.end(); comment++) stringLength += strlen((*comment)) +1; this->comment = new char[stringLength]; this->comment[0] = '\0'; while (!this->commentList.empty()) { if (*this->comment != '\0') strcat(this->comment, "\n"); strcat(this->comment, this->commentList.front()); delete[] this->commentList.front(); this->commentList.pop_front(); } } /** * takes lines together to form one SectionComment, ereasing the commentList */ void IniParser::setSectionComment() { if ((*this->currentSection).comment != NULL) delete[] (*this->currentSection).comment; if (this->commentList.empty()) { (*this->currentSection).comment = NULL; return; } unsigned int stringLength = 1; std::list::iterator comment; for (comment = this->commentList.begin(); comment != this->commentList.end(); comment++) stringLength += strlen((*comment)) +1; (*this->currentSection).comment = new char[stringLength]; (*this->currentSection).comment[0] = '\0'; while (!this->commentList.empty()) { if (*(*this->currentSection).comment != '\0') strcat((*this->currentSection).comment, "\n"); strcat((*this->currentSection).comment, this->commentList.front()); delete[] this->commentList.front(); this->commentList.pop_front(); } } /** * takes lines together to form one EntryComment, ereasing the commentList */ void IniParser::setEntryComment() { if ((*this->currentEntry).comment != NULL) delete[] (*this->currentEntry).comment; if (this->commentList.empty()) { (*this->currentEntry).comment = NULL; return; } unsigned int stringLength = 1; std::list::iterator comment; for (comment = this->commentList.begin(); comment != this->commentList.end(); comment++) stringLength += strlen((*comment)) +1; (*this->currentEntry).comment = new char[stringLength]; (*this->currentEntry).comment[0] = '\0'; while (!this->commentList.empty()) { if (*(*this->currentEntry).comment != '\0') strcat((*this->currentEntry).comment, "\n"); strcat((*this->currentEntry).comment, this->commentList.front()); delete[] this->commentList.front(); this->commentList.pop_front(); } } /** * @brief output the whole tree in a nice and easy way. */ void IniParser::debug() const { PRINTF(0)("Iniparser %s - debug\n", this->fileName); if (this->comment != NULL) PRINTF(0)("FileComment:\n %s\n\n", this->comment); if (this->fileName != NULL) { std::list::const_iterator section; for (section = this->sections.begin(); section != this->sections.end(); section++) { if ((*section).comment != NULL) PRINTF(0)(" %s\n", (*section).comment); PRINTF(0)(" [%s]\n", (*section).name); std::list::const_iterator entry; for (entry = (*section).entries.begin(); entry != (*section).entries.end(); entry++) { if ((*entry).comment != NULL) PRINTF(0)(" %s\n", (*entry).comment); PRINTF(0)(" '%s' -> '%s'\n", (*entry).name, (*entry).value); } } } else PRINTF(1)("no opened ini-file.\n"); }