From 51478deab22178d01db7fa8632ba72071b4dfc38 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Wed, 21 Oct 2020 15:15:09 -0400 Subject: [PATCH] rbd: ensure the help printer doesn't print past the end of the line When long command names and long optional names are combined, it's possible for the help text to be printed beyond the 80 character limit. Signed-off-by: Jason Dillaman --- src/test/cli/rbd/help.t | 4 +-- src/tools/rbd/OptionPrinter.cc | 61 ++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index aeb1620a36e..7eeb4ab6e4b 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -727,7 +727,7 @@ rbd help feature disable usage: rbd feature disable [--pool ] [--namespace ] [--image ] - [ ...] + [ ...] Disable the specified image feature. @@ -748,7 +748,7 @@ [--journal-splay-width ] [--journal-object-size ] [--journal-pool ] - [ ...] + [ ...] Enable the specified image feature. diff --git a/src/tools/rbd/OptionPrinter.cc b/src/tools/rbd/OptionPrinter.cc index a1874a769dc..5a5bf950498 100644 --- a/src/tools/rbd/OptionPrinter.cc +++ b/src/tools/rbd/OptionPrinter.cc @@ -19,38 +19,63 @@ OptionPrinter::OptionPrinter(const OptionsDescription &positional, } void OptionPrinter::print_short(std::ostream &os, size_t initial_offset) { - size_t name_width = std::min(initial_offset, MAX_DESCRIPTION_OFFSET) + 1; - - IndentStream indent_stream(name_width, initial_offset, LINE_WIDTH, os); - indent_stream.set_delimiter("["); + size_t max_option_width = 0; + std::vector optionals; for (size_t i = 0; i < m_optional.options().size(); ++i) { + std::stringstream option; + bool required = m_optional.options()[i]->semantic()->is_required(); if (!required) { - indent_stream << "["; + option << "["; } - indent_stream << "--" << m_optional.options()[i]->long_name(); + option << "--" << m_optional.options()[i]->long_name(); if (m_optional.options()[i]->semantic()->max_tokens() != 0) { - indent_stream << " <" << m_optional.options()[i]->long_name() << ">"; + option << " <" << m_optional.options()[i]->long_name() << ">"; } if (!required) { - indent_stream << "]"; + option << "]"; } - indent_stream << " "; + max_option_width = std::max(max_option_width, option.str().size()); + optionals.emplace_back(option.str()); } - if (m_optional.options().size() > 0 || m_positional.options().size() == 0) { + std::vector positionals; + for (size_t i = 0; i < m_positional.options().size(); ++i) { + std::stringstream option; + + option << "<" << m_positional.options()[i]->long_name() << ">"; + if (m_positional.options()[i]->semantic()->max_tokens() > 1) { + option << " [<" << m_positional.options()[i]->long_name() << "> ...]"; + } + + max_option_width = std::max(max_option_width, option.str().size()); + positionals.emplace_back(option.str()); + + if (m_positional.options()[i]->semantic()->max_tokens() > 1) { + break; + } + } + + size_t indent = std::min(initial_offset, MAX_DESCRIPTION_OFFSET) + 1; + if (indent + max_option_width + 2 > LINE_WIDTH) { + // decrease the indent so that we don't wrap past the end of the line + indent = LINE_WIDTH - max_option_width - 2; + } + + IndentStream indent_stream(indent, initial_offset, LINE_WIDTH, os); + indent_stream.set_delimiter("["); + for (auto& option : optionals) { + indent_stream << option << " "; + } + + if (optionals.size() > 0 || positionals.size() == 0) { indent_stream << std::endl; } - if (m_positional.options().size() > 0) { + if (positionals.size() > 0) { indent_stream.set_delimiter(" "); - for (size_t i = 0; i < m_positional.options().size(); ++i) { - indent_stream << "<" << m_positional.options()[i]->long_name() << "> "; - if (m_positional.options()[i]->semantic()->max_tokens() > 1) { - indent_stream << "[<" << m_positional.options()[i]->long_name() - << "> ...]"; - break; - } + for (auto& option : positionals) { + indent_stream << option << " "; } indent_stream << std::endl; }