diff options
| author | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-12 21:57:46 +1100 |
|---|---|---|
| committer | Nicolas James <Eele1Ephe7uZahRie@tutanota.com> | 2025-02-12 21:57:46 +1100 |
| commit | e4483eca01b48b943cd0461e24a74ae1a3139ed4 (patch) | |
| tree | ed58c3c246e3af1af337697695d780aa31f6ad9a /src/shared/init.cc | |
| parent | 1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (diff) | |
Update to most recent version (old initial commit)
Diffstat (limited to 'src/shared/init.cc')
| -rw-r--r-- | src/shared/init.cc | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/src/shared/init.cc b/src/shared/init.cc new file mode 100644 index 0000000..3415abb --- /dev/null +++ b/src/shared/init.cc @@ -0,0 +1,182 @@ +#include "shared/init.hh" + +namespace shared { + +static void set_signal(const decltype(SIGINT) signal, + void (*const callback)(int)) { + struct sigaction sa {}; + sa.sa_handler = callback; + sa.sa_flags = 0; + if (sigaction(signal, &sa, nullptr) == -1) { + throw std::runtime_error("failed to set signal handler for signal " + + std::to_string(signal)); + } +} + +static void set_terminate_backtrace() { + std::set_terminate([]() { + try { + throw; + } catch (const std::exception& e) { + shared::print::fault << shared::print::time + << "unhandled exception!\n" + << " what: " << e.what() << '\n'; + + constexpr std::size_t BACKTRACE_BUF_SIZE = 64; + std::array<void*, BACKTRACE_BUF_SIZE> bt_buffer{}; + const int nptrs = + backtrace(std::data(bt_buffer), std::size(bt_buffer)); + char** strings = backtrace_symbols(std::data(bt_buffer), nptrs); + + shared::print::message << "\n backtrace:\n"; + for (int i = 0; i < nptrs; ++i) { + shared::print::message << " " << strings[i] << '\n'; + } + free(strings); // controversial + + } catch (...) { + } + std::exit(EXIT_FAILURE); + }); +} + +void init() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + set_terminate_backtrace(); + set_signal(SIGPIPE, SIG_IGN); + set_signal(SIGINT, []([[maybe_unused]] const int signum) { + shared::print::warn << " interrupt signal received\n"; + shared::should_exit = true; // graceful cleanup + }); +#ifndef NDEBUG + shared::print::debug << "This binary is a debug build and will be slower " + "and more verbose than usual\n"; +#endif +} + +void try_main(const main_func_t& func, const std::string_view address, + const std::string_view port) { + try { + func(address, port); + } catch (const std::exception& e) { + shared::print::fault << shared::print::time << "caught exception!\n" + << " what: " << e.what() << '\n'; + } +} + +const args_t& get_options() { + static args_t ret{ + {.name = "help", .desc = "display this, quit", .val = "h"}}; + return ret; +} + +bool parse_arg(const int&, const char* const) { return false; } + +static std::vector<struct option> make_long_options(const args_t& args) { + std::vector<struct option> options{ + option{"help", no_argument, nullptr, 'h'}}; + std::transform(std::begin(args), std::end(args), + std::back_inserter(options), [](const auto& arg) { + const auto req = std::string{arg.val}.ends_with(':') + ? required_argument + : no_argument; + return option{arg.name, req, nullptr, *arg.val}; + }); + return options; +} + +static std::string make_long_vals(const args_t& args) { + std::string ret{"h"}; // always include help + for (const auto& arg : args) { + ret.append(arg.val); + } + return ret; +} + +static auto get_option_it(const auto& options, const decltype(optopt)& c) { + return std::ranges::find_if(options, + [&](const auto& opt) { return c == opt.val; }); +} + +static void print_help(const args_t& args, const auto& options) { + const auto print_table = [](const auto& val, const int padding) { + std::cout << std::left << std::setw(padding) << std::setfill(' ') + << val; + }; + + const int max = static_cast<int>(std::strlen( + std::ranges::max_element(args, [](const auto& a, const auto& b) { + return std::strlen(a.name) < std::strlen(b.name); + })->name)); + for (const auto& arg : args) { + const auto it = get_option_it(options, *arg.val); + if (it == std::end(options)) { + continue; + } + std::cout << " "; + print_table(std::string{"--"} + arg.name, 3 + max); + print_table(std::string{"-"} + static_cast<char>(it->val), 3); + std::cout << (it->has_arg == required_argument ? "<arg> " : " "); + std::cout << ": "; + print_table(arg.desc, 10); + std::cout << '\n'; + } +} + +void parse_args(const int argc, char* const argv[], const args_t& args, + const args_callbacks_t& callbacks) { + const auto long_options = make_long_options(args); + const auto long_vals = make_long_vals(args); + + for (int option_index = opterr = 0;;) { + const auto c = getopt_long(argc, argv, long_vals.c_str(), + std::data(long_options), &option_index); + if (c == -1) { + break; + } else if (c == '?') { // parsing error + const auto it = get_option_it(long_options, optopt); + if (it != std::end(long_options)) { + shared::print::fault + << " bad argument: \"" << it->name << "\" required " + << (it->has_arg == required_argument ? "an" : "no") + << " argument\n"; + } else { + shared::print::fault << " unknown argument: \"" + << /*TODO get arg <<*/ "\"\n"; + } + std::exit(EXIT_FAILURE); + } + + if (c == 'h') { + print_help(args, long_options); + std::exit(EXIT_SUCCESS); + } + + try { + const char* const arg = optarg + (optarg && optarg[0] == '='); + for (const auto& callback : callbacks) { + if (!callback(c, arg)) { + continue; + } + break; + } + } catch (const boost::bad_lexical_cast& e) { + const auto find_it = get_option_it(long_options, c); + shared::print::fault + << " bad argument: \"" + << (find_it == std::end(long_options) ? "" : find_it->name) + << "\" failed type conversion\n"; + std::exit(EXIT_FAILURE); + } + } + if (optind < argc) { + shared::print::fault << " unrecognised argument(s):"; + for (int i = optind; i < argc; ++i) { + shared::print::fault << std::string{" "} << argv[i]; + } + shared::print::fault << '\n'; + std::exit(EXIT_FAILURE); + } +} + +} // namespace shared |
