improve cli command parser (#74)

* implement new command line processor using CLI11.hpp

command reference section updates

readme

* fix include in control_tools.cpp
This commit is contained in:
Levi Neely 2024-11-27 21:11:40 +00:00 committed by GitHub
parent ca80afd658
commit 2dccc714e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 11122 additions and 44 deletions

10998
CLI11.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@ I use Artix and maintain an [AUR](https://aur.archlinux.org/packages/pcloudcc-ln
- Ivan Stoev
- pCloud
## Dependencies
## Dependencies
- zlib (-lz)
- boost (-lboost_system, -lboost_program_options)
- pthread (lpthread)
@ -79,23 +79,38 @@ starts.
### Command Prompt
Use the command prompt to interact with a running daemon.
Use the command prompt to interact with a running daemon.
> pcloudcc -k
Command Reference:
```
help(?): Show this help message
startcrypto <crypto pass>: Unlock crypto folder
stopcrypto: Lock crypto folder
finalize: Kill daemon and quit
syncls: List sync folders
syncadd <localpath> <remotepath>: Add sync folder (full sync)
syncrm <folderid>: Remove sync folder
quit(q): Exit this program
help(?): Show this help message
crypto(c):
start <crypto pass>: Unlock crypto folder
stop: Lock crypto folder
sync(s):
list(ls): List sync folders
add <localpath> <remotepath>: Add sync folder
remove(rm) <folderid>: Remove sync folder
finalize(f): Kill daemon and quit
quit(q): Exit this program
```
**Note**. Command line arguments that include special characters (e.g., the
`crypto start` password or paths with spaces) must now be quoted or
escaped. In other words, instead of:
`startcrypto Str0ng p4$$word 4 great jUSTicE!`
One must now type, for example:
`c start 'Str0ng p4$$word 4 great jUSTicE!'`
If the password includes quotes for some reason, then those characters must
be escaped properly.
## Warning
**Stopping daemon will break pending background transfers!**

View File

@ -38,11 +38,13 @@
#include <unistd.h>
#include "control_tools.h"
#include "overlay_client.h"
#include "pclsync/overlay_client.h"
#include "pclsync_lib.h"
#include "psynclib.h"
#include "CLI11.hpp"
namespace cc = console_client;
namespace control_tools {
@ -295,43 +297,106 @@ int finalize() {
void help() {
std::cout << "Supported commands are:" << std::endl
<< "help(?): Show this help message" << std::endl
<< "startcrypto <crypto pass>: Unlock crypto folder" << std::endl
<< "stopcrypto: Lock crypto folder" << std::endl
<< "finalize: Kill daemon and quit" << std::endl
<< "syncls: List sync folders" << std::endl
<< "syncadd <localpath> <remotepath>: Add sync folder (full sync)"
<< std::endl
<< "syncrm <folderid>: Remove sync folder" << std::endl
<< "quit(q): Exit this program" << std::endl;
<< " help(?): Show this help message" << std::endl
<< " crypto(c):" << std::endl
<< " start <crypto pass>: Unlock crypto folder" << std::endl
<< " stop: Lock crypto folder" << std::endl
<< " sync(s):" << std::endl
<< " list(ls): List sync folders" << std::endl
<< " add <localpath> <remotepath>: Add sync folder" << std::endl
<< " remove(rm) <folderid>: Remove sync folder" << std::endl
<< " finalize(f): Kill daemon and quit" << std::endl
<< " quit(q): Exit this program" << std::endl;
}
void process_commands() {
std::cout << "Type 'help' or '?' for a list of supported commands."
<< std::endl;
std::cout << "> ";
for (std::string line; std::getline(std::cin, line);) {
if (!line.compare("finalize")) {
finalize();
break;
} else if (!line.compare("help") || !line.compare("?")) {
help();
} else if (!line.compare("stopcrypto")) {
stop_crypto();
} else if (!line.compare(0, 11, "startcrypto", 0, 11) &&
(line.length() > 12)) {
start_crypto(line.c_str() + 12);
} else if (!line.compare("q") || !line.compare("quit")) {
break;
} else if (!line.compare("syncls")) {
list_sync_folders();
} else if (!line.compare(0, 7, "syncadd", 0, 7) && (line.length() > 7)) {
auto [lpath, rpath] = split_paths(line.c_str() + 8);
add_sync_folder(lpath, rpath);
} else if (!line.compare(0, 6, "syncrm", 0, 6) && (line.length() > 6)) {
remove_sync_folder(line.c_str() + 7);
}
CLI::App app{"pcloudcc-lneely"};
app.fallthrough();
app.footer("Type 'help' or '?' for a list of supported commands.");
// top-level commands
app.add_subcommand("help", "Show help")->alias("?")->callback(help);
auto crypto_cmd =
app.add_subcommand("crypto", "Crypto-related commands")->alias("c");
crypto_cmd->require_subcommand();
auto sync_cmd =
app.add_subcommand("sync", "Sync-related commands")->alias("s");
sync_cmd->require_subcommand();
app.add_subcommand("finalize", "Finalize and exit")->alias("f")->callback([] {
finalize();
exit(0);
});
app.add_subcommand("quit", "Quit the program")->alias("q")->callback([] {
exit(0);
});
// crypto subcommands
auto start_crypto_cmd = crypto_cmd->add_subcommand("start", "Start crypto");
std::string crypto_arg;
start_crypto_cmd->add_option("arg", crypto_arg, "Crypto argument")
->required();
start_crypto_cmd->callback([&] { start_crypto(crypto_arg.c_str()); });
crypto_cmd->add_subcommand("stop", "Stop crypto")->callback(stop_crypto);
// sync subcommands
sync_cmd->add_subcommand("list", "List sync folders")
->alias("ls")
->callback(list_sync_folders);
auto sync_add_cmd = sync_cmd->add_subcommand("add", "Add sync folder");
std::string syncadd_arg;
sync_add_cmd->add_option("arg", syncadd_arg, "Paths")->required();
sync_add_cmd->callback([&] {
auto [lpath, rpath] = split_paths(syncadd_arg.c_str());
add_sync_folder(lpath, rpath);
});
auto sync_remove_cmd =
sync_cmd->add_subcommand("remove", "Remove sync folder")->alias("rm");
std::string syncrm_arg;
sync_remove_cmd->add_option("arg", syncrm_arg, "Path")->required();
sync_remove_cmd->callback([&] { remove_sync_folder(syncrm_arg.c_str()); });
// command loop
while (true) {
std::cout << "> ";
std::string line;
if (!std::getline(std::cin, line))
break;
try {
app.parse(line);
} catch (const CLI::ParseError &e) {
std::vector<std::string> args;
std::istringstream iss(line);
std::string arg;
while (iss >> arg) {
args.push_back(arg);
}
if (!args.empty()) {
try {
auto *subcom = app.get_subcommand(args[0]);
if (subcom) {
std::cout << "Usage for '" << args[0] << "':" << std::endl;
std::cout << subcom->help() << std::endl;
} else {
std::cout
<< "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
}
} catch (...) {
std::cout << "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
}
} else {
std::cout << "Invalid command: '" << line
<< "'. Type 'help' or '?' to get a list of valid commands."
<< std::endl;
}
}
}
}