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