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