//////////////////////////////////////////////////////////////////////////////// // 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