| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/shell/console/app.h" |
| |
| #include <lib/cmdline/args_parser.h> |
| #include <unistd.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| namespace shell::console { |
| |
| const char kHelpIntro[] = R"(cliff [-c <command> ] |
| |
| A command line interface for Fuchsia. |
| |
| Options |
| |
| )"; |
| |
| const char kHelpHelp[] = R"( --help |
| -h |
| Prints all command-line switches.)"; |
| |
| const char kCommandHelp[] = R"( --command |
| -c |
| Execute the given command.)"; |
| |
| App::App(fidl::WireSyncClient<fuchsia_shell::Shell>* client, async_dispatcher_t* dispatcher) |
| : console_(this, dispatcher, STDIN_FILENO), executor_(client) {} |
| |
| App::~App() = default; |
| |
| bool App::Init(int argc, const char** argv, QuitCallback quit_callback) { |
| quit_callback_ = std::move(quit_callback); |
| |
| cmdline::ArgsParser<Options> parser; |
| parser.AddSwitch("command", 'c', kCommandHelp, &Options::command); |
| |
| // Special --help switch which doesn't exist in the options structure. |
| bool requested_help = false; |
| parser.AddGeneralSwitch("help", 'h', kHelpHelp, [&requested_help]() { requested_help = true; }); |
| |
| std::vector<std::string> params; |
| cmdline::Status parser_status = parser.Parse(argc, argv, &options_, ¶ms); |
| if (parser_status.has_error()) { |
| fprintf(stderr, "error: %s\n", parser_status.error_message().c_str()); |
| return false; |
| } |
| |
| // Handle --help switch since we're the one that knows about the switches. |
| if (requested_help) { |
| printf("%s", (kHelpIntro + parser.GetHelp()).c_str()); |
| Quit(); |
| return true; |
| } |
| |
| console_.Init("% "); |
| |
| if (options_.command) { |
| auto command = std::make_unique<Command>(); |
| command->Parse(options_.command.value()); |
| Err status = OnConsoleCommand(std::move(command)); |
| if (status.code == ZX_ERR_NEXT) { |
| Quit(); |
| } |
| return true; |
| } else { |
| console_.GetNextCommand(); |
| } |
| return true; |
| } |
| |
| Err App::OnConsoleCommand(std::unique_ptr<Command> command) { |
| Err status = executor_.Execute( |
| std::move(command), [this](const std::string& out) { console_.out() << out << "\n"; }, |
| [this](const std::string& err) { console_.err() << "Error: " << err << "\n"; }, |
| [this]() { |
| if (options_.command) { |
| Quit(); |
| } |
| }); |
| if (status.code == ZX_ERR_STOP) { |
| Quit(); |
| return status; |
| } |
| if (status.code > 0 || (status.code != ZX_ERR_NEXT && status.code != ZX_ERR_ASYNC)) { |
| console_.err() << "error: Failed to execute command: " << status.code << "(" << status.msg |
| << ")" << std::endl; |
| return Err(ZX_ERR_NEXT, zx_status_get_string(ZX_ERR_NEXT)); |
| } |
| return status; |
| } |
| |
| void App::OnConsoleInterrupt() { |
| executor_.KillForegroundTask(); |
| console_.GetNextCommand(); |
| } |
| |
| void App::OnConsoleError(zx_status_t status) { |
| fprintf(stderr, "error: Failed to read console: %d (%s)\n", status, zx_status_get_string(status)); |
| Quit(); |
| } |
| |
| void App::Quit() { |
| auto quit_callback = std::move(quit_callback_); |
| quit_callback(); |
| } |
| |
| } // namespace shell::console |