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 | |
34 | using namespace boost::placeholders; |
35 | |
36 | namespace 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 | |
82 | namespace 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::(style_parser s) |
711 | { |
712 | m_style_parser = s; |
713 | } |
714 | |
715 | |
716 | |
717 | }}} |
718 | |