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