1// Copyright Vladimir Prus 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#include <boost/program_options/value_semantic.hpp>
11#include <boost/program_options/detail/convert.hpp>
12#include <boost/program_options/detail/cmdline.hpp>
13#include <set>
14
15#include <cctype>
16
17namespace boost { namespace program_options {
18
19 using namespace std;
20
21
22#ifndef BOOST_NO_STD_WSTRING
23 namespace
24 {
25 std::string convert_value(const std::wstring& s)
26 {
27 try {
28 return to_local_8_bit(s);
29 }
30 catch(const std::exception&) {
31 return "<unrepresentable unicode string>";
32 }
33 }
34 }
35#endif
36
37 void
38 value_semantic_codecvt_helper<char>::
39 parse(boost::any& value_store,
40 const std::vector<std::string>& new_tokens,
41 bool utf8) const
42 {
43 if (utf8) {
44#ifndef BOOST_NO_STD_WSTRING
45 // Need to convert to local encoding.
46 std::vector<string> local_tokens;
47 for (unsigned i = 0; i < new_tokens.size(); ++i) {
48 std::wstring w = from_utf8(s: new_tokens[i]);
49 local_tokens.push_back(x: to_local_8_bit(s: w));
50 }
51 xparse(value_store, new_tokens: local_tokens);
52#else
53 boost::throw_exception(
54 std::runtime_error("UTF-8 conversion not supported."));
55#endif
56 } else {
57 // Already in local encoding, pass unmodified
58 xparse(value_store, new_tokens);
59 }
60 }
61
62#ifndef BOOST_NO_STD_WSTRING
63 void
64 value_semantic_codecvt_helper<wchar_t>::
65 parse(boost::any& value_store,
66 const std::vector<std::string>& new_tokens,
67 bool utf8) const
68 {
69 std::vector<wstring> tokens;
70 if (utf8) {
71 // Convert from utf8
72 for (unsigned i = 0; i < new_tokens.size(); ++i) {
73 tokens.push_back(x: from_utf8(s: new_tokens[i]));
74 }
75
76 } else {
77 // Convert from local encoding
78 for (unsigned i = 0; i < new_tokens.size(); ++i) {
79 tokens.push_back(x: from_local_8_bit(s: new_tokens[i]));
80 }
81 }
82
83 xparse(value_store, new_tokens: tokens);
84 }
85#endif
86
87 BOOST_PROGRAM_OPTIONS_DECL std::string arg("arg");
88
89 std::string
90 untyped_value::name() const
91 {
92 return arg;
93 }
94
95 unsigned
96 untyped_value::min_tokens() const
97 {
98 if (m_zero_tokens)
99 return 0;
100 else
101 return 1;
102 }
103
104 unsigned
105 untyped_value::max_tokens() const
106 {
107 if (m_zero_tokens)
108 return 0;
109 else
110 return 1;
111 }
112
113
114 void
115 untyped_value::xparse(boost::any& value_store,
116 const std::vector<std::string>& new_tokens) const
117 {
118 if (!value_store.empty())
119 boost::throw_exception(
120 e: multiple_occurrences());
121 if (new_tokens.size() > 1)
122 boost::throw_exception(e: multiple_values());
123 value_store = new_tokens.empty() ? std::string("") : new_tokens.front();
124 }
125
126 BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
127 bool_switch()
128 {
129 return bool_switch(v: 0);
130 }
131
132 BOOST_PROGRAM_OPTIONS_DECL typed_value<bool>*
133 bool_switch(bool* v)
134 {
135 typed_value<bool>* r = new typed_value<bool>(v);
136 r->default_value(v: 0);
137 r->zero_tokens();
138
139 return r;
140 }
141
142 /* Validates bool value.
143 Any of "1", "true", "yes", "on" will be converted to "1".<br>
144 Any of "0", "false", "no", "off" will be converted to "0".<br>
145 Case is ignored. The 'xs' vector can either be empty, in which
146 case the value is 'true', or can contain explicit value.
147 */
148 BOOST_PROGRAM_OPTIONS_DECL void validate(any& v, const vector<string>& xs,
149 bool*, int)
150 {
151 check_first_occurrence(value: v);
152 string s(get_single_string(v: xs, allow_empty: true));
153
154 for (size_t i = 0; i < s.size(); ++i)
155 s[i] = char(tolower(c: s[i]));
156
157 if (s.empty() || s == "on" || s == "yes" || s == "1" || s == "true")
158 v = any(true);
159 else if (s == "off" || s == "no" || s == "0" || s == "false")
160 v = any(false);
161 else
162 boost::throw_exception(e: invalid_bool_value(s));
163 }
164
165 // This is blatant copy-paste. However, templating this will cause a problem,
166 // since wstring can't be constructed/compared with char*. We'd need to
167 // create auxiliary 'widen' routine to convert from char* into
168 // needed string type, and that's more work.
169#if !defined(BOOST_NO_STD_WSTRING)
170 BOOST_PROGRAM_OPTIONS_DECL
171 void validate(any& v, const vector<wstring>& xs, bool*, int)
172 {
173 check_first_occurrence(value: v);
174 wstring s(get_single_string(v: xs, allow_empty: true));
175
176 for (size_t i = 0; i < s.size(); ++i)
177 s[i] = wchar_t(tolower(c: s[i]));
178
179 if (s.empty() || s == L"on" || s == L"yes" || s == L"1" || s == L"true")
180 v = any(true);
181 else if (s == L"off" || s == L"no" || s == L"0" || s == L"false")
182 v = any(false);
183 else
184 boost::throw_exception(e: invalid_bool_value(convert_value(s)));
185 }
186#endif
187 BOOST_PROGRAM_OPTIONS_DECL
188 void validate(any& v, const vector<string>& xs, std::string*, int)
189 {
190 check_first_occurrence(value: v);
191 v = any(get_single_string(v: xs));
192 }
193
194#if !defined(BOOST_NO_STD_WSTRING)
195 BOOST_PROGRAM_OPTIONS_DECL
196 void validate(any& v, const vector<wstring>& xs, std::string*, int)
197 {
198 check_first_occurrence(value: v);
199 v = any(get_single_string(v: xs));
200 }
201#endif
202
203 namespace validators {
204
205 BOOST_PROGRAM_OPTIONS_DECL
206 void check_first_occurrence(const boost::any& value)
207 {
208 if (!value.empty())
209 boost::throw_exception(
210 e: multiple_occurrences());
211 }
212 }
213
214
215 invalid_option_value::
216 invalid_option_value(const std::string& bad_value)
217 : validation_error(validation_error::invalid_option_value)
218 {
219 set_substitute(parameter_name: "value", value: bad_value);
220 }
221
222#ifndef BOOST_NO_STD_WSTRING
223 invalid_option_value::
224 invalid_option_value(const std::wstring& bad_value)
225 : validation_error(validation_error::invalid_option_value)
226 {
227 set_substitute(parameter_name: "value", value: convert_value(s: bad_value));
228 }
229#endif
230
231
232
233 invalid_bool_value::
234 invalid_bool_value(const std::string& bad_value)
235 : validation_error(validation_error::invalid_bool_value)
236 {
237 set_substitute(parameter_name: "value", value: bad_value);
238 }
239
240
241
242
243
244
245 error_with_option_name::error_with_option_name( const std::string& template_,
246 const std::string& option_name,
247 const std::string& original_token,
248 int option_style) :
249 error(template_),
250 m_option_style(option_style),
251 m_error_template(template_)
252 {
253 // parameter | placeholder | value
254 // --------- | ----------- | -----
255 set_substitute_default(parameter_name: "canonical_option", from: "option '%canonical_option%'", to: "option");
256 set_substitute_default(parameter_name: "value", from: "argument ('%value%')", to: "argument");
257 set_substitute_default(parameter_name: "prefix", from: "%prefix%", to: "");
258 m_substitutions["option"] = option_name;
259 m_substitutions["original_token"] = original_token;
260 }
261
262
263 const char* error_with_option_name::what() const BOOST_NOEXCEPT_OR_NOTHROW
264 {
265 // will substitute tokens each time what is run()
266 substitute_placeholders(error_template: m_error_template);
267
268 return m_message.c_str();
269 }
270
271 void error_with_option_name::replace_token(const string& from, const string& to) const
272 {
273 for (;;)
274 {
275 std::size_t pos = m_message.find(s: from.c_str(), pos: 0, n: from.length());
276 // not found: all replaced
277 if (pos == std::string::npos)
278 return;
279 m_message.replace(pos: pos, n: from.length(), str: to);
280 }
281 }
282
283 string error_with_option_name::get_canonical_option_prefix() const
284 {
285 switch (m_option_style)
286 {
287 case command_line_style::allow_dash_for_short:
288 return "-";
289 case command_line_style::allow_slash_for_short:
290 return "/";
291 case command_line_style::allow_long_disguise:
292 return "-";
293 case command_line_style::allow_long:
294 return "--";
295 case 0:
296 return "";
297 }
298 throw std::logic_error("error_with_option_name::m_option_style can only be "
299 "one of [0, allow_dash_for_short, allow_slash_for_short, "
300 "allow_long_disguise or allow_long]");
301 }
302
303
304 string error_with_option_name::get_canonical_option_name() const
305 {
306 if (!m_substitutions.find(x: "option")->second.length())
307 return m_substitutions.find(x: "original_token")->second;
308
309 string original_token = strip_prefixes(text: m_substitutions.find(x: "original_token")->second);
310 string option_name = strip_prefixes(text: m_substitutions.find(x: "option")->second);
311
312 // For long options, use option name
313 if (m_option_style == command_line_style::allow_long ||
314 m_option_style == command_line_style::allow_long_disguise)
315 return get_canonical_option_prefix() + option_name;
316
317 // For short options use first letter of original_token
318 if (m_option_style && original_token.length())
319 return get_canonical_option_prefix() + original_token[0];
320
321 // no prefix
322 return option_name;
323 }
324
325
326 void error_with_option_name::substitute_placeholders(const string& error_template) const
327 {
328 m_message = error_template;
329 std::map<std::string, std::string> substitutions(m_substitutions);
330 substitutions["canonical_option"] = get_canonical_option_name();
331 substitutions["prefix"] = get_canonical_option_prefix();
332
333
334 //
335 // replace placeholder with defaults if values are missing
336 //
337 for (map<string, string_pair>::const_iterator iter = m_substitution_defaults.begin();
338 iter != m_substitution_defaults.end(); ++iter)
339 {
340 // missing parameter: use default
341 if (substitutions.count(x: iter->first) == 0 ||
342 substitutions[iter->first].length() == 0)
343 replace_token(from: iter->second.first, to: iter->second.second);
344 }
345
346
347 //
348 // replace placeholder with values
349 // placeholder are denoted by surrounding '%'
350 //
351 for (map<string, string>::iterator iter = substitutions.begin();
352 iter != substitutions.end(); ++iter)
353 replace_token(from: '%' + iter->first + '%', to: iter->second);
354 }
355
356
357 void ambiguous_option::substitute_placeholders(const string& original_error_template) const
358 {
359 // For short forms, all alternatives must be identical, by
360 // definition, to the specified option, so we don't need to
361 // display alternatives
362 if (m_option_style == command_line_style::allow_dash_for_short ||
363 m_option_style == command_line_style::allow_slash_for_short)
364 {
365 error_with_option_name::substitute_placeholders(error_template: original_error_template);
366 return;
367 }
368
369
370 string error_template = original_error_template;
371 // remove duplicates using std::set
372 std::set<std::string> alternatives_set (m_alternatives.begin(), m_alternatives.end());
373 std::vector<std::string> alternatives_vec (alternatives_set.begin(), alternatives_set.end());
374
375 error_template += " and matches ";
376 // Being very cautious: should be > 1 alternative!
377 if (alternatives_vec.size() > 1)
378 {
379 for (unsigned i = 0; i < alternatives_vec.size() - 1; ++i)
380 error_template += "'%prefix%" + alternatives_vec[i] + "', ";
381 error_template += "and ";
382 }
383
384 // there is a programming error if multiple options have the same name...
385 if (m_alternatives.size() > 1 && alternatives_vec.size() == 1)
386 error_template += "different versions of ";
387
388 error_template += "'%prefix%" + alternatives_vec.back() + "'";
389
390
391 // use inherited logic
392 error_with_option_name::substitute_placeholders(error_template);
393 }
394
395
396
397
398
399
400 string
401 validation_error::get_template(kind_t kind)
402 {
403 // Initially, store the message in 'const char*' variable,
404 // to avoid conversion to std::string in all cases.
405 const char* msg;
406 switch(kind)
407 {
408 case invalid_bool_value:
409 msg = "the argument ('%value%') for option '%canonical_option%' is invalid. Valid choices are 'on|off', 'yes|no', '1|0' and 'true|false'";
410 break;
411 case invalid_option_value:
412 msg = "the argument ('%value%') for option '%canonical_option%' is invalid";
413 break;
414 case multiple_values_not_allowed:
415 msg = "option '%canonical_option%' only takes a single argument";
416 break;
417 case at_least_one_value_required:
418 msg = "option '%canonical_option%' requires at least one argument";
419 break;
420 // currently unused
421 case invalid_option:
422 msg = "option '%canonical_option%' is not valid";
423 break;
424 default:
425 msg = "unknown error";
426 }
427 return msg;
428 }
429
430}}
431

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