1 // Copyright 2010 The Kyua Authors. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "cli/cmd_help.hpp" 30 31 #include <algorithm> 32 #include <cstdlib> 33 34 #include "cli/common.ipp" 35 #include "utils/cmdline/commands_map.ipp" 36 #include "utils/cmdline/exceptions.hpp" 37 #include "utils/cmdline/globals.hpp" 38 #include "utils/cmdline/options.hpp" 39 #include "utils/cmdline/parser.hpp" 40 #include "utils/cmdline/ui.hpp" 41 #include "utils/defs.hpp" 42 #include "utils/format/macros.hpp" 43 #include "utils/sanity.hpp" 44 #include "utils/text/table.hpp" 45 46 namespace cmdline = utils::cmdline; 47 namespace config = utils::config; 48 namespace text = utils::text; 49 50 using cli::cmd_help; 51 52 53 namespace { 54 55 56 /// Creates a table with the help of a set of options. 57 /// 58 /// \param options The set of options to describe. May be empty. 59 /// 60 /// \return A 2-column wide table with the description of the options. 61 static text::table 62 options_help(const cmdline::options_vector& options) 63 { 64 text::table table(2); 65 66 for (cmdline::options_vector::const_iterator iter = options.begin(); 67 iter != options.end(); iter++) { 68 const cmdline::base_option* option = *iter; 69 70 std::string description = option->description(); 71 if (option->needs_arg() && option->has_default_value()) 72 description += F(" (default: %s)") % option->default_value(); 73 74 text::table_row row; 75 76 if (option->has_short_name()) 77 row.push_back(F("%s, %s") % option->format_short_name() % 78 option->format_long_name()); 79 else 80 row.push_back(F("%s") % option->format_long_name()); 81 row.push_back(F("%s.") % description); 82 83 table.add_row(row); 84 } 85 86 return table; 87 } 88 89 90 /// Prints the summary of commands and generic options. 91 /// 92 /// \param ui Object to interact with the I/O of the program. 93 /// \param options The set of program-wide options for which to print help. 94 /// \param commands The set of commands for which to print help. 95 static void 96 general_help(cmdline::ui* ui, const cmdline::options_vector* options, 97 const cmdline::commands_map< cli::cli_command >* commands) 98 { 99 PRE(!commands->empty()); 100 101 cli::write_version_header(ui); 102 ui->out(""); 103 ui->out_tag_wrap( 104 "Usage: ", 105 F("%s [general_options] command [command_options] [args]") % 106 cmdline::progname(), false); 107 108 const text::table options_table = options_help(*options); 109 text::widths_vector::value_type first_width = 110 options_table.column_width(0); 111 112 std::map< std::string, text::table > command_tables; 113 114 for (cmdline::commands_map< cli::cli_command >::const_iterator 115 iter = commands->begin(); iter != commands->end(); iter++) { 116 const std::string& category = (*iter).first; 117 const std::set< std::string >& command_names = (*iter).second; 118 119 command_tables.insert(std::map< std::string, text::table >::value_type( 120 category, text::table(2))); 121 text::table& table = command_tables.find(category)->second; 122 123 for (std::set< std::string >::const_iterator i2 = command_names.begin(); 124 i2 != command_names.end(); i2++) { 125 const cli::cli_command* command = commands->find(*i2); 126 text::table_row row; 127 row.push_back(command->name()); 128 row.push_back(F("%s.") % command->short_description()); 129 table.add_row(row); 130 } 131 132 if (table.column_width(0) > first_width) 133 first_width = table.column_width(0); 134 } 135 136 text::table_formatter formatter; 137 formatter.set_column_width(0, first_width); 138 formatter.set_column_width(1, text::table_formatter::width_refill); 139 formatter.set_separator(" "); 140 141 if (!options_table.empty()) { 142 ui->out_wrap(""); 143 ui->out_wrap("Available general options:"); 144 ui->out_table(options_table, formatter, " "); 145 } 146 147 // Iterate using the same loop as above to preserve ordering. 148 for (cmdline::commands_map< cli::cli_command >::const_iterator 149 iter = commands->begin(); iter != commands->end(); iter++) { 150 const std::string& category = (*iter).first; 151 ui->out_wrap(""); 152 ui->out_wrap(F("%s commands:") % 153 (category.empty() ? "Generic" : category)); 154 ui->out_table(command_tables.find(category)->second, formatter, " "); 155 } 156 157 ui->out_wrap(""); 158 ui->out_wrap("See kyua(1) for more details."); 159 } 160 161 162 /// Prints help for a particular subcommand. 163 /// 164 /// \param ui Object to interact with the I/O of the program. 165 /// \param general_options The options that apply to all commands. 166 /// \param command Pointer to the command to describe. 167 static void 168 subcommand_help(cmdline::ui* ui, 169 const utils::cmdline::options_vector* general_options, 170 const cli::cli_command* command) 171 { 172 cli::write_version_header(ui); 173 ui->out(""); 174 ui->out_tag_wrap( 175 "Usage: ", F("%s [general_options] %s%s%s") % 176 cmdline::progname() % command->name() % 177 (command->options().empty() ? "" : " [command_options]") % 178 (command->arg_list().empty() ? "" : (" " + command->arg_list())), 179 false); 180 ui->out_wrap(""); 181 ui->out_wrap(F("%s.") % command->short_description()); 182 183 const text::table general_table = options_help(*general_options); 184 const text::table command_table = options_help(command->options()); 185 186 const text::widths_vector::value_type first_width = 187 std::max(general_table.column_width(0), command_table.column_width(0)); 188 text::table_formatter formatter; 189 formatter.set_column_width(0, first_width); 190 formatter.set_column_width(1, text::table_formatter::width_refill); 191 formatter.set_separator(" "); 192 193 if (!general_table.empty()) { 194 ui->out_wrap(""); 195 ui->out_wrap("Available general options:"); 196 ui->out_table(general_table, formatter, " "); 197 } 198 199 if (!command_table.empty()) { 200 ui->out_wrap(""); 201 ui->out_wrap("Available command options:"); 202 ui->out_table(command_table, formatter, " "); 203 } 204 205 ui->out_wrap(""); 206 ui->out_wrap(F("See kyua-%s(1) for more details.") % command->name()); 207 } 208 209 210 } // anonymous namespace 211 212 213 /// Default constructor for cmd_help. 214 /// 215 /// \param options_ The set of program-wide options for which to provide help. 216 /// \param commands_ The set of commands for which to provide help. 217 cmd_help::cmd_help(const cmdline::options_vector* options_, 218 const cmdline::commands_map< cli_command >* commands_) : 219 cli_command("help", "[subcommand]", 0, 1, "Shows usage information"), 220 _options(options_), 221 _commands(commands_) 222 { 223 } 224 225 226 /// Entry point for the "help" subcommand. 227 /// 228 /// \param ui Object to interact with the I/O of the program. 229 /// \param cmdline Representation of the command line to the subcommand. 230 /// 231 /// \return 0 to indicate success. 232 int 233 cmd_help::run(utils::cmdline::ui* ui, const cmdline::parsed_cmdline& cmdline, 234 const config::tree& /* user_config */) 235 { 236 if (cmdline.arguments().empty()) { 237 general_help(ui, _options, _commands); 238 } else { 239 INV(cmdline.arguments().size() == 1); 240 const std::string& cmdname = cmdline.arguments()[0]; 241 const cli::cli_command* command = _commands->find(cmdname); 242 if (command == NULL) 243 throw cmdline::usage_error(F("The command %s does not exist") % 244 cmdname); 245 else 246 subcommand_help(ui, _options, command); 247 } 248 249 return EXIT_SUCCESS; 250 } 251