Chris@16: // Copyright Vladimir Prus 2002-2004. Chris@16: // Distributed under the Boost Software License, Version 1.0. Chris@16: // (See accompanying file LICENSE_1_0.txt Chris@16: // or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: Chris@16: #ifndef BOOST_CONFIG_FILE_VP_2003_01_02 Chris@16: #define BOOST_CONFIG_FILE_VP_2003_01_02 Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) Chris@16: #include // std::getline Chris@16: #endif Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: Chris@16: Chris@16: namespace boost { namespace program_options { namespace detail { Chris@16: Chris@16: /** Standalone parser for config files in ini-line format. Chris@16: The parser is a model of single-pass lvalue iterator, and Chris@16: default constructor creates past-the-end-iterator. The typical usage is: Chris@16: config_file_iterator i(is, ... set of options ...), e; Chris@16: for(; i !=e; ++i) { Chris@16: *i; Chris@16: } Chris@16: Chris@16: Syntax conventions: Chris@16: Chris@16: - config file can not contain positional options Chris@16: - '#' is comment character: it is ignored together with Chris@16: the rest of the line. Chris@16: - variable assignments are in the form Chris@16: name '=' value. Chris@16: spaces around '=' are trimmed. Chris@16: - Section names are given in brackets. Chris@16: Chris@16: The actual option name is constructed by combining current section Chris@16: name and specified option name, with dot between. If section_name Chris@16: already contains dot at the end, new dot is not inserted. For example: Chris@16: @verbatim Chris@16: [gui.accessibility] Chris@16: visual_bell=yes Chris@16: @endverbatim Chris@16: will result in option "gui.accessibility.visual_bell" with value Chris@16: "yes" been returned. Chris@16: Chris@16: TODO: maybe, we should just accept a pointer to options_description Chris@16: class. Chris@16: */ Chris@16: class common_config_file_iterator Chris@16: : public eof_iterator Chris@16: { Chris@16: public: Chris@16: common_config_file_iterator() { found_eof(); } Chris@16: common_config_file_iterator( Chris@16: const std::set& allowed_options, Chris@16: bool allow_unregistered = false); Chris@16: Chris@16: virtual ~common_config_file_iterator() {} Chris@16: Chris@16: public: // Method required by eof_iterator Chris@16: Chris@16: void get(); Chris@16: Chris@16: protected: // Stubs for derived classes Chris@16: Chris@16: // Obtains next line from the config file Chris@16: // Note: really, this design is a bit ugly Chris@16: // The most clean thing would be to pass 'line_iterator' to Chris@16: // constructor of this class, but to avoid templating this class Chris@16: // we'd need polymorphic iterator, which does not exist yet. Chris@16: virtual bool getline(std::string&) { return false; } Chris@16: Chris@16: private: Chris@16: /** Adds another allowed option. If the 'name' ends with Chris@16: '*', then all options with the same prefix are Chris@16: allowed. For example, if 'name' is 'foo*', then 'foo1' and Chris@16: 'foo_bar' are allowed. */ Chris@16: void add_option(const char* name); Chris@16: Chris@16: // Returns true if 's' is a registered option name. Chris@16: bool allowed_option(const std::string& s) const; Chris@16: Chris@16: // That's probably too much data for iterator, since Chris@16: // it will be copied, but let's not bother for now. Chris@16: std::set allowed_options; Chris@16: // Invariant: no element is prefix of other element. Chris@16: std::set allowed_prefixes; Chris@16: std::string m_prefix; Chris@16: bool m_allow_unregistered; Chris@16: }; Chris@16: Chris@16: template Chris@16: class basic_config_file_iterator : public common_config_file_iterator { Chris@16: public: Chris@16: basic_config_file_iterator() Chris@16: { Chris@16: found_eof(); Chris@16: } Chris@16: Chris@16: /** Creates a config file parser for the specified stream. Chris@16: */ Chris@16: basic_config_file_iterator(std::basic_istream& is, Chris@16: const std::set& allowed_options, Chris@16: bool allow_unregistered = false); Chris@16: Chris@16: private: // base overrides Chris@16: Chris@16: bool getline(std::string&); Chris@16: Chris@16: private: // internal data Chris@16: shared_ptr > is; Chris@16: }; Chris@16: Chris@16: typedef basic_config_file_iterator config_file_iterator; Chris@16: typedef basic_config_file_iterator wconfig_file_iterator; Chris@16: Chris@16: Chris@16: struct null_deleter Chris@16: { Chris@16: void operator()(void const *) const {} Chris@16: }; Chris@16: Chris@16: Chris@16: template Chris@16: basic_config_file_iterator:: Chris@16: basic_config_file_iterator(std::basic_istream& is, Chris@16: const std::set& allowed_options, Chris@16: bool allow_unregistered) Chris@16: : common_config_file_iterator(allowed_options, allow_unregistered) Chris@16: { Chris@16: this->is.reset(&is, null_deleter()); Chris@16: get(); Chris@16: } Chris@16: Chris@16: // Specializing this function for wchar_t causes problems on Chris@16: // borland and vc7, as well as on metrowerks. On the first two Chris@16: // I don't know a workaround, so make use of 'to_internal' to Chris@16: // avoid specialization. Chris@16: template Chris@16: bool Chris@16: basic_config_file_iterator::getline(std::string& s) Chris@16: { Chris@16: std::basic_string in; Chris@16: if (std::getline(*is, in)) { Chris@16: s = to_internal(in); Chris@16: return true; Chris@16: } else { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: Chris@16: // Specialization is needed to workaround getline bug on Comeau. Chris@16: #if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \ Chris@16: (defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741))) Chris@16: template<> Chris@16: bool Chris@16: basic_config_file_iterator::getline(std::string& s); Chris@16: #endif Chris@16: Chris@16: Chris@16: Chris@16: }}} Chris@16: Chris@16: #endif