Compare commits

...

4 Commits

Author SHA1 Message Date
BodgeMaster efa4fa560e 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.
2022-07-15 13:51:19 +02:00
BodgeMaster 4c3d2fdbbf lib/cli: ArgumentsParser constructor can accept both char** and const char** 2022-07-15 13:49:43 +02:00
BodgeMaster 3b1d288d1e test/cli_argument_parser add an assertion to the valid input test that expects the value of unpositional arguments that are not present to be "" 2022-07-15 13:44:56 +02:00
BodgeMaster 362ec5f4f9 Build system: symlink dependency libs to bin/lib 2022-07-15 13:43:58 +02:00
6 changed files with 52 additions and 115 deletions

View File

@ -73,7 +73,7 @@ mkdir -pv bin/tools
# add compile commands to this array
COMPILE_COMMANDS=(
"$CXX_WITH_FLAGS src/tools/dumpnbt.cpp -Lbin/lib -l:nbt.so -o bin/tools/dumpnbt"
"$CXX_WITH_FLAGS src/tools/hexnet.cpp -Ldependencies/sockpp-0.7.1/build -l:libsockpp.so -Idependencies/sockpp-0.7.1/include -o bin/tools/hexnet"
"$CXX_WITH_FLAGS src/tools/hexnet.cpp -Lbin/lib -l:cli.so -l:libsockpp.so -Idependencies/sockpp-0.7.1/include -o bin/tools/hexnet"
)
for command in ${!COMPILE_COMMANDS[@]}; do
echo "${COMPILE_COMMANDS[command]}"

View File

@ -19,5 +19,11 @@ rm -rv ./bin
rm -vf .endianness
rm -vf resources/check_endianness
mkdir -v ./bin
mkdir -v ./bin/lib
ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so bin/lib/
ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so.0 bin/lib/
ln -vs ../../dependencies/sockpp-0.7.1/build/libsockpp.so.0.7.1 bin/lib/
set -v
echo -n "" > ./bin/.placeholder

View File

@ -53,7 +53,7 @@ namespace CLI {
}
// using int here bc that's how main() is defined
ArgumentsParser::ArgumentsParser(int argc, const char* argv[], std::vector<Flag> flags, std::vector<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments) {
ArgumentsParser::ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments) {
this->wrongUsage = false;
this->wrongUsageMessages = std::vector<std::string>();
this->programName = std::string(argv[0]);

View File

@ -75,7 +75,7 @@ namespace CLI {
std::vector<std::string> wrongUsageMessages;
// using int here bc that's how main() is defined
ArgumentsParser(int argc, const char* argv[], std::vector<Flag> flags, std::vector<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments);
ArgumentsParser(int argc, const char* const argv[], std::vector<Flag> flags, std::vector<UnpositionalArgument> unpositionalArguments, std::vector<PositionalArgument> positionalArguments);
~ArgumentsParser();
ErrorOr<bool> getFlag(char shortName);

View File

@ -34,7 +34,6 @@ int main(int argc, char* argv[]) {
// test -0 -ab -12345 --long-flag -cconcatenated -d separate-value -efdouble-concatenated "positional argument 0" -gh concatenated-separate-value --long-argument-with-value-included="included value" --long-argument-with-value-separated "separate value" "positional argument 1" "positional argument 2"
std::vector<CLI::Flag> flags;
//flags.push_back(CLI::Flag(shortName, longName, description));
flags.push_back(CLI::Flag('0', "00000", "a short flag on its own"));
flags.push_back(CLI::Flag('a', "aaaaa", "concatenated short flags"));
flags.push_back(CLI::Flag('b', "bbbbb", "concatenated short flags"));
@ -148,6 +147,7 @@ int main(int argc, char* argv[]) {
ASSERT(parser.getPositionalArgument(2).value=="positional argument 2");
ASSERT(!parser.getUnpositionalArgument('z').isError);
ASSERT(parser.getUnpositionalArgument('z').errorCode == ErrorCodes::NOT_PRESENT);
ASSERT(parser.getUnpositionalArgument('z').value == std::string(""));
std::cout << "Passed valid input test." << std::endl;

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;
}