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
20namespace 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
170using boost::program_options::config_file;
171
172#include <sstream>
173#include <cassert>
174
175int 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

source code of boost/libs/program_options/src/config_file.cpp