1// Copyright Vladimir Prus 2002-2004.
2// Copyright Bertolt Mildner 2004.
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt
5// or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7#ifndef BOOST_PROGRAM_OPTIONS_SOURCE
8# define BOOST_PROGRAM_OPTIONS_SOURCE
9#endif
10#include <boost/program_options/config.hpp>
11#include <boost/program_options/options_description.hpp>
12// FIXME: this is only to get multiple_occurrences class
13// should move that to a separate headers.
14#include <boost/program_options/parsers.hpp>
15
16
17#include <boost/lexical_cast.hpp>
18#include <boost/tokenizer.hpp>
19#include <boost/detail/workaround.hpp>
20#include <boost/throw_exception.hpp>
21
22#include <cassert>
23#include <climits>
24#include <cstring>
25#include <cstdarg>
26#include <sstream>
27#include <iterator>
28using namespace std;
29
30namespace boost { namespace program_options {
31
32 namespace {
33
34 template< class charT >
35 std::basic_string< charT > tolower_(const std::basic_string< charT >& str)
36 {
37 std::basic_string< charT > result;
38 for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i)
39 {
40 result.append(1, static_cast< charT >(std::tolower(str[i])));
41 }
42 return result;
43 }
44
45 } // unnamed namespace
46
47
48 option_description::option_description()
49 {
50 }
51
52 option_description::
53 option_description(const char* names,
54 const value_semantic* s)
55 : m_value_semantic(s)
56 {
57 this->set_names(names);
58 }
59
60
61 option_description::
62 option_description(const char* names,
63 const value_semantic* s,
64 const char* description)
65 : m_description(description), m_value_semantic(s)
66 {
67 this->set_names(names);
68 }
69
70 option_description::~option_description()
71 {
72 }
73
74 option_description::match_result
75 option_description::match(const std::string& option,
76 bool approx,
77 bool long_ignore_case,
78 bool short_ignore_case) const
79 {
80 match_result result = no_match;
81 std::string local_option = (long_ignore_case ? tolower_(str: option) : option);
82
83 for(std::vector<std::string>::const_iterator it(m_long_names.begin()); it != m_long_names.end(); it++)
84 {
85 std::string local_long_name((long_ignore_case ? tolower_(str: *it) : *it));
86
87 if (!local_long_name.empty()) {
88
89
90 if ((result == no_match) && (*local_long_name.rbegin() == '*'))
91 {
92 // The name ends with '*'. Any specified name with the given
93 // prefix is OK.
94 if (local_option.find(str: local_long_name.substr(pos: 0, n: local_long_name.length()-1))
95 == 0)
96 result = approximate_match;
97 }
98
99 if (local_long_name == local_option)
100 {
101 result = full_match;
102 break;
103 }
104 else if (approx)
105 {
106 if (local_long_name.find(str: local_option) == 0)
107 {
108 result = approximate_match;
109 }
110 }
111 }
112
113 }
114
115 if (result != full_match)
116 {
117 std::string local_short_name(short_ignore_case ? tolower_(str: m_short_name) : m_short_name);
118
119 if (local_short_name == local_option)
120 {
121 result = full_match;
122 }
123 }
124
125 return result;
126 }
127
128 const std::string&
129 option_description::key(const std::string& option) const
130 {
131 // We make the arbitrary choise of using the first long
132 // name as the key, regardless of anything else
133 if (!m_long_names.empty()) {
134 const std::string& first_long_name = *m_long_names.begin();
135 if (first_long_name.find(c: '*') != string::npos)
136 // The '*' character means we're long_name
137 // matches only part of the input. So, returning
138 // long name will remove some of the information,
139 // and we have to return the option as specified
140 // in the source.
141 return option;
142 else
143 return first_long_name;
144 }
145 else
146 return m_short_name;
147 }
148
149 std::string
150 option_description::canonical_display_name(int prefix_style) const
151 {
152 // We prefer the first long name over any others
153 if (!m_long_names.empty())
154 {
155 if (prefix_style == command_line_style::allow_long)
156 return "--" + *m_long_names.begin();
157 if (prefix_style == command_line_style::allow_long_disguise)
158 return "-" + *m_long_names.begin();
159 }
160 // sanity check: m_short_name[0] should be '-' or '/'
161 if (m_short_name.length() == 2)
162 {
163 if (prefix_style == command_line_style::allow_slash_for_short)
164 return string("/") + m_short_name[1];
165 if (prefix_style == command_line_style::allow_dash_for_short)
166 return string("-") + m_short_name[1];
167 }
168 if (!m_long_names.empty())
169 return *m_long_names.begin();
170 else
171 return m_short_name;
172 }
173
174
175 const std::string&
176 option_description::long_name() const
177 {
178 static std::string empty_string("");
179 return m_long_names.empty() ? empty_string : *m_long_names.begin();
180 }
181
182 const std::pair<const std::string*, std::size_t>
183 option_description::long_names() const
184 {
185 // reinterpret_cast is to please msvc 10.
186 return (m_long_names.empty())
187 ? std::pair<const std::string*, size_t>(reinterpret_cast<const std::string*>(0), 0 )
188 : std::pair<const std::string*, size_t>( &(*m_long_names.begin()), m_long_names.size());
189 }
190
191 option_description&
192 option_description::set_names(const char* _names)
193 {
194 m_long_names.clear();
195 std::istringstream iss(_names);
196 std::string name;
197
198 while(std::getline(in&: iss, str&: name, delim: ',')) {
199 m_long_names.push_back(x: name);
200 }
201 assert(!m_long_names.empty() && "No option names were specified");
202
203 bool try_interpreting_last_name_as_a_switch = m_long_names.size() > 1;
204 if (try_interpreting_last_name_as_a_switch) {
205 const std::string& last_name = *m_long_names.rbegin();
206 if (last_name.length() == 1) {
207 m_short_name = '-' + last_name;
208 m_long_names.pop_back();
209 // The following caters to the (valid) input of ",c" for some
210 // character c, where the caller only wants this option to have
211 // a short name.
212 if (m_long_names.size() == 1 && (*m_long_names.begin()).empty()) {
213 m_long_names.clear();
214 }
215 }
216 }
217 // We could theoretically also ensure no remaining long names
218 // are empty, or that none of them have length 1
219 return *this;
220 }
221
222 const std::string&
223 option_description::description() const
224 {
225 return m_description;
226 }
227
228 shared_ptr<const value_semantic>
229 option_description::semantic() const
230 {
231 return m_value_semantic;
232 }
233
234 std::string
235 option_description::format_name() const
236 {
237 if (!m_short_name.empty())
238 {
239 return m_long_names.empty()
240 ? m_short_name
241 : string(m_short_name).append(s: " [ --").
242 append(str: *m_long_names.begin()).append(s: " ]");
243 }
244 return string("--").append(str: *m_long_names.begin());
245 }
246
247 std::string
248 option_description::format_parameter() const
249 {
250 if (m_value_semantic->max_tokens() != 0)
251 return m_value_semantic->name();
252 else
253 return "";
254 }
255
256 options_description_easy_init::
257 options_description_easy_init(options_description* owner)
258 : owner(owner)
259 {}
260
261 options_description_easy_init&
262 options_description_easy_init::
263 operator()(const char* name,
264 const char* description)
265 {
266 // Create untypes semantic which accepts zero tokens: i.e.
267 // no value can be specified on command line.
268 // FIXME: does not look exception-safe
269 shared_ptr<option_description> d(
270 new option_description(name, new untyped_value(true), description));
271
272 owner->add(desc: d);
273 return *this;
274 }
275
276 options_description_easy_init&
277 options_description_easy_init::
278 operator()(const char* name,
279 const value_semantic* s)
280 {
281 shared_ptr<option_description> d(new option_description(name, s));
282 owner->add(desc: d);
283 return *this;
284 }
285
286 options_description_easy_init&
287 options_description_easy_init::
288 operator()(const char* name,
289 const value_semantic* s,
290 const char* description)
291 {
292 shared_ptr<option_description> d(new option_description(name, s, description));
293
294 owner->add(desc: d);
295 return *this;
296 }
297
298 const unsigned options_description::m_default_line_length = 80;
299
300 options_description::options_description(unsigned line_length,
301 unsigned min_description_length)
302 : m_line_length(line_length)
303 , m_min_description_length(min_description_length)
304 {
305 // we require a space between the option and description parts, so add 1.
306 assert(m_min_description_length < m_line_length - 1);
307 }
308
309 options_description::options_description(const std::string& caption,
310 unsigned line_length,
311 unsigned min_description_length)
312 : m_caption(caption)
313 , m_line_length(line_length)
314 , m_min_description_length(min_description_length)
315 {
316 // we require a space between the option and description parts, so add 1.
317 assert(m_min_description_length < m_line_length - 1);
318 }
319
320 void
321 options_description::add(shared_ptr<option_description> desc)
322 {
323 m_options.push_back(x: desc);
324 belong_to_group.push_back(x: false);
325 }
326
327 options_description&
328 options_description::add(const options_description& desc)
329 {
330 shared_ptr<options_description> d(new options_description(desc));
331 groups.push_back(x: d);
332
333 for (size_t i = 0; i < desc.m_options.size(); ++i) {
334 add(desc: desc.m_options[i]);
335 belong_to_group.back() = true;
336 }
337
338 return *this;
339 }
340
341 options_description_easy_init
342 options_description::add_options()
343 {
344 return options_description_easy_init(this);
345 }
346
347 const option_description&
348 options_description::find(const std::string& name,
349 bool approx,
350 bool long_ignore_case,
351 bool short_ignore_case) const
352 {
353 const option_description* d = find_nothrow(name, approx,
354 long_ignore_case, short_ignore_case);
355 if (!d)
356 boost::throw_exception(e: unknown_option());
357 return *d;
358 }
359
360 const std::vector< shared_ptr<option_description> >&
361 options_description::options() const
362 {
363 return m_options;
364 }
365
366 const option_description*
367 options_description::find_nothrow(const std::string& name,
368 bool approx,
369 bool long_ignore_case,
370 bool short_ignore_case) const
371 {
372 shared_ptr<option_description> found;
373 bool had_full_match = false;
374 vector<string> approximate_matches;
375 vector<string> full_matches;
376
377 // We use linear search because matching specified option
378 // name with the declared option name need to take care about
379 // case sensitivity and trailing '*' and so we can't use simple map.
380 for(unsigned i = 0; i < m_options.size(); ++i)
381 {
382 option_description::match_result r =
383 m_options[i]->match(option: name, approx, long_ignore_case, short_ignore_case);
384
385 if (r == option_description::no_match)
386 continue;
387
388 if (r == option_description::full_match)
389 {
390 full_matches.push_back(x: m_options[i]->key(option: name));
391 found = m_options[i];
392 had_full_match = true;
393 }
394 else
395 {
396 // FIXME: the use of 'key' here might not
397 // be the best approach.
398 approximate_matches.push_back(x: m_options[i]->key(option: name));
399 if (!had_full_match)
400 found = m_options[i];
401 }
402 }
403 if (full_matches.size() > 1)
404 boost::throw_exception(e: ambiguous_option(full_matches));
405
406 // If we have a full match, and an approximate match,
407 // ignore approximate match instead of reporting error.
408 // Say, if we have options "all" and "all-chroots", then
409 // "--all" on the command line should select the first one,
410 // without ambiguity.
411 if (full_matches.empty() && approximate_matches.size() > 1)
412 boost::throw_exception(e: ambiguous_option(approximate_matches));
413
414 return found.get();
415 }
416
417 BOOST_PROGRAM_OPTIONS_DECL
418 std::ostream& operator<<(std::ostream& os, const options_description& desc)
419 {
420 desc.print(os);
421 return os;
422 }
423
424 namespace {
425
426 /* Given a string 'par', that contains no newline characters
427 outputs it to 'os' with wordwrapping, that is, as several
428 line.
429
430 Each output line starts with 'indent' space characters,
431 following by characters from 'par'. The total length of
432 line is no longer than 'line_length'.
433
434 */
435 void format_paragraph(std::ostream& os,
436 std::string par,
437 unsigned indent,
438 unsigned line_length)
439 {
440 // Through reminder of this function, 'line_length' will
441 // be the length available for characters, not including
442 // indent.
443 assert(indent < line_length);
444 line_length -= indent;
445
446 // index of tab (if present) is used as additional indent relative
447 // to first_column_width if paragrapth is spanned over multiple
448 // lines if tab is not on first line it is ignored
449 string::size_type par_indent = par.find(c: '\t');
450
451 if (par_indent == string::npos)
452 {
453 par_indent = 0;
454 }
455 else
456 {
457 // only one tab per paragraph allowed
458 if (count(first: par.begin(), last: par.end(), value: '\t') > 1)
459 {
460 boost::throw_exception(e: program_options::error(
461 "Only one tab per paragraph is allowed in the options description"));
462 }
463
464 // erase tab from string
465 par.erase(pos: par_indent, n: 1);
466
467 // this assert may fail due to user error or
468 // environment conditions!
469 assert(par_indent < line_length);
470
471 // ignore tab if not on first line
472 if (par_indent >= line_length)
473 {
474 par_indent = 0;
475 }
476 }
477
478 if (par.size() < line_length)
479 {
480 os << par;
481 }
482 else
483 {
484 string::const_iterator line_begin = par.begin();
485 const string::const_iterator par_end = par.end();
486
487 bool first_line = true; // of current paragraph!
488
489 while (line_begin < par_end) // paragraph lines
490 {
491 if (!first_line)
492 {
493 // If line starts with space, but second character
494 // is not space, remove the leading space.
495 // We don't remove double spaces because those
496 // might be intentianal.
497 if ((*line_begin == ' ') &&
498 ((line_begin + 1 < par_end) &&
499 (*(line_begin + 1) != ' ')))
500 {
501 line_begin += 1; // line_begin != line_end
502 }
503 }
504
505 // Take care to never increment the iterator past
506 // the end, since MSVC 8.0 (brokenly), assumes that
507 // doing that, even if no access happens, is a bug.
508 unsigned remaining = static_cast<unsigned>(std::distance(first: line_begin, last: par_end));
509 string::const_iterator line_end = line_begin +
510 ((remaining < line_length) ? remaining : line_length);
511
512 // prevent chopped words
513 // Is line_end between two non-space characters?
514 if ((*(line_end - 1) != ' ') &&
515 ((line_end < par_end) && (*line_end != ' ')))
516 {
517 // find last ' ' in the second half of the current paragraph line
518 string::const_iterator last_space =
519 find(first: reverse_iterator<string::const_iterator>(line_end),
520 last: reverse_iterator<string::const_iterator>(line_begin),
521 val: ' ')
522 .base();
523
524 if (last_space != line_begin)
525 {
526 // is last_space within the second half ot the
527 // current line
528 if (static_cast<unsigned>(std::distance(first: last_space, last: line_end)) <
529 (line_length / 2))
530 {
531 line_end = last_space;
532 }
533 }
534 } // prevent chopped words
535
536 // write line to stream
537 copy(first: line_begin, last: line_end, result: ostream_iterator<char>(os));
538
539 if (first_line)
540 {
541 indent += static_cast<unsigned>(par_indent);
542 line_length -= static_cast<unsigned>(par_indent); // there's less to work with now
543 first_line = false;
544 }
545
546 // more lines to follow?
547 if (line_end != par_end)
548 {
549 os << '\n';
550
551 for(unsigned pad = indent; pad > 0; --pad)
552 {
553 os.put(c: ' ');
554 }
555 }
556
557 // next line starts after of this line
558 line_begin = line_end;
559 } // paragraph lines
560 }
561 }
562
563 void format_description(std::ostream& os,
564 const std::string& desc,
565 unsigned first_column_width,
566 unsigned line_length)
567 {
568 // we need to use one char less per line to work correctly if actual
569 // console has longer lines
570 assert(line_length > 1);
571 if (line_length > 1)
572 {
573 --line_length;
574 }
575
576 // line_length must be larger than first_column_width
577 // this assert may fail due to user error or environment conditions!
578 assert(line_length > first_column_width);
579
580 // Note: can't use 'tokenizer' as name of typedef -- borland
581 // will consider uses of 'tokenizer' below as uses of
582 // boost::tokenizer, not typedef.
583 typedef boost::tokenizer<boost::char_separator<char> > tok;
584
585 tok paragraphs(
586 desc,
587 char_separator<char>("\n", "", boost::keep_empty_tokens));
588
589 tok::const_iterator par_iter = paragraphs.begin();
590 const tok::const_iterator par_end = paragraphs.end();
591
592 while (par_iter != par_end) // paragraphs
593 {
594 format_paragraph(os, par: *par_iter, indent: first_column_width,
595 line_length);
596
597 ++par_iter;
598
599 // prepair next line if any
600 if (par_iter != par_end)
601 {
602 os << '\n';
603
604 for(unsigned pad = first_column_width; pad > 0; --pad)
605 {
606 os.put(c: ' ');
607 }
608 }
609 } // paragraphs
610 }
611
612 void format_one(std::ostream& os, const option_description& opt,
613 unsigned first_column_width, unsigned line_length)
614 {
615 stringstream ss;
616 ss << " " << opt.format_name() << ' ' << opt.format_parameter();
617
618 // Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
619 os << ss.str();
620
621 if (!opt.description().empty())
622 {
623 if (ss.str().size() >= first_column_width)
624 {
625 os.put(c: '\n'); // first column is too long, lets put description in new line
626 for (unsigned pad = first_column_width; pad > 0; --pad)
627 {
628 os.put(c: ' ');
629 }
630 } else {
631 for(unsigned pad = first_column_width - static_cast<unsigned>(ss.str().size()); pad > 0; --pad)
632 {
633 os.put(c: ' ');
634 }
635 }
636
637 format_description(os, desc: opt.description(),
638 first_column_width, line_length);
639 }
640 }
641 }
642
643 unsigned
644 options_description::get_option_column_width() const
645 {
646 /* Find the maximum width of the option column */
647 unsigned width(23);
648 unsigned i; // vc6 has broken for loop scoping
649 for (i = 0; i < m_options.size(); ++i)
650 {
651 const option_description& opt = *m_options[i];
652 stringstream ss;
653 ss << " " << opt.format_name() << ' ' << opt.format_parameter();
654 width = (max)(a: width, b: static_cast<unsigned>(ss.str().size()));
655 }
656
657 /* Get width of groups as well*/
658 for (unsigned j = 0; j < groups.size(); ++j)
659 width = max(a: width, b: groups[j]->get_option_column_width());
660
661 /* this is the column were description should start, if first
662 column is longer, we go to a new line */
663 const unsigned start_of_description_column = m_line_length - m_min_description_length;
664
665 width = (min)(a: width, b: start_of_description_column-1);
666
667 /* add an additional space to improve readability */
668 ++width;
669 return width;
670 }
671
672 void
673 options_description::print(std::ostream& os, unsigned width) const
674 {
675 if (!m_caption.empty())
676 os << m_caption << ":\n";
677
678 if (!width)
679 width = get_option_column_width();
680
681 /* The options formatting style is stolen from Subversion. */
682 for (unsigned i = 0; i < m_options.size(); ++i)
683 {
684 if (belong_to_group[i])
685 continue;
686
687 const option_description& opt = *m_options[i];
688
689 format_one(os, opt, first_column_width: width, line_length: m_line_length);
690
691 os << "\n";
692 }
693
694 for (unsigned j = 0; j < groups.size(); ++j) {
695 os << "\n";
696 groups[j]->print(os, width);
697 }
698 }
699
700}}
701

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