]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/program_options/src/cmdline.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / program_options / src / cmdline.cpp
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 #define BOOST_PROGRAM_OPTIONS_SOURCE
7 #include <boost/program_options/config.hpp>
8
9 #include <boost/config.hpp>
10
11 #include <boost/program_options/detail/cmdline.hpp>
12 #include <boost/program_options/errors.hpp>
13 #include <boost/program_options/value_semantic.hpp>
14 #include <boost/program_options/options_description.hpp>
15 #include <boost/program_options/positional_options.hpp>
16 #include <boost/throw_exception.hpp>
17
18 #include <boost/bind.hpp>
19
20 #include <string>
21 #include <utility>
22 #include <vector>
23 #include <cassert>
24 #include <cstring>
25 #include <cctype>
26 #include <climits>
27
28 #include <cstdio>
29
30 #include <iostream>
31
32 namespace boost { namespace program_options {
33
34 using namespace std;
35 using namespace boost::program_options::command_line_style;
36
37
38 string
39 invalid_syntax::get_template(kind_t kind)
40 {
41 // Initially, store the message in 'const char*' variable,
42 // to avoid conversion to string in all cases.
43 const char* msg;
44 switch(kind)
45 {
46 case empty_adjacent_parameter:
47 msg = "the argument for option '%canonical_option%' should follow immediately after the equal sign";
48 break;
49 case missing_parameter:
50 msg = "the required argument for option '%canonical_option%' is missing";
51 break;
52 case unrecognized_line:
53 msg = "the options configuration file contains an invalid line '%invalid_line%'";
54 break;
55 // none of the following are currently used:
56 case long_not_allowed:
57 msg = "the unabbreviated option '%canonical_option%' is not valid";
58 break;
59 case long_adjacent_not_allowed:
60 msg = "the unabbreviated option '%canonical_option%' does not take any arguments";
61 break;
62 case short_adjacent_not_allowed:
63 msg = "the abbreviated option '%canonical_option%' does not take any arguments";
64 break;
65 case extra_parameter:
66 msg = "option '%canonical_option%' does not take any arguments";
67 break;
68 default:
69 msg = "unknown command line syntax error for '%s'";
70 }
71 return msg;
72 }
73
74
75 }}
76
77
78 namespace boost { namespace program_options { namespace detail {
79
80 // vc6 needs this, but borland chokes when this is added.
81 #if BOOST_WORKAROUND(_MSC_VER, < 1300)
82 using namespace std;
83 using namespace program_options;
84 #endif
85
86
87 cmdline::cmdline(const vector<string>& args)
88 {
89 init(args);
90 }
91
92 cmdline::cmdline(int argc, const char*const * argv)
93 {
94 #if defined(BOOST_NO_TEMPLATED_ITERATOR_CONSTRUCTORS)
95 vector<string> args;
96 copy(argv+1, argv+argc+!argc, inserter(args, args.end()));
97 init(args);
98 #else
99 init(vector<string>(argv+1, argv+argc+!argc));
100 #endif
101 }
102
103 void
104 cmdline::init(const vector<string>& args)
105 {
106 this->args = args;
107 m_style = command_line_style::default_style;
108 m_desc = 0;
109 m_positional = 0;
110 m_allow_unregistered = false;
111 }
112
113 void
114 cmdline::style(int style)
115 {
116 if (style == 0)
117 style = default_style;
118
119 check_style(style);
120 this->m_style = style_t(style);
121 }
122
123 void
124 cmdline::allow_unregistered()
125 {
126 this->m_allow_unregistered = true;
127 }
128
129 void
130 cmdline::check_style(int style) const
131 {
132 bool allow_some_long =
133 (style & allow_long) || (style & allow_long_disguise);
134
135 const char* error = 0;
136 if (allow_some_long &&
137 !(style & long_allow_adjacent) && !(style & long_allow_next))
138 error = "boost::program_options misconfiguration: "
139 "choose one or other of 'command_line_style::long_allow_next' "
140 "(whitespace separated arguments) or "
141 "'command_line_style::long_allow_adjacent' ('=' separated arguments) for "
142 "long options.";
143
144 if (!error && (style & allow_short) &&
145 !(style & short_allow_adjacent) && !(style & short_allow_next))
146 error = "boost::program_options misconfiguration: "
147 "choose one or other of 'command_line_style::short_allow_next' "
148 "(whitespace separated arguments) or "
149 "'command_line_style::short_allow_adjacent' ('=' separated arguments) for "
150 "short options.";
151
152 if (!error && (style & allow_short) &&
153 !(style & allow_dash_for_short) && !(style & allow_slash_for_short))
154 error = "boost::program_options misconfiguration: "
155 "choose one or other of 'command_line_style::allow_slash_for_short' "
156 "(slashes) or 'command_line_style::allow_dash_for_short' (dashes) for "
157 "short options.";
158
159 if (error)
160 boost::throw_exception(invalid_command_line_style(error));
161
162 // Need to check that if guessing and long disguise are enabled
163 // -f will mean the same as -foo
164 }
165
166 bool
167 cmdline::is_style_active(style_t style) const
168 {
169 return ((m_style & style) ? true : false);
170 }
171
172 void
173 cmdline::set_options_description(const options_description& desc)
174 {
175 m_desc = &desc;
176 }
177
178 void
179 cmdline::set_positional_options(
180 const positional_options_description& positional)
181 {
182 m_positional = &positional;
183 }
184
185 int
186 cmdline::get_canonical_option_prefix()
187 {
188 if (m_style & allow_long)
189 return allow_long;
190
191 if (m_style & allow_long_disguise)
192 return allow_long_disguise;
193
194 if ((m_style & allow_short) && (m_style & allow_dash_for_short))
195 return allow_dash_for_short;
196
197 if ((m_style & allow_short) && (m_style & allow_slash_for_short))
198 return allow_slash_for_short;
199
200 return 0;
201 }
202
203 vector<option>
204 cmdline::run()
205 {
206 // The parsing is done by having a set of 'style parsers'
207 // and trying then in order. Each parser is passed a vector
208 // of unparsed tokens and can consume some of them (by
209 // removing elements on front) and return a vector of options.
210 //
211 // We try each style parser in turn, untill some input
212 // is consumed. The returned vector of option may contain the
213 // result of just syntactic parsing of token, say --foo will
214 // be parsed as option with name 'foo', and the style parser
215 // is not required to care if that option is defined, and how
216 // many tokens the value may take.
217 // So, after vector is returned, we validate them.
218 assert(m_desc);
219
220 vector<style_parser> style_parsers;
221
222 if (m_style_parser)
223 style_parsers.push_back(m_style_parser);
224
225 if (m_additional_parser)
226 style_parsers.push_back(
227 boost::bind(&cmdline::handle_additional_parser, this, _1));
228
229 if (m_style & allow_long)
230 style_parsers.push_back(
231 boost::bind(&cmdline::parse_long_option, this, _1));
232
233 if ((m_style & allow_long_disguise))
234 style_parsers.push_back(
235 boost::bind(&cmdline::parse_disguised_long_option, this, _1));
236
237 if ((m_style & allow_short) && (m_style & allow_dash_for_short))
238 style_parsers.push_back(
239 boost::bind(&cmdline::parse_short_option, this, _1));
240
241 if ((m_style & allow_short) && (m_style & allow_slash_for_short))
242 style_parsers.push_back(boost::bind(&cmdline::parse_dos_option, this, _1));
243
244 style_parsers.push_back(boost::bind(&cmdline::parse_terminator, this, _1));
245
246 vector<option> result;
247 while(!args.empty())
248 {
249 bool ok = false;
250 for(unsigned i = 0; i < style_parsers.size(); ++i)
251 {
252 unsigned current_size = static_cast<unsigned>(args.size());
253 vector<option> next = style_parsers[i](args);
254
255 // Check that option names
256 // are valid, and that all values are in place.
257 if (!next.empty())
258 {
259 vector<string> e;
260 for(unsigned k = 0; k < next.size()-1; ++k) {
261 finish_option(next[k], e, style_parsers);
262 }
263 // For the last option, pass the unparsed tokens
264 // so that they can be added to next.back()'s values
265 // if appropriate.
266 finish_option(next.back(), args, style_parsers);
267 for (unsigned j = 0; j < next.size(); ++j)
268 result.push_back(next[j]);
269 }
270
271 if (args.size() != current_size) {
272 ok = true;
273 break;
274 }
275 }
276
277 if (!ok) {
278 option opt;
279 opt.value.push_back(args[0]);
280 opt.original_tokens.push_back(args[0]);
281 result.push_back(opt);
282 args.erase(args.begin());
283 }
284 }
285
286 /* If an key option is followed by a positional option,
287 can can consume more tokens (e.g. it's multitoken option),
288 give those tokens to it. */
289 vector<option> result2;
290 for (unsigned i = 0; i < result.size(); ++i)
291 {
292 result2.push_back(result[i]);
293 option& opt = result2.back();
294
295 if (opt.string_key.empty())
296 continue;
297
298 const option_description* xd;
299 try
300 {
301 xd = m_desc->find_nothrow(opt.string_key,
302 is_style_active(allow_guessing),
303 is_style_active(long_case_insensitive),
304 is_style_active(short_case_insensitive));
305 }
306 catch(error_with_option_name& e)
307 {
308 // add context and rethrow
309 e.add_context(opt.string_key, opt.original_tokens[0], get_canonical_option_prefix());
310 throw;
311 }
312
313 if (!xd)
314 continue;
315
316 unsigned min_tokens = xd->semantic()->min_tokens();
317 unsigned max_tokens = xd->semantic()->max_tokens();
318 if (min_tokens < max_tokens && opt.value.size() < max_tokens)
319 {
320 // This option may grab some more tokens.
321 // We only allow to grab tokens that are not already
322 // recognized as key options.
323
324 int can_take_more = max_tokens - static_cast<int>(opt.value.size());
325 unsigned j = i+1;
326 for (; can_take_more && j < result.size(); --can_take_more, ++j)
327 {
328 option& opt2 = result[j];
329 if (!opt2.string_key.empty())
330 break;
331
332 if (opt2.position_key == INT_MAX)
333 {
334 // We use INT_MAX to mark positional options that
335 // were found after the '--' terminator and therefore
336 // should stay positional forever.
337 break;
338 }
339
340 assert(opt2.value.size() == 1);
341
342 opt.value.push_back(opt2.value[0]);
343
344 assert(opt2.original_tokens.size() == 1);
345
346 opt.original_tokens.push_back(opt2.original_tokens[0]);
347 }
348 i = j-1;
349 }
350 }
351 result.swap(result2);
352
353
354 // Assign position keys to positional options.
355 int position_key = 0;
356 for(unsigned i = 0; i < result.size(); ++i) {
357 if (result[i].string_key.empty())
358 result[i].position_key = position_key++;
359 }
360
361 if (m_positional)
362 {
363 unsigned position = 0;
364 for (unsigned i = 0; i < result.size(); ++i) {
365 option& opt = result[i];
366 if (opt.position_key != -1) {
367 if (position >= m_positional->max_total_count())
368 {
369 boost::throw_exception(too_many_positional_options_error());
370 }
371 opt.string_key = m_positional->name_for_position(position);
372 ++position;
373 }
374 }
375 }
376
377 // set case sensitive flag
378 for (unsigned i = 0; i < result.size(); ++i) {
379 if (result[i].string_key.size() > 2 ||
380 (result[i].string_key.size() > 1 && result[i].string_key[0] != '-'))
381 {
382 // it is a long option
383 result[i].case_insensitive = is_style_active(long_case_insensitive);
384 }
385 else
386 {
387 // it is a short option
388 result[i].case_insensitive = is_style_active(short_case_insensitive);
389 }
390 }
391
392 return result;
393 }
394
395 void
396 cmdline::finish_option(option& opt,
397 vector<string>& other_tokens,
398 const vector<style_parser>& style_parsers)
399 {
400 if (opt.string_key.empty())
401 return;
402
403 //
404 // Be defensive:
405 // will have no original token if option created by handle_additional_parser()
406 std::string original_token_for_exceptions = opt.string_key;
407 if (opt.original_tokens.size())
408 original_token_for_exceptions = opt.original_tokens[0];
409
410 try
411 {
412 // First check that the option is valid, and get its description.
413 const option_description* xd = m_desc->find_nothrow(opt.string_key,
414 is_style_active(allow_guessing),
415 is_style_active(long_case_insensitive),
416 is_style_active(short_case_insensitive));
417
418 if (!xd)
419 {
420 if (m_allow_unregistered) {
421 opt.unregistered = true;
422 return;
423 } else {
424 boost::throw_exception(unknown_option());
425 }
426 }
427 const option_description& d = *xd;
428
429 // Canonize the name
430 opt.string_key = d.key(opt.string_key);
431
432 // We check that the min/max number of tokens for the option
433 // agrees with the number of tokens we have. The 'adjacent_value'
434 // (the value in --foo=1) counts as a separate token, and if present
435 // must be consumed. The following tokens on the command line may be
436 // left unconsumed.
437 unsigned min_tokens = d.semantic()->min_tokens();
438 unsigned max_tokens = d.semantic()->max_tokens();
439
440 unsigned present_tokens = static_cast<unsigned>(opt.value.size() + other_tokens.size());
441
442 if (present_tokens >= min_tokens)
443 {
444 if (!opt.value.empty() && max_tokens == 0)
445 {
446 boost::throw_exception(
447 invalid_command_line_syntax(invalid_command_line_syntax::extra_parameter));
448 }
449
450 // Grab min_tokens values from other_tokens, but only if those tokens
451 // are not recognized as options themselves.
452 if (opt.value.size() <= min_tokens)
453 {
454 min_tokens -= static_cast<unsigned>(opt.value.size());
455 }
456 else
457 {
458 min_tokens = 0;
459 }
460
461 // Everything's OK, move the values to the result.
462 for(;!other_tokens.empty() && min_tokens--; )
463 {
464 // check if extra parameter looks like a known option
465 // we use style parsers to check if it is syntactically an option,
466 // additionally we check if an option_description exists
467 vector<option> followed_option;
468 vector<string> next_token(1, other_tokens[0]);
469 for (unsigned i = 0; followed_option.empty() && i < style_parsers.size(); ++i)
470 {
471 followed_option = style_parsers[i](next_token);
472 }
473 if (!followed_option.empty())
474 {
475 original_token_for_exceptions = other_tokens[0];
476 const option_description* od = m_desc->find_nothrow(other_tokens[0],
477 is_style_active(allow_guessing),
478 is_style_active(long_case_insensitive),
479 is_style_active(short_case_insensitive));
480 if (od)
481 boost::throw_exception(
482 invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
483 }
484 opt.value.push_back(other_tokens[0]);
485 opt.original_tokens.push_back(other_tokens[0]);
486 other_tokens.erase(other_tokens.begin());
487 }
488 }
489 else
490 {
491 boost::throw_exception(
492 invalid_command_line_syntax(invalid_command_line_syntax::missing_parameter));
493
494 }
495 }
496 // use only original token for unknown_option / ambiguous_option since by definition
497 // they are unrecognised / unparsable
498 catch(error_with_option_name& e)
499 {
500 // add context and rethrow
501 e.add_context(opt.string_key, original_token_for_exceptions, get_canonical_option_prefix());
502 throw;
503 }
504
505 }
506
507 vector<option>
508 cmdline::parse_long_option(vector<string>& args)
509 {
510 vector<option> result;
511 const string& tok = args[0];
512 if (tok.size() >= 3 && tok[0] == '-' && tok[1] == '-')
513 {
514 string name, adjacent;
515
516 string::size_type p = tok.find('=');
517 if (p != tok.npos)
518 {
519 name = tok.substr(2, p-2);
520 adjacent = tok.substr(p+1);
521 if (adjacent.empty())
522 boost::throw_exception( invalid_command_line_syntax(
523 invalid_command_line_syntax::empty_adjacent_parameter,
524 name,
525 name,
526 get_canonical_option_prefix()) );
527 }
528 else
529 {
530 name = tok.substr(2);
531 }
532 option opt;
533 opt.string_key = name;
534 if (!adjacent.empty())
535 opt.value.push_back(adjacent);
536 opt.original_tokens.push_back(tok);
537 result.push_back(opt);
538 args.erase(args.begin());
539 }
540 return result;
541 }
542
543
544 vector<option>
545 cmdline::parse_short_option(vector<string>& args)
546 {
547 const string& tok = args[0];
548 if (tok.size() >= 2 && tok[0] == '-' && tok[1] != '-')
549 {
550 vector<option> result;
551
552 string name = tok.substr(0,2);
553 string adjacent = tok.substr(2);
554
555 // Short options can be 'grouped', so that
556 // "-d -a" becomes "-da". Loop, processing one
557 // option at a time. We exit the loop when either
558 // we've processed all the token, or when the remainder
559 // of token is considered to be value, not further grouped
560 // option.
561 for(;;) {
562 const option_description* d;
563 try
564 {
565
566 d = m_desc->find_nothrow(name, false, false,
567 is_style_active(short_case_insensitive));
568 }
569 catch(error_with_option_name& e)
570 {
571 // add context and rethrow
572 e.add_context(name, name, get_canonical_option_prefix());
573 throw;
574 }
575
576
577 // FIXME: check for 'allow_sticky'.
578 if (d && (m_style & allow_sticky) &&
579 d->semantic()->max_tokens() == 0 && !adjacent.empty()) {
580 // 'adjacent' is in fact further option.
581 option opt;
582 opt.string_key = name;
583 result.push_back(opt);
584
585 if (adjacent.empty())
586 {
587 args.erase(args.begin());
588 break;
589 }
590
591 name = string("-") + adjacent[0];
592 adjacent.erase(adjacent.begin());
593 } else {
594
595 option opt;
596 opt.string_key = name;
597 opt.original_tokens.push_back(tok);
598 if (!adjacent.empty())
599 opt.value.push_back(adjacent);
600 result.push_back(opt);
601 args.erase(args.begin());
602 break;
603 }
604 }
605 return result;
606 }
607 return vector<option>();
608 }
609
610 vector<option>
611 cmdline::parse_dos_option(vector<string>& args)
612 {
613 vector<option> result;
614 const string& tok = args[0];
615 if (tok.size() >= 2 && tok[0] == '/')
616 {
617 string name = "-" + tok.substr(1,1);
618 string adjacent = tok.substr(2);
619
620 option opt;
621 opt.string_key = name;
622 if (!adjacent.empty())
623 opt.value.push_back(adjacent);
624 opt.original_tokens.push_back(tok);
625 result.push_back(opt);
626 args.erase(args.begin());
627 }
628 return result;
629 }
630
631 vector<option>
632 cmdline::parse_disguised_long_option(vector<string>& args)
633 {
634 const string& tok = args[0];
635 if (tok.size() >= 2 &&
636 ((tok[0] == '-' && tok[1] != '-') ||
637 ((m_style & allow_slash_for_short) && tok[0] == '/')))
638 {
639 try
640 {
641 if (m_desc->find_nothrow(tok.substr(1, tok.find('=')-1),
642 is_style_active(allow_guessing),
643 is_style_active(long_case_insensitive),
644 is_style_active(short_case_insensitive)))
645 {
646 args[0].insert(0, "-");
647 if (args[0][1] == '/')
648 args[0][1] = '-';
649 return parse_long_option(args);
650 }
651 }
652 catch(error_with_option_name& e)
653 {
654 // add context and rethrow
655 e.add_context(tok, tok, get_canonical_option_prefix());
656 throw;
657 }
658 }
659 return vector<option>();
660 }
661
662 vector<option>
663 cmdline::parse_terminator(vector<string>& args)
664 {
665 vector<option> result;
666 const string& tok = args[0];
667 if (tok == "--")
668 {
669 for(unsigned i = 1; i < args.size(); ++i)
670 {
671 option opt;
672 opt.value.push_back(args[i]);
673 opt.original_tokens.push_back(args[i]);
674 opt.position_key = INT_MAX;
675 result.push_back(opt);
676 }
677 args.clear();
678 }
679 return result;
680 }
681
682 vector<option>
683 cmdline::handle_additional_parser(vector<string>& args)
684 {
685 vector<option> result;
686 pair<string, string> r = m_additional_parser(args[0]);
687 if (!r.first.empty()) {
688 option next;
689 next.string_key = r.first;
690 if (!r.second.empty())
691 next.value.push_back(r.second);
692 result.push_back(next);
693 args.erase(args.begin());
694 }
695 return result;
696 }
697
698 void
699 cmdline::set_additional_parser(additional_parser p)
700 {
701 m_additional_parser = p;
702 }
703
704 void
705 cmdline::extra_style_parser(style_parser s)
706 {
707 m_style_parser = s;
708 }
709
710
711
712 }}}