Skip to content

Commit

Permalink
Fix #48, fix #16 (mostly?...)
Browse files Browse the repository at this point in the history
  • Loading branch information
xparq committed Nov 3, 2023
1 parent b357093 commit dfbba86
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 21 deletions.
43 changes: 24 additions & 19 deletions Args.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Tiny "functional" cmdline processor (-> github.com/xparq/Args)
// v1.8
// v1.9

#include <string>
#include <vector>
Expand All @@ -12,10 +12,11 @@ class Args
{
public:
enum { Defaults,
RepeatAppends = 1, // for opts. expecting >1 params; default: override (replace) (GH #16)
//!! These two are implicitly forced by the current implementation (can't be disabled):
KeepInvalid = 2, // default: invalid opts. (i.e. those with incorrect # of params) are deleted (GH #44)
NonGreedy = 4 }; // undefined options don't try to take params; default: they do (GH #43)
RepeatAppends = 1, // for opts. expecting >1 params; default: override (replace) (GH #16)
//!! These three are implicitly enforced by the current implementation (can't be disabled):
RejectUnknown = 2, // undefined options are treated as positional args; default: accept
KeepInvalid = 4, // default: delete them (i.e. those with incorrect # of params) (GH #44)
NonGreedy = 8 }; // undefined options don't try to take params; default: they do (GH #43)
unsigned flags = Defaults;

enum Error { None, ParameterMissing, Unimplemented = -1 }
Expand All @@ -33,8 +34,8 @@ class Args
public:
Args(int argc_, char** argv_, const Rules& rules = {})
: argc(argc_), argv(argv_), param_count(rules) { proc_next("", 0); }
Args(int argc_, char** argv_, unsigned flags, const Rules& rules = {})
: argc(argc_), argv(argv_), flags(flags), param_count(rules) { proc_next("", 0); }
Args(int argc_, char** argv_, unsigned flags_, const Rules& rules = {})
: argc(argc_), argv(argv_), flags(flags_), param_count(rules) { proc_next("", 0); }
Args(const Args&) = default;
Args& operator=(const Args&) = default;

Expand Down Expand Up @@ -92,33 +93,37 @@ class Args
std::string new_opt;
if (a[1] == '-' && a.size() > 2) { // likely --long-option, or junk like --$G@%F or ---...
new_opt = a.substr(2); // OK, we don't check now... ;)
// Go ahead, extract the =value (only one for now...) now,
// because the loop/next won't!
// Extract the =value right now (up to the next space; no quoting support yet),
// because the next loop cycle can't (even see it)!
auto eqpos = new_opt.find_first_of(":=");
if (param_count[new_opt] && eqpos == std::string::npos) {
//error = ParameterMissing;
//! Not necessarily... Should `--opt val` be kept supported?
//! Simply ignoring this case would just work as before.
} else if (eqpos != std::string::npos) { //! This also allows `--unknown-opt=value` (no matter the rules)!
if (eqpos == std::string::npos) {
values_to_take = param_count[new_opt];
} else { //! This also allows `--unknown-opt=value` (no matter the rules)!
new_opt = new_opt.substr(0, eqpos); // chop off the `=...`
if (!(flags & RepeatAppends)) named_params[new_opt].clear(); // Reset in case it's not actually new?...
if (a.size() > 2 + eqpos + 1) { // value after the `=`?
//std::cerr << "val: " << a.substr(2, eqpos) << "\n";
named_params[new_opt].emplace_back(a.substr(2 + eqpos+1)); //! don't crash on `--opt=`
auto pc = param_count[new_opt];
// We have taken the offered value regardless of the rules,
// but if there's indeed a rule, we're good, 'coz if = 0, then
// the value can just be ignored, and if != 0, then we've just
// started taking params anyway, the only thing left is to
// make sure to continue that if expecting more:
auto pc = param_count[new_opt];
//std::cerr << tmp << " params expected for [" << new_opt << "]\n";
return proc_next(new_opt, pc < -1 ? pc+1 : pc-1);
return proc_next(new_opt, pc < -1 ? pc+1 : (pc ? pc-1 : 0));
}
}

//!! CHECK ERRORS!
//!! ...
//!! Should `--opt val` be kept supported?
//!! ... error = ParameterMissing;

} else if (a[1] != a[0]) { // a real short opt, or short opt. aggregate
new_opt = a.substr(1, 1);
} else { // '--' or '//...' will be considered unnamed params
goto process_unnamed_param;
} else { // '--' or '//...' are considered positional args for now
goto process_unnamed;
}

// We have a new option, process it...
Expand All @@ -133,7 +138,7 @@ class Args
return proc_next(new_opt, param_count[new_opt]);
}

process_unnamed_param:
process_unnamed:
//std::cerr << "- adding unnamed param\n";
if (values_to_take < 0) {
named_params[last_opt].emplace_back(a);
Expand Down
16 changes: 14 additions & 2 deletions test/issue 16 (override on repeat by default).case/case
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
RUN ../args-test --take-two=1 2 junk --take-two=a b
RUN ../args-test --undefined=x --undefined=y
EXPECT "-------- NAMED:
undefined = y
-------- POSITIONAL:
"

RUN ../args-test --undefined=x junk --undefined=y
EXPECT "-------- NAMED:
undefined = y
-------- POSITIONAL:
junk
"

RUN ../args-test --take-two=1 2 junk --take-two=a b
EXPECT "-------- NAMED:
take-two = a, b
-------- POSITIONAL:
junk
"

#--------------------------------------------------
# And try appending (Args::RepeatAppends), too...
# Also try appending (Args::RepeatAppends)...
#--------------------------------------------------
alt="$CASE"
RUN "$alt" --take-two=1 2 junk --take-two=a b
Expand Down
15 changes: 15 additions & 0 deletions test/issue 48 (too greedy).case
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
RUN args-test --arg=1 not-a-param!
EXPECT "\
-------- NAMED:
arg = 1
-------- POSITIONAL:
not-a-param!
"

RUN args-test --arg 1 not-a-param!
EXPECT "\
-------- NAMED:
arg
-------- POSITIONAL:
1, not-a-param!
"

0 comments on commit dfbba86

Please sign in to comment.