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