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#include <boost/config.hpp>
8
9#define BOOST_PROGRAM_OPTIONS_SOURCE
10#include <boost/program_options/config.hpp>
11#include <boost/program_options/parsers.hpp>
12#include <boost/program_options/options_description.hpp>
13#include <boost/program_options/positional_options.hpp>
14#include <boost/program_options/detail/cmdline.hpp>
15#include <boost/program_options/detail/config_file.hpp>
16#include <boost/program_options/environment_iterator.hpp>
17#include <boost/program_options/detail/convert.hpp>
18
19#include <boost/bind.hpp>
20#include <boost/throw_exception.hpp>
21
22#include <cctype>
23#include <fstream>
24
25#if !defined(__GNUC__) || __GNUC__ < 3
26#include <iostream>
27#else
28#include <istream>
29#endif
30
31#ifdef _WIN32
32#include <stdlib.h>
33#else
34#include <unistd.h>
35#endif
36
37// The 'environ' should be declared in some cases. E.g. Linux man page says:
38// (This variable must be declared in the user program, but is declared in
39// the header file unistd.h in case the header files came from libc4 or libc5,
40// and in case they came from glibc and _GNU_SOURCE was defined.)
41// To be safe, declare it here.
42
43// It appears that on Mac OS X the 'environ' variable is not
44// available to dynamically linked libraries.
45// See: http://article.gmane.org/gmane.comp.lib.boost.devel/103843
46// See: http://lists.gnu.org/archive/html/bug-guile/2004-01/msg00013.html
47#if defined(__APPLE__) && defined(__DYNAMIC__)
48// The proper include for this is crt_externs.h, however it's not
49// available on iOS. The right replacement is not known. See
50// https://svn.boost.org/trac/boost/ticket/5053
51extern "C" { extern char ***_NSGetEnviron(void); }
52#define environ (*_NSGetEnviron())
53#else
54#if defined(__MWERKS__)
55#include <crtl.h>
56#else
57#if !defined(_WIN32) || defined(__COMO_VERSION__)
58extern char** environ;
59#endif
60#endif
61#endif
62
63using namespace std;
64
65namespace boost { namespace program_options {
66
67#ifndef BOOST_NO_STD_WSTRING
68 namespace {
69 woption woption_from_option(const option& opt)
70 {
71 woption result;
72 result.string_key = opt.string_key;
73 result.position_key = opt.position_key;
74 result.unregistered = opt.unregistered;
75
76 std::transform(opt.value.begin(), opt.value.end(),
77 back_inserter(result.value),
78 boost::bind(from_utf8, _1));
79
80 std::transform(opt.original_tokens.begin(),
81 opt.original_tokens.end(),
82 back_inserter(result.original_tokens),
83 boost::bind(from_utf8, _1));
84 return result;
85 }
86 }
87
88 basic_parsed_options<wchar_t>
89 ::basic_parsed_options(const parsed_options& po)
90 : description(po.description),
91 utf8_encoded_options(po),
92 m_options_prefix(po.m_options_prefix)
93 {
94 for (unsigned i = 0; i < po.options.size(); ++i)
95 options.push_back(woption_from_option(po.options[i]));
96 }
97#endif
98
99 template<class charT>
100 basic_parsed_options<charT>
101 parse_config_file(std::basic_istream<charT>& is,
102 const options_description& desc,
103 bool allow_unregistered)
104 {
105 set<string> allowed_options;
106
107 const vector<shared_ptr<option_description> >& options = desc.options();
108 for (unsigned i = 0; i < options.size(); ++i)
109 {
110 const option_description& d = *options[i];
111
112 if (d.long_name().empty())
113 boost::throw_exception(
114 error("abbreviated option names are not permitted in options configuration files"));
115
116 allowed_options.insert(d.long_name());
117 }
118
119 // Parser return char strings
120 parsed_options result(&desc);
121 copy(detail::basic_config_file_iterator<charT>(
122 is, allowed_options, allow_unregistered),
123 detail::basic_config_file_iterator<charT>(),
124 back_inserter(result.options));
125 // Convert char strings into desired type.
126 return basic_parsed_options<charT>(result);
127 }
128
129 template
130 BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
131 parse_config_file(std::basic_istream<char>& is,
132 const options_description& desc,
133 bool allow_unregistered);
134
135#ifndef BOOST_NO_STD_WSTRING
136 template
137 BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
138 parse_config_file(std::basic_istream<wchar_t>& is,
139 const options_description& desc,
140 bool allow_unregistered);
141#endif
142
143 template<class charT>
144 basic_parsed_options<charT>
145 parse_config_file(const char* filename,
146 const options_description& desc,
147 bool allow_unregistered)
148 {
149 // Parser return char strings
150 std::basic_ifstream< charT > strm(filename);
151 if (!strm)
152 {
153 boost::throw_exception(reading_file(filename));
154 }
155
156 basic_parsed_options<charT> result
157 = parse_config_file(strm, desc, allow_unregistered);
158
159 if (strm.bad())
160 {
161 boost::throw_exception(reading_file(filename));
162 }
163
164 return result;
165 }
166
167 template
168 BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<char>
169 parse_config_file(const char* filename,
170 const options_description& desc,
171 bool allow_unregistered);
172
173#ifndef BOOST_NO_STD_WSTRING
174 template
175 BOOST_PROGRAM_OPTIONS_DECL basic_parsed_options<wchar_t>
176 parse_config_file(const char* filename,
177 const options_description& desc,
178 bool allow_unregistered);
179#endif
180
181
182// This versio, which accepts any options without validation, is disabled,
183// in the hope that nobody will need it and we cant drop it altogether.
184// Besides, probably the right way to handle all options is the '*' name.
185#if 0
186 BOOST_PROGRAM_OPTIONS_DECL parsed_options
187 parse_config_file(std::istream& is)
188 {
189 detail::config_file_iterator cf(is, false);
190 parsed_options result(0);
191 copy(cf, detail::config_file_iterator(),
192 back_inserter(result.options));
193 return result;
194 }
195#endif
196
197 BOOST_PROGRAM_OPTIONS_DECL parsed_options
198 parse_environment(const options_description& desc,
199 const function1<std::string, std::string>& name_mapper)
200 {
201 parsed_options result(&desc);
202
203 for(environment_iterator i(environ), e; i != e; ++i) {
204 string option_name = name_mapper(i->first);
205
206 if (!option_name.empty()) {
207 option n;
208 n.string_key = option_name;
209 n.value.push_back(i->second);
210 result.options.push_back(n);
211 }
212 }
213
214 return result;
215 }
216
217 namespace detail {
218 class prefix_name_mapper {
219 public:
220 prefix_name_mapper(const std::string& prefix)
221 : prefix(prefix)
222 {}
223
224 std::string operator()(const std::string& s)
225 {
226 string result;
227 if (s.find(prefix) == 0) {
228 for(string::size_type n = prefix.size(); n < s.size(); ++n)
229 {
230 // Intel-Win-7.1 does not understand
231 // push_back on string.
232 result += static_cast<char>(tolower(s[n]));
233 }
234 }
235 return result;
236 }
237 private:
238 std::string prefix;
239 };
240 }
241
242 BOOST_PROGRAM_OPTIONS_DECL parsed_options
243 parse_environment(const options_description& desc,
244 const std::string& prefix)
245 {
246 return parse_environment(desc, detail::prefix_name_mapper(prefix));
247 }
248
249 BOOST_PROGRAM_OPTIONS_DECL parsed_options
250 parse_environment(const options_description& desc, const char* prefix)
251 {
252 return parse_environment(desc, string(prefix));
253 }
254
255
256
257
258}}
259