1 // Copyright Vladimir Prus 2002-2004. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt 4 // or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 7 #define BOOST_PROGRAM_OPTIONS_SOURCE 8 #include <boost/program_options/config.hpp> 9 10 #include <boost/program_options/detail/config_file.hpp> 11 #include <boost/program_options/errors.hpp> 12 #include <boost/program_options/detail/convert.hpp> 13 #include <boost/throw_exception.hpp> 14 15 #include <iostream> 16 #include <fstream> 17 #include <cassert> 18 19 namespace boost { namespace program_options { namespace detail { 20 21 using namespace std; 22 common_config_file_iterator(const std::set<std::string> & allowed_options,bool allow_unregistered)23 common_config_file_iterator::common_config_file_iterator( 24 const std::set<std::string>& allowed_options, 25 bool allow_unregistered) 26 : allowed_options(allowed_options), 27 m_allow_unregistered(allow_unregistered) 28 { 29 for(std::set<std::string>::const_iterator i = allowed_options.begin(); 30 i != allowed_options.end(); 31 ++i) 32 { 33 add_option(i->c_str()); 34 } 35 } 36 37 void add_option(const char * name)38 common_config_file_iterator::add_option(const char* name) 39 { 40 string s(name); 41 assert(!s.empty()); 42 if (*s.rbegin() == '*') { 43 s.resize(s.size()-1); 44 bool bad_prefixes(false); 45 // If 's' is a prefix of one of allowed suffix, then 46 // lower_bound will return that element. 47 // If some element is prefix of 's', then lower_bound will 48 // return the next element. 49 set<string>::iterator i = allowed_prefixes.lower_bound(s); 50 if (i != allowed_prefixes.end()) { 51 if (i->find(s) == 0) 52 bad_prefixes = true; 53 } 54 if (i != allowed_prefixes.begin()) { 55 --i; 56 if (s.find(*i) == 0) 57 bad_prefixes = true; 58 } 59 if (bad_prefixes) 60 boost::throw_exception(error("options '" + string(name) + "' and '" + 61 *i + "*' will both match the same " 62 "arguments from the configuration file")); 63 allowed_prefixes.insert(s); 64 } 65 } 66 67 namespace { trim_ws(const string & s)68 string trim_ws(const string& s) 69 { 70 string::size_type n, n2; 71 n = s.find_first_not_of(" \t\r\n"); 72 if (n == string::npos) 73 return string(); 74 else { 75 n2 = s.find_last_not_of(" \t\r\n"); 76 return s.substr(n, n2-n+1); 77 } 78 } 79 } 80 81 get()82 void common_config_file_iterator::get() 83 { 84 string s; 85 string::size_type n; 86 bool found = false; 87 88 while(this->getline(s)) { 89 90 // strip '#' comments and whitespace 91 if ((n = s.find('#')) != string::npos) 92 s = s.substr(0, n); 93 s = trim_ws(s); 94 95 if (!s.empty()) { 96 // Handle section name 97 if (*s.begin() == '[' && *s.rbegin() == ']') { 98 m_prefix = s.substr(1, s.size()-2); 99 if (*m_prefix.rbegin() != '.') 100 m_prefix += '.'; 101 } 102 else if ((n = s.find('=')) != string::npos) { 103 104 string name = m_prefix + trim_ws(s.substr(0, n)); 105 string value = trim_ws(s.substr(n+1)); 106 107 bool registered = allowed_option(name); 108 if (!registered && !m_allow_unregistered) 109 boost::throw_exception(unknown_option(name)); 110 111 found = true; 112 this->value().string_key = name; 113 this->value().value.clear(); 114 this->value().value.push_back(value); 115 this->value().unregistered = !registered; 116 this->value().original_tokens.clear(); 117 this->value().original_tokens.push_back(name); 118 this->value().original_tokens.push_back(value); 119 break; 120 121 } else { 122 boost::throw_exception(invalid_config_file_syntax(s, invalid_syntax::unrecognized_line)); 123 } 124 } 125 } 126 if (!found) 127 found_eof(); 128 } 129 130 131 bool allowed_option(const std::string & s) const132 common_config_file_iterator::allowed_option(const std::string& s) const 133 { 134 set<string>::const_iterator i = allowed_options.find(s); 135 if (i != allowed_options.end()) 136 return true; 137 // If s is "pa" where "p" is allowed prefix then 138 // lower_bound should find the element after "p". 139 // This depends on 'allowed_prefixes' invariant. 140 i = allowed_prefixes.lower_bound(s); 141 if (i != allowed_prefixes.begin() && s.find(*--i) == 0) 142 return true; 143 return false; 144 } 145 146 #if BOOST_WORKAROUND(__COMO_VERSION__, BOOST_TESTED_AT(4303)) || \ 147 (defined(__sgi) && BOOST_WORKAROUND(_COMPILER_VERSION, BOOST_TESTED_AT(741))) 148 template<> 149 bool getline(std::string & s)150 basic_config_file_iterator<wchar_t>::getline(std::string& s) 151 { 152 std::wstring ws; 153 // On Comeau, using two-argument version causes 154 // call to some internal function with std::wstring, and '\n' 155 // (not L'\n') and compile can't resolve that call. 156 157 if (std::getline(*is, ws, L'\n')) { 158 s = to_utf8(ws); 159 return true; 160 } else { 161 return false; 162 } 163 } 164 #endif 165 166 }}} 167 168 #if 0 169 using boost::program_options::config_file; 170 171 #include <sstream> 172 #include <cassert> 173 174 int main() 175 { 176 try { 177 stringstream s( 178 "a = 1\n" 179 "b = 2\n"); 180 181 config_file cf(s); 182 cf.add_option("a"); 183 cf.add_option("b"); 184 185 assert(++cf); 186 assert(cf.name() == "a"); 187 assert(cf.value() == "1"); 188 assert(++cf); 189 assert(cf.name() == "b"); 190 assert(cf.value() == "2"); 191 assert(!++cf); 192 } 193 catch(exception& e) 194 { 195 cout << e.what() << "\n"; 196 } 197 } 198 #endif 199