Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/program_options/src/options_description.cpp @ 12

Last change on this file since 12 was 12, checked in by landauf, 17 years ago

added boost

File size: 16.8 KB
Line 
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_occureces 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>
26using namespace std;
27
28namespace boost { namespace program_options {
29
30    option_description::option_description()
31    {
32    }
33   
34    option_description::
35    option_description(const char* name,
36                       const value_semantic* s)
37    : m_value_semantic(s)
38    {
39        this->set_name(name);
40    }
41                                           
42
43    option_description::
44    option_description(const char* name,
45                       const value_semantic* s,
46                       const char* description)
47    : m_description(description), m_value_semantic(s)
48    {
49        this->set_name(name);
50    }
51
52    option_description::~option_description()
53    {
54    }
55
56    bool 
57    option_description::match(const std::string& option, bool approx) const
58    {
59        bool result = false;
60        if (!m_long_name.empty()) {
61
62            if (*m_long_name.rbegin() == '*')
63            {
64                // The name ends with '*'. Any specified name with the given
65                // prefix is OK.
66                if (option.find(m_long_name.substr(0, m_long_name.length()-1))
67                    == 0)
68                    result = true;
69            }
70
71            if (approx)
72            {
73                if (m_long_name.find(option) == 0)
74                    result = true;
75            }
76            else
77            {
78                if (m_long_name == option)
79                    result = true;
80            }
81        }
82         
83        if (m_short_name == option)
84            result = true;
85
86        return result;       
87    }
88
89    const std::string& 
90    option_description::key(const std::string& option) const
91    {       
92        if (!m_long_name.empty()) 
93            if (m_long_name.find('*') != string::npos)
94                // The '*' character means we're long_name
95                // matches only part of the input. So, returning
96                // long name will remove some of the information,
97                // and we have to return the option as specified
98                // in the source.
99                return option;
100            else
101                return m_long_name;
102        else
103            return m_short_name;
104    }
105
106    const std::string&
107    option_description::long_name() const
108    {
109        return m_long_name;
110    }
111
112    option_description&
113    option_description::set_name(const char* _name)
114    {
115        std::string name(_name);
116        string::size_type n = name.find(',');
117        if (n != string::npos) {
118            assert(n == name.size()-2);
119            m_long_name = name.substr(0, n);
120            m_short_name = '-' + name.substr(n+1,1);
121        } else {
122            m_long_name = name;
123        }
124        return *this;
125    }
126
127    const std::string&
128    option_description::description() const
129    {
130        return m_description;
131    }
132
133    shared_ptr<const value_semantic>
134    option_description::semantic() const
135    {
136        return m_value_semantic;
137    }
138   
139    std::string
140    option_description::format_name() const
141    {
142        if (!m_short_name.empty())
143            return string(m_short_name).append(" [ --").
144            append(m_long_name).append(" ]");
145        else
146            return string("--").append(m_long_name);
147    }
148
149    std::string
150    option_description::format_parameter() const
151    {
152        if (m_value_semantic->max_tokens() != 0)
153            return m_value_semantic->name();
154        else
155            return "";
156    }
157
158    options_description_easy_init::
159    options_description_easy_init(options_description* owner)
160    : owner(owner)
161    {}
162
163    options_description_easy_init&
164    options_description_easy_init::
165    operator()(const char* name,
166               const char* description)
167    {
168        // Create untypes semantic which accepts zero tokens: i.e.
169        // no value can be specified on command line.
170        // FIXME: does not look exception-safe
171        shared_ptr<option_description> d(
172            new option_description(name, new untyped_value(true), description));
173
174        owner->add(d);
175        return *this;
176    }
177
178    options_description_easy_init&
179    options_description_easy_init::
180    operator()(const char* name,
181               const value_semantic* s)
182    {
183        shared_ptr<option_description> d(new option_description(name, s));
184        owner->add(d);
185        return *this;
186    }
187
188    options_description_easy_init&
189    options_description_easy_init::
190    operator()(const char* name,
191               const value_semantic* s,
192               const char* description)
193    {
194        shared_ptr<option_description> d(new option_description(name, s, description));
195
196        owner->add(d);
197        return *this;
198    }
199
200    options_description::options_description(unsigned line_length)
201    : m_line_length(line_length)
202    {}
203
204    options_description::options_description(const string& caption,
205                                             unsigned line_length)
206    : m_caption(caption), m_line_length(line_length)
207    {}
208
209    void
210    options_description::add(shared_ptr<option_description> desc)
211    {
212        m_options.push_back(desc);
213        belong_to_group.push_back(false);
214    }
215
216    options_description&
217    options_description::add(const options_description& desc)
218    {
219        shared_ptr<options_description> d(new options_description(desc));
220        groups.push_back(d);
221
222        for (size_t i = 0; i < desc.m_options.size(); ++i) {
223            add(desc.m_options[i]);
224            belong_to_group.back() = true;
225        }
226
227        return *this;
228    }
229
230    options_description_easy_init
231    options_description::add_options()
232    {       
233        return options_description_easy_init(this);
234    }
235
236    const option_description&
237    options_description::find(const std::string& name, bool approx) const
238    {
239        const option_description* d = find_nothrow(name, approx);
240        if (!d)
241            boost::throw_exception(unknown_option(name));
242        return *d;
243    }
244
245    const std::vector< shared_ptr<option_description> >& 
246    options_description::options() const
247    {
248        return m_options;
249    }
250
251    const option_description* 
252    options_description::find_nothrow(const std::string& name, 
253                                      bool approx) const
254    {
255        int found = -1;
256        // We use linear search because matching specified option
257        // name with the declared option name need to take care about
258        // case sensitivity and trailing '*' and so we can't use simple map.
259        for(unsigned i = 0; i < m_options.size(); ++i)
260        {
261            if (m_options[i]->match(name, approx))
262            {
263                if (found != -1)
264                {
265                    vector<string> alts;
266                    // FIXME: the use of 'key' here might not
267                    // be the best approach.
268                    alts.push_back(m_options[found]->key(name));
269                    alts.push_back(m_options[i]->key(name));
270                    boost::throw_exception(ambiguous_option(name, alts));
271                }
272                else
273                {
274                    found = i;
275                }
276            }
277        }
278        if (found != -1) {
279            return m_options[found].get();
280        } else {
281            return 0;
282        }
283    }
284
285    BOOST_PROGRAM_OPTIONS_DECL
286    std::ostream& operator<<(std::ostream& os, const options_description& desc)
287    {
288        desc.print(os);
289        return os;
290    }
291
292    namespace {
293
294        void format_paragraph(std::ostream& os,
295                              std::string par,
296                              unsigned first_column_width,
297                              unsigned line_length)
298        {                   
299            // index of tab (if present) is used as additional indent relative
300            // to first_column_width if paragrapth is spanned over multiple
301            // lines if tab is not on first line it is ignored
302            string::size_type par_indent = par.find('\t');
303
304            if (par_indent == string::npos)
305            {
306                par_indent = 0;
307            }
308            else
309            {
310                // only one tab per paragraph allowed
311                if (count(par.begin(), par.end(), '\t') > 1)
312                {
313                    boost::throw_exception(program_options::error(
314                        "Only one tab per paragraph is allowed"));
315                }
316         
317                // erase tab from string
318                par.erase(par_indent, 1);
319
320                // this assert may fail due to user error or
321                // environment conditions!
322                assert(par_indent < (line_length - first_column_width));
323
324                // ignore tab if not on first line
325                if (par_indent >= (line_length - first_column_width))
326                {
327                    par_indent = 0;
328                }           
329            }
330         
331            if (par.size() < (line_length - first_column_width))
332            {
333                os << par;
334            }
335            else
336            {
337                string::const_iterator       line_begin = par.begin();
338                const string::const_iterator par_end = par.end();
339
340                bool first_line = true; // of current paragraph!
341           
342                unsigned indent = first_column_width;
343           
344                while (line_begin < par_end)  // paragraph lines
345                {
346                    if (!first_line)
347                    {
348                        // trimm leading single spaces
349                        // if (firstchar == ' ') &&
350                        //    ((exists(firstchar + 1) && (firstchar + 1 != ' '))
351                        if ((*line_begin == ' ') &&
352                            ((line_begin + 1 < par_end) &&
353                             (*(line_begin + 1) != ' ')))
354                        {
355                            line_begin += 1;  // line_begin != line_end
356                        }
357                    }
358
359                    string::const_iterator line_end;
360               
361                    if (line_begin + (line_length - indent) > par_end)
362                    {
363                        line_end = par_end;
364                    }
365                    else
366                    {
367                        line_end = line_begin + (line_length - indent);
368                    }
369           
370                    // prevent chopped words
371                    // if (lastchar != ' ') &&
372                    //    ((exists(lastchar + 1) && (lastchar + 1 != ' '))
373                    if ((*(line_end - 1) != ' ') &&
374                        ((line_end < par_end) && (*line_end != ' ')))
375                    {
376                        // find last ' ' in the second half of the current paragraph line
377                        string::const_iterator last_space =
378                            find(reverse_iterator<string::const_iterator>(line_end - 1),
379                                 reverse_iterator<string::const_iterator>(line_begin - 1),
380                                 ' ')
381                            .base();
382               
383                        if (last_space != line_begin - 1)
384                        {                 
385                            // is last_space within the second half ot the
386                            // current line
387                            if (unsigned(distance(last_space, line_end)) < 
388                                (line_length - indent) / 2)
389                            {
390                                line_end = last_space;
391                            }
392                        }                                               
393                    } // prevent chopped words
394             
395                    // write line to stream
396                    copy(line_begin, line_end, ostream_iterator<char>(os));
397             
398                    if (first_line)
399                    {
400                        indent = first_column_width + par_indent;
401                        first_line = false;
402                    }
403
404                    // more lines to follow?
405                    if (line_end != par_end)
406                    {
407                        os << '\n';
408               
409                        for(unsigned pad = indent; pad > 0; --pad)
410                        {
411                            os.put(' ');
412                        }                                                       
413                    }
414             
415                    // next line starts after of this line
416                    line_begin = line_end;             
417                } // paragraph lines
418            }         
419        }                             
420       
421        void format_description(std::ostream& os,
422                                const std::string& desc, 
423                                unsigned first_column_width,
424                                unsigned line_length)
425        {
426            // we need to use one char less per line to work correctly if actual
427            // console has longer lines
428            assert(line_length > 1);
429            if (line_length > 1)
430            {
431                --line_length;
432            }
433
434            // line_length must be larger than first_column_width
435            // this assert may fail due to user error or environment conditions!
436            assert(line_length > first_column_width);
437
438            // Note: can't use 'tokenizer' as name of typedef -- borland
439            // will consider subsequence uses of 'tokenizer' as uses of
440            // boost::tokenizer, not typedef.
441            typedef boost::tokenizer<boost::char_separator<char> > tok;
442         
443            tok paragraphs(
444                desc,
445                char_separator<char>("\n", "", boost::keep_empty_tokens));
446         
447            tok::const_iterator       par_iter = paragraphs.begin();               
448            const tok::const_iterator par_end = paragraphs.end();
449
450            while (par_iter != par_end)  // paragraphs
451            {
452                format_paragraph(os, *par_iter, first_column_width, 
453                                 line_length);
454           
455                ++par_iter;
456           
457                // prepair next line if any
458                if (par_iter != par_end)
459                {
460                    os << '\n';
461             
462                    for(unsigned pad = first_column_width; pad > 0; --pad)
463                    {
464                        os.put(' ');
465                    }                   
466                }           
467            }  // paragraphs
468        }
469   
470        void format_one(std::ostream& os, const option_description& opt, 
471                        unsigned first_column_width, unsigned line_length)
472        {
473            stringstream ss;
474            ss << "  " << opt.format_name() << ' ' << opt.format_parameter();
475           
476            // Don't use ss.rdbuf() since g++ 2.96 is buggy on it.
477            os << ss.str();
478
479            if (!opt.description().empty())
480            {
481                for(unsigned pad = first_column_width - ss.str().size(); 
482                    pad > 0; 
483                    --pad)
484                {
485                    os.put(' ');
486                }
487           
488                format_description(os, opt.description(),
489                                   first_column_width, line_length);
490            }
491        }
492    }
493
494    void 
495    options_description::print(std::ostream& os) const
496    {
497        if (!m_caption.empty())
498            os << m_caption << ":\n";
499
500        /* Find the maximum width of the option column */
501        unsigned width(23);
502        unsigned i; // vc6 has broken for loop scoping
503        for (i = 0; i < m_options.size(); ++i)
504        {
505            const option_description& opt = *m_options[i];
506            stringstream ss;
507            ss << "  " << opt.format_name() << ' ' << opt.format_parameter();
508            width = (max)(width, static_cast<unsigned>(ss.str().size()));           
509        }
510       
511        /* add an additional space to improve readability */
512        ++width;
513           
514        /* The options formatting style is stolen from Subversion. */
515        for (i = 0; i < m_options.size(); ++i)
516        {
517            if (belong_to_group[i])
518                continue;
519
520            const option_description& opt = *m_options[i];
521
522            format_one(os, opt, width, m_line_length);
523
524            os << "\n";
525        }
526
527        for (unsigned j = 0; j < groups.size(); ++j) {           
528            os << "\n" << *groups[j];
529        }
530    }
531
532}}
Note: See TracBrowser for help on using the repository browser.