Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optparse: test and allow adjustment of posixly-correct behavior #1049

Merged
merged 7 commits into from Apr 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 60 additions & 6 deletions src/common/liboptparse/optparse.c
Expand Up @@ -68,6 +68,7 @@ struct opt_parser {
unsigned int skip_subcmds:1; /* Do not Print subcommands in --help */
unsigned int no_options:1; /* Skip option processing for subcmd */
unsigned int hidden:1; /* If subcmd, skip in --help output */
unsigned int posixly_correct:1; /* Value of GNU getopt posixly correct*/

zhash_t * dhash; /* Hash of ancillary data */

Expand Down Expand Up @@ -698,6 +699,7 @@ optparse_t *optparse_create (const char *prog)
p->left_margin = 2;
p->option_width = 25;
p->option_index = -1;
p->posixly_correct = 1;

/*
* Register -h, --help
Expand Down Expand Up @@ -1049,6 +1051,10 @@ optparse_err_t optparse_set (optparse_t *p, optparse_item_t item, ...)
n = va_arg (vargs, int);
p->hidden = n;
break;
case OPTPARSE_POSIXLY_CORRECT:
n = va_arg (vargs, int);
p->posixly_correct = n;
break;
default:
e = OPTPARSE_BAD_ARG;
}
Expand Down Expand Up @@ -1110,11 +1116,10 @@ void *optparse_get_data (optparse_t *p, const char *s)

static char * optstring_create ()
{
char *optstring = malloc (2);
char *optstring = malloc (1);
if (optstring == NULL)
return (NULL);
optstring[0] = '+';
optstring[1] = '\0';
optstring[0] = '\0';
return (optstring);
}

Expand Down Expand Up @@ -1248,10 +1253,10 @@ static void opt_append_optarg (optparse_t *p, struct option_info *opt, const cha
*/
static int getopt_long_r (int argc, char *const *argv, const char *options,
const struct option *long_options, int *opt_index,
struct _getopt_data *d)
struct _getopt_data *d, int posixly_correct)
{
return _getopt_internal_r (argc, argv, options, long_options, opt_index,
0, d, 0);
0, d, posixly_correct);
}

int optparse_parse_args (optparse_t *p, int argc, char *argv[])
Expand All @@ -1269,7 +1274,8 @@ int optparse_parse_args (optparse_t *p, int argc, char *argv[])
*/
memset (&d, 0, sizeof (d));

