Skip to content

Commit 57ac333

Browse files
committed
choice: don't allow both switch choice and arg choice
When both a switch or long choice and an arg choice is specified `(-t|<foo>)`, and the switch or long choice is given, then we no longer accept the arg choice.
1 parent 85dd8cd commit 57ac333

File tree

2 files changed

+60
-6
lines changed

2 files changed

+60
-6
lines changed

adopt.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,32 @@ INLINE(const adopt_spec *) spec_nextarg(adopt_parser *parser)
8585
return NULL;
8686
}
8787

88+
INLINE(int) spec_is_choice(const adopt_spec *spec)
89+
{
90+
return ((spec + 1)->type &&
91+
((spec + 1)->usage & ADOPT_USAGE_CHOICE));
92+
}
93+
94+
/*
95+
* If we have a choice with switches and bare arguments, and we see
96+
* the switch, then we no longer expect the bare argument.
97+
*/
98+
INLINE(void) consume_choices(const adopt_spec *spec, adopt_parser *parser)
99+
{
100+
/* back up to the beginning of the choices */
101+
while (spec->type && (spec->usage & ADOPT_USAGE_CHOICE))
102+
--spec;
103+
104+
if (!spec_is_choice(spec))
105+
return;
106+
107+
do {
108+
if (spec->type == ADOPT_ARG)
109+
parser->arg_idx++;
110+
++spec;
111+
} while(spec->type && (spec->usage & ADOPT_USAGE_CHOICE));
112+
}
113+
88114
static adopt_status_t parse_long(adopt_opt *opt, adopt_parser *parser)
89115
{
90116
const adopt_spec *spec;
@@ -130,6 +156,8 @@ static adopt_status_t parse_long(adopt_opt *opt, adopt_parser *parser)
130156
else
131157
opt->status = ADOPT_STATUS_OK;
132158

159+
consume_choices(opt->spec, parser);
160+
133161
done:
134162
return opt->status;
135163
}
@@ -172,6 +200,8 @@ static adopt_status_t parse_short(adopt_opt *opt, adopt_parser *parser)
172200
else
173201
opt->status = ADOPT_STATUS_OK;
174202

203+
consume_choices(opt->spec, parser);
204+
175205
done:
176206
return opt->status;
177207
}
@@ -259,12 +289,6 @@ INLINE(int) spec_included(const adopt_spec **specs, const adopt_spec *spec)
259289
return 0;
260290
}
261291

262-
INLINE(int) spec_is_choice(const adopt_spec *spec)
263-
{
264-
return ((spec + 1)->type &&
265-
((spec + 1)->usage & ADOPT_USAGE_CHOICE));
266-
}
267-
268292
static adopt_status_t validate_required(
269293
adopt_opt *opt,
270294
const adopt_spec specs[],

tests/adopt.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,3 +825,33 @@ void test_adopt__required_choice_specified(void)
825825
cl_assert_equal_p(NULL, result.value);
826826
cl_assert_equal_i(0, result.args_len);
827827
}
828+
829+
void test_adopt__choice_switch_or_arg_advances_arg(void)
830+
{
831+
int foo = 0;
832+
char *bar = NULL, *baz = NULL, *final = NULL;
833+
adopt_opt result;
834+
835+
adopt_spec specs[] = {
836+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f', NULL, NULL },
837+
{ ADOPT_SWITCH, "fooz", 'z', &foo, 'z', NULL, NULL, ADOPT_USAGE_CHOICE },
838+
{ ADOPT_VALUE, "bar", 0, &bar, 'b', NULL, NULL, ADOPT_USAGE_CHOICE },
839+
{ ADOPT_ARG, "baz", 0, &baz, 0, NULL, NULL, ADOPT_USAGE_CHOICE },
840+
{ ADOPT_ARG, "final", 0, &final, 0, NULL, NULL, 0 },
841+
{ 0 },
842+
};
843+
844+
char *args[] = { "-z", "actually_final" };
845+
846+
cl_assert_equal_i(ADOPT_STATUS_DONE, adopt_parse(&result, specs, args, 2));
847+
848+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
849+
cl_assert_equal_p(NULL, result.arg);
850+
cl_assert_equal_p(NULL, result.value);
851+
cl_assert_equal_i(0, result.args_len);
852+
853+
cl_assert_equal_i('z', foo);
854+
cl_assert_equal_p(NULL, bar);
855+
cl_assert_equal_p(NULL, baz);
856+
cl_assert_equal_s("actually_final", final);
857+
}

0 commit comments

Comments
 (0)