TYPE3
[iec.git] / src / type3_AndroidCloud / anbox-master / src / anbox / cli.cpp
1 /*
2  * Copyright (C) 2016 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authored by: Thomas Voß <thomas.voss@canonical.com>
17  *
18  */
19
20 #include <boost/format.hpp>
21 #include <boost/program_options.hpp>
22
23 #include "anbox/cli.h"
24
25 namespace cli = anbox::cli;
26 namespace po = boost::program_options;
27
28 namespace {
29 namespace pattern {
30 static constexpr const char* help_for_command_with_subcommands =
31     "NAME:\n"
32     "    %1% - %2%\n"
33     "\n"
34     "USAGE:\n"
35     "    %3% [command options] [arguments...]";
36
37 static constexpr const char* commands = "COMMANDS:";
38 static constexpr const char* command = "    %1% %2%";
39
40 static constexpr const char* options = "OPTIONS:";
41 static constexpr const char* option = "    --%1% %2%";
42 }
43
44 void add_to_desc_for_flags(po::options_description& desc,
45                            const std::set<cli::Flag::Ptr>& flags) {
46   for (auto flag : flags) {
47     po::value_semantic *spec = nullptr;
48     flag->specify_option(spec);
49     if (!spec) continue;
50     desc.add_options()(flag->name().as_string().c_str(), spec,
51                        flag->description().as_string().c_str());
52   }
53 }
54 }
55
56 std::vector<std::string> cli::args(int argc, char** argv) {
57   std::vector<std::string> result;
58   for (int i = 1; i < argc; i++) result.push_back(argv[i]);
59   return result;
60 }
61
62 const cli::Name& cli::Flag::name() const { return name_; }
63
64 const cli::Description& cli::Flag::description() const { return description_; }
65
66 cli::Flag::Flag(const Name& name, const Description& description)
67     : name_{name}, description_{description} {}
68
69 cli::Command::FlagsWithInvalidValue::FlagsWithInvalidValue()
70     : std::runtime_error{"Flags with invalid value"} {}
71
72 cli::Command::FlagsMissing::FlagsMissing()
73     : std::runtime_error{"Flags are missing in command invocation"} {}
74
75 cli::Name cli::Command::name() const { return name_; }
76
77 cli::Usage cli::Command::usage() const { return usage_; }
78
79 cli::Description cli::Command::description() const { return description_; }
80
81 bool cli::Command::hidden() const { return hidden_; }
82
83 cli::Command::Command(const cli::Name& name, const cli::Usage& usage,
84                       const cli::Description& description, bool hidden)
85     : name_(name), usage_(usage), description_(description), hidden_(hidden) {}
86
87 cli::CommandWithSubcommands::CommandWithSubcommands(
88     const Name& name, const Usage& usage, const Description& description)
89     : Command{name, usage, description} {
90   command(std::make_shared<cmd::Help>(*this));
91 }
92
93 cli::CommandWithSubcommands& cli::CommandWithSubcommands::command(
94     const Command::Ptr& command) {
95   commands_[command->name().as_string()] = command;
96   return *this;
97 }
98
99 cli::CommandWithSubcommands& cli::CommandWithSubcommands::flag(
100     const Flag::Ptr& flag) {
101   flags_.insert(flag);
102   return *this;
103 }
104
105 void cli::CommandWithSubcommands::help(std::ostream& out) {
106   out << boost::format(pattern::help_for_command_with_subcommands) %
107              name().as_string() % usage().as_string() % name().as_string()
108       << std::endl;
109
110   if (flags_.size() > 0) {
111     out << std::endl
112         << pattern::options << std::endl;
113     for (const auto& flag : flags_)
114       out << boost::format(pattern::option) % flag->name() % flag->description()
115           << std::endl;
116   }
117
118   if (commands_.size() > 0) {
119     out << std::endl
120         << pattern::commands << std::endl;
121     for (const auto& cmd : commands_) {
122       if (cmd.second && !cmd.second->hidden())
123         out << boost::format(pattern::command) % cmd.second->name() %
124                    cmd.second->description()
125             << std::endl;
126     }
127   }
128 }
129
130 int cli::CommandWithSubcommands::run(const cli::Command::Context& ctxt) {
131   po::positional_options_description pdesc;
132   pdesc.add("command", 1);
133
134   po::options_description desc("Options");
135   desc.add_options()("command", po::value<std::string>()->required(),
136                      "the command to be executed");
137
138   add_to_desc_for_flags(desc, flags_);
139
140   try {
141     po::variables_map vm;
142     auto parsed = po::command_line_parser(ctxt.args)
143                       .options(desc)
144                       .positional(pdesc)
145                       .style(po::command_line_style::unix_style)
146                       .allow_unregistered()
147                       .run();
148
149     po::store(parsed, vm);
150     po::notify(vm);
151
152     auto cmd = commands_[vm["command"].as<std::string>()];
153     if (!cmd) {
154       ctxt.cout << "Unknown command '" << vm["command"].as<std::string>() << "'"
155                 << std::endl;
156       help(ctxt.cout);
157       return EXIT_FAILURE;
158     }
159
160     return cmd->run(cli::Command::Context{
161         ctxt.cin, ctxt.cout,
162         po::collect_unrecognized(parsed.options, po::include_positional)});
163   } catch (const po::error& e) {
164     ctxt.cout << e.what() << std::endl;
165     help(ctxt.cout);
166     return EXIT_FAILURE;
167   }
168
169   return EXIT_FAILURE;
170 }
171
172 cli::CommandWithFlagsAndAction::CommandWithFlagsAndAction(
173     const Name& name, const Usage& usage, const Description& description, bool hidden)
174     : Command{name, usage, description, hidden} {}
175
176 cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::flag(
177     const Flag::Ptr& flag) {
178   flags_.insert(flag);
179   return *this;
180 }
181
182 cli::CommandWithFlagsAndAction& cli::CommandWithFlagsAndAction::action(
183     const Action& action) {
184   action_ = action;
185   return *this;
186 }
187
188 int cli::CommandWithFlagsAndAction::run(const Context& ctxt) {
189   po::options_description cd(name().as_string());
190
191   bool help_requested{false};
192   cd.add_options()("help", po::bool_switch(&help_requested),
193                    "produces a help message");
194
195   add_to_desc_for_flags(cd, flags_);
196
197   try {
198     po::variables_map vm;
199     auto parsed = po::command_line_parser(ctxt.args)
200                       .options(cd)
201                       .style(po::command_line_style::unix_style)
202                       .allow_unregistered()
203                       .run();
204     po::store(parsed, vm);
205     po::notify(vm);
206
207     if (help_requested) {
208       help(ctxt.cout);
209       return EXIT_SUCCESS;
210     }
211
212     return action_(cli::Command::Context{
213         ctxt.cin, ctxt.cout,
214         po::collect_unrecognized(parsed.options, po::include_positional)});
215   } catch (const po::error& e) {
216     ctxt.cout << e.what() << std::endl;
217     help(ctxt.cout);
218     return EXIT_FAILURE;
219   }
220
221   return EXIT_FAILURE;
222 }
223
224 void cli::CommandWithFlagsAndAction::help(std::ostream& out) {
225   out << boost::format(pattern::help_for_command_with_subcommands) %
226              name().as_string() % description().as_string() % name().as_string()
227       << std::endl;
228
229   if (flags_.size() > 0) {
230     out << std::endl
231         << boost::format(pattern::options) << std::endl;
232     for (const auto& flag : flags_)
233       out << boost::format(pattern::option) % flag->name() % flag->description()
234           << std::endl;
235   }
236 }
237
238 cli::cmd::Help::Help(Command& cmd)
239     : Command{cli::Name{"help"}, cli::Usage{"Print a short help message"},
240               cli::Description{"Print a short help message"}},
241       command{cmd} {}
242
243 // From Command
244 int cli::cmd::Help::run(const Context& context) {
245   command.help(context.cout);
246   return EXIT_FAILURE;
247 }
248
249 void cli::cmd::Help::help(std::ostream& out) { command.help(out); }