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>
27 using namespace std;
28 
29 namespace boost { namespace program_options {
30 
31    namespace {
32 
33        template< class charT >
tolower_(const std::basic_string<charT> & str)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 
option_description()47     option_description::option_description()
48     {
49     }
50 
51     option_description::
option_description(const char * names,const value_semantic * s)52     option_description(const char* names,
53                        const value_semantic* s)
54     : m_value_semantic(s)
55     {
56         this->set_names(names);
57     }
58 
59 
60     option_description::
option_description(const char * names,const value_semantic * s,const char * description)61     option_description(const char* names,
62                        const value_semantic* s,
63                        const char* description)
64     : m_description(description), m_value_semantic(s)
65     {
66         this->set_names(names);
67     }
68 
~option_description()69     option_description::~option_description()
70     {
71     }
72 
73     option_description::match_result
match(const std::string & option,bool approx,bool long_ignore_case,bool short_ignore_case) const74     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;
80         std::string local_option = (long_ignore_case ? tolower_(option) : option);
81 
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));
85 
86             if (!local_long_name.empty()) {
87 
88 
89                 if ((result == no_match) && (*local_long_name.rbegin() == '*'))
90                 {
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                     }
109                 }
110             }
111 
112         }
113 
114         if (result != full_match)
115         {
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&
key(const std::string & option) const128     option_description::key(const std::string& option) const
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)
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
142                 return first_long_name;
143         }
144         else
145             return m_short_name;
146     }
147 
148     std::string
canonical_display_name(int prefix_style) const149     option_description::canonical_display_name(int prefix_style) const
150     {
151         // We prefer the first long name over any others
152         if (!m_long_names.empty())
153         {
154             if (prefix_style == command_line_style::allow_long)
155                 return "--" + *m_long_names.begin();
156             if (prefix_style == command_line_style::allow_long_disguise)
157                 return "-" + *m_long_names.begin();
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         }
167         if (!m_long_names.empty())
168             return *m_long_names.begin();
169         else
170             return m_short_name;
171     }
172 
173 
174     const std::string&
long_name() const175     option_description::long_name() const
176     {
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>
long_names() const182     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());
188     }
189 
190     option_description&
set_names(const char * _names)191     option_description::set_names(const char* _names)
192     {
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             }
215         }
216         // We could theoretically also ensure no remaining long names
217         // are empty, or that none of them have length 1
218         return *this;
219     }
220 
221     const std::string&
description() const222     option_description::description() const
223     {
224         return m_description;
225     }
226 
227     shared_ptr<const value_semantic>
semantic() const228     option_description::semantic() const
229     {
230         return m_value_semantic;
231     }
232 
233     std::string
format_name() const234     option_description::format_name() const
235     {
236         if (!m_short_name.empty())
237         {
238             return m_long_names.empty()
239                 ? m_short_name
240                 : string(m_short_name).append(" [ --").
241                   append(*m_long_names.begin()).append(" ]");
242         }
243         return string("--").append(*m_long_names.begin());
244     }
245 
246     std::string
format_parameter() const247     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::
options_description_easy_init(options_description * owner)256     options_description_easy_init(options_description* owner)
257     : owner(owner)
258     {}
259 
260     options_description_easy_init&
261     options_description_easy_init::
operator ()(const char * name,const char * description)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::
operator ()(const char * name,const value_semantic * s)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::
operator ()(const char * name,const value_semantic * s,const char * description)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 
options_description(unsigned line_length,unsigned min_description_length)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 
options_description(const std::string & caption,unsigned line_length,unsigned min_description_length)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
add(shared_ptr<option_description> desc)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&
add(const options_description & desc)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
add_options()341     options_description::add_options()
342     {
343         return options_description_easy_init(this);
344     }
345 
346     const option_description&
find(const std::string & name,bool approx,bool long_ignore_case,bool short_ignore_case) const347     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> >&
options() const360     options_description::options() const
361     {
362         return m_options;
363     }
364 
365     const option_description*
find_nothrow(const std::string & name,bool approx,bool long_ignore_case,bool short_ignore_case) const366     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
operator <<(std::ostream & os,const options_description & desc)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         */
format_paragraph(std::ostream & os,std::string par,unsigned indent,unsigned line_length)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 
format_description(std::ostream & os,const std::string & desc,unsigned first_column_width,unsigned line_length)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 
format_one(std::ostream & os,const option_description & opt,unsigned first_column_width,unsigned line_length)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
get_option_column_width() const643     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
print(std::ostream & os,unsigned width) const672     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 }}
700