|
|
@ -1,5 +1,5 @@ |
|
|
|
/**
|
|
|
|
* Copyright (C) 2019 Jessica James. |
|
|
|
* Copyright (C) 2019-2021 Jessica James. |
|
|
|
* |
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any |
|
|
|
* purpose with or without fee is hereby granted, provided that the above |
|
|
@ -17,28 +17,60 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "app_parameters.hpp" |
|
|
|
#include "jessilib/unicode.hpp" |
|
|
|
#include "jessilib/split.hpp" |
|
|
|
|
|
|
|
namespace jessilib { |
|
|
|
|
|
|
|
app_parameters::app_parameters(int in_argc, char** in_argv) |
|
|
|
: app_parameters{ in_argc, const_cast<const char**>(in_argv) } { |
|
|
|
app_parameters::app_parameters(int, char** in_argv, char** in_envp) |
|
|
|
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { |
|
|
|
// Empty ctor body
|
|
|
|
} |
|
|
|
|
|
|
|
app_parameters::app_parameters(int in_argc, const char** in_argv) { |
|
|
|
// TODO: discard argc/argv and use GetCommandLineW on Windows
|
|
|
|
// TODO: not assume argv is utf-8; it often will not be
|
|
|
|
app_parameters::app_parameters(int, const char** in_argv, const char** in_envp) |
|
|
|
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { |
|
|
|
// Empty ctor body
|
|
|
|
} |
|
|
|
|
|
|
|
app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp) |
|
|
|
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { |
|
|
|
// Empty ctor body
|
|
|
|
} |
|
|
|
|
|
|
|
app_parameters::app_parameters(int, const wchar_t** in_argv, const wchar_t** in_envp) |
|
|
|
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { |
|
|
|
// Empty ctor body
|
|
|
|
} |
|
|
|
|
|
|
|
#ifdef __cpp_lib_generic_unordered_lookup |
|
|
|
#define WRAP_MAP_KEY(in_key) in_key |
|
|
|
#else // We can't use std::string_view for InKeyType until GCC 11 & clang 12, and I still want to support GCC 9
|
|
|
|
#define WRAP_MAP_KEY(in_key) static_cast<string_type>(in_key) |
|
|
|
#endif // __cpp_lib_generic_unordered_lookup
|
|
|
|
|
|
|
|
bool app_parameters::has_switch(string_view_type in_switch) const { |
|
|
|
return m_switches_set.find(WRAP_MAP_KEY(in_switch)) != m_switches_set.end(); |
|
|
|
} |
|
|
|
|
|
|
|
app_parameters::app_parameters(std::vector<string_type> in_args, std::vector<string_type> in_env) { |
|
|
|
// Parse in environment variables first to ensure they're parsed before the early-out
|
|
|
|
for (const auto& env : in_env) { |
|
|
|
auto split = jessilib::split_once(env, u8'='); |
|
|
|
m_values[split.first] = split.second; |
|
|
|
m_env_values[split.first] = std::move(split.second); |
|
|
|
} |
|
|
|
|
|
|
|
// Sanity safety check; should never happen
|
|
|
|
if (in_argc <= 0 || in_argv == nullptr) { |
|
|
|
if (in_args.empty()) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Populate path
|
|
|
|
m_path = reinterpret_cast<const char8_t*>(in_argv[0]); |
|
|
|
m_path = in_args[0]; |
|
|
|
|
|
|
|
// Process args
|
|
|
|
std::u8string_view key; |
|
|
|
std::u8string value; |
|
|
|
string_view_type key; |
|
|
|
string_type value; |
|
|
|
auto flush_value = [&key, &value, this]() { |
|
|
|
// This is the start of a key; flush what we were previously processing
|
|
|
|
if (!key.empty()) { |
|
|
@ -46,29 +78,32 @@ app_parameters::app_parameters(int in_argc, const char** in_argv) { |
|
|
|
m_switches.emplace_back(key); |
|
|
|
} |
|
|
|
else { |
|
|
|
m_values.emplace(key, std::move(value)); |
|
|
|
string_type key_str{ key }; |
|
|
|
m_values[key_str] = value; |
|
|
|
m_arg_values[key_str] = std::move(value); |
|
|
|
value.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
for (int index = 1; index < in_argc; ++index) { |
|
|
|
const char8_t* arg = reinterpret_cast<const char8_t*>(in_argv[index]); |
|
|
|
if (arg != nullptr && *arg != '\0') { |
|
|
|
for (size_t index = 1; index < in_args.size(); ++index) { |
|
|
|
const string_type& arg = in_args[index]; |
|
|
|
if (!arg.empty()) { |
|
|
|
// Check if this is a key or value
|
|
|
|
if (*arg == '-') { |
|
|
|
if (arg.front() == '-') { |
|
|
|
// Flush pending value (if any)
|
|
|
|
flush_value(); |
|
|
|
|
|
|
|
// Strip any leading '-' or '--' and set key
|
|
|
|
key = arg + 1; |
|
|
|
if (key[0] == '-') { |
|
|
|
key = arg; |
|
|
|
key.remove_prefix(1); |
|
|
|
if (key.front() == '-') { |
|
|
|
key.remove_prefix(1); |
|
|
|
} |
|
|
|
|
|
|
|
// Parse key for any value denominator ('=')
|
|
|
|
size_t key_end = key.find('='); |
|
|
|
if (key_end != std::u8string_view::npos) { |
|
|
|
if (key_end != string_view_type::npos) { |
|
|
|
// arg contains start of a value
|
|
|
|
value = key.substr(key_end + 1); |
|
|
|
key = key.substr(0, key_end); |
|
|
@ -93,26 +128,34 @@ app_parameters::app_parameters(int in_argc, const char** in_argv) { |
|
|
|
flush_value(); |
|
|
|
|
|
|
|
// Populate m_switches_set from m_switches
|
|
|
|
m_switches_set = std::unordered_set<std::u8string_view>{ m_switches.begin(), m_switches.end() }; |
|
|
|
m_switches_set = { m_switches.begin(), m_switches.end() }; |
|
|
|
} |
|
|
|
|
|
|
|
std::u8string_view app_parameters::path() const { |
|
|
|
app_parameters::string_view_type app_parameters::path() const { |
|
|
|
return m_path; |
|
|
|
} |
|
|
|
|
|
|
|
const std::vector<std::u8string_view>& app_parameters::arguments() const { |
|
|
|
const std::vector<app_parameters::string_type>& app_parameters::arguments() const { |
|
|
|
return m_args; |
|
|
|
} |
|
|
|
|
|
|
|
const std::vector<std::u8string_view>& app_parameters::switches() const { |
|
|
|
const std::vector<app_parameters::string_type>& app_parameters::switches() const { |
|
|
|
return m_switches; |
|
|
|
} |
|
|
|
|
|
|
|
const std::unordered_set<std::u8string_view>& app_parameters::switches_set() const { |
|
|
|
const app_parameters::set_type& app_parameters::switches_set() const { |
|
|
|
return m_switches_set; |
|
|
|
} |
|
|
|
|
|
|
|
const std::unordered_map<std::u8string_view, std::u8string>& app_parameters::values() const { |
|
|
|
const app_parameters::map_type& app_parameters::arg_values() const { |
|
|
|
return m_arg_values; |
|
|
|
} |
|
|
|
|
|
|
|
const app_parameters::map_type& app_parameters::env_values() const { |
|
|
|
return m_env_values; |
|
|
|
} |
|
|
|
|
|
|
|
const app_parameters::map_type& app_parameters::values() const { |
|
|
|
return m_values; |
|
|
|
} |
|
|
|
|
|
|
@ -126,20 +169,40 @@ object app_parameters::as_object() const { |
|
|
|
return object{}; |
|
|
|
} |
|
|
|
|
|
|
|
return std::map<std::u8string, object>{ |
|
|
|
return std::map<string_type, object>{ |
|
|
|
{ u8"Path"s, m_path }, |
|
|
|
{ u8"Args"s, m_args }, |
|
|
|
{ u8"Switches"s, m_switches }, |
|
|
|
{ u8"ArgValues"s, m_arg_values }, |
|
|
|
{ u8"EnvValues"s, m_env_values }, |
|
|
|
{ u8"Values"s, m_values } |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
bool app_parameters::has_switch(std::u8string_view in_switch) const { |
|
|
|
return m_switches_set.find(in_switch) != m_switches_set.end(); |
|
|
|
app_parameters::string_view_type app_parameters::get_arg_value(string_view_type in_key, string_view_type in_default) const { |
|
|
|
auto result = m_arg_values.find(WRAP_MAP_KEY(in_key)); |
|
|
|
|
|
|
|
// Safety check
|
|
|
|
if (result == m_arg_values.end()) { |
|
|
|
return in_default; |
|
|
|
} |
|
|
|
|
|
|
|
return result->second; |
|
|
|
} |
|
|
|
|
|
|
|
app_parameters::string_view_type app_parameters::get_env_value(string_view_type in_key, string_view_type in_default) const { |
|
|
|
auto result = m_env_values.find(WRAP_MAP_KEY(in_key)); |
|
|
|
|
|
|
|
// Safety check
|
|
|
|
if (result == m_env_values.end()) { |
|
|
|
return in_default; |
|
|
|
} |
|
|
|
|
|
|
|
return result->second; |
|
|
|
} |
|
|
|
|
|
|
|
std::u8string_view app_parameters::get_value(std::u8string_view in_key, std::u8string_view in_default) const { |
|
|
|
auto result = m_values.find(in_key); |
|
|
|
app_parameters::string_view_type app_parameters::get_value(string_view_type in_key, string_view_type in_default) const { |
|
|
|
auto result = m_values.find(WRAP_MAP_KEY(in_key)); |
|
|
|
|
|
|
|
// Safety check
|
|
|
|
if (result == m_values.end()) { |
|
|
|