• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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