Skip to content

Commit 0d7a704

Browse files
committed
bool: implicitly accept --no-bool when --bool is specified
1 parent 54244d1 commit 0d7a704

File tree

3 files changed

+96
-11
lines changed

3 files changed

+96
-11
lines changed

adopt.c

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,32 @@
3232
(x)->type == ADOPT_TYPE_VALUE)
3333

3434
INLINE(const adopt_spec *) spec_byname(
35-
adopt_parser *parser, const char *name, size_t namelen)
35+
adopt_parser *parser,
36+
const char *name,
37+
size_t namelen,
38+
int *is_converse)
3639
{
3740
const adopt_spec *spec;
3841

42+
*is_converse = 0;
43+
3944
for (spec = parser->specs; spec->type; ++spec) {
4045
if (spec->type == ADOPT_TYPE_LITERAL && namelen == 0)
4146
return spec;
4247

48+
/* Handle "no-" prefix for boolean types */
49+
if (spec->type == ADOPT_TYPE_BOOL &&
50+
strlen(spec->name) + 3 == namelen &&
51+
strncmp(name, "no-", 3) == 0 &&
52+
strncmp(name + 3, spec->name, namelen) == 0) {
53+
*is_converse = 1;
54+
return spec;
55+
}
56+
4357
if (spec_is_named_type(spec) &&
44-
spec->name &&
45-
strlen(spec->name) == namelen &&
46-
strncmp(name, spec->name, namelen) == 0)
58+
spec->name &&
59+
strlen(spec->name) == namelen &&
60+
strncmp(name, spec->name, namelen) == 0)
4761
return spec;
4862
}
4963

@@ -114,13 +128,14 @@ static adopt_status_t parse_long(adopt_opt *opt, adopt_parser *parser)
114128
{
115129
const adopt_spec *spec;
116130
char *arg = parser->args[parser->idx++], *name = arg + 2, *eql;
131+
int converse = 0;
117132
size_t namelen;
118133

119134
namelen = (eql = strrchr(arg, '=')) ? (size_t)(eql - name) : strlen(name);
120135

121136
opt->arg = arg;
122137

123-
if ((spec = spec_byname(parser, name, namelen)) == NULL) {
138+
if ((spec = spec_byname(parser, name, namelen, &converse)) == NULL) {
124139
opt->spec = NULL;
125140
opt->status = ADOPT_STATUS_UNKNOWN_OPTION;
126141
goto done;
@@ -132,14 +147,14 @@ static adopt_status_t parse_long(adopt_opt *opt, adopt_parser *parser)
132147
if (spec->type == ADOPT_TYPE_LITERAL)
133148
parser->in_literal = 1;
134149

135-
if (spec->type == ADOPT_TYPE_BOOL && spec->value)
136-
*((int *)spec->value) = 1;
150+
else if (spec->type == ADOPT_TYPE_BOOL && spec->value)
151+
*((int *)spec->value) = !converse;
137152

138-
if (spec->type == ADOPT_TYPE_SWITCH && spec->value)
153+
else if (spec->type == ADOPT_TYPE_SWITCH && spec->value)
139154
*((int *)spec->value) = spec->switch_value;
140155

141156
/* Parse values as "--foo=bar" or "--foo bar" */
142-
if (spec->type == ADOPT_TYPE_VALUE) {
157+
else if (spec->type == ADOPT_TYPE_VALUE) {
143158
if (eql && *(eql+1))
144159
opt->value = eql + 1;
145160
else if ((parser->idx + 1) <= parser->args_len)

adopt.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ typedef enum {
2020

2121
/**
2222
* An argument that, when specified, sets a given value to true.
23-
* This is useful for arguments like "--debug". The `value` pointer
24-
* in the returned option will be set to `1` when this is set.
23+
* This is useful for arguments like "--debug". A converse
24+
* argument (beginning with "no-") is implicitly specified; for
25+
* example "--no-debug". The `value` pointer in the returned
26+
* option will be set to `1` when this is specified, and set to
27+
* `0` when the converse "no-" argument is specified.
2528
*/
2629
ADOPT_TYPE_BOOL,
2730

tests/adopt.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,73 @@ void test_adopt__returns_unknown_option(void)
131131
cl_assert_equal_i(ADOPT_STATUS_UNKNOWN_OPTION, status);
132132
}
133133

134+
void test_adopt__bool(void)
135+
{
136+
int foo = 0, bar = 0;
137+
138+
adopt_spec specs[] = {
139+
{ ADOPT_TYPE_BOOL, "foo", 0, &foo, 0 },
140+
{ ADOPT_TYPE_BOOL, "bar", 0, &bar, 0 },
141+
{ 0 }
142+
};
143+
144+
char *args[] = { "--foo", "-b" };
145+
adopt_expected expected[] = {
146+
{ &specs[0], NULL },
147+
{ NULL, "-b" },
148+
};
149+
150+
/* Parse an arg list with only bare arguments */
151+
test_parse(specs, args, 2, expected, 2);
152+
cl_assert_equal_i(1, foo);
153+
cl_assert_equal_i(0, bar);
154+
}
155+
156+
void test_adopt__bool_converse(void)
157+
{
158+
int foo = 1, bar = 0;
159+
160+
adopt_spec specs[] = {
161+
{ ADOPT_TYPE_BOOL, "foo", 0, &foo, 0 },
162+
{ ADOPT_TYPE_BOOL, "bar", 0, &bar, 0 },
163+
{ 0 }
164+
};
165+
166+
char *args[] = { "--no-foo", "--bar" };
167+
adopt_expected expected[] = {
168+
{ &specs[0], NULL },
169+
{ &specs[1], NULL },
170+
};
171+
172+
/* Parse an arg list with only bare arguments */
173+
test_parse(specs, args, 2, expected, 2);
174+
cl_assert_equal_i(0, foo);
175+
cl_assert_equal_i(1, bar);
176+
}
177+
178+
void test_adopt__bool_converse_overrides(void)
179+
{
180+
int foo = 0, bar = 0;
181+
182+
adopt_spec specs[] = {
183+
{ ADOPT_TYPE_BOOL, "foo", 0, &foo, 0 },
184+
{ ADOPT_TYPE_BOOL, "bar", 0, &bar, 0 },
185+
{ 0 }
186+
};
187+
188+
char *args[] = { "--foo", "--bar", "--no-foo" };
189+
adopt_expected expected[] = {
190+
{ &specs[0], NULL },
191+
{ &specs[1], NULL },
192+
{ &specs[0], NULL },
193+
};
194+
195+
/* Parse an arg list with only bare arguments */
196+
test_parse(specs, args, 3, expected, 3);
197+
cl_assert_equal_i(0, foo);
198+
cl_assert_equal_i(1, bar);
199+
}
200+
134201
void test_adopt__long_switches1(void)
135202
{
136203
int foo = 0, bar = 0;

0 commit comments

Comments
 (0)