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

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