diff --git a/c++utilities.pro b/c++utilities.pro index bce7e3f..cce8a5c 100644 --- a/c++utilities.pro +++ b/c++utilities.pro @@ -29,7 +29,8 @@ SOURCES += \ io/ansiescapecodes.cpp \ misc/random.cpp \ io/bitreader.cpp \ - application/commandlineutils.cpp + application/commandlineutils.cpp \ + io/inifile.cpp HEADERS += \ application/global.h \ @@ -54,7 +55,8 @@ HEADERS += \ misc/memory.h \ misc/random.h \ io/bitreader.h \ - application/commandlineutils.h + application/commandlineutils.h \ + io/inifile.h OTHER_FILES += \ README.md \ diff --git a/io/inifile.cpp b/io/inifile.cpp new file mode 100644 index 0000000..09574ae --- /dev/null +++ b/io/inifile.cpp @@ -0,0 +1,155 @@ +#include "inifile.h" + +#include + +using namespace std; + +namespace IoUtilities { + +/*! + * \brief Parses all data from the specified \a inputStream. + */ +void IniFile::parse(std::istream &inputStream) +{ + // define variable for state machine + enum State {Init, Comment, ScopeName, Key, Value} state = Init; + // current character + char c; + // number of postponed whitespaces + unsigned int whitespace = 0; + // current scope, key and value + string scope, key, value; + scope.reserve(16); + key.reserve(16); + value.reserve(256); + // define actions for state machine + // called when key/value pair is complete + auto finishKeyValue = [&scope, &key, &value, &whitespace, this] { + m_data[scope].insert(make_pair(key, value)); + key.clear(); + value.clear(); + whitespace = 0; + }; + // called to add current character to current key or value + auto addChar = [&whitespace, &c] (string &to) { + if(c == ' ') { + ++whitespace; + } else { + if(!to.empty()) { + while(whitespace) { + to += ' '; + --whitespace; + } + } else { + whitespace = 0; + } + to += c; + } + }; + // thorw an exception when an IO error occurs + inputStream.exceptions(ios_base::failbit | ios_base::badbit); + // parse the file char by char + try { + while(inputStream.get(c)) { + switch(state) { + case Init: + switch(c) { + case '\n': + break; + case '#': + state = Comment; + break; + case '=': + whitespace = 0; + state = Value; + break; + case '[': + scope.clear(); + state = ScopeName; + break; + default: + addChar(key); + state = Key; + } + break; + case Key: + switch(c) { + case '\n': + finishKeyValue(); + state = Init; + break; + case '#': + finishKeyValue(); + state = Comment; + break; + case '=': + whitespace = 0; + state = Value; + break; + default: + addChar(key); + } + break; + case Comment: + switch(c) { + case '\n': + state = Init; + break; + default: + ; + } + break; + case ScopeName: + switch(c) { + case ']': + state = Init; + break; + default: + scope += c; + } + break; + case Value: + switch(c) { + case '\n': + finishKeyValue(); + state = Init; + break; + case '#': + finishKeyValue(); + state = Comment; + break; + default: + addChar(value); + } + break; + } + } + } catch (const ios_base::failure &) { + if(inputStream.eof()) { + // we just reached the end of the file + // don't forget to save the last key/value pair + finishKeyValue(); + } else { + throw; + } + } +} + +/*! + * \brief Write the current data to the specified \a outputStream. + */ +void IniFile::make(ostream &outputStream) +{ + // thorw an exception when an IO error occurs + outputStream.exceptions(ios_base::failbit | ios_base::badbit); + for(const auto &scope : m_data) { + outputStream << '[' << scope.first << ']' << '\n'; + for(const auto &field : scope.second) { + outputStream << field.first << '=' << field.second << '\n'; + } + outputStream << '\n'; + } +} + +} // namespace IoUtilities + diff --git a/io/inifile.h b/io/inifile.h new file mode 100644 index 0000000..5c438b1 --- /dev/null +++ b/io/inifile.h @@ -0,0 +1,55 @@ +#ifndef IOUTILITIES_INIFILE_H +#define IOUTILITIES_INIFILE_H + +#include "../application/global.h" + +#include + +namespace IoUtilities { + +class LIB_EXPORT IniFile +{ +public: + IniFile(); + + std::map > &data(); + const std::map > &data() const; + void parse(std::istream &inputStream); + void make(std::ostream &outputStream); + +private: + std::map > m_data; +}; + +/*! + * \brief Constructs an empty ini file. + */ +inline IniFile::IniFile() +{} + +/*! + * \brief Returns the data of the file. + * + * - The keys in the returned map represent the [scope name]s. + * - The values in the returned map are maps representing "key = value"-pairs within the scope. + * - The data might be modified an saved using the make() method. + */ +inline std::map > &IniFile::data() +{ + return m_data; +} + +/*! + * \brief Returns the data of the file. + * + * - The keys in the returned map represent the [scope name]s. + * - The values in the returned map are maps representing "key = value"-pairs within the scope. + */ +inline const std::map > &IniFile::data() const +{ + return m_data; +} + +} // namespace IoUtilities + +#endif // IOUTILITIES_INIFILE_H