tools/hexnet: replace built-in argument parser with lib/cli

This is the entire reason I build lib/cli in the first place:
Parsing arguments directly in a given program tends to get really messy,
not to mention all the different ways that arguments can be specified
and the redundant work required to do that on multiple programs.
BodgeMaster-unfinished
BodgeMaster 2022-07-15 13:51:19 +02:00
parent 4c3d2fdbbf
commit efa4fa560e
1 changed files with 42 additions and 111 deletions

View File

@ -18,7 +18,8 @@
#include <cstdint>
#include <cctype>
#define usage std::cerr << "Usage: " << argv[0] << " [-4|-6] [-t|-u] <<host> <port> | -l <port>>" << std::endl << "<port> may be hexadecimal (prefixed with 0x) or binary (prefixed with 0b)." << std::endl
#include "../lib/error.h++"
#include "../lib/cli.h++"
#define EXIT_SUCCESS 0
#define EXIT_RUNTIME 1
@ -33,117 +34,47 @@ int main(int argc, char* argv[]){
bool tcp = true;
bool udp = true;
bool listen = false;
std::string host = "";
std::string host;
uint16_t port;
if (argc<2) {
usage;
return EXIT_USAGE;
}
for (int i=1; i<argc; i++) {
std::string argument(argv[i]);
if (argument=="-4") {
ipv4 = true;
ipv6 = false;
continue;
}
if (argument=="-6") {
ipv4 = false;
ipv6 = true;
continue;
}
if (argument=="-t") {
tcp = true;
udp = false;
continue;
}
if (argument=="-u") {
tcp = false;
udp = true;
continue;
}
if (argument=="-l") {
listen = true;
continue;
}
if (argument=="-h") {
usage;
return EXIT_SUCCESS;
}
if (argument=="--help") {
usage;
return EXIT_SUCCESS;
}
if (!listen && i==argc-2) {
host = argument;
continue;
}
if (i==argc-1) {
if (!listen && host=="") {
std::cerr << "Not running in listen mode and no host specified." << std::endl;
return EXIT_USAGE;
}
if (argument.substr(0, 2)=="0b" || argument.substr(0, 2)=="0B") {
// check for 16 bit binary number
for (char const &digit : argument.substr(2,argument.length())) {
if (digit=='0' || digit=='1') {
;
} else {
std::cerr << argument << " is not a valid port." << std::endl;
usage;
return EXIT_USAGE;
}
}
if (argument.length()>18) {
std::cerr << argument << " is too big for a valid port." << std::endl;
usage;
return EXIT_USAGE;
}
port = (uint16_t) std::stoul(argument.substr(2, argument.length()), nullptr, 2);
} else if (argument.substr(0, 2)=="0x" || argument.substr(0, 2)=="0X") {
// check for four digit hex number
for (char const &digit : argument.substr(2,argument.length())) {
if (std::isxdigit(digit)==0) {
std::cerr << argument << " is not a valid port." << digit << std::endl;
usage;
return EXIT_USAGE;
}
}
if (argument.length()>6) {
std::cerr << argument << " is too big for a valid port." << std::endl;
usage;
return EXIT_USAGE;
}
port = (uint16_t) std::stoul(argument.substr(2, argument.length()), nullptr, 16);
} else {
// check for decimal number between 0 and 65536 exclusively
for (char const &digit : argument) {
if (std::isdigit(digit)==0) {
std::cerr << argument << " is not a valid port." << std::endl;
usage;
return EXIT_USAGE;
}
}
// using unsigned long here because that's how stoul()
// is defined
unsigned long argumentNumber = std::stoul(argument, nullptr, 10);
if (argumentNumber > 65535) {
std::cerr << argument << " is too big for a valid port." << std::endl;
usage;
return EXIT_USAGE;
}
port = (uint16_t) argumentNumber;
}
break; // last argument anyway
}
usage;
return EXIT_USAGE;
}
// Argument parsing end ############################################
if (!listen) {
//TODO: verify that host is possibly a valid host (or usage error)
//TODO: look up host if necessary, determine IPv4 or IPv6 mode if necessary
std::cerr << "Client mode not implemented" << std::endl;
return EXIT_UNIMPLEMENTED;
std::vector<CLI::Flag> flags;
flags.push_back(CLI::Flag('4', "ipv4", "use IPv4, defaults to both when -4 and -6 are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('6', "ipv6", "use IPv6, defaults to both when -4 and -6 are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('t', "tcp", "use TCP, defaults to both when -t and -u are omitted, otherwise uses what is specified"));
flags.push_back(CLI::Flag('u', "udp", "use UDP, defaults to both when -t and -u are omitted, otherwise uses what is specified"));
std::vector<CLI::UnpositionalArgument> unpositionalArguments;
unpositionalArguments.push_back(CLI::UnpositionalArgument('c', "connect", "HOST", "connect to HOST, listen for incoming connections if omitted"));
std::vector<CLI::PositionalArgument> positionalArguments;
positionalArguments.push_back(CLI::PositionalArgument("PORT", "the port to use"));
CLI::ArgumentsParser cliParser = CLI::ArgumentsParser(argc, argv, flags, unpositionalArguments, positionalArguments);
if (cliParser.wrongUsage) {
//TODO: spit out usage information generated by the parser
return EXIT_USAGE;
}
if (cliParser.getFlag('4').value || cliParser.getFlag('6').value) {
ipv4 = cliParser.getFlag('4').value;
ipv6 = cliParser.getFlag('6').value;
}
if (cliParser.getFlag('t').value || cliParser.getFlag('u').value) {
tcp = cliParser.getFlag('t').value;
udp = cliParser.getFlag('u').value;
}
if (cliParser.getUnpositionalArgument('c').errorCode == ErrorCodes::NOT_PRESENT) {
listen = true;
}
host = cliParser.getUnpositionalArgument('c').value;
//FIXME: use a function that returns a fixed-width data type instead
port = (uint16_t) std::stoi(cliParser.getPositionalArgument(0).value);
std::cerr << "Port: " << (int) port << std::endl;
std::cerr << (listen? "Listening" : "Host: ") << host << std::endl;
std::cerr << "IPv4: " << (ipv4? "yes" : "no") << std::endl;
std::cerr << "IPv6: " << (ipv6? "yes" : "no") << std::endl;
std::cerr << "TCP: " << ( tcp? "yes" : "no") << std::endl;
std::cerr << "UDP: " << ( udp? "yes" : "no") << std::endl;
return EXIT_UNIMPLEMENTED;
}