Skip to content

Commit 9bbc3d3

Browse files
committed
Better support for ADOPT_ARGS in one-shot mode
`ADOPT_ARGS` were useful if you were parsing each argument individually, but were challenging when parsing in one-shot (using `adopt_parse`). Now we set the `spec->value` to the start of the arguments, and set the number of arguments remaining to `opt->args_len` when parsing is complete.
1 parent 6be7c54 commit 9bbc3d3

File tree

3 files changed

+214
-6
lines changed

3 files changed

+214
-6
lines changed

adopt.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,28 @@ static adopt_status_t parse_arg(adopt_opt *opt, adopt_parser *parser)
181181
const adopt_spec *spec = spec_nextarg(parser);
182182

183183
opt->spec = spec;
184-
opt->arg = parser->args[parser->idx++];
184+
opt->arg = parser->args[parser->idx];
185185

186-
if (spec && spec->value)
187-
*((char **)spec->value) = opt->arg;
186+
if (!spec) {
187+
parser->idx++;
188+
opt->status = ADOPT_STATUS_UNKNOWN_OPTION;
189+
} else if (spec->type == ADOPT_ARGS) {
190+
if (spec->value)
191+
*((char ***)spec->value) = &parser->args[parser->idx];
192+
193+
/* Args consume all the remaining arguments. */
194+
parser->in_args = (parser->args_len - parser->idx);
195+
parser->idx = parser->args_len;
196+
opt->args_len = parser->in_args;
197+
opt->status = ADOPT_STATUS_OK;
198+
} else {
199+
if (spec->value)
200+
*((char **)spec->value) = parser->args[parser->idx];
201+
202+
parser->idx++;
203+
opt->status = ADOPT_STATUS_OK;
204+
}
188205

189-
opt->status = spec ? ADOPT_STATUS_OK : ADOPT_STATUS_UNKNOWN_OPTION;
190206
return opt->status;
191207
}
192208

@@ -211,8 +227,10 @@ adopt_status_t adopt_parser_next(adopt_opt *opt, adopt_parser *parser)
211227

212228
memset(opt, 0x0, sizeof(adopt_opt));
213229

214-
if (parser->idx >= parser->args_len)
230+
if (parser->idx >= parser->args_len) {
231+
opt->args_len = parser->in_args;
215232
return ADOPT_STATUS_DONE;
233+
}
216234

