mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-27 11:43:37 +08:00
Clean up some memory usage in argparse
Clarify some ownership models
This commit is contained in:
parent
a374b8ede7
commit
3c4c47f516
|
@ -33,28 +33,20 @@ const wcstring var_name_prefix = L"_flag_";
|
|||
|
||||
#define BUILTIN_ERR_INVALID_OPT_SPEC _(L"%ls: Invalid option spec '%ls' at char '%lc'\n")
|
||||
|
||||
class option_spec_t {
|
||||
public:
|
||||
wchar_t short_flag;
|
||||
struct option_spec_t {
|
||||
const wchar_t short_flag;
|
||||
wcstring long_flag;
|
||||
wcstring validation_command;
|
||||
wcstring_list_t vals;
|
||||
bool short_flag_valid;
|
||||
int num_allowed;
|
||||
int num_seen;
|
||||
bool short_flag_valid{true};
|
||||
int num_allowed{0};
|
||||
int num_seen{0};
|
||||
|
||||
option_spec_t(wchar_t s)
|
||||
: short_flag(s),
|
||||
long_flag(),
|
||||
validation_command(),
|
||||
vals(),
|
||||
short_flag_valid(true),
|
||||
num_allowed(0),
|
||||
num_seen(0) {}
|
||||
option_spec_t(wchar_t s) : short_flag(s) {}
|
||||
};
|
||||
using option_spec_ref_t = std::unique_ptr<option_spec_t>;
|
||||
|
||||
class argparse_cmd_opts_t {
|
||||
public:
|
||||
struct argparse_cmd_opts_t {
|
||||
bool print_help = false;
|
||||
bool stop_nonopt = false;
|
||||
size_t min_args = 0;
|
||||
|
@ -63,15 +55,9 @@ class argparse_cmd_opts_t {
|
|||
wcstring name = L"argparse";
|
||||
wcstring_list_t raw_exclusive_flags;
|
||||
wcstring_list_t argv;
|
||||
std::unordered_map<wchar_t, option_spec_t *> options;
|
||||
std::unordered_map<wchar_t, option_spec_ref_t> options;
|
||||
std::unordered_map<wcstring, wchar_t> long_to_short_flag;
|
||||
std::vector<std::vector<wchar_t>> exclusive_flag_sets;
|
||||
|
||||
~argparse_cmd_opts_t() {
|
||||
for (auto it : options) {
|
||||
delete it.second;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const wchar_t *short_options = L"+:hn:sx:N:X:";
|
||||
|
@ -86,8 +72,8 @@ static const struct woption long_options[] = {{L"stop-nonopt", no_argument, NULL
|
|||
// Check if any pair of mutually exclusive options was seen. Note that since every option must have
|
||||
// a short name we only need to check those.
|
||||
static int check_for_mutually_exclusive_flags(argparse_cmd_opts_t &opts, io_streams_t &streams) {
|
||||
for (auto it : opts.options) {
|
||||
auto opt_spec = it.second;
|
||||
for (const auto &kv : opts.options) {
|
||||
const auto &opt_spec = kv.second;
|
||||
if (opt_spec->num_seen == 0) continue;
|
||||
|
||||
// We saw this option at least once. Check all the sets of mutually exclusive options to see
|
||||
|
@ -98,7 +84,10 @@ static int check_for_mutually_exclusive_flags(argparse_cmd_opts_t &opts, io_stre
|
|||
// Okay, this option is in a mutually exclusive set of options. Check if any of the
|
||||
// other mutually exclusive options have been seen.
|
||||
for (auto xflag : xarg_set) {
|
||||
auto xopt_spec = opts.options[xflag];
|
||||
auto xopt_spec_iter = opts.options.find(xflag);
|
||||
if (xopt_spec_iter == opts.options.end()) continue;
|
||||
|
||||
const auto &xopt_spec = xopt_spec_iter->second;
|
||||
// Ignore this flag in the list of mutually exclusive flags.
|
||||
if (xopt_spec->short_flag == opt_spec->short_flag) continue;
|
||||
|
||||
|
@ -171,7 +160,7 @@ static int parse_exclusive_args(argparse_cmd_opts_t &opts, io_streams_t &streams
|
|||
return STATUS_CMD_OK;
|
||||
}
|
||||
|
||||
static bool parse_flag_modifiers(argparse_cmd_opts_t &opts, option_spec_t *opt_spec,
|
||||
static bool parse_flag_modifiers(const argparse_cmd_opts_t &opts, const option_spec_ref_t &opt_spec,
|
||||
const wcstring &option_spec, const wchar_t **opt_spec_str,
|
||||
io_streams_t &streams) {
|
||||
const wchar_t *s = *opt_spec_str;
|
||||
|
@ -215,14 +204,13 @@ static bool parse_flag_modifiers(argparse_cmd_opts_t &opts, option_spec_t *opt_s
|
|||
return false;
|
||||
}
|
||||
|
||||
opts.options.emplace(opt_spec->short_flag, opt_spec);
|
||||
*opt_spec_str = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Parse the text following the short flag letter.
|
||||
static bool parse_option_spec_sep(argparse_cmd_opts_t &opts, option_spec_t *opt_spec,
|
||||
wcstring &option_spec, const wchar_t **opt_spec_str,
|
||||
static bool parse_option_spec_sep(argparse_cmd_opts_t &opts, const option_spec_ref_t &opt_spec,
|
||||
const wcstring &option_spec, const wchar_t **opt_spec_str,
|
||||
io_streams_t &streams) {
|
||||
const wchar_t *s = *opt_spec_str;
|
||||
if (*(s - 1) == L'#') {
|
||||
|
@ -264,7 +252,6 @@ static bool parse_option_spec_sep(argparse_cmd_opts_t &opts, option_spec_t *opt_
|
|||
opts.implicit_int_flag = opt_spec->short_flag;
|
||||
opt_spec->num_allowed = 1; // mandatory arg and can appear only once
|
||||
s++; // the struct is initialized assuming short_flag_valid should be true
|
||||
if (!*s) opts.options.emplace(opt_spec->short_flag, opt_spec);
|
||||
} else {
|
||||
// Long flag name not allowed if second char isn't '/', '-' or '#' so just check for
|
||||
// behavior modifier chars.
|
||||
|
@ -277,7 +264,7 @@ static bool parse_option_spec_sep(argparse_cmd_opts_t &opts, option_spec_t *opt_
|
|||
|
||||
/// This parses an option spec string into a struct option_spec.
|
||||
static bool parse_option_spec(argparse_cmd_opts_t &opts, //!OCLINT(high npath complexity)
|
||||
wcstring option_spec, io_streams_t &streams) {
|
||||
const wcstring &option_spec, io_streams_t &streams) {
|
||||
if (option_spec.empty()) {
|
||||
streams.err.append_format(_(L"%ls: An option spec must have a short flag letter\n"),
|
||||
opts.name.c_str());
|
||||
|
@ -291,29 +278,40 @@ static bool parse_option_spec(argparse_cmd_opts_t &opts, //!OCLINT(high npath c
|
|||
return false;
|
||||
}
|
||||
|
||||
option_spec_t *opt_spec = new option_spec_t(*s++);
|
||||
if (!*s) {
|
||||
// Bool short flag only.
|
||||
opts.options.emplace(opt_spec->short_flag, opt_spec);
|
||||
return true;
|
||||
}
|
||||
if (!parse_option_spec_sep(opts, opt_spec, option_spec, &s, streams)) return false;
|
||||
if (!*s) return true; // parsed the entire string so the option spec doesn't have a long flag
|
||||
std::unique_ptr<option_spec_t> opt_spec(new option_spec_t{*s++});
|
||||
|
||||
// Collect the long flag name.
|
||||
const wchar_t *e = s;
|
||||
while (*e && (*e == L'-' || *e == L'_' || iswalnum(*e))) e++;
|
||||
if (e != s) {
|
||||
opt_spec->long_flag = wcstring(s, e - s);
|
||||
if (opts.long_to_short_flag.find(opt_spec->long_flag) != opts.long_to_short_flag.end()) {
|
||||
streams.err.append_format(L"%ls: Long flag '%ls' already defined\n", opts.name.c_str(),
|
||||
opt_spec->long_flag.c_str());
|
||||
// Try parsing stuff after the short flag.
|
||||
if (*s && !parse_option_spec_sep(opts, opt_spec, option_spec, &s, streams)) {
|
||||
return false;
|
||||
}
|
||||
opts.long_to_short_flag.emplace(opt_spec->long_flag, opt_spec->short_flag);
|
||||
|
||||
// Collect any long flag name.
|
||||
if (*s) {
|
||||
const wchar_t *const long_flag_start = s;
|
||||
while (*s && (*s == L'-' || *s == L'_' || iswalnum(*s))) s++;
|
||||
if (s != long_flag_start) {
|
||||
opt_spec->long_flag = wcstring(long_flag_start, s);
|
||||
if (opts.long_to_short_flag.count(opt_spec->long_flag) > 0) {
|
||||
streams.err.append_format(L"%ls: Long flag '%ls' already defined\n",
|
||||
opts.name.c_str(), opt_spec->long_flag.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!parse_flag_modifiers(opts, opt_spec, option_spec, &s, streams)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parse_flag_modifiers(opts, opt_spec, option_spec, &e, streams);
|
||||
// Record our long flag if we have one.
|
||||
if (!opt_spec->long_flag.empty()) {
|
||||
auto ins = opts.long_to_short_flag.emplace(opt_spec->long_flag, opt_spec->short_flag);
|
||||
assert(ins.second && "Should have inserted long flag");
|
||||
(void)ins;
|
||||
}
|
||||
|
||||
// Record our option under its short flag.
|
||||
opts.options[opt_spec->short_flag] = std::move(opt_spec);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int collect_option_specs(argparse_cmd_opts_t &opts, int *optind, int argc, wchar_t **argv,
|
||||
|
@ -420,8 +418,8 @@ static void populate_option_strings(
|
|||
argparse_cmd_opts_t &opts, wcstring &short_options,
|
||||
std::unique_ptr<woption, std::function<void(woption *)>> &long_options) {
|
||||
int i = 0;
|
||||
for (auto it : opts.options) {
|
||||
option_spec_t *opt_spec = it.second;
|
||||
for (const auto &kv : opts.options) {
|
||||
const auto &opt_spec = kv.second;
|
||||
if (opt_spec->short_flag_valid) short_options.push_back(opt_spec->short_flag);
|
||||
|
||||
int arg_type = no_argument;
|
||||
|
@ -492,8 +490,8 @@ static int check_for_implicit_int(argparse_cmd_opts_t &opts, const wchar_t *val,
|
|||
// Okay, it looks like an integer. See if it passes the validation checks.
|
||||
auto found = opts.options.find(opts.implicit_int_flag);
|
||||
assert(found != opts.options.end());
|
||||
auto opt_spec = found->second;
|
||||
int retval = validate_arg(opts, opt_spec, long_idx != -1, val, streams);
|
||||
const auto &opt_spec = found->second;
|
||||
int retval = validate_arg(opts, opt_spec.get(), long_idx != -1, val, streams);
|
||||
if (retval != STATUS_CMD_OK) return retval;
|
||||
|
||||
// It's a valid integer so store it and return success.
|
||||
|
@ -566,8 +564,7 @@ static int argparse_parse_flags(argparse_cmd_opts_t &opts, const wchar_t *short_
|
|||
auto found = opts.options.find(opt);
|
||||
assert(found != opts.options.end());
|
||||
|
||||
option_spec_t *opt_spec = found->second;
|
||||
int retval = handle_flag(opts, opt_spec, long_idx, w.woptarg, streams);
|
||||
int retval = handle_flag(opts, found->second.get(), long_idx, w.woptarg, streams);
|
||||
if (retval != STATUS_CMD_OK) return retval;
|
||||
long_idx = -1;
|
||||
}
|
||||
|
@ -631,8 +628,8 @@ static int check_min_max_args_constraints(argparse_cmd_opts_t &opts, parser_t &p
|
|||
|
||||
/// Put the result of parsing the supplied args into the caller environment as local vars.
|
||||
static void set_argparse_result_vars(argparse_cmd_opts_t &opts) {
|
||||
for (auto it : opts.options) {
|
||||
option_spec_t *opt_spec = it.second;
|
||||
for (const auto &kv : opts.options) {
|
||||
const auto &opt_spec = kv.second;
|
||||
if (!opt_spec->num_seen) continue;
|
||||
|
||||
if (opt_spec->short_flag_valid) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user