]> Dogcows Code - chaz/yoink/blobdiff - src/stlplus/subsystems/ini_manager.cpp
testing new non-autotools build system
[chaz/yoink] / src / stlplus / subsystems / ini_manager.cpp
diff --git a/src/stlplus/subsystems/ini_manager.cpp b/src/stlplus/subsystems/ini_manager.cpp
new file mode 100644 (file)
index 0000000..41f5b69
--- /dev/null
@@ -0,0 +1,1243 @@
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004-2009\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "ini_manager.hpp"\r
+#include "file_system.hpp"\r
+#include <fstream>\r
+#include <list>\r
+#include <algorithm>\r
+#include <sys/stat.h>\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // local utilities\r
+\r
+  static std::string trim(const std::string& val)\r
+  {\r
+    std::string result = val;\r
+    while (!result.empty() && isspace(result[0]))\r
+      result.erase(result.begin());\r
+    while (!result.empty() && isspace(result[result.size()-1]))\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structure for storing a single entry (i.e. line) from an ini file\r
+  // lines are categorised as blanks, comments or variables\r
+  // TODO - do I need an error category?\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class ini_entry\r
+  {\r
+  public:\r
+    enum kind_t {BLANK, COMMENT, VARIABLE};\r
+    friend std::string to_string(kind_t kind)\r
+      {\r
+        switch(kind)\r
+        {\r
+        case BLANK: return "BLANK";\r
+        case COMMENT: return "COMMENT";\r
+        case VARIABLE: return "VARIABLE";\r
+        }\r
+        return "<*unknown kind*>";\r
+      }\r
+\r
+  private:\r
+    unsigned m_line;\r
+    kind_t m_kind;\r
+    std::string m_text;\r
+    std::string m_name;\r
+    std::string m_value;\r
+\r
+  public:\r
+    ini_entry(unsigned line) : m_line(line), m_kind(BLANK) {}\r
+    ini_entry(unsigned line, const std::string& comment) : m_line(line), m_kind(COMMENT), m_text("; " + comment) {}\r
+    ini_entry(unsigned line, const std::string& name, const std::string& value) : m_line(line), m_kind(VARIABLE), m_text(name + " = " + value), m_name(name), m_value(value) {}\r
+    ~ini_entry(void) {}\r
+\r
+    unsigned line(void) const {return m_line;}\r
+    kind_t kind(void) const {return m_kind;}\r
+    bool blank(void) const {return m_kind == BLANK;}\r
+    bool comment(void) const {return m_kind == COMMENT;}\r
+    bool variable(void) const {return m_kind == VARIABLE;}\r
+\r
+    const std::string& text(void) const {return m_text;}\r
+    const std::string& variable_name(void) const {return m_name;}\r
+    const std::string& variable_value(void) const {return m_value;}\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "    " << m_line << ":" << to_string(m_kind) << ": " << m_text << std::endl;\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structure representing an ini file section containing all the\r
+  // entries for that section from a single ini file\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class ini_section\r
+  {\r
+  private:\r
+    friend class ini_file;\r
+    std::string m_title;\r
+    std::list<ini_entry> m_entries;\r
+\r
+  public:\r
+    ini_section(const std::string& title) : \r
+      m_title(title)\r
+      {\r
+      }\r
+\r
+    ~ini_section(void)\r
+      {\r
+      }\r
+\r
+    const std::string& title(void) const\r
+      {\r
+        return m_title;\r
+      }\r
+\r
+    bool empty(void) const\r
+      {\r
+        // a section is empty if it contains no variables\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable())\r
+            return false;\r
+        }\r
+        return true;\r
+      }\r
+\r
+    void clear(void)\r
+      {\r
+        m_entries.clear();\r
+      }\r
+\r
+    bool variable_exists(const std::string variable) const\r
+      {\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+            return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    unsigned variables_size(void) const\r
+      {\r
+        unsigned result = 0;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+          if (i->variable())\r
+            result++;\r
+        return result;\r
+      }\r
+\r
+    std::string variable_name(unsigned offset) const\r
+      {\r
+        unsigned j = 0;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable())\r
+          {\r
+            if (j == offset)\r
+              return i->variable_name();\r
+            j++;\r
+          }\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    std::vector<std::string> variable_names(void) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+          if (i->variable())\r
+            result.push_back(i->variable_name());\r
+        return result;\r
+      }\r
+\r
+    std::string variable_value(unsigned offset) const\r
+      {\r
+        unsigned j = 0;\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable())\r
+          {\r
+            if (j == offset)\r
+              return i->variable_value();\r
+            j++;\r
+          }\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    std::string variable_value(const std::string variable) const\r
+      {\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+            return i->variable_value();\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    unsigned variable_line(const std::string variable) const\r
+      {\r
+        for (std::list<ini_entry>::const_iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+            return i->line();\r
+        }\r
+        return 0;\r
+      }\r
+\r
+    bool add_variable(unsigned line, const std::string& variable, const std::string& value)\r
+      {\r
+        erase_variable(variable);\r
+        m_entries.push_back(ini_entry(line ? line : m_entries.size(), variable, value));\r
+        return true;\r
+      }\r
+\r
+    bool add_comment(unsigned line, const std::string& comment)\r
+      {\r
+        m_entries.push_back(ini_entry(line ? line : m_entries.size(), comment));\r
+        return true;\r
+      }\r
+\r
+    bool add_blank(unsigned line)\r
+      {\r
+        m_entries.push_back(ini_entry(line ? line : m_entries.size()));\r
+        return true;\r
+      }\r
+\r
+    bool erase_variable(const std::string& variable)\r
+      {\r
+        for (std::list<ini_entry>::iterator i = m_entries.begin(); i != m_entries.end(); i++)\r
+        {\r
+          if (i->variable() && i->variable_name() == variable)\r
+          {\r
+            m_entries.erase(i);\r
+            return true;\r
+          }\r
+        }\r
+        return false;\r
+      }\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "  [" << m_title << "]" << std::endl;\r
+        for (std::list<ini_entry>::const_iterator entry = m_entries.begin(); entry != m_entries.end(); entry++)\r
+          entry->print(str);\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // internal data structure representing a single ini file\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  class ini_file\r
+  {\r
+  private:\r
+    friend class ini_section;\r
+    std::string m_filename;\r
+    bool m_changed;\r
+    bool m_writable;\r
+    std::list<ini_section> m_sections;\r
+\r
+    std::list<ini_section>::const_iterator find_section(const std::string& section) const\r
+      {\r
+        for (std::list<ini_section>::const_iterator i = m_sections.begin(); i != m_sections.end(); i++)\r
+        {\r
+          if (i->title() == section)\r
+            return i;\r
+        }\r
+        return m_sections.end();\r
+      }\r
+\r
+    std::list<ini_section>::iterator find_section(const std::string& section)\r
+      {\r
+        for (std::list<ini_section>::iterator i = m_sections.begin(); i != m_sections.end(); i++)\r
+        {\r
+          if (i->title() == section)\r
+            return i;\r
+        }\r
+        return m_sections.end();\r
+      }\r
+\r
+  public:\r
+\r
+    ini_file(void) : m_changed(false), m_writable(false)\r
+      {\r
+      }\r
+\r
+    ini_file(const std::string& filename) : m_changed(false), m_writable(false)\r
+      {\r
+        read_file(filename);\r
+      }\r
+\r
+    ~ini_file(void)\r
+      {\r
+        if (writable())\r
+          save_file();\r
+      }\r
+\r
+    bool initialised(void) const\r
+      {\r
+        return !m_filename.empty();\r
+      }\r
+\r
+    bool writable(void) const\r
+      {\r
+        return m_writable;\r
+      }\r
+\r
+    bool read_file(const std::string& filename)\r
+      {\r
+        m_filename = filename;\r
+        m_changed = false;\r
+        // file may not yet exist - possible to create an Ini file using this class\r
+        // so it is writable if the file exists and is writable or if the folder is writable\r
+        m_writable = file_writable(m_filename);\r
+        if (!file_exists(m_filename))\r
+          return true;\r
+        // create a dummy top section to hold file comments\r
+        std::list<ini_section>::iterator section = m_sections.insert(m_sections.end(), ini_section(""));\r
+        std::ifstream source(m_filename.c_str());\r
+        unsigned line_number = 0;\r
+        std::string line;\r
+        while(std::getline(source,line))\r
+        {\r
+          line_number++;\r
+          unsigned i = 0;\r
+          while(i < line.size() && isspace(line[i]))\r
+            i++;\r
+          if (i < line.size() && line[i] == '[')\r
+          {\r
+            // found a section title\r
+            // skip the [\r
+            i++;\r
+            // get the text up to the end ] or the end of the line\r
+            std::string title;\r
+            while (i < line.size() && line[i] != ']')\r
+              title += line[i++];\r
+            // create a new section and make it the current section\r
+            section = m_sections.insert(m_sections.end(), ini_section(title));\r
+          }\r
+          else if (i < line.size() && line[i] == ';')\r
+          {\r
+            // found a comment\r
+            // skip the ; because that is not part of the comment\r
+            i++;\r
+            // add the rest of the line as a comment to the current section\r
+            section->add_comment(line_number, line.substr(i, line.size()-1));\r
+          }\r
+          else if (i == line.size())\r
+          {\r
+            // found a blank line\r
+            section->add_blank(line_number);\r
+          }\r
+          else\r
+          {\r
+            // found a *possible* variable - now scan forwards for the = operator\r
+            std::string name;\r
+            while (i < line.size() && line[i] != '=')\r
+              name += line[i++];\r
+            // skip the = sign\r
+            // TODO - detect a missing = sign here and convert to an error\r
+            if (i < line.size())\r
+              i++;\r
+            // trim trailing whitespace off the name\r
+            name = trim(name);\r
+            // now get the value, minus any leading whitespace\r
+            std::string value;\r
+            while(i < line.size() && isspace(line[i]))\r
+              i++;\r
+            while (i < line.size())\r
+              value += line[i++];\r
+            // trim trailing whitespace off the value\r
+            value = trim(value);\r
+            // and finally add the name/value pair to the data structure\r
+            section->add_variable(line_number, name, value);\r
+          }\r
+        }\r
+        return true;\r
+      }\r
+\r
+    bool save_file(void)\r
+      {\r
+        if (!initialised()) return false;\r
+        if (!m_changed) return true;\r
+        if (!m_writable) return false;\r
+        std::ofstream output(m_filename.c_str());\r
+        for (std::list<ini_section>::iterator section = m_sections.begin(); section != m_sections.end(); section++)\r
+        {\r
+          if (!(section->title().empty()))\r
+            output << "[" << section->title() << "]" << std::endl;\r
+          for (std::list<ini_entry>::iterator entry = section->m_entries.begin(); entry != section->m_entries.end(); entry++)\r
+            output << entry->text() << std::endl;\r
+        }\r
+        m_changed = false;\r
+        return true;\r
+      }\r
+\r
+    std::string filename(void) const\r
+      {\r
+        return m_filename;\r
+      }\r
+\r
+    bool empty(void) const\r
+      {\r
+        // file is empty if it has either no sections or an empty header section\r
+        if (m_sections.empty()) return true;\r
+        if ((m_sections.begin() == --m_sections.end()) && m_sections.begin()->empty()) return true;\r
+        return false;\r
+      }\r
+\r
+    bool section_exists(const std::string& title) const\r
+      {\r
+        return find_section(title) != m_sections.end();\r
+      }\r
+\r
+    bool add_section(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        m_sections.push_back(ini_section(section));\r
+        m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool empty_section(const std::string& section)\r
+      {\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        return found->empty();\r
+      }\r
+\r
+    bool erase_section(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        m_sections.erase(found);\r
+        m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool clear_section(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        found->clear();\r
+        m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    unsigned sections_size(void) const\r
+      {\r
+        return m_sections.size();\r
+      }\r
+\r
+    const std::string& section_name(unsigned i) const\r
+      {\r
+        std::list<ini_section>::const_iterator result = m_sections.begin();\r
+        for (unsigned j = 1; j <= i; j++)\r
+          result++;\r
+        return result->title();\r
+      }\r
+\r
+    std::vector<std::string> section_names(void) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (unsigned j = 0; j < sections_size(); j++)\r
+          result.push_back(section_name(j));\r
+        return result;\r
+      }\r
+\r
+    bool variable_exists(const std::string& section, const std::string variable) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        return found->variable_exists(variable);\r
+      }\r
+\r
+    std::vector<std::string> variable_names(const std::string& section) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::vector<std::string>();\r
+        return found->variable_names();\r
+      }\r
+\r
+    unsigned variables_size(const std::string& section) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return 0;\r
+        return found->variables_size();\r
+      }\r
+\r
+    unsigned variable_line(const std::string& section, const std::string variable) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return 0;\r
+        return found->variable_line(variable);\r
+      }\r
+\r
+    std::string variable_name(const std::string& section, unsigned i) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::string();\r
+        return found->variable_name(i);\r
+      }\r
+\r
+    std::string variable_value(const std::string& section, unsigned i) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::string();\r
+        return found->variable_value(i);\r
+      }\r
+\r
+    std::string variable_value(const std::string& section, const std::string variable) const\r
+      {\r
+        std::list<ini_section>::const_iterator found = find_section(section);\r
+        if (found == m_sections.end()) return std::string();\r
+        return found->variable_value(variable);\r
+      }\r
+\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::string& value)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section));\r
+        if (found->add_variable(0,variable,value))\r
+          m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool add_comment(const std::string& section, const std::string& comment)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section));\r
+        if (found->add_comment(0,comment))\r
+          m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool add_blank(const std::string& section)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section));\r
+        if (found->add_blank(0))\r
+          m_changed = true;\r
+        return true;\r
+      }\r
+\r
+    bool erase_variable(const std::string& section, const std::string& variable)\r
+      {\r
+        if (!m_writable) return false;\r
+        std::list<ini_section>::iterator found = find_section(section);\r
+        if (found == m_sections.end()) return false;\r
+        if (found->erase_variable(variable))\r
+        {\r
+          m_changed = true;\r
+          return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "file: " << m_filename << std::endl;\r
+        for (std::list<ini_section>::const_iterator section = m_sections.begin(); section != m_sections.end(); section++)\r
+          section->print(str);\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // body data structure contains all the data and is pointed-to by exported data structure\r
+\r
+  class ini_manager_body\r
+  {\r
+  private:\r
+    std::vector<ini_file> m_files;\r
+    unsigned m_count;\r
+\r
+    ini_manager_body(const ini_manager_body&);\r
+    ini_manager_body& operator= (const ini_manager_body&);\r
+\r
+  public:\r
+\r
+    ini_manager_body(void) : m_count(1)\r
+      {\r
+      }\r
+\r
+    ~ini_manager_body(void)\r
+      {\r
+        save();\r
+      }\r
+\r
+    void increment(void)\r
+      {\r
+        ++m_count;\r
+      }\r
+\r
+    bool decrement(void)\r
+      {\r
+        --m_count;\r
+        return m_count == 0;\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // file management\r
+\r
+    // add files starting with the most local file (e.g. the current project) which has depth 0\r
+    // and working back to the most global (e.g. the installation settings) which has a depth of size()-1\r
+    // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice.\r
+    // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did\r
+    // not exist, only read errors cause a failure)\r
+    bool add_file(const std::string& filename)\r
+      {\r
+        // if this file has been loaded, don't load it again\r
+        // this is not an error\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (filespec_to_path(filename) == filespec_to_path(m_files[i].filename()))\r
+            return true;\r
+        }\r
+        // add a new file to the back of the list and then load it\r
+        // I do it in two steps rather than passing the filename to the constructor in order to return the load status\r
+        m_files.push_back(ini_file());\r
+        return m_files.back().read_file(filename);\r
+      }\r
+\r
+    // as above, returns false if *none* of the files were added\r
+    // filenames[0] is the local file, and so on\r
+    bool add_files(const std::vector<std::string>& filenames)\r
+      {\r
+        bool result = true;\r
+        for (unsigned i = 0; i < filenames.size(); i++)\r
+          result &= add_file(filenames[i]);\r
+        return result;\r
+      }\r
+\r
+    // saves modified ini files - returns true if all modified files were written successfully\r
+    bool save(void)\r
+      {\r
+        bool result = true;\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+          result &= m_files[i].save_file();\r
+        return result;\r
+      }\r
+\r
+    // get the number of files being managed\r
+    unsigned size(void) const\r
+      {\r
+        return m_files.size();\r
+      }\r
+\r
+    // get the ini filename associated with a depth\r
+    std::string filename(unsigned depth) const\r
+      {\r
+        return m_files[depth].filename();\r
+      }\r
+\r
+    // test whether a file in the ini manager is writable\r
+    bool writable(unsigned depth) const\r
+      {\r
+        return m_files[depth].writable();\r
+      }\r
+\r
+    // test whether a file is empty\r
+    // An ini file is considered empty if it has no named sections and the header is empty or missing\r
+    bool empty(unsigned depth) const\r
+      {\r
+        return m_files[depth].empty();\r
+      }\r
+\r
+    // erase the ini file from the ini manager and from the disk\r
+    bool erase(unsigned depth)\r
+      {\r
+        std::string file = filename(depth);\r
+        remove(depth);\r
+        return file_delete(file);\r
+      }\r
+\r
+    // remove the file from the ini manager but do not erase it from the disk\r
+    bool remove(unsigned depth)\r
+      {\r
+        if (m_files[depth].writable())\r
+          m_files[depth].save_file();\r
+        m_files.erase(m_files.begin() + depth);\r
+        return true;\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // section management\r
+\r
+    // returns the union of all section names in all of the ini files\r
+    std::vector<std::string> section_names(void) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          std::vector<std::string> file_result = section_names(i);\r
+          for (unsigned j = 0; j < file_result.size(); j++)\r
+          {\r
+            if (std::find(result.begin(), result.end(), file_result[j]) == result.end())\r
+              result.push_back(file_result[j]);\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    // returns the section names in one of the ini files\r
+    std::vector<std::string> section_names(unsigned depth) const\r
+      {\r
+        return m_files[depth].section_names();\r
+      }\r
+\r
+    // tests whether a section is found in any of the ini files\r
+    bool section_exists(const std::string& title) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (m_files[i].section_exists(title))\r
+            return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    // tests whether the section is found in the specific ini file\r
+    bool section_exists(const std::string& title, unsigned depth) const\r
+      {\r
+        return m_files[depth].section_exists(title);\r
+      }\r
+\r
+    // adds a section to the specified ini file - does nothing if it is already present\r
+    bool add_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].add_section(section);\r
+      }\r
+\r
+    // test whether a section is empty\r
+    bool empty_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].empty_section(section);\r
+      }\r
+\r
+    // removes a section from the specified ini file if it exists there but cannot remove it from any other file\r
+    bool erase_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].erase_section(section);\r
+      }\r
+\r
+    // removes all the contents of a section from the specified ini file but keeps the empty section\r
+    bool clear_section(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].clear_section(section);\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // variable management\r
+\r
+    // test whether a variable exists in any of the ini files\r
+    bool variable_exists(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return true;\r
+        }\r
+        return false;\r
+      }\r
+\r
+    // test whether a variable exists in specified ini file\r
+    bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const\r
+      {\r
+        return m_files[depth].variable_exists(section, variable);\r
+      }\r
+\r
+    // get the union of all variables declared in all ini files\r
+    std::vector<std::string> variable_names(const std::string& section) const\r
+      {\r
+        std::vector<std::string> result;\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          std::vector<std::string> file_result = variable_names(section, i);\r
+          for (unsigned j = 0; j < file_result.size(); j++)\r
+          {\r
+            if (std::find(result.begin(), result.end(), file_result[j]) == result.end())\r
+              result.push_back(file_result[j]);\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    // get the set of all varaibale names from one file\r
+    std::vector<std::string> variable_names(const std::string& section, unsigned depth) const\r
+      {\r
+        return m_files[depth].variable_names(section);\r
+      }\r
+\r
+    // get the depth of the first ini file to define a variable\r
+    // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist\r
+    unsigned variable_depth(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return i;\r
+        }\r
+        return (unsigned)-1;\r
+      }\r
+\r
+    // get the filename that first defines the variable\r
+    std::string variable_filename(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return filename(i);\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    unsigned variable_linenumber(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return m_files[i].variable_line(section,variable);\r
+        }\r
+        return 0;\r
+      }\r
+\r
+    // get the value of a variable as a single unprocessed string\r
+    // if the variable does not exist the string will be empty, but beware that\r
+    // you also get an empty string if a variable exists but has no value\r
+    // you can differentiate between the two cases by using variable_exists_all above\r
+    std::string variable_value(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return variable_value(section, variable, i);\r
+        }\r
+        return std::string();\r
+      }\r
+\r
+    // get the value from the specified file\r
+    std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const\r
+      {\r
+        return m_files[depth].variable_value(section, variable);\r
+      }\r
+\r
+    // get the value of a variable as a processed string\r
+    // processing splits the value at commas and furthermore supports quoted\r
+    // strings (so that values can contain commas for example)\r
+    // quoted strings are dequoted before they are added to the result\r
+    // the result is a vector of dequoted strings, one per value in the comma-separated list\r
+    std::vector<std::string> variable_values(const std::string& section, const std::string variable) const\r
+      {\r
+        for (unsigned i = 0; i < m_files.size(); i++)\r
+        {\r
+          if (variable_exists(section, variable, i))\r
+            return variable_values(section, variable, i);\r
+        }\r
+        return std::vector<std::string>();\r
+      }\r
+\r
+    // get the processed variable from the specified file\r
+    std::vector<std::string> variable_values(const std::string& section, const std::string variable, unsigned depth) const\r
+      {\r
+        // get the unprocessed value and then do the processing into processed separate values\r
+        std::string value = variable_value(section, variable, depth);\r
+        std::vector<std::string> result;\r
+        if (!value.empty())\r
+        {\r
+          result.push_back(std::string());\r
+          unsigned i = 0;\r
+          // loop which is repeated for each element in the comma-separated list\r
+          while(i < value.size())\r
+          {\r
+            // skip leading whitespace\r
+            while (i < value.size() && isspace(value[i])) i++;\r
+            // get the value - this means all text up to the next comma or end of line\r
+            // also allow escaped characters\r
+            while (i < value.size())\r
+            {\r
+              if (value[i] == ',') break;\r
+              if (value[i] == '\\')\r
+              {\r
+                // found an escaped character - de-escape it by only getting the next character\r
+                // beware of an escape character at the end of the line which just gets ignored\r
+                i++;\r
+                if (i < value.size()) result.back() += value[i++];\r
+              }\r
+              else if (value[i] == '"')\r
+              {\r
+                // get a quoted substring\r
+                // first skip the opening quote\r
+                i++;\r
+                // keep getting characters until a close-quote, but allow the quote character to be escaped by itself\r
+                while (i < value.size())\r
+                {\r
+                  if (value[i] == '"')\r
+                  {\r
+                    // found a quote skip it\r
+                    i++;\r
+                    // now establish whether its an escaped quote\r
+                    // if it is, keep it, but de-escape it by only getting the next character\r
+                    // it it isn't, break out and continue processing the value as a non-quoted string\r
+                    if (i < value.size() && value[i] != '"') break;\r
+                    if (i < value.size()) result.back() += value[i++];\r
+                  }\r
+                  else\r
+                  {\r
+                    // non-quote and non-escape so just get the character\r
+                    result.back() += value[i++];\r
+                  }\r
+                }\r
+              }\r
+              else\r
+              {\r
+                // non-escape so just get the character\r
+                result.back() += value[i++];\r
+              }\r
+            }\r
+            // trim trailing whitespace off the value\r
+            while (!result.back().empty() && isspace(result.back()[result.back().size()-1])) result.back().erase(result.back().size()-1,1);\r
+            // now check for whether there is a comma or end of line\r
+            if (i <= value.size() && value[i] == ',')\r
+            {\r
+              // skip the comma and add a new string to the result ready for the next iteration\r
+              i++;\r
+              result.push_back(std::string());\r
+            }\r
+            else\r
+            {\r
+              // end of line found so break out\r
+              break;\r
+            }\r
+          }\r
+        }\r
+        return result;\r
+      }\r
+\r
+    // add a variable to the specified file\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth)\r
+      {\r
+        return m_files[depth].add_variable(section, variable, value);\r
+      }\r
+\r
+    // add a variable as a processed string\r
+    // processing means that the values in the string vector are converted into a comma-separated list\r
+    // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself\r
+    bool add_variable(const std::string& section, const std::string& variable, const std::vector<std::string>& values, unsigned depth)\r
+      {\r
+        // convert the values vector into a comma-separated string with each value escaped so that special characters do not confuse the reader\r
+        // the characters escaped are: '\', ',', '"'\r
+        std::string value;\r
+        for (unsigned v = 0; v < values.size(); v++)\r
+        {\r
+          const std::string& element = values[v];\r
+          // add the comma between values === add a comma before all but the first value\r
+          if (v > 0) value += ',';\r
+          for (unsigned i = 0; i < element.size(); i++)\r
+          {\r
+            // add a character at a time, escaping special characters\r
+            if (element[i] == '"' || element[i] == ',' || element[i] == '\\')\r
+            {\r
+              // escape the character\r
+              value += '\\';\r
+            }\r
+            value += element[i];\r
+          }\r
+        }\r
+        return add_variable(section, variable, value, depth);\r
+      }\r
+\r
+    // erase a variable from the specified file\r
+    // this does not remove the variable from other ini files, so the variable may still exist\r
+    // to mask a global variable, set the variable to an empty string instead\r
+    bool erase_variable(const std::string& section, const std::string& variable, unsigned depth)\r
+      {\r
+        return m_files[depth].erase_variable(section, variable);\r
+      }\r
+\r
+    //////////////////////////////////////////////////////////////////////////////\r
+    // sundry line-entry management\r
+\r
+    // add a comment to the specified ini file\r
+    bool add_comment(const std::string& section, const std::string& comment, unsigned depth)\r
+      {\r
+        return m_files[depth].add_comment(section, comment);\r
+      }\r
+\r
+    // add a blank line to the specified ini file\r
+    bool add_blank(const std::string& section, unsigned depth)\r
+      {\r
+        return m_files[depth].add_blank(section);\r
+      }\r
+\r
+    bool print(std::ostream& str) const\r
+      {\r
+        str << "----------------------------------------" << std::endl;\r
+        for (unsigned depth = 0; depth < m_files.size(); depth++)\r
+        {\r
+          m_files[depth].print(str);\r
+          str << "----------------------------------------" << std::endl;\r
+        }\r
+        return !str.fail();\r
+      }\r
+  };\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // exported data structure representing the set of all ini files and providing\r
+  // the access functions exported by the class\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // constructors/destructors\r
+\r
+  ini_manager::ini_manager(void) : m_body(new ini_manager_body)\r
+  {\r
+  }\r
+\r
+  ini_manager::ini_manager(const std::vector<std::string>& filenames) : m_body(new ini_manager_body)\r
+  {\r
+    add_files(filenames);\r
+  }\r
+\r
+  ini_manager::ini_manager(const ini_manager& manager) : m_body(0)\r
+  {\r
+    m_body = manager.m_body;\r
+    m_body->increment();\r
+  }\r
+\r
+  ini_manager& ini_manager::operator= (const ini_manager& manager)\r
+  {\r
+    if (m_body == manager.m_body) return *this;\r
+    if (m_body->decrement())\r
+      delete m_body;\r
+    m_body = manager.m_body;\r
+    m_body->increment();\r
+    return *this;\r
+  }\r
+\r
+  ini_manager::~ini_manager(void)\r
+  {\r
+    if (m_body->decrement())\r
+      delete m_body;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // file management\r
+\r
+  bool ini_manager::add_file(const std::string& filename)\r
+  {\r
+    return m_body->add_file(filename);\r
+  }\r
+\r
+  bool ini_manager::add_files(const std::vector<std::string>& filenames)\r
+  {\r
+    return m_body->add_files(filenames);\r
+  }\r
+\r
+  bool ini_manager::save(void)\r
+  {\r
+    return m_body->save();\r
+  }\r
+\r
+  unsigned ini_manager::size(void) const\r
+  {\r
+    return m_body->size();\r
+  }\r
+\r
+  std::string ini_manager::filename(unsigned depth) const\r
+  {\r
+    return m_body->filename(depth);\r
+  }\r
+\r
+  bool ini_manager::writable(unsigned depth) const\r
+  {\r
+    return m_body->writable(depth);\r
+  }\r
+\r
+  bool ini_manager::empty(unsigned depth) const\r
+  {\r
+    return m_body->empty(depth);\r
+  }\r
+\r
+  bool ini_manager::erase(unsigned depth)\r
+  {\r
+    return m_body->erase(depth);\r
+  }\r
+\r
+  bool ini_manager::remove(unsigned depth)\r
+  {\r
+    return m_body->remove(depth);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // section management\r
+\r
+  std::vector<std::string> ini_manager::section_names(void) const\r
+  {\r
+    return m_body->section_names();\r
+  }\r
+\r
+  std::vector<std::string> ini_manager::section_names(unsigned depth) const\r
+  {\r
+    return m_body->section_names(depth);\r
+  }\r
+\r
+  bool ini_manager::section_exists(const std::string& section) const\r
+  {\r
+    return m_body->section_exists(section);\r
+  }\r
+\r
+  bool ini_manager::section_exists(const std::string& section, unsigned depth) const\r
+  {\r
+    return m_body->section_exists(section, depth);\r
+  }\r
+\r
+  bool ini_manager::add_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->add_section(section, depth);  \r
+  }\r
+\r
+  bool ini_manager::empty_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->empty_section(section, depth);  \r
+  }\r
+\r
+  bool ini_manager::erase_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->erase_section(section, depth);  \r
+  }\r
+\r
+  bool ini_manager::clear_section(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->clear_section(section, depth);  \r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // variable management\r
+\r
+  bool ini_manager::variable_exists(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_exists(section, variable);  \r
+  }\r
+\r
+  bool ini_manager::variable_exists(const std::string& section, const std::string variable, unsigned depth) const\r
+  {\r
+    return m_body->variable_exists(section, variable, depth);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_names(const std::string& section) const\r
+  {\r
+    return m_body->variable_names(section);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_names(const std::string& section, unsigned depth) const\r
+  {\r
+    return m_body->variable_names(section, depth);  \r
+  }\r
+\r
+  unsigned ini_manager::variable_depth(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_depth(section, variable);  \r
+  }\r
+\r
+  std::string ini_manager::variable_filename(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_filename(section, variable);  \r
+  }\r
+\r
+  unsigned ini_manager::variable_linenumber(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_linenumber(section, variable);  \r
+  }\r
+\r
+  std::string ini_manager::variable_value(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_value(section, variable);  \r
+  }\r
+\r
+  std::string ini_manager::variable_value(const std::string& section, const std::string variable, unsigned depth) const\r
+  {\r
+    return m_body->variable_value(section, variable, depth);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_values(const std::string& section, const std::string variable) const\r
+  {\r
+    return m_body->variable_values(section, variable);  \r
+  }\r
+\r
+  std::vector<std::string> ini_manager::variable_values(const std::string& section, const std::string variable, unsigned depth) const\r
+  {\r
+    return m_body->variable_values(section, variable, depth);  \r
+  }\r
+\r
+  bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth)\r
+  {\r
+    return m_body->add_variable(section, variable, value, depth);\r
+  }\r
+\r
+  bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::vector<std::string>& values, unsigned depth)\r
+  {\r
+    return m_body->add_variable(section, variable, values, depth);\r
+  }\r
+\r
+  bool ini_manager::erase_variable(const std::string& section, const std::string& variable, unsigned depth)\r
+  {\r
+    return m_body->erase_variable(section, variable, depth);\r
+  }\r
+\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // sundry entries\r
+\r
+  bool ini_manager::add_comment(const std::string& section, const std::string& comment, unsigned depth)\r
+  {\r
+    return m_body->add_comment(section, comment, depth);\r
+  }\r
+\r
+  bool ini_manager::add_blank(const std::string& section, unsigned depth)\r
+  {\r
+    return m_body->add_blank(section, depth);\r
+  }\r
+\r
+  bool ini_manager::print(std::ostream& str) const\r
+  {\r
+    return m_body->print(str);\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // diagnostic print\r
+\r
+  std::ostream& operator << (std::ostream& str, const ini_manager& manager)\r
+  {\r
+    manager.print(str);\r
+    return str;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
This page took 0.048858 seconds and 4 git commands to generate.