// Copyright (c) 2005-2007 Andre Merzky (andre@merzky.net) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // System Header Files #include #include #include #include #include #include #include #include #include #include "ini.hpp" #include #include #include #ifdef __APPLE__ #include #define environ (*_NSGetEnviron()) #elif !defined(BOOST_WINDOWS) extern char **environ; #endif /////////////////////////////////////////////////////////////////////////////// void saga_ini_read_env (void); int saga_ini_regex_init (void); void saga_ini_line_msg (std::string msg, std::string file, int lnum = -1); /////////////////////////////////////////////////////////////////////////////// // example ini line: line # comment const char pattern_comment[] = "^([^#]*)(#.*)$"; // example uses ini line: [sec.ssec] const char pattern_section[] = "^\\[([^\\]]+)\\]$"; // example uses ini line: key = val const char pattern_entry[] = "^([^\\s=]+)\\s*=\\s*(.*[^\\s])\\s*$"; // FIXME: allow empty values, interpret as "" TR1::regex const saga::impl::ini::section::regex_comment = TR1::regex (pattern_comment, TR1::regex::perl | TR1::regex::icase); TR1::regex const saga::impl::ini::section::regex_section = TR1::regex (pattern_section, TR1::regex::perl | TR1::regex::icase); TR1::regex const saga::impl::ini::section::regex_entry = TR1::regex (pattern_entry, TR1::regex::perl | TR1::regex::icase); /////////////////////////////////////////////////////////////////////////////// static saga::impl::ini::entry_map saga_ini_env; /////////////////////////////////////////////////////////////////////////////// saga::impl::ini::section::section (std::string filename) : root (this_()) { if ( filename != "" ) { name = filename; read (filename); } } saga::impl::ini::section::section (const saga::impl::ini::section & in) : root (this_()) { name = in.get_name (); { entry_map e = in.get_entries (); entry_map :: iterator i; for ( i = e.begin (); i != e.end (); ++i ) { add_entry (i->first, i->second); } } { section_map s = in.get_sections (); section_map :: iterator i; for ( i = s.begin (); i != s.end (); ++i ) { add_section (i->first, i->second, get_root ()); } } } /////////////////////////////////////////////////////////////////////////////// namespace { /////////////////////////////////////////////////////////////////////////// inline std::string trim_whitespace (std::string const &s) { typedef std::string::size_type size_type; size_type first = s.find_first_not_of (" \t\r\n"); if ( std::string::npos == first ) return (std::string ()); size_type last = s.find_last_not_of (" \t\r\n"); return s.substr (first, last - first + 1); } /////////////////////////////////////////////////////////////////////////// inline saga::impl::ini::section * add_section_if_new (saga::impl::ini::section * current, std::string const & sec_name) { // do we know this one? if ( ! current->has_section (sec_name) ) { // no - add it! saga::impl::ini::section sec; current->add_section (sec_name, sec, current->get_root ()); } return current->get_section (sec_name); } /////////////////////////////////////////////////////////////////////////////// } // namespace void saga::impl::ini::section::read (std::string filename) { int linenum = 0; std::ifstream input; // build ini - open file and parse each line input.open (filename.c_str (), std::ios_base::in); if ( ! input.is_open () ) { saga_ini_line_msg ("Cannot open file ", filename); } // initialize if ( saga_ini_regex_init () ) { saga_ini_line_msg ("Cannot init regex for ", filename); } // parse file saga::impl::ini::section * current = this; std::string line; while ( std::getline (input, line) ) { ++linenum; // remove trailing new lines and white spaces line = trim_whitespace (line); // skip if empty line if ( line.empty () ) continue; // weep out comments boost::smatch what_comment; if ( boost::regex_match (line, what_comment, regex_comment) ) { BOOST_ASSERT (3 == what_comment.size ()); line = trim_whitespace (what_comment[1]); if ( line.empty () ) continue; } // no comments anymore: line is either section, key=val, // or garbage/empty boost::smatch what; if ( boost::regex_match (line, what, regex_section) ) { // found a section line if ( 2 != what.size () ) { saga_ini_line_msg ("Cannot parse sec in ", filename, linenum); } current = this; // start adding sections at the root // got the section name. It might be hierarchical, so split it up, and // for each elem, check if we have it. If not, create it, and add std::string sec_name (what[1]); std::string::size_type pos = 0; for (std::string::size_type pos1 = sec_name.find_first_of ('.'); std::string::npos != pos1; pos1 = sec_name.find_first_of ('.', pos = pos1 + 1) ) { current = add_section_if_new (current, sec_name.substr (pos, pos1 - pos)); } current = add_section_if_new (current, sec_name.substr (pos)); } // did not match section, so might be key/val entry else if ( boost::regex_match (line, what, regex_entry) ) { // found a entry line if ( 3 != what.size () ) { saga_ini_line_msg ("Cannot parse key/value in ", filename, linenum); } // add key/val to current section current->add_entry (what[1], what[2]); } else { // Hmm, is not a section, is not an entry, is not empty - must be an error! saga_ini_line_msg ("Cannot parse line at ", filename, linenum); } } // std::getline } ///////////////////////////////////////////////////////////////// void saga::impl::ini::section::add_section (std::string sec_name, saga::impl::ini::section & sec, saga::impl::ini::section * root) { // setting name and root sec.name = sec_name; sec.set_root (root); sections[sec_name] = sec; } bool saga::impl::ini::section::has_section (std::string sec_name) const { if ( sections.find (sec_name) == sections.end () ) { return false; } return (true); } bool saga::impl::ini::section::has_section_full (std::string sec_name) const { std::string::size_type i = sec_name.find ("."); if ( i != std::string::npos ) { std::string cor_sec_name = sec_name.substr (0, i); std::string sub_sec_name = sec_name.substr (1 + i); section_map::const_iterator it = sections.find (cor_sec_name); if ( it != sections.end () ) { return (*it).second.has_section_full (sub_sec_name); } return false; } return has_section (sec_name); } saga::impl::ini::section * saga::impl::ini::section::get_section (std::string sec_name) { std::string::size_type i = sec_name.find ("."); if ( i != std::string::npos ) { std::string cor_sec_name = sec_name.substr (0, i); std::string sub_sec_name = sec_name.substr (1 + i); if ( has_section (cor_sec_name) ) { return (sections[cor_sec_name].get_section (sub_sec_name)); } SAGA_THROW("No such section (" + cor_sec_name + ") in section: " + get_name(), saga::DoesNotExist); } if ( has_section (sec_name) ) { return (&(sections [sec_name])); } SAGA_THROW("No such section (" + sec_name + ") in section: " + get_name (), saga::DoesNotExist); return NULL; } const saga::impl::ini::section_map & saga::impl::ini::section::get_sections (void) const { return (sections); } void saga::impl::ini::section::add_entry (std::string key, std::string val) { entries[key] = val; } bool saga::impl::ini::section::has_entry (std::string key) const { if ( entries.find (key) == entries.end () ) { return false; } return (true); } std::string saga::impl::ini::section::get_entry (std::string key) const { std::string::size_type i = key.find (".", 0); if ( i != std::string::npos ) { std::string sub_sec = key.substr (0, i); std::string sub_key = key.substr (i+1, key.length () - i); if ( has_section (sub_sec) ) { section_map::const_iterator cit = sections.find(sub_sec); BOOST_ASSERT(cit != sections.end()); return ((*cit).second.get_entry (sub_key)); } SAGA_THROW("No such section (" + sub_sec + ") in section " + get_name (), saga::DoesNotExist); } if ( has_entry (key) ) { entry_map::const_iterator cit = entries.find(key); BOOST_ASSERT(cit != entries.end()); return (expand_entry ((*cit).second)); } SAGA_THROW("No such key (" + key + ") in section " + get_name (), saga::DoesNotExist); return std::string(); } const saga::impl::ini::entry_map & saga::impl::ini::section::get_entries (void) const { return (entries); } saga::impl::ini::section saga::impl::ini::section::clone (saga::impl::ini::section * root) const { saga::impl::ini::section out; out.name = name; if ( NULL == root ) { root = &out; } { entry_map e = get_entries (); entry_map :: iterator i; for ( i = e.begin (); i != e.end (); ++i ) { out.add_entry (i->first, i->second); } } { section_map s = get_sections (); section_map :: iterator i; for ( i = s.begin (); i != s.end (); ++i ) { saga::impl::ini::section sub = i->second.clone (); out.add_section (i->first, sub, root); } } return (out); } void saga::impl::ini::section::dump (int ind, std::ostream& strm) const { bool header = false; if ( 0 == ind ) { header = true; } ++ind; if ( header ) { if ( get_root () == this ) { strm << "============================\n"; } else { strm << "============================\n" << get_name () << "\n" << "----------------------------\n"; } } { entry_map e = get_entries (); entry_map :: iterator i; for ( i = e.begin (); i != e.end (); ++i ) { indent (ind); strm << "'" << i->first << "' : '" << i->second << "'\n"; } } { section_map s = get_sections (); section_map :: iterator i; for ( i = s.begin (); i != s.end (); ++i ) { indent (ind); strm << "[" << i->first << "]\n"; i->second.dump (ind); } } if ( header ) { strm << "============================\n"; } } void saga::impl::ini::section::indent (int ind) const { for ( int i = 0; i < ind; ++i ) { std::cout << " "; } } void saga::impl::ini::section::merge (std::string filename) { saga::impl::ini::section tmp (filename); merge (tmp); } void saga::impl::ini::section::merge (saga::impl::ini::section & second) { // merge entries: keep own entries, and add other entries { saga::impl::ini::entry_map s_entries = second.get_entries (); saga::impl::ini::entry_map :: iterator i; for ( i = s_entries.begin (); i != s_entries.end (); ++i ) { entries[i->first] = s_entries[i->first]; } } // merge subsection known in first section { section_map s = get_sections (); section_map :: iterator i; for ( i = s.begin (); i != s.end (); ++i ) { // is there something to merge with? if ( second.has_section (i->first) ) { sections[i->first].merge (second.sections[i->first]); } } } // merge subsection known in second section { section_map s = second.get_sections (); section_map :: iterator i; for ( i = s.begin (); i != s.end (); ++i ) { // if THIS knows the section, we already merged it above if ( ! has_section (i->first) ) { // it is not known here, so we can't merge, but have to add it. add_section (i->first, i->second, get_root ()); } } } } ///////////////////////////////////////////////////////////////////////////////// int saga_ini_regex_init (void) { saga_ini_read_env (); return (0); } void saga_ini_read_env (void) { int i = 0; const char * entry = environ[i]; while ( entry != NULL ) { std::string s (entry); std::string::size_type idx = s.find ("=", 0); if ( idx != std::string::npos ) { std::string key, val; if ( idx + 1 < s.length () ) { key = s.substr (0, idx); val = s.substr (idx+1, s.length ()); } else { key = s.substr (0, idx); val = ""; } saga_ini_env[key] = val; } entry = environ[++i]; } } void saga_ini_line_msg (std::string msg, std::string file, int lnum) { if ( lnum > 0 ) { using namespace std; // some systems have snprintf in std:: char lstr[20] = ""; snprintf (lstr, 19, "%d", lnum); SAGA_THROW_VERBATIM(saga::object(), msg + " " + file + ":" + lstr, saga::NoSuccess); } SAGA_THROW_VERBATIM(saga::object(), msg + " " + file, saga::NoSuccess); } std::string saga::impl::ini::section::expand_entry (std::string in, std::string::size_type start) const { std::string out (in); // search for something to expand std::string::size_type idx_1 = in.find ("$", start); if ( idx_1 != std::string::npos ) { std::string type = in.substr (idx_1 + 1, 1); // handle env expansion if ( type == "{" ) { std::string::size_type idx_2 = in.find ("}", idx_1 + 1); if ( idx_2 != std::string::npos ) { std::string key = in.substr (idx_1 + 2, idx_2 - idx_1 - 2); std::string val = saga_ini_env[key]; out.replace (idx_1, idx_2 - idx_1 + 1, val); } } // handle ini key expansion if ( type == "[" ) { std::string::size_type idx_2 = in.find ("]", idx_1 + 1); if ( idx_2 != std::string::npos ) { std::string key = in.substr (idx_1 + 2, idx_2 - idx_1 - 2); std::string val = get_root ()->get_entry (key); out.replace (idx_1, idx_2 - idx_1 + 1, val); } } // expand remainder of string return (expand_entry (out, idx_1 + 1)); } // nothing more to expand... return (out); }