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