while ((c = getopt_long_r (argc, argv, optstring, optz, &li, &d)) >= 0) {
while ((c = getopt_long_r (argc, argv, optstring, optz,
&li, &d, p->posixly_correct)) >= 0) {
struct option_info *opt;
struct optparse_option *o;
if (c == '?') {
Expand Down Expand Up @@ -1371,6 +1377,54 @@ int optparse_option_index (optparse_t *p)
return (p->option_index);
}

/*
* Reset one optparse_t object -- set option_index to zero and
* reset and free option arguments, etc.
*/
static void optparse_reset_one (optparse_t *p)
{
struct option_info *o;
ListIterator i;

if (!p)
return;

p->option_index = -1;

if (!p->option_list || !list_count (p->option_list))
return;

i = list_iterator_create (p->option_list);
while ((o = list_next (i))) {
if (o->isdoc)
continue;
o->found = 0;
if (o->optargs) {
list_destroy (o->optargs);
o->optargs = NULL;
}
o->optarg = NULL;
}
list_iterator_destroy (i);
return;
}

void optparse_reset (optparse_t *p)
{
zlist_t *cmds = subcmd_list_sorted (p);

if ((cmds = subcmd_list_sorted (p))) {
const char *cmd = zlist_first (cmds);
while (cmd) {
optparse_t *o = zhash_lookup (p->subcommands, cmd);
optparse_reset_one (o);
cmd = zlist_next (cmds);
}
zlist_destroy (&cmds);
}
optparse_reset_one (p);
}

/*
* vi: ts=4 sw=4 expandtab
*/
8 changes: 8 additions & 0 deletions src/common/liboptparse/optparse.h
Expand Up @@ -56,6 +56,7 @@ typedef enum {
OPTPARSE_PRINT_SUBCMDS,/* Print all subcommands in --help (default = T */
OPTPARSE_SUBCMD_NOOPTS,/* Don't parse options for this subcommand */
OPTPARSE_SUBCMD_HIDE, /* Don't output this subcmd in --help output */
OPTPARSE_POSIXLY_CORRECT, /* Set POSIXLY_CORRECT value */
} optparse_item_t;

/*
Expand Down Expand Up @@ -197,6 +198,13 @@ optparse_err_t optparse_reg_subcommands (optparse_t *p,
*/
void optparse_destroy (optparse_t *p);

/*
* Reset option processing for optparse object [p]. Forget all previous
* options processed and their arguments. Useful to restart option
* processing or parse a new argument vector.
*/
void optparse_reset (optparse_t *p);

/*
* Register the option [o] with the program options object [p].
*
Expand Down
151 changes: 150 additions & 1 deletion src/common/liboptparse/test/optparse.c
Expand Up @@ -926,10 +926,157 @@ void test_corner_case (void)
optparse_destroy (p);
}

void test_reset (void)
{
optparse_err_t e;
int called = 0;
int n;
optparse_t *p, *q;

p = optparse_create ("test");

ok (p != NULL, "optparse_create");
q = optparse_add_subcommand (p, "one", subcmd_one);
ok (q != NULL, "optparse_add_subcommand (subcmd_one)");
optparse_set_data (q, "called", &called);
ok (optparse_get_data (q, "called") == &called, "optparse_set_data ()");

// Add option to command
e = optparse_add_option (p, &(struct optparse_option) {
.name = "test", .key = 't', .has_arg = 1,
.arginfo = "N",
.usage = "Test option with numeric argument N",
});
ok (e == OPTPARSE_SUCCESS, "optparse_add_option to command");

// Add option to subcommand
e = optparse_add_option (q, &(struct optparse_option) {
.name = "test-opt", .key = 't', .has_arg = 1,
.arginfo = "N",
.usage = "Test option with numeric argument N",
});
ok (e == OPTPARSE_SUCCESS, "optparse_add_option to subcommand");

ok (optparse_option_index (p) == -1, "option index is -1");
ok (optparse_option_index (q) == -1, "subcmd: option index is -1");

char *av[] = { "test", "-t", "2", "one", "--test-opt=5", NULL };
int ac = sizeof (av) / sizeof (av[0]) - 1;

n = optparse_parse_args (p, ac, av);
ok (n == 3, "optparse_parse_args() expected 3 got %d", n);
n = optparse_run_subcommand (p, ac, av);
ok (n >= 0, "optparse_run_subcommand() got %d", n);
ok (called == 1, "optparse_run_subcommand: called subcmd_one()");

n = optparse_option_index (p);
ok (n == 3, "option index for p: expected 3 got %d", n);

// option index for subcommand is relative to subcommand as argv0:
n = optparse_option_index (q);
ok (n == 2, "option index for q: expected 2 got %d", n);

ok (optparse_getopt (p, "test", NULL) == 1, "got --test option");
ok (optparse_getopt (q, "test-opt", NULL) == 1, "got --test-opt in subcmd");

optparse_reset (p);

n = optparse_option_index (p);
ok (n == -1, "after reset: option index for p: expected -1 got %d", n);
n = optparse_option_index (q);
ok (n == -1, "after reset: option index for q: expected -1 got %d", n);

ok (optparse_getopt (p, "test", NULL) == 0,
"after reset: optparse_getopt returns 0");
ok (optparse_getopt (q, "test-opt", NULL) == 0,
"after reset: optparse_getopt returns 0 for subcmd");

optparse_destroy (p);
}

/*
* Test for posixly-correct behavior of stopping at first non-option argument.
*/
void test_non_option_arguments (void)
{
optparse_err_t e;
struct optparse_option opts [] = {
{ .name = "test",
.key = 't',
.has_arg = 1,
.arginfo = "S",
.usage = "test option"
},
OPTPARSE_TABLE_END,
};
optparse_t *p = optparse_create ("non-option-arg");
char *av[] = { "non-option-arg", "--test=foo", "--", "baz", NULL };
int ac = sizeof (av) / sizeof (*av) - 1;
int optindex;

ok (p != NULL, "optparse_create");

e = optparse_add_option_table (p, opts);
ok (e == OPTPARSE_SUCCESS, "register options");

ok (optparse_parse_args (p, ac, av) != -1, "optparse_parse_args");
optindex = optparse_option_index (p);
ok (optindex == 3, "post parse optindex points after '--'");

optparse_reset (p);
char *av2[] = { "non-option-arg", "foo", "bar", NULL };
ac = sizeof (av2) / sizeof (*av2) - 1;
ok (optparse_parse_args (p, ac, av2) != -1, "optparse_parse_args");
optindex = optparse_option_index (p);
ok (optindex == 1, "argv with no options, optindex is 1");

#if 0
// XXX: Can't stop processing at arguments that *look* like an option,
// as this is detected as "unknown option" error. Possibly a flag could
// be added to optparse object to better handle this case? As of now
// there's no need however.
//
optparse_reset (p);
char *av3[] = { "non-option-arg", "-1234", NULL };
ac = sizeof (av3) / sizeof (*av3) - 1;
ok (optparse_parse_args (p, ac, av3) != -1, "optparse_parse_args");
optindex = optparse_option_index (p);
ok (optindex == 1,
"argv with non-option arg starting with '-', optindex should be 1");
#endif

optparse_reset (p);
char *av4[] = { "non-option-arg", "1234", "--test=foo", NULL };
ac = sizeof (av4) / sizeof (*av4) - 1;
ok (optparse_parse_args (p, ac, av4) != -1, "optparse_parse_args");
optindex = optparse_option_index (p);
ok (optindex == 1,
"argv stops processing at non-option even with real options follow");
ok (optparse_getopt (p, "test", NULL) == 0,
"didn't process --test=foo (expected 0 got %d)",
optparse_getopt (p, "test", NULL));

/* Test OPTPARSE_POSIXLY_CORRECT */
optparse_reset (p);
e = optparse_set (p, OPTPARSE_POSIXLY_CORRECT, 0);
ok (optparse_parse_args (p, ac, av4) != -1,
"!posixly_correct: optparse_parse_args");
optindex = optparse_option_index (p);
ok (optindex == 2,
"!posixly_correct: argv elements are permuted");
is (av4[1], "--test=foo",
"!posixly_correct: argv[1] is now --test=foo");
is (av4[2], "1234",
"!posixly_correct: argv[2] is now non-option arg (1234)");

optparse_destroy (p);
}


int main (int argc, char *argv[])
{

plan (212);
plan (245);

test_convenience_accessors (); /* 35 tests */
test_usage_output (); /* 42 tests */
Expand All @@ -941,6 +1088,8 @@ int main (int argc, char *argv[])
test_long_only (); /* 13 tests */
test_optional_argument (); /* 9 tests */
test_corner_case (); /* 3 tests */
test_reset (); /* 9 tests */
test_non_option_arguments (); /* 13 tests */

done_testing ();
return (0);
Expand Down
2 changes: 1 addition & 1 deletion src/test/travis-dep-builder.sh
Expand Up @@ -51,7 +51,7 @@ declare -A extra_cmake_opts=(\
pips="\
cffi==1.1 \
coverage \
pylint
pylint==1.5.6
"

#
Expand Down