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