diff --git a/CMakeLists.txt b/CMakeLists.txt index 428b39950..65c14df80 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,15 +286,20 @@ if(SHADOW) else() if(NOT WIN32) add_executable(${EXE} ${EXE_SRC}) + add_executable(lokinet-rcutil daemon/rcutil.cpp) elseif(NOT MSVC_VERSION) add_executable(${EXE} ${EXE_SRC} llarp/win32/version.rc) + add_executable(lokinet-rcutil daemon/rcutil.cpp llarp/win32/version.rc) else() add_executable(${EXE} ${EXE_SRC}) + add_executable(lokinet-rcutil daemon/rcutil.cpp) endif(NOT WIN32) add_log_tag(${EXE}) + add_log_tag(lokinet-rcutil) install(TARGETS ${EXE} RUNTIME DESTINATION bin) + install(TARGETS lokinet-rcutil RUNTIME DESTINATION bin) if(WIN32) install(PROGRAMS ${CMAKE_SOURCE_DIR}/lokinet-bootstrap.exe DESTINATION bin) else() @@ -306,8 +311,10 @@ else() endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") target_link_directories(${EXE} PRIVATE /usr/local/lib) + target_link_directories(lokinet-rcutil PRIVATE /usr/local/lib) endif() target_link_libraries(${EXE} PUBLIC ${EXE_LIBS} ${LIBS}) + target_link_libraries(lokinet-rcutil PUBLIC ${EXE_LIBS} ${LIBS}) if(ANDROID) add_library(${ANDROID_LIB} SHARED jni/lokinet_android.cpp) diff --git a/daemon/rcutil.cpp b/daemon/rcutil.cpp index 380ed7b62..23631e0b0 100644 --- a/daemon/rcutil.cpp +++ b/daemon/rcutil.cpp @@ -1,598 +1,98 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include -#include -#include -#include +#include +#include +#include -struct llarp_main *ctx = 0; - -void -handle_signal(int sig) +bool +dumpRc(const std::vector< std::string >& files, bool json) { - if(ctx) - llarp_main_signal(ctx, sig); -} - -#ifndef TESTNET -#define TESTNET 0 -#endif - -void -displayRC(const llarp::RouterContact &rc) -{ - std::cout << rc.pubkey << std::endl; - for(const auto &addr : rc.addrs) + nlohmann::json result; + for(const auto& file : files) { - std::cout << "AddressInfo: " << addr << std::endl; - } -} + llarp::RouterContact rc; + const bool ret = rc.Read(file.c_str()); -// fwd declr -struct check_online_request; - -void -HandleDHTLocate(llarp_router_lookup_job *job) -{ - llarp::LogInfo("DHT result: ", job->found ? "found" : "not found"); - if(job->found) - { - // save to nodedb? - displayRC(job->result); - } - // shutdown router - - // well because we're in the gotroutermessage, we can't sigint because we'll - // deadlock because we're session locked - // llarp_main_signal(ctx, SIGINT); - - // llarp_timer_run(logic->timer, logic->thread); - // we'll we don't want logic thread - // but we want to switch back to the main thread - // llarp_logic_stop(); - // still need to exit this logic thread... - llarp_main_abort(ctx); -} - -int -main(int argc, char *argv[]) -{ - // take -c to set location of daemon.ini - // take -o to set log level - // --generate-blank /path/to/file.signed - // --update-ifs /path/to/file.signed - // --key /path/to/long_term_identity.key - // --import - // --export - - // --generate /path/to/file.signed - // --update /path/to/file.signed - // --verify /path/to/file.signed - // printf("has [%d]options\n", argc); - if(argc < 2) - { - printf( - "please specify: \n" - "--generate with a path to a router contact file\n" - "--update with a path to a router contact file\n" - "--list path to nodedb skiplist\n" - "--import with a path to a router contact file\n" - "--export a hex formatted public key\n" - "--locate a hex formatted public key\n" - "--find a base32 formatted service address\n" - "--b32 a hex formatted public key\n" - "--hex a base32 formatted public key\n" - "--localInfo \n" - "--read with a path to a router contact file\n" - "--verify with a path to a router contact file\n" - "\n"); - return 0; - } - bool haveRequiredOptions = false; - bool genMode = false; - bool updMode = false; - bool listMode = false; - bool importMode = false; - bool exportMode = false; - bool locateMode = false; - bool findMode = false; - bool localMode = false; - bool verifyMode = false; - bool readMode = false; - bool toHexMode = false; - bool toB32Mode = false; - int c; - char *conffname; - char defaultConfName[] = "daemon.ini"; - conffname = defaultConfName; - char *rcfname = nullptr; - char *nodesdir = nullptr; - - llarp::RouterContact rc; - while(1) - { - static struct option long_options[] = { - {"file", required_argument, 0, 'f'}, - {"config", required_argument, 0, 'c'}, - {"logLevel", required_argument, 0, 'o'}, - {"generate", required_argument, 0, 'g'}, - {"update", required_argument, 0, 'u'}, - {"list", required_argument, 0, 'l'}, - {"import", required_argument, 0, 'i'}, - {"export", required_argument, 0, 'e'}, - {"locate", required_argument, 0, 'q'}, - {"find", required_argument, 0, 'F'}, - {"localInfo", no_argument, 0, 'n'}, - {"read", required_argument, 0, 'r'}, - {"b32", required_argument, 0, 'b'}, - {"hex", required_argument, 0, 'h'}, - {"verify", required_argument, 0, 'V'}, - {0, 0, 0, 0}}; - int option_index = 0; - c = getopt_long(argc, argv, "c:f:o:g:lu:i:e:q:F:nr:b:h:V:", long_options, - &option_index); -#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) - if(c == -1) - break; - switch(c) + if(ret) { - case 0: - break; - case 'c': - conffname = optarg; - break; - case 'o': - if(strncmp(optarg, "debug", - MIN(strlen(optarg), static_cast< unsigned long >(5))) - == 0) - { - llarp::SetLogLevel(llarp::eLogDebug); - } - else if(strncmp(optarg, "info", - MIN(strlen(optarg), static_cast< unsigned long >(4))) - == 0) - { - llarp::SetLogLevel(llarp::eLogInfo); - } - else if(strncmp(optarg, "warn", - MIN(strlen(optarg), static_cast< unsigned long >(4))) - == 0) - { - llarp::SetLogLevel(llarp::eLogWarn); - } - else if(strncmp(optarg, "error", - MIN(strlen(optarg), static_cast< unsigned long >(5))) - == 0) - { - llarp::SetLogLevel(llarp::eLogError); - } - break; - case 'V': - rcfname = optarg; - haveRequiredOptions = true; - verifyMode = true; - break; - case 'f': - rcfname = optarg; - break; - case 'l': - nodesdir = optarg; - listMode = true; - break; - case 'i': - // printf ("option -g with value `%s'\n", optarg); - nodesdir = optarg; - importMode = true; - break; - case 'e': - // printf ("option -g with value `%s'\n", optarg); - rcfname = optarg; - exportMode = true; - break; - case 'q': - // printf ("option -g with value `%s'\n", optarg); - rcfname = optarg; - locateMode = true; - break; - case 'F': - rcfname = optarg; - haveRequiredOptions = true; - findMode = true; - break; - case 'g': - // printf ("option -g with value `%s'\n", optarg); - rcfname = optarg; - genMode = true; - break; - case 'u': - // printf ("option -u with value `%s'\n", optarg); - rcfname = optarg; - updMode = true; - break; - case 'n': - localMode = true; - break; - case 'r': - rcfname = optarg; - readMode = true; - break; - case 'b': - rcfname = optarg; - haveRequiredOptions = true; - toB32Mode = true; - break; - case 'h': - rcfname = optarg; - haveRequiredOptions = true; - toHexMode = true; - break; - default: - printf("Bad option: %c\n", c); - return -1; - } - } -#undef MIN - if(!haveRequiredOptions) - { - llarp::LogError("Parameters dont all have their required parameters.\n"); - return 0; - } - // printf("parsed options\n"); - - if(!genMode && !updMode && !listMode && !importMode && !exportMode - && !locateMode && !localMode && !readMode && !findMode && !toB32Mode - && !toHexMode && !verifyMode) - { - llarp::LogError( - "I don't know what to do, no generate or update parameter\n"); - return 1; - } - -#ifdef LOKINET_DEBUG - absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); -#endif - - llarp::RouterContact tmp; - - if(verifyMode) - { - llarp::Crypto crypto(llarp::Crypto::sodium{}); - if(!rc.Read(rcfname)) - { - std::cout << "failed to read " << rcfname << std::endl; - return 1; - } - if(!rc.Verify(&crypto)) - { - std::cout << rcfname << " is invalid" << std::endl; - return 1; - } - if(!rc.IsPublicRouter()) - { - std::cout << rcfname << " is not a public router"; - if(rc.addrs.size() == 0) + if(json) { - std::cout << " because it has no public addresses"; + result[file] = rc.ToJson(); } - std::cout << std::endl; - return 1; - } - - std::cout << "router identity and dht routing key: " << rc.pubkey - << std::endl; - - std::cout << "router encryption key: " << rc.enckey << std::endl; - - if(rc.HasNick()) - std::cout << "router nickname: " << rc.Nick() << std::endl; - - std::cout << "advertised addresses: " << std::endl; - for(const auto &addr : rc.addrs) - { - std::cout << addr << std::endl; - } - std::cout << std::endl; - - std::cout << "advertised exits: "; - if(rc.exits.size()) - { - for(const auto &exit : rc.exits) + else { - std::cout << exit << std::endl; + std::cout << "file = " << file << "\n"; + std::cout << rc << "\n\n"; } } else { - std::cout << "none"; + std::cerr << "file = " << file << " was not a valid rc file\n"; } - std::cout << std::endl; - return 0; } - ctx = llarp_main_init(conffname, !TESTNET); - if(!ctx) + if(json) + std::cout << result << "\n"; + + return true; +} + +int +main(int argc, char* argv[]) +{ +#ifdef LOKINET_DEBUG + absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort); +#endif + + // clang-format off + cxxopts::Options options( + "lokinet-rcutil", + "LokiNET is a free, open source, private, decentralized, \"market based sybil resistant\" and IP based onion routing network" + ); + + options.add_options() + ("v,verbose", "Verbose", cxxopts::value()) + ("h,help", "help", cxxopts::value()) + ("j,json", "output in json", cxxopts::value()) + ("dump", "dump rc file", cxxopts::value >(), "FILE"); + // clang-format on + + try { - llarp::LogError("Cant set up context"); + const auto result = options.parse(argc, argv); + + const bool json = result["json"].as< bool >(); + + if(result.count("verbose") > 0) + { + SetLogLevel(llarp::eLogDebug); + llarp::LogContext::Instance().logStream = + std::make_unique< llarp::OStreamLogStream >(std::cerr); + llarp::LogDebug("debug logging activated"); + } + + if(result.count("help") > 0) + { + std::cout << options.help() << std::endl; + return 0; + } + + if(result.count("dump") > 0) + { + if(!dumpRc(result["dump"].as< std::vector< std::string > >(), json)) + { + return 1; + } + } + } + catch(const cxxopts::OptionParseException& ex) + { + std::cerr << ex.what() << std::endl; + std::cout << options.help() << std::endl; return 1; } - signal(SIGINT, handle_signal); - // is this Neuro or Jeff's? - // this is the only one... - if(listMode) - { - llarp::Crypto crypto(llarp::Crypto::sodium{}); - auto nodedb = llarp_nodedb_new(&crypto); - llarp_nodedb_iter itr; - itr.visit = [](llarp_nodedb_iter *i) -> bool { - std::cout << i->rc->pubkey << std::endl; - return true; - }; - if(llarp_nodedb_load_dir(nodedb, nodesdir) > 0) - llarp_nodedb_iterate_all(nodedb, itr); - llarp_nodedb_free(&nodedb); - return 0; - } - - if(importMode) - { - if(rcfname == nullptr) - { - std::cout << "no file to import" << std::endl; - return 1; - } - llarp::Crypto crypto(llarp::Crypto::sodium{}); - auto nodedb = llarp_nodedb_new(&crypto); - if(!llarp_nodedb_ensure_dir(nodesdir)) - { - std::cout << "failed to ensure " << nodesdir << strerror(errno) - << std::endl; - return 1; - } - llarp_nodedb_set_dir(nodedb, nodesdir); - if(!rc.Read(rcfname)) - { - std::cout << "failed to read " << rcfname << " " << strerror(errno) - << std::endl; - return 1; - } - - if(!rc.Verify(&crypto)) - { - std::cout << rcfname << " is invalid" << std::endl; - return 1; - } - - if(!llarp_nodedb_put_rc(nodedb, rc)) - { - std::cout << "failed to store " << strerror(errno) << std::endl; - return 1; - } - - std::cout << "imported " << rc.pubkey << std::endl; - - return 0; - } - - if(genMode) - { - printf("Creating [%s]\n", rcfname); - // if we zero it out then - // set updated timestamp - rc.last_updated = llarp::time_now_ms(); - // load longterm identity - llarp::Crypto crypt(llarp::Crypto::sodium{}); - - // which is in daemon.ini config: router.encryption-privkey (defaults - // "encryption.key") - fs::path encryption_keyfile = "encryption.key"; - llarp::SecretKey encryption; - - llarp_findOrCreateEncryption(&crypt, encryption_keyfile, encryption); - - rc.enckey = llarp::seckey_topublic(encryption); - - // get identity public sig key - fs::path ident_keyfile = "identity.key"; - llarp::SecretKey identity; - llarp_findOrCreateIdentity(&crypt, ident_keyfile, identity); - - rc.pubkey = llarp::seckey_topublic(identity); - - // this causes a segfault - if(!rc.Sign(&crypt, identity)) - { - std::cout << "failed to sign" << std::endl; - return 1; - } - // set filename - fs::path our_rc_file = rcfname; - // write file - rc.Write(our_rc_file.string().c_str()); - - // llarp_rc_write(&tmp, our_rc_file.string().c_str()); - - // release memory for tmp lists - // llarp_rc_free(&tmp); - } - if(updMode) - { - printf("rcutil.cpp - Loading [%s]\n", rcfname); - llarp::RouterContact tmp; - // llarp_rc_clear(&rc); - rc.Clear(); - // FIXME: new rc api - // llarp_rc_read(rcfname, &rc); - - // set updated timestamp - rc.last_updated = llarp::time_now_ms(); - // load longterm identity - llarp::Crypto crypt(llarp::Crypto::sodium{}); - - // no longer used? - // llarp_crypto_libsodium_init(&crypt); - llarp::SecretKey identityKey; // FIXME: Jeff requests we use this - fs::path ident_keyfile = "identity.key"; - llarp::SecretKey identity; - llarp_findOrCreateIdentity(&crypt, ident_keyfile, identity); - - // FIXME: update RC API - // get identity public key - // const uint8_t *pubkey = llarp::seckey_topublic(identity); - - // FIXME: update RC API - // llarp_rc_set_pubsigkey(&rc, pubkey); - // // FIXME: update RC API - // llarp_rc_sign(&crypt, identity, &rc); - - // set filename - fs::path our_rc_file_out = "update_debug.rc"; - // write file - // FIXME: update RC API - // rc.Write(our_rc_file.string().c_str()); - // llarp_rc_write(&tmp, our_rc_file_out.string().c_str()); - } - - if(listMode) - { - llarp::Crypto crypto(llarp::Crypto::sodium{}); - auto nodedb = llarp_nodedb_new(&crypto); - llarp_nodedb_iter itr; - itr.visit = [](llarp_nodedb_iter *i) -> bool { - std::cout << llarp::PubKey(i->rc->pubkey) << std::endl; - return true; - }; - if(llarp_nodedb_load_dir(nodedb, nodesdir) > 0) - llarp_nodedb_iterate_all(nodedb, itr); - llarp_nodedb_free(&nodedb); - return 0; - } - if(exportMode) - { - llarp_main_loadDatabase(ctx); - // llarp::LogInfo("Looking for string: ", rcfname); - - llarp::PubKey binaryPK; - llarp::HexDecode(rcfname, binaryPK.data(), binaryPK.size()); - - llarp::LogInfo("Looking for binary: ", binaryPK); - llarp::RouterContact *rc = llarp_main_getDatabase(ctx, binaryPK.data()); - if(!rc) - { - llarp::LogError("Can't load RC from database"); - } - std::string filename(rcfname); - filename.append(".signed"); - llarp::LogInfo("Writing out: ", filename); - // FIXME: update RC API - // rc.Write(our_rc_file.string().c_str()); - // llarp_rc_write(rc, filename.c_str()); - } - if(locateMode) - { - llarp::LogInfo("Going online"); - llarp_main_setup(ctx); - - llarp::PubKey binaryPK; - llarp::HexDecode(rcfname, binaryPK.data(), binaryPK.size()); - - llarp::LogInfo("Queueing job"); - llarp_router_lookup_job *job = new llarp_router_lookup_job; - job->iterative = true; - job->found = false; - job->hook = &HandleDHTLocate; - // llarp_rc_new(&job->result); - job->target = binaryPK; // set job's target - - // create query DHT request - check_online_request *request = new check_online_request; - request->ptr = ctx; - request->job = job; - request->online = false; - request->nodes = 0; - request->first = false; - llarp_main_queryDHT(request); - - llarp::LogInfo("Processing"); - // run system and wait - llarp_main_run(ctx); - } - if(findMode) - { - llarp::LogInfo("Going online"); - llarp_main_setup(ctx); - - llarp::LogInfo("Please find ", rcfname); - std::string str(rcfname); - - llarp::service::Tag tag(rcfname); - llarp::LogInfo("Tag ", tag); - - llarp::service::Address addr; - str = str.append(".loki"); - llarp::LogInfo("Prestring ", str); - bool res = addr.FromString(str.c_str()); - llarp::LogInfo(res ? "Success" : "not a base32 string"); - - // Base32Decode(rcfname, addr); - llarp::LogInfo("Addr ", addr); - - // uint64_t txid, const llarp::service::Address& addr - // FIXME: new API? - // msg->M.push_back(new llarp::dht::FindIntroMessage(tag, 1)); - - // I guess we may need a router to get any replies - llarp::LogInfo("Processing"); - // run system and wait - llarp_main_run(ctx); - } - if(localMode) - { - // FIXME: update llarp_main_getLocalRC - // llarp::RouterContact *rc = llarp_main_getLocalRC(ctx); - // displayRC(rc); - // delete it - } - { - if(rc.Read(rcfname)) - displayRC(rc); - } - - if(toB32Mode) - { - llarp::LogInfo("Converting hex string ", rcfname); - std::string str(rcfname); - - llarp::PubKey binaryPK; - // llarp::service::Address::FromString - llarp::HexDecode(rcfname, binaryPK.data(), binaryPK.size()); - char tmp[(1 + 32) * 2] = {0}; - std::string b32 = llarp::Base32Encode(binaryPK, tmp); - llarp::LogInfo("to base32 ", b32); - } - if(toHexMode) - { - llarp::service::Address addr; - llarp::Base32Decode(rcfname, addr); - llarp::LogInfo("Converting base32 string ", addr); - - // llarp::service::Address::ToString - char ftmp[68] = {0}; - const char *hexname = - llarp::HexEncode< llarp::service::Address, decltype(ftmp) >(addr, ftmp); - - llarp::LogInfo("to hex ", hexname); - } - // it's a unique_ptr, should clean up itself - // llarp_main_free(ctx); - return 0; // success + return 0; } diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/ev_libuv.cpp index 6588832ee..8868f0e65 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/ev_libuv.cpp @@ -633,17 +633,16 @@ namespace libuv Loop::CloseAll() { llarp::LogInfo("Closing all handles"); - uv_walk( - m_Impl.get(), - [](uv_handle_t* h, void*) { - if(uv_is_closing(h)) - return; - if(h->data && uv_is_active(h)) - { - static_cast< glue* >(h->data)->Close(); - } - }, - nullptr); + uv_walk(m_Impl.get(), + [](uv_handle_t* h, void*) { + if(uv_is_closing(h)) + return; + if(h->data && uv_is_active(h)) + { + static_cast< glue* >(h->data)->Close(); + } + }, + nullptr); } void diff --git a/llarp/path/path_context.cpp b/llarp/path/path_context.cpp index 84a19f575..b2460f84b 100644 --- a/llarp/path/path_context.cpp +++ b/llarp/path/path_context.cpp @@ -164,15 +164,14 @@ namespace llarp HopHandler_ptr PathContext::GetByUpstream(const RouterID& remote, const PathID_t& id) { - auto own = MapGet( - m_OurPaths, id, - [](const PathSet_ptr) -> bool { - // TODO: is this right? - return true; - }, - [remote, id](PathSet_ptr p) -> HopHandler_ptr { - return p->GetByUpstream(remote, id); - }); + auto own = MapGet(m_OurPaths, id, + [](const PathSet_ptr) -> bool { + // TODO: is this right? + return true; + }, + [remote, id](PathSet_ptr p) -> HopHandler_ptr { + return p->GetByUpstream(remote, id); + }); if(own) return own; diff --git a/llarp/router_contact.hpp b/llarp/router_contact.hpp index a65f7bcea..e7eb10bc7 100644 --- a/llarp/router_contact.hpp +++ b/llarp/router_contact.hpp @@ -10,6 +10,7 @@ #include #include +#include #include #define MAX_RC_SIZE (1024) @@ -107,6 +108,12 @@ namespace llarp util::StatusObject ExtractStatus() const; + nlohmann::json + ToJson() const + { + return ExtractStatus().get(); + } + bool BEncode(llarp_buffer_t *buf) const; diff --git a/vendor/cxxopts/CHANGELOG.md b/vendor/cxxopts/CHANGELOG.md index 7bcbf72ab..bd0eed198 100644 --- a/vendor/cxxopts/CHANGELOG.md +++ b/vendor/cxxopts/CHANGELOG.md @@ -3,16 +3,29 @@ This is the changelog for `cxxopts`, a C++11 library for parsing command line options. The project adheres to semantic versioning. +## Next version + +### Changed + +* Only search for a C++ compiler in CMakeLists.txt. +* Allow for exceptions to be disabled. +* Fix duplicate default options when there is a short and long option. +* Add `CXXOPTS_NO_EXCEPTIONS` to disable exceptions. + ## 2.2 ### Changed * Allow integers to have leading zeroes. * Build the tests by default. +* Don't check for container when showing positional help. ### Added * Iterator inputs to `parse_positional`. +* Throw an exception if the option in `parse_positional` doesn't exist. +* Parse a delimited list in a single argument for vector options. +* Add an option to disable implicit value on booleans. ### Bug Fixes @@ -22,6 +35,7 @@ options. The project adheres to semantic versioning. * Throw on invalid option syntax when beginning with a `-`. * Throw in `as` when option wasn't present. * Fix catching exceptions by reference. +* Fix out of bounds errors parsing integers. ## 2.1.1 diff --git a/vendor/cxxopts/CMakeLists.txt b/vendor/cxxopts/CMakeLists.txt index e866cb128..1b524f723 100644 --- a/vendor/cxxopts/CMakeLists.txt +++ b/vendor/cxxopts/CMakeLists.txt @@ -30,12 +30,13 @@ endforeach() set(VERSION ${CXXOPTS__VERSION_MAJOR}.${CXXOPTS__VERSION_MINOR}.${CXXOPTS__VERSION_PATCH}) message(STATUS "cxxopts version ${VERSION}") -project(cxxopts VERSION "${VERSION}") +project(cxxopts VERSION "${VERSION}" LANGUAGES CXX) enable_testing() option(CXXOPTS_BUILD_EXAMPLES "Set to ON to build examples" ON) option(CXXOPTS_BUILD_TESTS "Set to ON to build tests" ON) +option(CXXOPTS_ENABLE_INSTALL "Generate the install target" ON) # request c++11 without gnu extension for the whole project and enable more warnings if (CXXOPTS_CXX_STANDARD) @@ -53,6 +54,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES endif() add_library(cxxopts INTERFACE) +add_library(cxxopts::cxxopts ALIAS cxxopts) # optionally, enable unicode support using the ICU library set(CXXOPTS_USE_UNICODE_HELP FALSE CACHE BOOL "Use ICU Unicode library") @@ -70,35 +72,37 @@ target_include_directories(cxxopts INTERFACE $ ) -include(CMakePackageConfigHelpers) -set(CXXOPTS_CMAKE_DIR "lib/cmake/cxxopts" CACHE STRING - "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") -set(version_config "${PROJECT_BINARY_DIR}/cxxopts-config-version.cmake") -set(project_config "${PROJECT_BINARY_DIR}/cxxopts-config.cmake") -set(targets_export_name cxxopts-targets) +if(CXXOPTS_ENABLE_INSTALL) + include(CMakePackageConfigHelpers) + set(CXXOPTS_CMAKE_DIR "lib/cmake/cxxopts" CACHE STRING + "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") + set(version_config "${PROJECT_BINARY_DIR}/cxxopts-config-version.cmake") + set(project_config "${PROJECT_BINARY_DIR}/cxxopts-config.cmake") + set(targets_export_name cxxopts-targets) -# Generate the version, config and target files into the build directory. -write_basic_package_version_file( - ${version_config} - VERSION ${VERSION} - COMPATIBILITY AnyNewerVersion) -configure_package_config_file( - ${PROJECT_SOURCE_DIR}/cxxopts-config.cmake.in - ${project_config} - INSTALL_DESTINATION ${CXXOPTS_CMAKE_DIR}) -export(TARGETS cxxopts NAMESPACE cxxopts:: - FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) + # Generate the version, config and target files into the build directory. + write_basic_package_version_file( + ${version_config} + VERSION ${VERSION} + COMPATIBILITY AnyNewerVersion) + configure_package_config_file( + ${PROJECT_SOURCE_DIR}/cxxopts-config.cmake.in + ${project_config} + INSTALL_DESTINATION ${CXXOPTS_CMAKE_DIR}) + export(TARGETS cxxopts NAMESPACE cxxopts:: + FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) -# Install version, config and target files. -#install( -# FILES ${project_config} ${version_config} -# DESTINATION ${CXXOPTS_CMAKE_DIR}) -#install(EXPORT ${targets_export_name} DESTINATION ${CXXOPTS_CMAKE_DIR} -# NAMESPACE cxxopts::) + # Install version, config and target files. + install( + FILES ${project_config} ${version_config} + DESTINATION ${CXXOPTS_CMAKE_DIR}) + install(EXPORT ${targets_export_name} DESTINATION ${CXXOPTS_CMAKE_DIR} + NAMESPACE cxxopts::) -# Install the header file and export the target -#install(TARGETS cxxopts EXPORT ${targets_export_name} DESTINATION lib) -#install(FILES ${PROJECT_SOURCE_DIR}/include/cxxopts.hpp DESTINATION include) + # Install the header file and export the target + install(TARGETS cxxopts EXPORT ${targets_export_name} DESTINATION lib) + install(FILES ${PROJECT_SOURCE_DIR}/include/cxxopts.hpp DESTINATION include) +endif() add_subdirectory(src) add_subdirectory(test) diff --git a/vendor/cxxopts/README.md b/vendor/cxxopts/README.md index fa2bce56c..93dcf3b10 100644 --- a/vendor/cxxopts/README.md +++ b/vendor/cxxopts/README.md @@ -116,6 +116,18 @@ There is no way to disambiguate positional arguments from the value following a boolean, so we have chosen that they will be positional arguments, and therefore, `-o false` does not work. +## `std::vector` values + +Parsing of list of values in form of an `std::vector` is also supported, as long as `T` +can be parsed. To separate single values in a list the definition `CXXOPTS_VECTOR_DELIMITER` +is used, which is ',' by default. Ensure that you use no whitespaces between values because +those would be interpreted as the next command line option. Example for a command line option +that can be parsed as a `std::vector`: + +~~~ +--my_list=1,-2.1,3,4.5 +~~~ + ## Custom help The string after the program name on the first line of the help can be diff --git a/vendor/cxxopts/include/cxxopts.hpp b/vendor/cxxopts/include/cxxopts.hpp index 94b81ef0b..9cde72d6b 100644 --- a/vendor/cxxopts/include/cxxopts.hpp +++ b/vendor/cxxopts/include/cxxopts.hpp @@ -29,6 +29,7 @@ THE SOFTWARE. #include #include #include +#include #include #include #include @@ -43,6 +44,10 @@ THE SOFTWARE. #define CXXOPTS_HAS_OPTIONAL #endif +#ifndef CXXOPTS_VECTOR_DELIMITER +#define CXXOPTS_VECTOR_DELIMITER ',' +#endif + #define CXXOPTS__VERSION_MAJOR 2 #define CXXOPTS__VERSION_MINOR 2 #define CXXOPTS__VERSION_PATCH 0 @@ -309,6 +314,9 @@ namespace cxxopts virtual std::shared_ptr implicit_value(const std::string& value) = 0; + virtual std::shared_ptr + no_implicit_value() = 0; + virtual bool is_boolean() const = 0; }; @@ -354,7 +362,7 @@ namespace cxxopts { public: option_exists_error(const std::string& option) - : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + u8" already exists") + : OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists") { } }; @@ -363,7 +371,7 @@ namespace cxxopts { public: invalid_option_format_error(const std::string& format) - : OptionSpecException(u8"Invalid option format " + LQUOTE + format + RQUOTE) + : OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE) { } }; @@ -371,8 +379,8 @@ namespace cxxopts class option_syntax_exception : public OptionParseException { public: option_syntax_exception(const std::string& text) - : OptionParseException(u8"Argument " + LQUOTE + text + RQUOTE + - u8" starts with a - but has incorrect syntax") + : OptionParseException("Argument " + LQUOTE + text + RQUOTE + + " starts with a - but has incorrect syntax") { } }; @@ -381,7 +389,7 @@ namespace cxxopts { public: option_not_exists_exception(const std::string& option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" does not exist") + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist") { } }; @@ -391,7 +399,7 @@ namespace cxxopts public: missing_argument_exception(const std::string& option) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" is missing an argument" + "Option " + LQUOTE + option + RQUOTE + " is missing an argument" ) { } @@ -402,7 +410,7 @@ namespace cxxopts public: option_requires_argument_exception(const std::string& option) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" requires an argument" + "Option " + LQUOTE + option + RQUOTE + " requires an argument" ) { } @@ -417,8 +425,8 @@ namespace cxxopts const std::string& arg ) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + - u8" does not take an argument, but argument " + + "Option " + LQUOTE + option + RQUOTE + + " does not take an argument, but argument " + LQUOTE + arg + RQUOTE + " given" ) { @@ -429,7 +437,7 @@ namespace cxxopts { public: option_not_present_exception(const std::string& option) - : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" not present") + : OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present") { } }; @@ -442,7 +450,7 @@ namespace cxxopts const std::string& arg ) : OptionParseException( - u8"Argument " + LQUOTE + arg + RQUOTE + u8" failed to parse" + "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" ) { } @@ -453,12 +461,32 @@ namespace cxxopts public: option_required_exception(const std::string& option) : OptionParseException( - u8"Option " + LQUOTE + option + RQUOTE + u8" is required but not present" + "Option " + LQUOTE + option + RQUOTE + " is required but not present" ) { } }; + template + void throw_or_mimic(const std::string& text) + { + static_assert(std::is_base_of::value, + "throw_or_mimic only works on std::exception and " + "deriving classes"); + +#ifndef CXXOPTS_NO_EXCEPTIONS + // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw + throw T{text}; +#else + // Otherwise manually instantiate the exception, print what() to stderr, + // and abort + T exception{text}; + std::cerr << exception.what() << std::endl; + std::cerr << "Aborting (exceptions disabled)..." << std::endl; + std::abort(); +#endif + } + namespace values { namespace @@ -466,9 +494,9 @@ namespace cxxopts std::basic_regex integer_pattern ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); std::basic_regex truthy_pattern - ("(t|T)(rue)?"); + ("(t|T)(rue)?|1"); std::basic_regex falsy_pattern - ("((f|F)(alse)?)?"); + ("(f|F)(alse)?|0"); } namespace detail @@ -485,16 +513,16 @@ namespace cxxopts { if (negative) { - if (u > static_cast(-(std::numeric_limits::min)())) + if (u > static_cast((std::numeric_limits::min)())) { - throw argument_incorrect_type(text); + throw_or_mimic(text); } } else { if (u > static_cast((std::numeric_limits::max)())) { - throw argument_incorrect_type(text); + throw_or_mimic(text); } } } @@ -523,14 +551,15 @@ namespace cxxopts // if we got to here, then `t` is a positive number that fits into // `R`. So to avoid MSVC C4146, we first cast it to `R`. // See https://github.com/jarro2783/cxxopts/issues/62 for more details. - return -static_cast(t); + return -static_cast(t-1)-1; } template T - checked_negate(T&&, const std::string& text, std::false_type) + checked_negate(T&& t, const std::string& text, std::false_type) { - throw argument_incorrect_type(text); + throw_or_mimic(text); + return t; } template @@ -542,7 +571,7 @@ namespace cxxopts if (match.length() == 0) { - throw argument_incorrect_type(text); + throw_or_mimic(text); } if (match.length(4) > 0) @@ -553,7 +582,6 @@ namespace cxxopts using US = typename std::make_unsigned::type; - constexpr auto umax = (std::numeric_limits::max)(); constexpr bool is_signed = std::numeric_limits::is_signed; const bool negative = match.length(1) > 0; const uint8_t base = match.length(2) > 0 ? 16 : 10; @@ -568,27 +596,28 @@ namespace cxxopts if (*iter >= '0' && *iter <= '9') { - digit = *iter - '0'; + digit = static_cast(*iter - '0'); } else if (base == 16 && *iter >= 'a' && *iter <= 'f') { - digit = *iter - 'a' + 10; + digit = static_cast(*iter - 'a' + 10); } else if (base == 16 && *iter >= 'A' && *iter <= 'F') { - digit = *iter - 'A' + 10; + digit = static_cast(*iter - 'A' + 10); } else { - throw argument_incorrect_type(text); + throw_or_mimic(text); } - if (umax - digit < result * base) + US next = result * base + digit; + if (result > next) { - throw argument_incorrect_type(text); + throw_or_mimic(text); } - result = result * base + digit; + result = next; } detail::check_signed_range(negative, result, text); @@ -601,7 +630,7 @@ namespace cxxopts } else { - value = result; + value = static_cast(result); } } @@ -611,7 +640,7 @@ namespace cxxopts std::stringstream in(text); in >> value; if (!in) { - throw argument_incorrect_type(text); + throw_or_mimic(text); } } @@ -691,7 +720,7 @@ namespace cxxopts return; } - throw argument_incorrect_type(text); + throw_or_mimic(text); } inline @@ -714,9 +743,13 @@ namespace cxxopts void parse_value(const std::string& text, std::vector& value) { - T v; - parse_value(text, v); - value.push_back(v); + std::stringstream in(text); + std::string token; + while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { + T v; + parse_value(token, v); + value.emplace_back(std::move(v)); + } } #ifdef CXXOPTS_HAS_OPTIONAL @@ -825,6 +858,13 @@ namespace cxxopts return shared_from_this(); } + std::shared_ptr + no_implicit_value() + { + m_implicit = false; + return shared_from_this(); + } + std::string get_default_value() const { @@ -1035,21 +1075,29 @@ namespace cxxopts parse_default(std::shared_ptr details) { ensure_value(details); + m_default = true; m_value->parse(); } size_t - count() const + count() const noexcept { return m_count; } + // TODO: maybe default options should count towards the number of arguments + bool + has_default() const noexcept + { + return m_default; + } + template const T& as() const { if (m_value == nullptr) { - throw std::domain_error("No value"); + throw_or_mimic("No value"); } #ifdef CXXOPTS_NO_RTTI @@ -1071,6 +1119,7 @@ namespace cxxopts std::shared_ptr m_value; size_t m_count = 0; + bool m_default = false; }; class KeyValue @@ -1143,7 +1192,7 @@ namespace cxxopts if (iter == m_options->end()) { - throw option_not_present_exception(option); + throw_or_mimic(option); } auto riter = m_results.find(iter->second); @@ -1202,6 +1251,28 @@ namespace cxxopts std::vector m_sequential; }; + struct Option + { + Option + ( + const std::string& opts, + const std::string& desc, + const std::shared_ptr& value = ::cxxopts::value(), + const std::string& arg_help = "" + ) + : opts_(opts) + , desc_(desc) + , value_(value) + , arg_help_(arg_help) + { + } + + std::string opts_; + std::string desc_; + std::shared_ptr value_; + std::string arg_help_; + }; + class Options { typedef std::unordered_map> @@ -1254,6 +1325,20 @@ namespace cxxopts OptionAdder add_options(std::string group = ""); + void + add_options + ( + const std::string& group, + std::initializer_list