summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '3rdParty/Boost/src/libs/program_options/src/options_description.cpp')
-rw-r--r--3rdParty/Boost/src/libs/program_options/src/options_description.cpp621
1 files changed, 621 insertions, 0 deletions
diff --git a/3rdParty/Boost/src/libs/program_options/src/options_description.cpp b/3rdParty/Boost/src/libs/program_options/src/options_description.cpp
new file mode 100644
index 0000000..bfd113d
--- /dev/null
+++ b/3rdParty/Boost/src/libs/program_options/src/options_description.cpp
@@ -0,0 +1,621 @@
+// Copyright Vladimir Prus 2002-2004.
+// Copyright Bertolt Mildner 2004.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt
+// or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+
+#define BOOST_PROGRAM_OPTIONS_SOURCE
+#include <boost/program_options/config.hpp>
+#include <boost/program_options/options_description.hpp>
+// FIXME: this is only to get multiple_occurences class
+// should move that to a separate headers.
+#include <boost/program_options/parsers.hpp>
+
+
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/detail/workaround.hpp>
+#include <boost/throw_exception.hpp>
+
+#include <cassert>
+#include <climits>
+#include <cstring>
+#include <cstdarg>
+#include <sstream>
+#include <iterator>
+using namespace std;
+
+namespace boost { namespace program_options {
+
+ namespace {
+
+ template< class charT >
+ std::basic_string< charT > tolower_(const std::basic_string< charT >& str)
+ {
+ std::basic_string< charT > result;
+ for (typename std::basic_string< charT >::size_type i = 0; i < str.size(); ++i)
+ {
+ result.append(1, static_cast< charT >(std::tolower(str[i])));
+ }
+ return result;
+ }
+
+ } // unnamed namespace
+
+
+ option_description::option_description()
+ {
+ }
+
+ option_description::
+ option_description(const char* name,
+ const value_semantic* s)
+ : m_value_semantic(s)
+ {
+ this->set_name(name);
+ }
+
+
+ option_description::
+ option_description(const char* name,
+ const value_semantic* s,
+ const char* description)
+ : m_description(description), m_value_semantic(s)
+ {
+ this->set_name(name);
+ }
+
+ option_description::~option_description()
+ {
+ }
+
+ option_description::match_result
+ option_description::match(const std::string& option,
+ bool approx,
+ bool long_ignore_case,
+ bool short_ignore_case) const
+ {
+ match_result result = no_match;
+
+ std::string local_long_name((long_ignore_case ? tolower_(m_long_name) : m_long_name));
+
+ if (!local_long_name.empty()) {
+
+ std::string local_option = (long_ignore_case ? tolower_(option) : option);
+
+ if (*local_long_name.rbegin() == '*')
+ {
+ // The name ends with '*'. Any specified name with the given
+ // prefix is OK.
+ if (local_option.find(local_long_name.substr(0, local_long_name.length()-1))
+ == 0)
+ result = approximate_match;
+ }
+
+ if (local_long_name == local_option)
+ {
+ result = full_match;
+ }
+ else if (approx)
+ {
+ if (local_long_name.find(local_option) == 0)
+ {
+ result = approximate_match;
+ }
+ }
+ }
+
+ if (result != full_match)
+ {
+ std::string local_option(short_ignore_case ? tolower_(option) : option);
+ std::string local_short_name(short_ignore_case ? tolower_(m_short_name) : m_short_name);
+
+ if (local_short_name == local_option)
+ {
+ result = full_match;
+ }
+ }
+
+ return result;
+ }
+
+ const std::string&
+ option_description::key(const std::string& option) const
+ {
+ if (!m_long_name.empty())
+ if (m_long_name.find('*') != string::npos)
+ // The '*' character means we're long_name
+ // matches only part of the input. So, returning
+ // long name will remove some of the information,
+ // and we have to return the option as specified
+ // in the source.
+ return option;
+ else
+ return m_long_name;
+ else
+ return m_short_name;
+ }
+
+ const std::string&
+ option_description::long_name() const
+ {
+ return m_long_name;
+ }
+
+ option_description&
+ option_description::set_name(const char* _name)
+ {
+ std::string name(_name);
+ string::size_type n = name.find(',');
+ if (n != string::npos) {
+ assert(n == name.size()-2);
+ m_long_name = name.substr(0, n);
+ m_short_name = '-' + name.substr(n+1,1);
+ } else {
+ m_long_name = name;
+ }
+ return *this;
+ }
+
+ const std::string&
+ option_description::description() const
+ {
+ return m_description;
+ }
+
+ shared_ptr<const value_semantic>
+ option_description::semantic() const
+ {
+ return m_value_semantic;
+ }
+
+ std::string
+ option_description::format_name() const
+ {
+ if (!m_short_name.empty())
+ return string(m_short_name).append(" [ --").
+ append(m_long_name).append(" ]");
+ else
+ return string("--").append(m_long_name);
+ }
+
+ std::string
+ option_description::format_parameter() const
+ {
+ if (m_value_semantic->max_tokens() != 0)
+ return m_value_semantic->name();
+ else
+ return "";
+ }
+
+ options_description_easy_init::
+ options_description_easy_init(options_description* owner)
+ : owner(owner)
+ {}
+
+ options_description_easy_init&
+ options_description_easy_init::
+ operator()(const char* name,
+ const char* description)
+ {
+ // Create untypes semantic which accepts zero tokens: i.e.
+ // no value can be specified on command line.
+ // FIXME: does not look exception-safe
+ shared_ptr<option_description> d(
+ new option_description(name, new untyped_value(true), description));
+
+ owner->add(d);
+ return *this;
+ }
+
+ options_description_easy_init&
+ options_description_easy_init::
+ operator()(const char* name,
+ const value_semantic* s)
+ {
+ shared_ptr<option_description> d(new option_description(name, s));
+ owner->add(d);
+ return *this;
+ }
+
+ options_description_easy_init&
+ options_description_easy_init::
+ operator()(const char* name,
+ const value_semantic* s,
+ const char* description)
+ {
+ shared_ptr<option_description> d(new option_description(name, s, description));
+
+ owner->add(d);
+ return *this;
+ }
+
+ const unsigned options_description::m_default_line_length = 80;
+
+ options_description::options_description(unsigned line_length,
+ unsigned min_description_length)
+ : m_line_length(line_length)
+ , m_min_description_length(min_description_length)
+ {
+ // we require a space between the option and description parts, so add 1.
+ assert(m_min_description_length < m_line_length - 1);
+ }
+
+ options_description::options_description(const std::string& caption,
+ unsigned line_length,
+ unsigned min_description_length)
+ : m_caption(caption)
+ , m_line_length(line_length)
+ , m_min_description_length(min_description_length)
+ {
+ // we require a space between the option and description parts, so add 1.
+ assert(m_min_description_length < m_line_length - 1);
+ }
+
+ void
+ options_description::add(shared_ptr<option_description> desc)
+ {
+ m_options.push_back(desc);
+ belong_to_group.push_back(false);
+ }
+
+ options_description&
+ options_description::add(const options_description& desc)
+ {
+ shared_ptr<options_description> d(new options_description(desc));
+ groups.push_back(d);
+
+ for (size_t i = 0; i < desc.m_options.size(); ++i) {
+ add(desc.m_options[i]);
+ belong_to_group.back() = true;
+ }
+
+ return *this;
+ }
+
+ options_description_easy_init
+ options_description::add_options()
+ {
+ return options_description_easy_init(this);
+ }
+
+ const option_description&
+ options_description::find(const std::string& name,
+ bool approx,
+ bool long_ignore_case,
+ bool short_ignore_case) const
+ {
+ const option_description* d = find_nothrow(name, approx,
+ long_ignore_case, short_ignore_case);
+ if (!d)
+ boost::throw_exception(unknown_option(name));
+ return *d;
+ }
+
+ const std::vector< shared_ptr<option_description> >&
+ options_description::options() const
+ {
+ return m_options;
+ }
+
+ const option_description*
+ options_description::find_nothrow(const std::string& name,
+ bool approx,
+ bool long_ignore_case,
+ bool short_ignore_case) const
+ {
+ shared_ptr<option_description> found;
+ vector<string> approximate_matches;
+ vector<string> full_matches;
+
+ // We use linear search because matching specified option
+ // name with the declared option name need to take care about
+ // case sensitivity and trailing '*' and so we can't use simple map.
+ for(unsigned i = 0; i < m_options.size(); ++i)
+ {
+ option_description::match_result r =
+ m_options[i]->match(name, approx, long_ignore_case, short_ignore_case);
+
+ if (r == option_description::no_match)
+ continue;
+
+ if (r == option_description::full_match)
+ {
+ full_matches.push_back(m_options[i]->key(name));
+ }
+ else
+ {
+ // FIXME: the use of 'key' here might not
+ // be the best approach.
+ approximate_matches.push_back(m_options[i]->key(name));
+ }
+
+ found = m_options[i];
+ }
+ if (full_matches.size() > 1)
+ boost::throw_exception(
+ ambiguous_option(name, full_matches));
+
+ // If we have a full match, and an approximate match,
+ // ignore approximate match instead of reporting error.
+ // Say, if we have options "all" and "all-chroots", then
+ // "--all" on the command line should select the first one,
+ // without ambiguity.
+ if (full_matches.empty() && approximate_matches.size() > 1)
+ boost::throw_exception(
+ ambiguous_option(name, approximate_matches));
+
+ return found.get();
+ }
+
+ BOOST_PROGRAM_OPTIONS_DECL
+ std::ostream& operator<<(std::ostream& os, const options_description& desc)
+ {
+ desc.print(os);
+ return os;
+ }
+
+ namespace {
+
+ /* Given a string 'par', that contains no newline characters
+ outputs it to 'os' with wordwrapping, that is, as several
+ line.
+
+ Each output line starts with 'indent' space characters,
+ following by characters from 'par'. The total length of
+ line is no longer than 'line_length'.
+
+ */
+ void format_paragraph(std::ostream& os,
+ std::string par,
+ unsigned indent,
+ unsigned line_length)
+ {
+ // Through reminder of this function, 'line_length' will
+ // be the length available for characters, not including
+ // indent.
+ assert(indent < line_length);
+ line_length -= indent;
+
+ // index of tab (if present) is used as additional indent relative
+ // to first_column_width if paragrapth is spanned over multiple
+ // lines if tab is not on first line it is ignored
+ string::size_type par_indent = par.find('\t');
+
+ if (par_indent == string::npos)
+ {
+ par_indent = 0;
+ }
+ else
+ {
+ // only one tab per paragraph allowed
+ if (count(par.begin(), par.end(), '\t') > 1)
+ {
+ boost::throw_exception(program_options::error(
+ "Only one tab per paragraph is allowed"));
+ }
+
+ // erase tab from string
+ par.erase(par_indent, 1);
+
+ // this assert may fail due to user error or
+ // environment conditions!
+ assert(par_indent < line_length);
+
+ // ignore tab if not on first line
+ if (par_indent >= line_length)
+ {
+ par_indent = 0;
+ }
+ }
+
+ if (par.size() < line_length)
+ {
+ os << par;
+ }
+ else
+ {
+ string::const_iterator line_begin = par.begin();
+ const string::const_iterator par_end = par.end();
+
+ bool first_line = true; // of current paragraph!
+
+ while (line_begin < par_end) // paragraph lines
+ {
+ if (!first_line)
+ {
+ // If line starts with space, but second character
+ // is not space, remove the leading space.
+ // We don't remove double spaces because those
+ // might be intentianal.
+ if ((*line_begin == ' ') &&
+ ((line_begin + 1 < par_end) &&
+ (*(line_begin + 1) != ' ')))
+ {
+ line_begin += 1; // line_begin != line_end
+ }
+ }
+
+ // Take care to never increment the iterator past
+ // the end, since MSVC 8.0 (brokenly), assumes that
+ // doing that, even if no access happens, is a bug.
+ unsigned remaining = distance(line_begin, par_end);
+ string::const_iterator line_end = line_begin +
+ ((remaining < line_length) ? remaining : line_length);
+
+ // prevent chopped words
+ // Is line_end between two non-space characters?
+ if ((*(line_end - 1) != ' ') &&
+ ((line_end < par_end) && (*line_end != ' ')))
+ {
+ // find last ' ' in the second half of the current paragraph line
+ string::const_iterator last_space =
+ find(reverse_iterator<string::const_iterator>(line_end),
+ reverse_iterator<string::const_iterator>(line_begin),
+ ' ')
+ .base();
+
+ if (last_space != line_begin)
+ {
+ // is last_space within the second half ot the
+ // current line
+ if (static_cast<unsigned>(distance(last_space, line_end)) <
+ (line_length / 2))
+ {
+ line_end = last_space;
+ }
+ }
+ } // prevent chopped words
+
+ // write line to stream
+ copy(line_begin, line_end, ostream_iterator<char>(os));
+
+ if (first_line)
+ {
+ indent += par_indent;
+ line_length -= par_indent; // there's less to work with now
+ first_line = false;
+ }
+
+ // more lines to follow?
+ if (line_end != par_end)
+ {
+ os << '\n';
+
+ for(unsigned pad = indent; pad > 0; --pad)
+ {
+ os.put(' ');
+ }
+ }
+
+ // next line starts after of this line
+ line_begin = line_end;
+ } // paragraph lines
+ }
+ }
+
+ void format_description(std::ostream& os,
+ const std::string& desc,
+ unsigned first_column_width,
+ unsigned line_length)
+ {
+ // we need to use one char less per line to work correctly if actual
+ // console has longer lines
+ assert(line_length > 1);
+ if (line_length > 1)
+ {
+ --line_length;
+ }
+
+ // line_length must be larger than first_column_width
+ // this assert may fail due to user error or environment conditions!
+ assert(line_length > first_column_width);
+
+ // Note: can't use 'tokenizer' as name of typedef -- borland
+ // will consider uses of 'tokenizer' below as uses of
+ // boost::tokenizer, not typedef.
+ typedef boost::tokenizer<boost::char_separator<char> > tok;
+
+ tok paragraphs(
+ desc,
+ char_separator<char>("\n", "", boost::keep_empty_tokens));
+
+ tok::const_iterator par_iter = paragraphs.begin();
+ const tok::const_iterator par_end = paragraphs.end();
+
+ while (par_iter != par_end) // paragraphs
+ {
+ format_paragraph(os, *par_iter, first_column_width,
+ line_length);
+
+ ++par_iter;
+
+ // prepair next line if any
+ if (par_iter != par_end)
+ {
+ os << '\n';
+
+ for(unsigned pad = first_column_width; pad > 0; --pad)
+ {
+ os.put(' ');
+ }
+ }
+ } // paragraphs
+ }
+
+ void format_one(std::ostream& os, const option_description& opt,
+ unsigned first_column_width, unsigned line_length)
+ {
+ stringstream ss;
+ ss << " " << opt.format_name() << ' ' << opt.format_parameter();
+
+ // Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
+ os << ss.str();
+
+ if (!opt.description().empty())
+ {
+ if (ss.str().size() >= first_column_width)
+ {
+ os.put('\n'); // first column is too long, lets put description in new line
+ for (unsigned pad = first_column_width; pad > 0; --pad)
+ {
+ os.put(' ');
+ }
+ } else {
+ for(unsigned pad = first_column_width - ss.str().size(); pad > 0; --pad)
+ {
+ os.put(' ');
+ }
+ }
+
+ format_description(os, opt.description(),
+ first_column_width, line_length);
+ }
+ }
+ }
+
+ void
+ options_description::print(std::ostream& os) const
+ {
+ if (!m_caption.empty())
+ os << m_caption << ":\n";
+
+ /* Find the maximum width of the option column */
+ unsigned width(23);
+ unsigned i; // vc6 has broken for loop scoping
+ for (i = 0; i < m_options.size(); ++i)
+ {
+ const option_description& opt = *m_options[i];
+ stringstream ss;
+ ss << " " << opt.format_name() << ' ' << opt.format_parameter();
+ width = (max)(width, static_cast<unsigned>(ss.str().size()));
+ }
+ /* this is the column were description should start, if first
+ column is longer, we go to a new line */
+ const unsigned start_of_description_column = m_line_length - m_min_description_length;
+
+ width = (min)(width, start_of_description_column-1);
+
+ /* add an additional space to improve readability */
+ ++width;
+
+ /* The options formatting style is stolen from Subversion. */
+ for (i = 0; i < m_options.size(); ++i)
+ {
+ if (belong_to_group[i])
+ continue;
+
+ const option_description& opt = *m_options[i];
+
+ format_one(os, opt, width, m_line_length);
+
+ os << "\n";
+ }
+
+ for (unsigned j = 0; j < groups.size(); ++j) {
+ os << "\n" << *groups[j];
+ }
+ }
+
+}}