/* 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 "list.h" #include #include #include "debug.h" using namespace std; /** * constructs an IniParser using a file * @param fileName: the path and name of the file to parse */ IniParser::IniParser (const char* fileName) { this->currentEntry = NULL; this->currentSection = NULL; this->sections = NULL; this->fileName = NULL; if (fileName != NULL) this->readFile(fileName); } /** * removes the IniParser from memory */ IniParser::~IniParser () { deleteSections(); } /** * removes all the sections. This is like delete, but even cooler :) */ void IniParser::deleteSections() { if (this->sections) { tIterator* sectionIt = this->sections->getIterator(); IniSection* sectionEnum = sectionIt->firstElement(); while (sectionEnum) { tIterator* entryIt = sectionEnum->entries->getIterator(); IniEntry* entryEnum = entryIt->firstElement(); while (entryEnum) { delete []entryEnum->name; delete []entryEnum->value; delete entryEnum; entryEnum = entryIt->nextElement(); } delete entryIt; delete []sectionEnum->name; delete sectionEnum->entries; delete sectionEnum; sectionEnum = sectionIt->nextElement(); } delete sectionIt; } delete this->sections; this->currentEntry = NULL; this->currentSection = NULL; this->sections = NULL; this->setFileName(NULL); } /** * opens another file to parse * @param fileName: path and name of the new file to parse * @return true on success false otherwise; */ bool IniParser::readFile(const char* fileName) { FILE* stream; //!< The stream we use to read the file. if (sections != NULL) deleteSections(); if( fileName == NULL) return false; this->setFileName(fileName); if( (stream = fopen (fileName, "r")) == NULL) { PRINTF(1)("IniParser could not open %s\n", fileName); return false; } else { this->currentEntry = NULL; this->currentSection = NULL; this->sections = new tList; ///////////////////////////// // 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; if (strlen(lineBegin) <= 1 || *lineBegin == '#' || *lineBegin == ';') continue;//printf("empty Line\n"); // check for section identifyer else if( sscanf (lineBegin, "[%s", buffer) == 1) { if( (ptr = strchr( buffer, ']')) != NULL) { *ptr = 0; this->addSection(buffer); } } // 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); continue; } if( ptr == lineBegin) 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); } } } fclose(stream); return true; } /** * 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) { FILE* stream; //!< The stream we use to read the file. if( fileName == NULL) return false; if( (stream = fopen (fileName, "w")) == NULL) { PRINTF(1)("IniParser could not open %s\n", fileName); return false; } else { if (this->sections) { tIterator* sectionIt = this->sections->getIterator(); IniSection* sectionEnum = sectionIt->firstElement(); while (sectionEnum) { fprintf(stream, "\n [%s]\n", sectionEnum->name); tIterator* entryIt = sectionEnum->entries->getIterator(); IniEntry* entryEnum = entryIt->firstElement(); while (entryEnum) { fprintf(stream, " %s = %s\n", entryEnum->name, entryEnum->value); entryEnum = entryIt->nextElement(); } delete entryIt; sectionEnum = sectionIt->nextElement(); } delete sectionIt; } else PRINTF(1)("%s no sections defined yet\n", fileName); } fclose(stream); } /** * 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 (this->sections == NULL) this->sections = new tList; IniSection* newSection = new IniSection; newSection->name = new char[strlen(sectionName)+1]; strcpy(newSection->name, sectionName); newSection->entries = new tList; this->currentSection = newSection; this->sections->add(newSection); PRINTF(5)("Added Section %s\n", sectionName); return true; } /** * 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) { tIterator* sectionIt = this->sections->getIterator(); IniSection* sectionEnum = sectionIt->firstElement(); while (sectionEnum) { if (!strcmp(sectionEnum->name, sectionName)) { this->currentSection = sectionEnum; this->currentEntry = NULL; delete sectionIt; return true; } sectionEnum = sectionIt->nextElement(); } delete sectionIt; return false; } /** * moves to the first section */ void IniParser::getFirstSection() { if (this->sections) this->currentSection = this->sections->firstElement(); else this->currentSection = NULL; this->currentEntry = NULL; } /** * searches the next section * @returns the name of the section if found, NULL otherwise */ const char* IniParser::nextSection() { if (this->currentSection == NULL) return NULL; else { if (this->sections) { if (this->currentSection == this->sections->lastElement()) this->currentSection = NULL; else this->currentSection = this->sections->nextElement(this->currentSection); } } if (this->currentSection != NULL) return this->currentSection->name; else return NULL; } /** * moves to the first Variable of the current Section */ void IniParser::getFirstVar() { if (this->currentSection) this->currentEntry = this->currentSection->entries->firstElement(); else this->currentEntry = NULL; } /** * 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->currentSection == NULL || this->currentEntry == NULL || this->currentEntry == this->currentSection->entries->lastElement()) { this->currentEntry = NULL; return false; } this->currentEntry = this->currentSection->entries->nextElement(this->currentEntry); if (this->currentEntry == NULL) return false; else return true; } /** * 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) { IniSection* addSection = NULL; if (sectionName != NULL) { tIterator* sectionIt = this->sections->getIterator(); IniSection* sectionEnum = sectionIt->firstElement(); while (sectionEnum) { if (!strcmp(sectionEnum->name, sectionName)) { addSection = sectionEnum; break; } sectionEnum = sectionIt->nextElement(); } delete sectionIt; } else addSection = this->currentSection; if (addSection == NULL) { PRINTF(2)("section not found for value %s\n", entryName); return false; } else { IniEntry* newEntry = new IniEntry; newEntry->name = new char[strlen (entryName)+1]; strcpy(newEntry->name, entryName); newEntry->value = new char[strlen(value)+1]; strcpy(newEntry->value, value); this->currentSection->entries->add(newEntry); PRINTF(5)("Added Entry %s with Value '%s' to Section %s\n", newEntry->name, newEntry->name, addSection->name); return true; } } /** * 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->sections) { tIterator* sectionIt = this->sections->getIterator(); IniSection* sectionEnum = sectionIt->firstElement(); while (sectionEnum) { if (!strcmp(sectionEnum->name, sectionName)) { tIterator* entryIt = sectionEnum->entries->getIterator(); IniEntry* entryEnum = entryIt->firstElement(); while (entryEnum) { if (!strcmp(entryEnum->name, entryName)) { delete entryIt; delete sectionIt; return entryEnum->value; } entryEnum = entryIt->nextElement(); } delete entryIt; PRINTF(2)("Entry %s in section %s not found.\n", entryName, sectionName); break; } sectionEnum = sectionIt->nextElement(); } delete sectionIt; PRINTF(2)("Section %s that should be containing %s not found.\n", sectionName, entryName); } else PRINTF(1)("%s not opened\n", fileName); return defaultValue; } void IniParser::setFileName(const char* fileName) { if (this->fileName) delete []this->fileName; if (fileName) { this->fileName = new char[strlen(fileName)+1]; strcpy(this->fileName, fileName); } else this->fileName = NULL; } /** * output the whole tree in a nice and easy way. */ void IniParser::debug() const { PRINTF(0)("Iniparser %s - debug\n", this->fileName); if (this->sections) { tIterator* sectionIt = this->sections->getIterator(); IniSection* sectionEnum = sectionIt->firstElement(); while (sectionEnum) { PRINTF(0)(" [%s]\n", sectionEnum->name); tIterator* entryIt = sectionEnum->entries->getIterator(); IniEntry* entryEnum = entryIt->firstElement(); while (entryEnum) { PRINTF(0)(" :%s: -> '%s'\n", entryEnum->name, entryEnum->value); entryEnum = entryIt->nextElement(); } delete entryIt; sectionEnum = sectionIt->nextElement(); } delete sectionIt; } else PRINTF(1)("%s not opened\n", fileName); }