X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fstlplus%2Fsubsystems%2Fini_manager.cpp;fp=src%2Fstlplus%2Fsubsystems%2Fini_manager.cpp;h=41f5b6934bd4106b3431e5fc7ff26be051df88ab;hb=6b0a0d0efafe34d48ab344fca3b479553bd4e62c;hp=0000000000000000000000000000000000000000;hpb=85783316365181491a3e3c0c63659972477cebba;p=chaz%2Fyoink diff --git a/src/stlplus/subsystems/ini_manager.cpp b/src/stlplus/subsystems/ini_manager.cpp new file mode 100644 index 0000000..41f5b69 --- /dev/null +++ b/src/stlplus/subsystems/ini_manager.cpp @@ -0,0 +1,1243 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "ini_manager.hpp" +#include "file_system.hpp" +#include +#include +#include +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // local utilities + + static std::string trim(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[0])) + result.erase(result.begin()); + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.end()-1); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // internal data structure for storing a single entry (i.e. line) from an ini file + // lines are categorised as blanks, comments or variables + // TODO - do I need an error category? + //////////////////////////////////////////////////////////////////////////////// + + class ini_entry + { + public: + enum kind_t {BLANK, COMMENT, VARIABLE}; + friend std::string to_string(kind_t kind) + { + switch(kind) + { + case BLANK: return "BLANK"; + case COMMENT: return "COMMENT"; + case VARIABLE: return "VARIABLE"; + } + return "<*unknown kind*>"; + } + + private: + unsigned m_line; + kind_t m_kind; + std::string m_text; + std::string m_name; + std::string m_value; + + public: + ini_entry(unsigned line) : m_line(line), m_kind(BLANK) {} + ini_entry(unsigned line, const std::string& comment) : m_line(line), m_kind(COMMENT), m_text("; " + comment) {} + 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) {} + ~ini_entry(void) {} + + unsigned line(void) const {return m_line;} + kind_t kind(void) const {return m_kind;} + bool blank(void) const {return m_kind == BLANK;} + bool comment(void) const {return m_kind == COMMENT;} + bool variable(void) const {return m_kind == VARIABLE;} + + const std::string& text(void) const {return m_text;} + const std::string& variable_name(void) const {return m_name;} + const std::string& variable_value(void) const {return m_value;} + + bool print(std::ostream& str) const + { + str << " " << m_line << ":" << to_string(m_kind) << ": " << m_text << std::endl; + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // internal data structure representing an ini file section containing all the + // entries for that section from a single ini file + //////////////////////////////////////////////////////////////////////////////// + + class ini_section + { + private: + friend class ini_file; + std::string m_title; + std::list m_entries; + + public: + ini_section(const std::string& title) : + m_title(title) + { + } + + ~ini_section(void) + { + } + + const std::string& title(void) const + { + return m_title; + } + + bool empty(void) const + { + // a section is empty if it contains no variables + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable()) + return false; + } + return true; + } + + void clear(void) + { + m_entries.clear(); + } + + bool variable_exists(const std::string variable) const + { + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + return true; + } + return false; + } + + unsigned variables_size(void) const + { + unsigned result = 0; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + if (i->variable()) + result++; + return result; + } + + std::string variable_name(unsigned offset) const + { + unsigned j = 0; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable()) + { + if (j == offset) + return i->variable_name(); + j++; + } + } + return std::string(); + } + + std::vector variable_names(void) const + { + std::vector result; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + if (i->variable()) + result.push_back(i->variable_name()); + return result; + } + + std::string variable_value(unsigned offset) const + { + unsigned j = 0; + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable()) + { + if (j == offset) + return i->variable_value(); + j++; + } + } + return std::string(); + } + + std::string variable_value(const std::string variable) const + { + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + return i->variable_value(); + } + return std::string(); + } + + unsigned variable_line(const std::string variable) const + { + for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + return i->line(); + } + return 0; + } + + bool add_variable(unsigned line, const std::string& variable, const std::string& value) + { + erase_variable(variable); + m_entries.push_back(ini_entry(line ? line : m_entries.size(), variable, value)); + return true; + } + + bool add_comment(unsigned line, const std::string& comment) + { + m_entries.push_back(ini_entry(line ? line : m_entries.size(), comment)); + return true; + } + + bool add_blank(unsigned line) + { + m_entries.push_back(ini_entry(line ? line : m_entries.size())); + return true; + } + + bool erase_variable(const std::string& variable) + { + for (std::list::iterator i = m_entries.begin(); i != m_entries.end(); i++) + { + if (i->variable() && i->variable_name() == variable) + { + m_entries.erase(i); + return true; + } + } + return false; + } + + bool print(std::ostream& str) const + { + str << " [" << m_title << "]" << std::endl; + for (std::list::const_iterator entry = m_entries.begin(); entry != m_entries.end(); entry++) + entry->print(str); + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // internal data structure representing a single ini file + //////////////////////////////////////////////////////////////////////////////// + + class ini_file + { + private: + friend class ini_section; + std::string m_filename; + bool m_changed; + bool m_writable; + std::list m_sections; + + std::list::const_iterator find_section(const std::string& section) const + { + for (std::list::const_iterator i = m_sections.begin(); i != m_sections.end(); i++) + { + if (i->title() == section) + return i; + } + return m_sections.end(); + } + + std::list::iterator find_section(const std::string& section) + { + for (std::list::iterator i = m_sections.begin(); i != m_sections.end(); i++) + { + if (i->title() == section) + return i; + } + return m_sections.end(); + } + + public: + + ini_file(void) : m_changed(false), m_writable(false) + { + } + + ini_file(const std::string& filename) : m_changed(false), m_writable(false) + { + read_file(filename); + } + + ~ini_file(void) + { + if (writable()) + save_file(); + } + + bool initialised(void) const + { + return !m_filename.empty(); + } + + bool writable(void) const + { + return m_writable; + } + + bool read_file(const std::string& filename) + { + m_filename = filename; + m_changed = false; + // file may not yet exist - possible to create an Ini file using this class + // so it is writable if the file exists and is writable or if the folder is writable + m_writable = file_writable(m_filename); + if (!file_exists(m_filename)) + return true; + // create a dummy top section to hold file comments + std::list::iterator section = m_sections.insert(m_sections.end(), ini_section("")); + std::ifstream source(m_filename.c_str()); + unsigned line_number = 0; + std::string line; + while(std::getline(source,line)) + { + line_number++; + unsigned i = 0; + while(i < line.size() && isspace(line[i])) + i++; + if (i < line.size() && line[i] == '[') + { + // found a section title + // skip the [ + i++; + // get the text up to the end ] or the end of the line + std::string title; + while (i < line.size() && line[i] != ']') + title += line[i++]; + // create a new section and make it the current section + section = m_sections.insert(m_sections.end(), ini_section(title)); + } + else if (i < line.size() && line[i] == ';') + { + // found a comment + // skip the ; because that is not part of the comment + i++; + // add the rest of the line as a comment to the current section + section->add_comment(line_number, line.substr(i, line.size()-1)); + } + else if (i == line.size()) + { + // found a blank line + section->add_blank(line_number); + } + else + { + // found a *possible* variable - now scan forwards for the = operator + std::string name; + while (i < line.size() && line[i] != '=') + name += line[i++]; + // skip the = sign + // TODO - detect a missing = sign here and convert to an error + if (i < line.size()) + i++; + // trim trailing whitespace off the name + name = trim(name); + // now get the value, minus any leading whitespace + std::string value; + while(i < line.size() && isspace(line[i])) + i++; + while (i < line.size()) + value += line[i++]; + // trim trailing whitespace off the value + value = trim(value); + // and finally add the name/value pair to the data structure + section->add_variable(line_number, name, value); + } + } + return true; + } + + bool save_file(void) + { + if (!initialised()) return false; + if (!m_changed) return true; + if (!m_writable) return false; + std::ofstream output(m_filename.c_str()); + for (std::list::iterator section = m_sections.begin(); section != m_sections.end(); section++) + { + if (!(section->title().empty())) + output << "[" << section->title() << "]" << std::endl; + for (std::list::iterator entry = section->m_entries.begin(); entry != section->m_entries.end(); entry++) + output << entry->text() << std::endl; + } + m_changed = false; + return true; + } + + std::string filename(void) const + { + return m_filename; + } + + bool empty(void) const + { + // file is empty if it has either no sections or an empty header section + if (m_sections.empty()) return true; + if ((m_sections.begin() == --m_sections.end()) && m_sections.begin()->empty()) return true; + return false; + } + + bool section_exists(const std::string& title) const + { + return find_section(title) != m_sections.end(); + } + + bool add_section(const std::string& section) + { + if (!m_writable) return false; + m_sections.push_back(ini_section(section)); + m_changed = true; + return true; + } + + bool empty_section(const std::string& section) + { + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + return found->empty(); + } + + bool erase_section(const std::string& section) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + m_sections.erase(found); + m_changed = true; + return true; + } + + bool clear_section(const std::string& section) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + found->clear(); + m_changed = true; + return true; + } + + unsigned sections_size(void) const + { + return m_sections.size(); + } + + const std::string& section_name(unsigned i) const + { + std::list::const_iterator result = m_sections.begin(); + for (unsigned j = 1; j <= i; j++) + result++; + return result->title(); + } + + std::vector section_names(void) const + { + std::vector result; + for (unsigned j = 0; j < sections_size(); j++) + result.push_back(section_name(j)); + return result; + } + + bool variable_exists(const std::string& section, const std::string variable) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return false; + return found->variable_exists(variable); + } + + std::vector variable_names(const std::string& section) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::vector(); + return found->variable_names(); + } + + unsigned variables_size(const std::string& section) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return 0; + return found->variables_size(); + } + + unsigned variable_line(const std::string& section, const std::string variable) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return 0; + return found->variable_line(variable); + } + + std::string variable_name(const std::string& section, unsigned i) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::string(); + return found->variable_name(i); + } + + std::string variable_value(const std::string& section, unsigned i) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::string(); + return found->variable_value(i); + } + + std::string variable_value(const std::string& section, const std::string variable) const + { + std::list::const_iterator found = find_section(section); + if (found == m_sections.end()) return std::string(); + return found->variable_value(variable); + } + + bool add_variable(const std::string& section, const std::string& variable, const std::string& value) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); + if (found->add_variable(0,variable,value)) + m_changed = true; + return true; + } + + bool add_comment(const std::string& section, const std::string& comment) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); + if (found->add_comment(0,comment)) + m_changed = true; + return true; + } + + bool add_blank(const std::string& section) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); + if (found->add_blank(0)) + m_changed = true; + return true; + } + + bool erase_variable(const std::string& section, const std::string& variable) + { + if (!m_writable) return false; + std::list::iterator found = find_section(section); + if (found == m_sections.end()) return false; + if (found->erase_variable(variable)) + { + m_changed = true; + return true; + } + return false; + } + + bool print(std::ostream& str) const + { + str << "file: " << m_filename << std::endl; + for (std::list::const_iterator section = m_sections.begin(); section != m_sections.end(); section++) + section->print(str); + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // body data structure contains all the data and is pointed-to by exported data structure + + class ini_manager_body + { + private: + std::vector m_files; + unsigned m_count; + + ini_manager_body(const ini_manager_body&); + ini_manager_body& operator= (const ini_manager_body&); + + public: + + ini_manager_body(void) : m_count(1) + { + } + + ~ini_manager_body(void) + { + save(); + } + + void increment(void) + { + ++m_count; + } + + bool decrement(void) + { + --m_count; + return m_count == 0; + } + + ////////////////////////////////////////////////////////////////////////////// + // 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) + { + // if this file has been loaded, don't load it again + // this is not an error + for (unsigned i = 0; i < m_files.size(); i++) + { + if (filespec_to_path(filename) == filespec_to_path(m_files[i].filename())) + return true; + } + // add a new file to the back of the list and then load it + // I do it in two steps rather than passing the filename to the constructor in order to return the load status + m_files.push_back(ini_file()); + return m_files.back().read_file(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& filenames) + { + bool result = true; + for (unsigned i = 0; i < filenames.size(); i++) + result &= add_file(filenames[i]); + return result; + } + + // saves modified ini files - returns true if all modified files were written successfully + bool save(void) + { + bool result = true; + for (unsigned i = 0; i < m_files.size(); i++) + result &= m_files[i].save_file(); + return result; + } + + // get the number of files being managed + unsigned size(void) const + { + return m_files.size(); + } + + // get the ini filename associated with a depth + std::string filename(unsigned depth) const + { + return m_files[depth].filename(); + } + + // test whether a file in the ini manager is writable + bool writable(unsigned depth) const + { + return m_files[depth].writable(); + } + + // 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) const + { + return m_files[depth].empty(); + } + + // erase the ini file from the ini manager and from the disk + bool erase(unsigned depth) + { + std::string file = filename(depth); + remove(depth); + return file_delete(file); + } + + // remove the file from the ini manager but do not erase it from the disk + bool remove(unsigned depth) + { + if (m_files[depth].writable()) + m_files[depth].save_file(); + m_files.erase(m_files.begin() + depth); + return true; + } + + ////////////////////////////////////////////////////////////////////////////// + // section management + + // returns the union of all section names in all of the ini files + std::vector section_names(void) const + { + std::vector result; + for (unsigned i = 0; i < m_files.size(); i++) + { + std::vector file_result = section_names(i); + for (unsigned j = 0; j < file_result.size(); j++) + { + if (std::find(result.begin(), result.end(), file_result[j]) == result.end()) + result.push_back(file_result[j]); + } + } + return result; + } + + // returns the section names in one of the ini files + std::vector section_names(unsigned depth) const + { + return m_files[depth].section_names(); + } + + // tests whether a section is found in any of the ini files + bool section_exists(const std::string& title) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (m_files[i].section_exists(title)) + return true; + } + return false; + } + + // tests whether the section is found in the specific ini file + bool section_exists(const std::string& title, unsigned depth) const + { + return m_files[depth].section_exists(title); + } + + // adds a section to the specified ini file - does nothing if it is already present + bool add_section(const std::string& section, unsigned depth) + { + return m_files[depth].add_section(section); + } + + // test whether a section is empty + bool empty_section(const std::string& section, unsigned depth) + { + return m_files[depth].empty_section(section); + } + + // 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) + { + return m_files[depth].erase_section(section); + } + + // 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) + { + return m_files[depth].clear_section(section); + } + + ////////////////////////////////////////////////////////////////////////////// + // 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 + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return true; + } + return false; + } + + // test whether a variable exists in specified ini file + bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const + { + return m_files[depth].variable_exists(section, variable); + } + + // get the union of all variables declared in all ini files + std::vector variable_names(const std::string& section) const + { + std::vector result; + for (unsigned i = 0; i < m_files.size(); i++) + { + std::vector file_result = variable_names(section, i); + for (unsigned j = 0; j < file_result.size(); j++) + { + if (std::find(result.begin(), result.end(), file_result[j]) == result.end()) + result.push_back(file_result[j]); + } + } + return result; + } + + // get the set of all varaibale names from one file + std::vector variable_names(const std::string& section, unsigned depth) const + { + return m_files[depth].variable_names(section); + } + + // 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 + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return i; + } + return (unsigned)-1; + } + + // get the filename that first defines the variable + std::string variable_filename(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return filename(i); + } + return std::string(); + } + + unsigned variable_linenumber(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return m_files[i].variable_line(section,variable); + } + return 0; + } + + // 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 + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return variable_value(section, variable, i); + } + return std::string(); + } + + // get the value from the specified file + std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const + { + return m_files[depth].variable_value(section, variable); + } + + // 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 variable_values(const std::string& section, const std::string variable) const + { + for (unsigned i = 0; i < m_files.size(); i++) + { + if (variable_exists(section, variable, i)) + return variable_values(section, variable, i); + } + return std::vector(); + } + + // get the processed variable from the specified file + std::vector variable_values(const std::string& section, const std::string variable, unsigned depth) const + { + // get the unprocessed value and then do the processing into processed separate values + std::string value = variable_value(section, variable, depth); + std::vector result; + if (!value.empty()) + { + result.push_back(std::string()); + unsigned i = 0; + // loop which is repeated for each element in the comma-separated list + while(i < value.size()) + { + // skip leading whitespace + while (i < value.size() && isspace(value[i])) i++; + // get the value - this means all text up to the next comma or end of line + // also allow escaped characters + while (i < value.size()) + { + if (value[i] == ',') break; + if (value[i] == '\\') + { + // found an escaped character - de-escape it by only getting the next character + // beware of an escape character at the end of the line which just gets ignored + i++; + if (i < value.size()) result.back() += value[i++]; + } + else if (value[i] == '"') + { + // get a quoted substring + // first skip the opening quote + i++; + // keep getting characters until a close-quote, but allow the quote character to be escaped by itself + while (i < value.size()) + { + if (value[i] == '"') + { + // found a quote skip it + i++; + // now establish whether its an escaped quote + // if it is, keep it, but de-escape it by only getting the next character + // it it isn't, break out and continue processing the value as a non-quoted string + if (i < value.size() && value[i] != '"') break; + if (i < value.size()) result.back() += value[i++]; + } + else + { + // non-quote and non-escape so just get the character + result.back() += value[i++]; + } + } + } + else + { + // non-escape so just get the character + result.back() += value[i++]; + } + } + // trim trailing whitespace off the value + while (!result.back().empty() && isspace(result.back()[result.back().size()-1])) result.back().erase(result.back().size()-1,1); + // now check for whether there is a comma or end of line + if (i <= value.size() && value[i] == ',') + { + // skip the comma and add a new string to the result ready for the next iteration + i++; + result.push_back(std::string()); + } + else + { + // end of line found so break out + break; + } + } + } + return result; + } + + // add a variable to the specified file + bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth) + { + return m_files[depth].add_variable(section, variable, value); + } + + // 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& values, unsigned depth) + { + // convert the values vector into a comma-separated string with each value escaped so that special characters do not confuse the reader + // the characters escaped are: '\', ',', '"' + std::string value; + for (unsigned v = 0; v < values.size(); v++) + { + const std::string& element = values[v]; + // add the comma between values === add a comma before all but the first value + if (v > 0) value += ','; + for (unsigned i = 0; i < element.size(); i++) + { + // add a character at a time, escaping special characters + if (element[i] == '"' || element[i] == ',' || element[i] == '\\') + { + // escape the character + value += '\\'; + } + value += element[i]; + } + } + return add_variable(section, variable, value, depth); + } + + // 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) + { + return m_files[depth].erase_variable(section, variable); + } + + ////////////////////////////////////////////////////////////////////////////// + // 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) + { + return m_files[depth].add_comment(section, comment); + } + + // add a blank line to the specified ini file + bool add_blank(const std::string& section, unsigned depth) + { + return m_files[depth].add_blank(section); + } + + bool print(std::ostream& str) const + { + str << "----------------------------------------" << std::endl; + for (unsigned depth = 0; depth < m_files.size(); depth++) + { + m_files[depth].print(str); + str << "----------------------------------------" << std::endl; + } + return !str.fail(); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // exported data structure representing the set of all ini files and providing + // the access functions exported by the class + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + // constructors/destructors + + ini_manager::ini_manager(void) : m_body(new ini_manager_body) + { + } + + ini_manager::ini_manager(const std::vector& filenames) : m_body(new ini_manager_body) + { + add_files(filenames); + } + + ini_manager::ini_manager(const ini_manager& manager) : m_body(0) + { + m_body = manager.m_body; + m_body->increment(); + } + + ini_manager& ini_manager::operator= (const ini_manager& manager) + { + if (m_body == manager.m_body) return *this; + if (m_body->decrement()) + delete m_body; + m_body = manager.m_body; + m_body->increment(); + return *this; + } + + ini_manager::~ini_manager(void) + { + if (m_body->decrement()) + delete m_body; + } + + //////////////////////////////////////////////////////////////////////////////// + // file management + + bool ini_manager::add_file(const std::string& filename) + { + return m_body->add_file(filename); + } + + bool ini_manager::add_files(const std::vector& filenames) + { + return m_body->add_files(filenames); + } + + bool ini_manager::save(void) + { + return m_body->save(); + } + + unsigned ini_manager::size(void) const + { + return m_body->size(); + } + + std::string ini_manager::filename(unsigned depth) const + { + return m_body->filename(depth); + } + + bool ini_manager::writable(unsigned depth) const + { + return m_body->writable(depth); + } + + bool ini_manager::empty(unsigned depth) const + { + return m_body->empty(depth); + } + + bool ini_manager::erase(unsigned depth) + { + return m_body->erase(depth); + } + + bool ini_manager::remove(unsigned depth) + { + return m_body->remove(depth); + } + + //////////////////////////////////////////////////////////////////////////////// + // section management + + std::vector ini_manager::section_names(void) const + { + return m_body->section_names(); + } + + std::vector ini_manager::section_names(unsigned depth) const + { + return m_body->section_names(depth); + } + + bool ini_manager::section_exists(const std::string& section) const + { + return m_body->section_exists(section); + } + + bool ini_manager::section_exists(const std::string& section, unsigned depth) const + { + return m_body->section_exists(section, depth); + } + + bool ini_manager::add_section(const std::string& section, unsigned depth) + { + return m_body->add_section(section, depth); + } + + bool ini_manager::empty_section(const std::string& section, unsigned depth) + { + return m_body->empty_section(section, depth); + } + + bool ini_manager::erase_section(const std::string& section, unsigned depth) + { + return m_body->erase_section(section, depth); + } + + bool ini_manager::clear_section(const std::string& section, unsigned depth) + { + return m_body->clear_section(section, depth); + } + + //////////////////////////////////////////////////////////////////////////////// + // variable management + + bool ini_manager::variable_exists(const std::string& section, const std::string variable) const + { + return m_body->variable_exists(section, variable); + } + + bool ini_manager::variable_exists(const std::string& section, const std::string variable, unsigned depth) const + { + return m_body->variable_exists(section, variable, depth); + } + + std::vector ini_manager::variable_names(const std::string& section) const + { + return m_body->variable_names(section); + } + + std::vector ini_manager::variable_names(const std::string& section, unsigned depth) const + { + return m_body->variable_names(section, depth); + } + + unsigned ini_manager::variable_depth(const std::string& section, const std::string variable) const + { + return m_body->variable_depth(section, variable); + } + + std::string ini_manager::variable_filename(const std::string& section, const std::string variable) const + { + return m_body->variable_filename(section, variable); + } + + unsigned ini_manager::variable_linenumber(const std::string& section, const std::string variable) const + { + return m_body->variable_linenumber(section, variable); + } + + std::string ini_manager::variable_value(const std::string& section, const std::string variable) const + { + return m_body->variable_value(section, variable); + } + + std::string ini_manager::variable_value(const std::string& section, const std::string variable, unsigned depth) const + { + return m_body->variable_value(section, variable, depth); + } + + std::vector ini_manager::variable_values(const std::string& section, const std::string variable) const + { + return m_body->variable_values(section, variable); + } + + std::vector ini_manager::variable_values(const std::string& section, const std::string variable, unsigned depth) const + { + return m_body->variable_values(section, variable, depth); + } + + bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth) + { + return m_body->add_variable(section, variable, value, depth); + } + + bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth) + { + return m_body->add_variable(section, variable, values, depth); + } + + bool ini_manager::erase_variable(const std::string& section, const std::string& variable, unsigned depth) + { + return m_body->erase_variable(section, variable, depth); + } + + + //////////////////////////////////////////////////////////////////////////////// + // sundry entries + + bool ini_manager::add_comment(const std::string& section, const std::string& comment, unsigned depth) + { + return m_body->add_comment(section, comment, depth); + } + + bool ini_manager::add_blank(const std::string& section, unsigned depth) + { + return m_body->add_blank(section, depth); + } + + bool ini_manager::print(std::ostream& str) const + { + return m_body->print(str); + } + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic print + + std::ostream& operator << (std::ostream& str, const ini_manager& manager) + { + manager.print(str); + return str; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus