# subsystems/ini_manager.hpp

    #ifndef STLPLUS_INI_MANAGER
    #define STLPLUS_INI_MANAGER
    ////////////////////////////////////////////////////////////////////////////////

    //   Author:    Andy Rushton
    //   Copyright: (c) Southampton University 1999-2004
    //              (c) Andy Rushton           2004 onwards
    //   License:   BSD License, see ../docs/license.html

    //   A subsystem for managing INI (i.e. .ini) files
    //   An INI file has the following format

    //     file           ::= header { section }*
    //     header         ::= { comment | blank }*
    //     section        ::= section_header { declaration | comment | blank }*
    //     section_header ::= '[' title ']' '\n'
    //     declaration    ::= variable '=' value '\n'
    //     comment        ::= ';' text '\n'
    //     blank          ::= '\n'
    //     title          ::= [~']']*
    //     variable       ::= [~'=']*
    //     value          ::= .*
    //     text           ::= .*

    //   Whitespace is trimmed from the leading and trailing ends of title, variable and value
    //   Note: a header is represented internally as a Clint section (i.e. a section with no name)

    ////////////////////////////////////////////////////////////////////////////////
    #include "subsystems_fixes.hpp"
    #include <vector>
    #include <string>
    #include <iostream>

    namespace stlplus
    {

      ////////////////////////////////////////////////////////////////////////////////
      // Internals

      class ini_manager_body;

      ////////////////////////////////////////////////////////////////////////////////
      // Ini-file manager class

      class ini_manager
      {
      public:

        ini_manager(void);

        explicit ini_manager(const std::vector<std::string>& filenames);

        ini_manager(const ini_manager&);
        ini_manager& operator= (const ini_manager&);

        ~ini_manager(void);

        //////////////////////////////////////////////////////////////////////////////
        // file management

        // add files starting with the most local file (e.g. the current project) which has depth 0
        // and working back to the most global (e.g. the installation settings) which has a depth of size()-1
        // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice.
        // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did
        // not exist, only read errors cause a failure)
        bool add_file(const std::string& filename);

        // as above, returns false if *none* of the files were added
        // filenames[0] is the local file, and so on
        bool add_files(const std::vector<std::string>& filenames);

        // saves modified ini files - returns true if all modified files were written successfully
        bool save(void);

        // get the number of files being managed
        unsigned size(void) const;

        // get the ini filename associated with a depth
        std::string filename(unsigned depth = 0) const;

        // test whether a file in the ini manager is writable
        bool writable(unsigned depth = 0) const;

        // test whether a file is empty
        // An ini file is considered empty if it has no named sections and the header is empty or missing
        bool empty(unsigned depth = 0) const;

        // erase the ini file from the ini manager and from the disk
        bool erase(unsigned depth = 0);

        // remove the file from the ini manager but do not erase it from the disk
        bool remove(unsigned depth = 0);

        //////////////////////////////////////////////////////////////////////////////
        // section management

        // returns the union of all section names in all of the ini files
        std::vector<std::string> section_names(void) const;

        // returns the section names in one of the ini files
        std::vector<std::string> section_names(unsigned depth) const;

        // tests whether a section is found in any of the ini files
        bool section_exists(const std::string& title) const;

        // tests whether the section is found in the specific ini file
        bool section_exists(const std::string& title, unsigned depth) const;

        // adds a section to the specified ini file - does nothing if it is already present
        bool add_section(const std::string& section, unsigned depth = 0);

        // test whether a section is empty
        bool empty_section(const std::string& section, unsigned depth = 0);

        // removes a section from the specified ini file if it exists there but cannot remove it from any other file
        bool erase_section(const std::string& section, unsigned depth = 0);

        // removes all the contents of a section from the specified ini file but keeps the empty section
        bool clear_section(const std::string& section, unsigned depth = 0);

        //////////////////////////////////////////////////////////////////////////////
        // variable management

        // test whether a variable exists in any of the ini files
        bool variable_exists(const std::string& section, const std::string variable) const;

        // test whether a variable exists in specified ini file
        bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const;

        // get the union of all variables declared in all ini files
        std::vector<std::string> variable_names(const std::string& section) const;

        // get the set of all varaibale names from one file
        std::vector<std::string> variable_names(const std::string& section, unsigned depth) const;

        // get the depth of the first ini file to define a variable
        // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist
        unsigned variable_depth(const std::string& section, const std::string variable) const;

        // get the filename that first defines the variable
        std::string variable_filename(const std::string& section, const std::string variable) const;
        // ditto for its linenumber within that file
        unsigned variable_linenumber(const std::string& section, const std::string variable) const;

        // get the value of a variable as a single unprocessed string
        // if the variable does not exist the string will be empty, but beware that
        // you also get an empty string if a variable exists but has no value
        // you can differentiate between the two cases by using variable_exists_all above
        std::string variable_value(const std::string& section, const std::string variable) const;

        // get the value from the specified file
        std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const;

        // get the value of a variable as a processed string
        // processing splits the value at commas and furthermore supports quoted strings (so that values can contain commas for example)
        // quoted strings are dequoted before they are added to the result
        // the result is a vector of dequoted strings, one per value in the comma-separated list
        std::vector<std::string> variable_values(const std::string& section, const std::string variable) const;

        // get the processed variable from the specified file
        std::vector<std::string> variable_values(const std::string& section, const std::string variable, unsigned depth) const;

        // add a variable to the specified file
        bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth = 0);

        // add a variable as a processed string
        // processing means that the values in the string vector are converted into a comma-separated list
        // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself
        bool add_variable(const std::string& section, const std::string& variable, const std::vector<std::string>& values, unsigned depth = 0);

        // erase a variable from the specified file
        // this does not remove the variable from other ini files, so the variable may still exist
        // to mask a global variable, set the variable to an empty string instead
        bool erase_variable(const std::string& section, const std::string& variable, unsigned depth = 0);

        //////////////////////////////////////////////////////////////////////////////
        // sundry line-entry management

        // add a comment to the specified ini file
        bool add_comment(const std::string& section, const std::string& comment, unsigned depth = 0);

        // add a blank line to the specified ini file
        bool add_blank(const std::string& section, unsigned depth = 0);

        bool print(std::ostream&) const;

      private:
        friend class ini_manager_body;
        ini_manager_body* m_body;
      };

      ////////////////////////////////////////////////////////////////////////////////
      // diagnostic print routine

      std::ostream& operator << (std::ostream&, const ini_manager&);

      ////////////////////////////////////////////////////////////////////////////////

    } // end namespace stlplus

    #endif