/******************************************************************************* * CLI - A simple command line interface. * Copyright (C) 2016-2021 Daniele Pallastrelli * * Boost Software License - Version 1.0 - August 17th, 2003 * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by * this license (the "Software") to use, reproduce, display, distribute, * execute, and transmit the Software, and to prepare derivative works of the * Software, and to permit third-parties to whom the Software is furnished to * do so, all subject to the following: * * The copyright notices in the Software and this entire statement, including * the above license grant, this restriction and the following disclaimer, * must be included in all copies of the Software, in whole or in part, and * all derivative works of the Software, unless such copies or derivative * works are solely in the form of machine-executable object code generated by * a source language processor. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ******************************************************************************/ #ifdef CLI_EXAMPLES_USE_STANDALONEASIO_SCHEDULER #include #include namespace cli { using MainScheduler = StandaloneAsioScheduler; using CliTelnetServer = StandaloneAsioCliTelnetServer; } // namespace cli #elif defined(CLI_EXAMPLES_USE_BOOSTASIO_SCHEDULER) // TODO: NB boostasioscheduler.h includes boost asio // so in Windows it should appear before cli.h and clilocalsession.h that include rang, // because both include WinSock.h // (consider to provide a global header file for the library) #include #include namespace cli { using MainScheduler = BoostAsioScheduler; using CliTelnetServer = BoostAsioCliTelnetServer; } #else #error either CLI_EXAMPLES_USE_STANDALONEASIO_SCHEDULER or CLI_EXAMPLES_USE_BOOSTASIO_SCHEDULER must be defined #endif #include #include #include #include #include // std::copy #include using namespace cli; using namespace std; // a free function to be used as handler static void foo(std::ostream& out, int x) { out << x << std::endl; } // a custom struct to be used as a user-defined parameter type struct Bar { string to_string() const { return std::to_string(value); } friend istream & operator >> (istream &in, Bar& p); int value; }; istream & operator >> (istream& in, Bar& p) { in >> p.value; return in; } // needed only for generic help, you can omit this namespace cli { template <> struct TypeDesc { static const char* Name() { return ""; } }; } // needed only for generic help, you can omit this namespace cli { template <> struct TypeDesc> { static const char* Name() { return ""; } }; } int main() { try { CmdHandler colorCmd; CmdHandler nocolorCmd; // setup cli auto rootMenu = make_unique("cli"); rootMenu->Insert( "free_function", foo, "Call a free function that echoes the parameter passed" ); rootMenu->Insert( "hello", [](std::ostream& out){ out << "Hello, world\n"; }, "Print hello world" ); rootMenu->Insert( "hello_everysession", [](std::ostream&){ Cli::cout() << "Hello, everybody" << std::endl; }, "Print hello everybody on all open sessions" ); rootMenu->Insert( "answer", [](std::ostream& out, int x){ out << "The answer is: " << x << "\n"; }, "Print the answer to Life, the Universe and Everything" ); rootMenu->Insert( "file", [](std::ostream& out, int fd) { out << "file descriptor: " << fd << "\n"; }, "Print the file descriptor specified", {"file_descriptor"} ); rootMenu->Insert( "echo", {"string to echo"}, [](std::ostream& out, const string& arg) { out << arg << "\n"; }, "Print the string passed as parameter" ); rootMenu->Insert( "echo", {"first string to echo", "second string to echo"}, [](std::ostream& out, const string& arg1, const string& arg2) { out << arg1 << ' ' << arg2 << "\n"; }, "Print the strings passed as parameter" ); rootMenu->Insert( "error", [](std::ostream&) { throw std::logic_error("Error in cmd"); }, "Throw an exception in the command handler" ); rootMenu->Insert( "reverse", {"string_to_revert"}, [](std::ostream& out, const string& arg) { string copy(arg); std::reverse(copy.begin(), copy.end()); out << copy << "\n"; }, "Print the reverse string" ); rootMenu->Insert( "add", {"first_term", "second_term"}, [](std::ostream& out, int x, int y) { out << x << " + " << y << " = " << (x+y) << "\n"; }, "Print the sum of the two numbers" ); rootMenu->Insert( "add", [](std::ostream& out, int x, int y, int z) { out << x << " + " << y << " + " << z << " = " << (x+y+z) << "\n"; }, "Print the sum of the three numbers" ); rootMenu->Insert( "sort", {"list of strings separated by space"}, [](std::ostream& out, std::vector data) { std::sort(data.begin(), data.end()); out << "sorted list: "; std::copy(data.begin(), data.end(), std::ostream_iterator(out, " ")); out << "\n"; }, "Alphabetically sort a list of words" ); rootMenu->Insert( "bar", [](std::ostream& out, Bar x){ out << "You entered bar: " << x.to_string() << "\n"; }, "Custom type" ); rootMenu->Insert( "complex", [](std::ostream& out, std::complex x){ out << "You entered complex : " << x << "\n"; }, "Print a complex number" ); colorCmd = rootMenu->Insert( "color", [&](std::ostream& out) { out << "Colors ON\n"; SetColor(); colorCmd.Disable(); nocolorCmd.Enable(); }, "Enable colors in the cli" ); nocolorCmd = rootMenu->Insert( "nocolor", [&](std::ostream& out) { out << "Colors OFF\n"; SetNoColor(); colorCmd.Enable(); nocolorCmd.Disable(); }, "Disable colors in the cli" ); nocolorCmd.Disable(); // start w/o colors, so we disable this command rootMenu->Insert( "removecmds", [&](std::ostream&) { colorCmd.Remove(); nocolorCmd.Remove(); }, "Remove both color and nocolor commands from the menu" ); // a submenu // first parameter is the command to enter the submenu // second parameter (optional) is the description of the menu in the help // third parameter (optional) is the prompt of the menu (default is the name of the command) auto subMenu = make_unique("sub", "Enter a submenu", "submenu"); subMenu->Insert( "hello", [](std::ostream& out){ out << "Hello, submenu world\n"; }, "Print hello world in the submenu" ); subMenu->Insert( "demo", [](std::ostream& out){ out << "This is a sample!\n"; }, "Print a demo string" ); auto subSubMenu = make_unique("subsub", "Enter a submenu of second level"); subSubMenu->Insert( "hello", [](std::ostream& out){ out << "Hello, subsubmenu world\n"; }, "Print hello world in the sub-submenu" ); subMenu->Insert( std::move(subSubMenu) ); rootMenu->Insert( std::move(subMenu) ); // create a cli with the given root menu and a persistent storage // you must pass to FileHistoryStorage the path of the history file // if you don't pass the second argument, the cli will use a VolatileHistoryStorage object that keeps in memory // the history of all the sessions, until the cli is shut down. Cli cli( std::move(rootMenu), std::make_unique(".cli") ); // global exit action cli.ExitAction( [](auto& out){ out << "Goodbye and thanks for all the fish.\n"; } ); // std exception custom handler cli.StdExceptionHandler( [](std::ostream& out, const std::string& cmd, const std::exception& e) { out << "Exception caught in CLI handler: " << e.what() << " while handling command: " << cmd << ".\n"; } ); // custom handler for unknown commands cli.WrongCommandHandler( [](std::ostream& out, const std::string& cmd) { out << "Unknown command or incorrect parameters: " << cmd << ".\n"; } ); MainScheduler scheduler; CliLocalTerminalSession localSession(cli, scheduler, std::cout, 200); localSession.ExitAction( [&scheduler](auto& out) // session exit action { out << "Closing App...\n"; scheduler.Stop(); } ); // setup server CliTelnetServer server(cli, scheduler, 5000); // exit action for all the connections server.ExitAction( [](auto& out) { out << "Terminating this session...\n"; } ); scheduler.Run(); return 0; } catch (const std::exception& e) { cerr << "Exception caught in main: " << e.what() << '\n'; } catch (...) { cerr << "Unknown exception caught in main.\n"; } return -1; }