217235
/* Handle arguments in long form, those beginning with "--" */
218236
if (strncmp(parser->args[parser->idx], "--", 2) == 0 &&

adopt.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ typedef struct adopt_spec {
8787
* If this spec is of type `ADOPT_VALUE` or `ADOPT_VALUE_OPTIONAL`,
8888
* this is a pointer to a `char *`, that will be set to the value
8989
* specified on the command line.
90+
*
91+
* If this spec is of type `ADOPT_VALUES`, this is a pointer to a
92+
* `char **` that will be set to the remaining values specified on
93+
* the command line.
9094
*/
9195
void *value;
9296

@@ -162,6 +166,13 @@ typedef struct adopt_opt {
162166
* this is the value provided to the argument.
163167
*/
164168
char *value;
169+
170+
/**
171+
* If the argument is of type `ADOPT_ARGS`, this is the number of
172+
* arguments remaining. This value is persisted even when parsing
173+
* is complete and `status` == `ADOPT_STATUS_DONE`.
174+
*/
175+
size_t args_len;
165176
} adopt_opt;
166177

167178
/* The internal parser state. Callers should not modify this structure. */
@@ -172,8 +183,9 @@ typedef struct adopt_parser {
172183

173184
size_t idx;
174185
size_t arg_idx;
186+
size_t in_args;
175187
int in_literal : 1,
176-
in_short : 1;
188+
in_short : 1;
177189
} adopt_parser;
178190

179191
/**

tests/adopt.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,3 +577,181 @@ void test_adopt__parse_oneshot_missing_value(void)
577577

578578
cl_assert_equal_i(ADOPT_STATUS_MISSING_VALUE, adopt_parse(&result, specs, args, 2));
579579
}
580+
581+
void test_adopt__parse_arg(void)
582+
{
583+
int foo = 0;
584+
char *bar = NULL, *arg1 = NULL, *arg2 = NULL;
585+
adopt_opt result;
586+
587+
adopt_spec specs[] = {
588+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f' },
589+
{ ADOPT_VALUE, "bar", 0, &bar, 'b' },
590+
{ ADOPT_ARG, "arg1", 0, &arg1, 0 },
591+
{ ADOPT_ARG, "arg2", 0, &arg2, 0 },
592+
{ 0 },
593+
};
594+
595+
char *args[] = { "-f", "bar", "baz" };
596+
597+
cl_must_pass(adopt_parse(&result, specs, args, 3));
598+
599+
cl_assert_equal_i('f', foo);
600+
cl_assert_equal_p(NULL, bar);
601+
cl_assert_equal_s("bar", arg1);
602+
cl_assert_equal_p("baz", arg2);
603+
604+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
605+
cl_assert_equal_p(NULL, result.arg);
606+
cl_assert_equal_p(NULL, result.value);
607+
}
608+
609+
void test_adopt__parse_arg_mixed_with_switches(void)
610+
{
611+
int foo = 0;
612+
char *bar = NULL, *arg1 = NULL, *arg2 = NULL;
613+
adopt_opt result;
614+
615+
adopt_spec specs[] = {
616+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f' },
617+
{ ADOPT_ARG, "arg1", 0, &arg1, 0 },
618+
{ ADOPT_SWITCH, "bar", 0, &bar, 'b' },
619+
{ ADOPT_ARG, "arg2", 0, &arg2, 0 },
620+
{ 0 },
621+
};
622+
623+
char *args[] = { "-f", "bar", "baz", "--bar" };
624+
625+
cl_must_pass(adopt_parse(&result, specs, args, 4));
626+
627+
cl_assert_equal_i('f', foo);
628+
cl_assert_equal_p('b', bar);
629+
cl_assert_equal_s("bar", arg1);
630+
cl_assert_equal_p("baz", arg2);
631+
632+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
633+
cl_assert_equal_p(NULL, result.arg);
634+
cl_assert_equal_p(NULL, result.value);
635+
}
636+
637+
void test_adopt__parse_arg_with_literal(void)
638+
{
639+
int foo = 0;
640+
char *bar = NULL, *arg1 = NULL, *arg2 = NULL;
641+
adopt_opt result;
642+
643+
adopt_spec specs[] = {
644+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f' },
645+
{ ADOPT_VALUE, "bar", 0, &bar, 'b' },
646+
{ ADOPT_LITERAL },
647+
{ ADOPT_ARG, "arg1", 0, &arg1, 0 },
648+
{ ADOPT_ARG, "arg2", 0, &arg2, 0 },
649+
{ 0 },
650+
};
651+
652+
char *args[] = { "-f", "--", "--bar" };
653+
654+
cl_must_pass(adopt_parse(&result, specs, args, 3));
655+
656+
cl_assert_equal_i('f', foo);
657+
cl_assert_equal_p(NULL, bar);
658+
cl_assert_equal_s("--bar", arg1);
659+
cl_assert_equal_p(NULL, arg2);
660+
661+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
662+
cl_assert_equal_p(NULL, result.arg);
663+
cl_assert_equal_p(NULL, result.value);
664+
}
665+
666+
void test_adopt__parse_args(void)
667+
{
668+
int foo = 0;
669+
char *bar = NULL, **argz = NULL;
670+
adopt_opt result;
671+
672+
adopt_spec specs[] = {
673+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f' },
674+
{ ADOPT_VALUE, "bar", 0, &bar, 'b' },
675+
{ ADOPT_ARGS, "argz", 0, &argz, 0 },
676+
{ 0 },
677+
};
678+
679+
char *args[] = { "-f", "--bar", "BRR", "one", "two", "three", "four" };
680+
681+
cl_must_pass(adopt_parse(&result, specs, args, 7));
682+
683+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
684+
cl_assert_equal_p(NULL, result.arg);
685+
cl_assert_equal_p(NULL, result.value);
686+
cl_assert_equal_i(4, result.args_len);
687+
688+
cl_assert_equal_i('f', foo);
689+
cl_assert_equal_s("BRR", bar);
690+
cl_assert(argz);
691+
cl_assert_equal_s("one", argz[0]);
692+
cl_assert_equal_s("two", argz[1]);
693+
cl_assert_equal_s("three", argz[2]);
694+
cl_assert_equal_s("four", argz[3]);
695+
}
696+
697+
void test_adopt__parse_args_with_literal(void)
698+
{
699+
int foo = 0;
700+
char *bar = NULL, **argz = NULL;
701+
adopt_opt result;
702+
703+
adopt_spec specs[] = {
704+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f' },
705+
{ ADOPT_VALUE, "bar", 0, &bar, 'b' },
706+
{ ADOPT_LITERAL },
707+
{ ADOPT_ARGS, "argz", 0, &argz, 0 },
708+
{ 0 },
709+
};
710+
711+
char *args[] = { "-f", "--", "--bar", "asdf", "--baz" };
712+
713+
cl_must_pass(adopt_parse(&result, specs, args, 5));
714+
715+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
716+
cl_assert_equal_p(NULL, result.arg);
717+
cl_assert_equal_p(NULL, result.value);
718+
cl_assert_equal_i(3, result.args_len);
719+
720+
cl_assert_equal_i('f', foo);
721+
cl_assert_equal_p(NULL, bar);
722+
cl_assert(argz);
723+
cl_assert_equal_s("--bar", argz[0]);
724+
cl_assert_equal_s("asdf", argz[1]);
725+
cl_assert_equal_s("--baz", argz[2]);
726+
}
727+
728+
void test_adopt__parse_args_implies_literal(void)
729+
{
730+
int foo = 0;
731+
char *bar = NULL, **argz = NULL;
732+
adopt_opt result;
733+
734+
adopt_spec specs[] = {
735+
{ ADOPT_SWITCH, "foo", 'f', &foo, 'f' },
736+
{ ADOPT_VALUE, "bar", 0, &bar, 'b' },
737+
{ ADOPT_ARGS, "argz", 0, &argz, 0 },
738+
{ 0 },
739+
};
740+
741+
char *args[] = { "-f", "foo", "bar", "--bar" };
742+
743+
cl_must_pass(adopt_parse(&result, specs, args, 4));
744+
745+
cl_assert_equal_i(ADOPT_STATUS_DONE, result.status);
746+
cl_assert_equal_p(NULL, result.arg);
747+
cl_assert_equal_p(NULL, result.value);
748+
cl_assert_equal_i(3, result.args_len);
749+
750+
cl_assert_equal_i('f', foo);
751+
cl_assert_equal_p(NULL, bar);
752+
cl_assert(argz);
753+
cl_assert_equal_s("foo", argz[0]);
754+
cl_assert_equal_s("bar", argz[1]);
755+
cl_assert_equal_s("--bar", argz[2]);
756+
}
757+

0 commit comments

Comments
 (0)