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
19namespace boost { namespace program_options { namespace detail {
20
21 using namespace std;
22
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
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 {
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
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
132 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
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
169using boost::program_options::config_file;
170
171#include <sstream>
172#include <cassert>
173
174int 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