Commit 9056c31b authored by Rusty Russell's avatar Rusty Russell

opt: wean off getopt_long, beef up tests.

Doing our own parsing lost a few lines of code, too.

Our coverage is over 99% now.
parent 9071df66
......@@ -91,10 +91,8 @@ char *opt_set_longval(const char *arg, long *l)
*l = strtol(arg, &endp, 0);
if (*endp || !arg[0])
return arg_bad("'%s' is not a number", arg);
if (errno == ERANGE)
return arg_bad("'%s' is out of range", arg);
if (errno)
return opt_invalid_argument(arg);
return arg_bad("'%s' is out of range", arg);
return NULL;
}
......
#include <ccan/opt/opt.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
......@@ -53,7 +52,7 @@ static const char *next_opt(const char *p, unsigned *i, unsigned *len)
return NULL;
}
static const char *first_lopt(unsigned *i, unsigned *len)
const char *first_lopt(unsigned *i, unsigned *len)
{
const char *p;
for (p = first_opt(i, len); p; p = next_opt(p, i, len)) {
......@@ -67,7 +66,7 @@ static const char *first_lopt(unsigned *i, unsigned *len)
return p;
}
static const char *next_lopt(const char *p, unsigned *i, unsigned *len)
const char *next_lopt(const char *p, unsigned *i, unsigned *len)
{
for (p = next_opt(p, i, len); p; p = next_opt(p, i, len)) {
if (p[0] == '-') {
......@@ -128,22 +127,15 @@ static void check_opt(const struct opt_table *entry)
if (len != 1)
errx(1, "Option %s: invalid short option"
" '%.*s'", entry->names, len+1, p-1);
if (*p == ':')
errx(1, "Option %s: invalid short option '-:'",
entry->names);
opt_num_short++;
if (entry->type == OPT_HASARG) {
if (entry->type == OPT_HASARG)
opt_num_short_arg++;
if (*p == '?')
errx(1, "Option %s: '-?' cannot take"
" an argument", entry->names);
}
}
/* Don't document args unless there are some. */
if (entry->type == OPT_NOARG) {
if (p[len] == ' ' || p[len] == '=')
errx(1, "Option %s: does not take arguments"
"'%s'", entry->names, p+len+1);
" '%s'", entry->names, p+len+1);
}
}
}
......@@ -194,151 +186,19 @@ void opt_register_table(const struct opt_table entry[], const char *desc)
opt_table[start].arg = (void *)(intptr_t)(opt_count - start);
}
static char *make_optstring(void)
{
char *str = malloc(1 + opt_num_short + opt_num_short_arg + 1);
const char *p;
unsigned int i, num = 0;
/* This tells getopt_long we want a ':' returned for missing arg. */
str[num++] = ':';
for (p = first_sopt(&i); p; p = next_sopt(p, &i)) {
str[num++] = *p;
if (opt_table[i].type == OPT_HASARG)
str[num++] = ':';
}
str[num++] = '\0';
assert(num == 1 + opt_num_short + opt_num_short_arg + 1);
return str;
}
static struct option *make_options(void)
{
struct option *options = malloc(sizeof(*options) * (opt_num_long + 1));
unsigned int i, num = 0, len = 0 /* GCC bogus warning */;
const char *p;
for (p = first_lopt(&i, &len); p; p = next_lopt(p, &i, &len)) {
char *buf = malloc(len + 1);
memcpy(buf, p, len);
buf[len] = 0;
options[num].name = buf;
options[num].has_arg = (opt_table[i].type == OPT_HASARG);
options[num].flag = NULL;
options[num].val = 0;
num++;
}
memset(&options[num], 0, sizeof(options[num]));
assert(num == opt_num_long);
return options;
}
static struct opt_table *find_short(char shortopt)
{
unsigned int i;
const char *p;
for (p = first_sopt(&i); p; p = next_sopt(p, &i)) {
if (*p == shortopt)
return &opt_table[i];
}
abort();
}
/* We want the index'th long entry. */
static struct opt_table *find_long(int index, const char **name)
{
unsigned int i, len;
const char *p;
for (p = first_lopt(&i, &len); p; p = next_lopt(p, &i, &len)) {
if (index == 0) {
*name = p;
return &opt_table[i];
}
index--;
}
abort();
}
/* glibc does this as:
/tmp/opt-example: invalid option -- 'x'
/tmp/opt-example: unrecognized option '--long'
/tmp/opt-example: option '--someflag' doesn't allow an argument
/tmp/opt-example: option '--s' is ambiguous
/tmp/opt-example: option requires an argument -- 's'
*/
static void parse_fail(void (*errlog)(const char *fmt, ...),
char shortopt, const char *longopt, const char *problem)
{
if (shortopt)
errlog("%s: -%c: %s", opt_argv0, shortopt, problem);
else
errlog("%s: --%.*s: %s", opt_argv0,
strcspn(longopt, "|"), longopt, problem);
}
/* Parse your arguments. */
bool opt_parse(int *argc, char *argv[], void (*errlog)(const char *fmt, ...))
{
char *optstring = make_optstring();
struct option *options = make_options();
int ret, longidx = 0;
struct opt_table *e;
int ret;
unsigned offset = 0;
/* We will do our own error reporting. */
opterr = 0;
/* This helps opt_usage. */
opt_argv0 = argv[0];
/* Reset in case we're called more than once. */
optopt = 0;
optind = 0;
while ((ret = getopt_long(*argc, argv, optstring, options, &longidx))
!= -1) {
char *problem;
const char *name = NULL; /* GCC bogus warning */
/* optopt is 0 if it's an unknown long option, *or* if
* -? is a valid short option. */
if (ret == '?') {
if (optopt || strncmp(argv[optind-1], "--", 2) == 0) {
parse_fail(errlog, optopt, argv[optind-1]+2,
"unrecognized option");
break;
}
} else if (ret == ':') {
/* Missing argument: longidx not updated :( */
parse_fail(errlog, optopt, argv[optind-1]+2,
"option requires an argument");
break;
}
if (ret != 0)
e = find_short(ret);
else
e = find_long(longidx, &name);
if (e->type == OPT_HASARG)
problem = e->cb_arg(optarg, e->arg);
else
problem = e->cb(e->arg);
if (problem) {
parse_fail(errlog, ret, name, problem);
free(problem);
break;
}
}
free(optstring);
free(options);
if (ret != -1)
return false;
/* We hide everything but remaining arguments. */
memmove(&argv[1], &argv[optind], sizeof(argv[1]) * (*argc-optind+1));
*argc -= optind - 1;
while ((ret = parse_one(argc, argv, &offset, errlog)) == 1);
return ret == -1 ? true : false;
/* parse_one returns 0 on finish, -1 on error */
return (ret == 0);
}
void opt_log_stderr(const char *fmt, ...)
......
/* Actual code to parse commandline. */
#include <ccan/opt/opt.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "private.h"
/* glibc does this as:
/tmp/opt-example: invalid option -- 'x'
/tmp/opt-example: unrecognized option '--long'
/tmp/opt-example: option '--someflag' doesn't allow an argument
/tmp/opt-example: option '--s' is ambiguous
/tmp/opt-example: option requires an argument -- 's'
*/
static int parse_err(void (*errlog)(const char *fmt, ...),
const char *argv0, const char *arg, unsigned len,
const char *problem)
{
errlog("%s: %.*s: %s", argv0, len, arg, problem);
return -1;
}
static void consume_option(int *argc, char *argv[], unsigned optnum)
{
memmove(&argv[optnum], &argv[optnum+1],
sizeof(argv[optnum]) * (*argc-optnum));
(*argc)--;
}
/* Returns 1 if argument consumed, 0 if all done, -1 on error. */
int parse_one(int *argc, char *argv[], unsigned *offset,
void (*errlog)(const char *fmt, ...))
{
unsigned i, arg, len;
const char *o, *optarg = NULL;
char *problem;
if (getenv("POSIXLY_CORRECT")) {
/* Don't find options after non-options. */
arg = 1;
} else {
for (arg = 1; argv[arg]; arg++) {
if (argv[arg][0] == '-')
break;
}
}
if (!argv[arg] || argv[arg][0] != '-')
return 0;
/* Special arg terminator option. */
if (strcmp(argv[arg], "--") == 0) {
consume_option(argc, argv, arg);
return 0;
}
/* Long options start with -- */
if (argv[arg][1] == '-') {
assert(*offset == 0);
for (o = first_lopt(&i, &len); o; o = next_lopt(o, &i, &len)) {
if (strncmp(argv[arg] + 2, o, len) != 0)
continue;
if (argv[arg][2 + len] == '=')
optarg = argv[arg] + 2 + len + 1;
else if (argv[arg][2 + len] != '\0')
continue;
break;
}
if (!o)
return parse_err(errlog, argv[0],
argv[arg], strlen(argv[arg]),
"unrecognized option");
/* For error messages, we include the leading '--' */
o -= 2;
len += 2;
} else {
/* offset allows us to handle -abc */
for (o = first_sopt(&i); o; o = next_sopt(o, &i)) {
if (argv[arg][*offset + 1] != *o)
continue;
(*offset)++;
break;
}
if (!o)
return parse_err(errlog, argv[0],
argv[arg], strlen(argv[arg]),
"unrecognized option");
/* For error messages, we include the leading '-' */
o--;
len = 2;
}
if (opt_table[i].type == OPT_NOARG) {
if (optarg)
return parse_err(errlog, argv[0], o, len,
"doesn't allow an argument");
problem = opt_table[i].cb(opt_table[i].arg);
} else {
if (!optarg) {
/* Swallow any short options as optarg, eg -afile */
if (*offset && argv[arg][*offset + 1]) {
optarg = argv[arg] + *offset + 1;
*offset = 0;
} else
optarg = argv[arg+1];
}
if (!optarg)
return parse_err(errlog, argv[0], o, len,
"requires an argument");
problem = opt_table[i].cb_arg(optarg, opt_table[i].arg);
}
if (problem) {
parse_err(errlog, argv[0], o, len, problem);
free(problem);
return -1;
}
/* If no more letters in that short opt, reset offset. */
if (*offset && !argv[arg][*offset + 1])
*offset = 0;
/* All finished with that option? */
if (*offset == 0) {
consume_option(argc, argv, arg);
if (optarg && optarg == argv[arg])
consume_option(argc, argv, arg);
}
return 1;
}
......@@ -10,5 +10,10 @@ extern const char *opt_argv0;
const char *first_sopt(unsigned *i);
const char *next_sopt(const char *names, unsigned *i);
const char *first_lopt(unsigned *i, unsigned *len);
const char *next_lopt(const char *p, unsigned *i, unsigned *len);
int parse_one(int *argc, char *argv[], unsigned *offset,
void (*errlog)(const char *fmt, ...));
#endif /* CCAN_OPT_PRIVATE_H */
#define _GNU_SOURCE
#include <stdio.h>
#include <ccan/tap/tap.h>
#include <setjmp.h>
#include <stdlib.h>
#include <limits.h>
#include <err.h>
#include "utils.h"
/* We don't actually want it to exit... */
static jmp_buf exited;
#define errx save_and_jump
static void save_and_jump(int ecode, const char *fmt, ...);
#include <ccan/opt/helpers.c>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/parse.c>
static char *output = NULL;
static int saved_vprintf(const char *fmt, va_list ap)
{
char *p;
int ret = vasprintf(&p, fmt, ap);
if (output) {
output = realloc(output, strlen(output) + strlen(p) + 1);
strcat(output, p);
free(p);
} else
output = p;
return ret;
}
static void save_and_jump(int ecode, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
saved_vprintf(fmt, ap);
va_end(ap);
longjmp(exited, ecode + 1);
}
static void reset(void)
{
free(output);
output = NULL;
free(opt_table);
opt_table = NULL;
opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
}
int main(int argc, char *argv[])
{
int exitval;
plan_tests(14);
exitval = setjmp(exited);
if (exitval == 0) {
/* Bad type. */
_opt_register("-a", OPT_SUBTABLE, (void *)opt_version_and_exit,
NULL, NULL, "1.2.3", "");
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output, "Option -a: unknown entry type"));
}
reset();
exitval = setjmp(exited);
if (exitval == 0) {
/* NULL description. */
opt_register_noarg("-a", test_noarg, "", NULL);
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output, "Option -a: description cannot be NULL"));
}
reset();
exitval = setjmp(exited);
if (exitval == 0) {
/* Bad option name. */
opt_register_noarg("a", test_noarg, "", "");
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output, "Option a: does not begin with '-'"));
}
reset();
exitval = setjmp(exited);
if (exitval == 0) {
/* Bad option name. */
opt_register_noarg("--", test_noarg, "", "");
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output, "Option --: invalid long option '--'"));
}
reset();
exitval = setjmp(exited);
if (exitval == 0) {
/* Bad option name. */
opt_register_noarg("--a|-aaa", test_noarg, "", "");
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output,
"Option --a|-aaa: invalid short option '-aaa'"));
}
reset();
exitval = setjmp(exited);
if (exitval == 0) {
/* Documentation for non-optios. */
opt_register_noarg("--a foo", test_noarg, "", "");
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output,
"Option --a foo: does not take arguments 'foo'"));
}
reset();
exitval = setjmp(exited);
if (exitval == 0) {
/* Documentation for non-optios. */
opt_register_noarg("--a=foo", test_noarg, "", "");
fail("_opt_register returned?");
} else {
ok1(exitval - 1 == 1);
ok1(strstr(output,
"Option --a=foo: does not take arguments 'foo'"));
}
return exit_status();
}
......@@ -5,6 +5,7 @@
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
#include "utils.h"
int main(int argc, char *argv[])
......@@ -14,30 +15,30 @@ int main(int argc, char *argv[])
/* --aaa without args. */
opt_register_arg("-a|--aaa", test_arg, NULL, "aaa", "");
ok1(!parse_args(&argc, &argv, "--aaa", NULL));
ok1(strstr(err_output, ": --aaa: option requires an argument"));
ok1(strstr(err_output, ": --aaa: requires an argument"));
free(err_output);
err_output = NULL;
ok1(!parse_args(&argc, &argv, "-a", NULL));
ok1(strstr(err_output, ": -a: option requires an argument"));
ok1(strstr(err_output, ": -a: requires an argument"));
free(err_output);
err_output = NULL;
/* Multiple */
opt_register_arg("--bbb|-b|-c|--ccc", test_arg, NULL, "aaa", "");
ok1(!parse_args(&argc, &argv, "--bbb", NULL));
ok1(strstr(err_output, ": --bbb: option requires an argument"));
ok1(strstr(err_output, ": --bbb: requires an argument"));
free(err_output);
err_output = NULL;
ok1(!parse_args(&argc, &argv, "-b", NULL));
ok1(strstr(err_output, ": -b: option requires an argument"));
ok1(strstr(err_output, ": -b: requires an argument"));
free(err_output);
err_output = NULL;
ok1(!parse_args(&argc, &argv, "-c", NULL));
ok1(strstr(err_output, ": -c: option requires an argument"));
ok1(strstr(err_output, ": -c: requires an argument"));
free(err_output);
err_output = NULL;
ok1(!parse_args(&argc, &argv, "--ccc", NULL));
ok1(strstr(err_output, ": --ccc: option requires an argument"));
ok1(strstr(err_output, ": --ccc: requires an argument"));
free(err_output);
err_output = NULL;
......
......@@ -22,6 +22,7 @@ static int saved_vprintf(const char *fmt, va_list ap);
#include <ccan/opt/helpers.c>
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/parse.c>
static void reset_options(void)
{
......
......@@ -8,70 +8,82 @@
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
static void reset_options(void)
{
free(opt_table);
opt_table = NULL;
opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
}
/* Test iterators. */
int main(int argc, char *argv[])
{
unsigned i, len;
unsigned j, i, len;
const char *p;
plan_tests(37);
opt_register_table(subtables, NULL);
plan_tests(37 * 2);
for (j = 0; j < 2; j ++) {
reset_options();
/* Giving subtable a title makes an extra entry! */
opt_register_table(subtables, j == 0 ? NULL : "subtable");
p = first_lopt(&i, &len);
ok1(i == 0);
ok1(len == 3);
ok1(strncmp(p, "jjj", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == 0);
ok1(len == 3);
ok1(strncmp(p, "lll", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == 1);
ok1(len == 3);
ok1(strncmp(p, "mmm", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == 5);
ok1(len == 3);
ok1(strncmp(p, "ddd", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == 6);
ok1(len == 3);
ok1(strncmp(p, "eee", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == 7);
ok1(len == 3);
ok1(strncmp(p, "ggg", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == 8);
ok1(len == 3);
ok1(strncmp(p, "hhh", len) == 0);
p = next_lopt(p, &i, &len);
ok1(!p);
p = first_lopt(&i, &len);
ok1(i == j + 0);
ok1(len == 3);
ok1(strncmp(p, "jjj", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == j + 0);
ok1(len == 3);
ok1(strncmp(p, "lll", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == j + 1);
ok1(len == 3);
ok1(strncmp(p, "mmm", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == j + 5);
ok1(len == 3);
ok1(strncmp(p, "ddd", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == j + 6);
ok1(len == 3);
ok1(strncmp(p, "eee", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == j + 7);
ok1(len == 3);
ok1(strncmp(p, "ggg", len) == 0);
p = next_lopt(p, &i, &len);
ok1(i == j + 8);
ok1(len == 3);
ok1(strncmp(p, "hhh", len) == 0);
p = next_lopt(p, &i, &len);
ok1(!p);
p = first_sopt(&i);
ok1(i == 0);
ok1(*p == 'j');
p = next_sopt(p, &i);
ok1(i == 0);
ok1(*p == 'l');
p = next_sopt(p, &i);
ok1(i == 1);
ok1(*p == 'm');
p = next_sopt(p, &i);
ok1(i == 2);
ok1(*p == 'a');
p = next_sopt(p, &i);
ok1(i == 3);
ok1(*p == 'b');
p = next_sopt(p, &i);
ok1(i == 7);
ok1(*p == 'g');
p = next_sopt(p, &i);
ok1(i == 8);
ok1(*p == 'h');
p = next_sopt(p, &i);
ok1(!p);
p = first_sopt(&i);
ok1(i == j + 0);
ok1(*p == 'j');
p = next_sopt(p, &i);
ok1(i == j + 0);
ok1(*p == 'l');
p = next_sopt(p, &i);
ok1(i == j + 1);
ok1(*p == 'm');
p = next_sopt(p, &i);
ok1(i == j + 2);
ok1(*p == 'a');
p = next_sopt(p, &i);
ok1(i == j + 3);
ok1(*p == 'b');
p = next_sopt(p, &i);
ok1(i == j + 7);
ok1(*p == 'g');
p = next_sopt(p, &i);
ok1(i == j + 8);
ok1(*p == 'h');
p = next_sopt(p, &i);
ok1(!p);
}
return exit_status();
}
......@@ -4,6 +4,7 @@
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
#include "utils.h"
int main(int argc, char *argv[])
......
......@@ -8,18 +8,26 @@
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
static char *my_cb(void *p)
{
return NULL;
}
static void reset_options(void)
{
free(opt_table);
opt_table = NULL;
opt_count = opt_num_short = opt_num_short_arg = opt_num_long = 0;
}
/* Test helpers. */
int main(int argc, char *argv[])
{
char *output;
plan_tests(38);
plan_tests(42);
opt_register_table(subtables, NULL);
opt_register_noarg("--kkk|-k", my_cb, NULL, "magic kkk option");
opt_register_noarg("-?", opt_usage_and_exit, "<MyArgs>...",
......@@ -73,5 +81,19 @@ int main(int argc, char *argv[])
ok1(!strstr(output, "--mmm|-m"));
free(output);
reset_options();
/* Empty table test. */
output = opt_usage("nothing", NULL);
ok1(strstr(output, "Usage: nothing \n"));
free(output);
/* No short args. */
opt_register_noarg("--aaa", test_noarg, NULL, "AAAAll");
output = opt_usage("onearg", NULL);
ok1(strstr(output, "Usage: onearg \n"));
ok1(strstr(output, "--aaa"));
ok1(strstr(output, "AAAAll"));
free(output);
return exit_status();
}
......@@ -3,6 +3,7 @@
#include <ccan/opt/opt.c>
#include <ccan/opt/usage.c>
#include <ccan/opt/helpers.c>
#include <ccan/opt/parse.c>
#include "utils.h"
static void reset_options(void)
......@@ -18,7 +19,7 @@ int main(int argc, char *argv[])
{
const char *myname = argv[0];
plan_tests(148);
plan_tests(215);
/* Simple short arg.*/
opt_register_noarg("-a", test_noarg, NULL, "All");
......@@ -52,6 +53,14 @@ int main(int argc, char *argv[])
ok1(strcmp(argv[2], "args") == 0);
ok1(test_cb_called == 6);
/* Malformed versions. */
ok1(!parse_args(&argc, &argv, "--aaa=arg", NULL));
ok1(strstr(err_output, ": --aaa: doesn't allow an argument"));
ok1(!parse_args(&argc, &argv, "--aa", NULL));
ok1(strstr(err_output, ": --aa: unrecognized option"));
ok1(!parse_args(&argc, &argv, "--aaargh", NULL));
ok1(strstr(err_output, ": --aaargh: unrecognized option"));
/* Argument variants. */
reset_options();
test_cb_called = 0;
......@@ -71,6 +80,16 @@ int main(int argc, char *argv[])
ok1(argv[0] == myname);
ok1(test_cb_called == 3);
/* Malformed versions. */
ok1(!parse_args(&argc, &argv, "-a", NULL));
ok1(strstr(err_output, ": -a: requires an argument"));
ok1(!parse_args(&argc, &argv, "--aaa", NULL));
ok1(strstr(err_output, ": --aaa: requires an argument"));
ok1(!parse_args(&argc, &argv, "--aa", NULL));
ok1(strstr(err_output, ": --aa: unrecognized option"));
ok1(!parse_args(&argc, &argv, "--aaargh", NULL));
ok1(strstr(err_output, ": --aaargh: unrecognized option"));
/* Now, tables. */
/* Short table: */
reset_options();
......@@ -215,5 +234,62 @@ int main(int argc, char *argv[])
test_cb_called = 0;
reset_options();
/* Corner cases involving short arg parsing weirdness. */
opt_register_noarg("-a|--aaa", test_noarg, NULL, "a");
opt_register_arg("-b|--bbb", test_arg, NULL, "bbb", "b");
opt_register_arg("-c|--ccc", test_arg, NULL, "aaa", "c");
/* -aa == -a -a */
ok1(parse_args(&argc, &argv, "-aa", NULL));
ok1(test_cb_called == 2);
ok1(parse_args(&argc, &argv, "-aab", NULL) == false);
ok1(test_cb_called == 4);
ok1(strstr(err_output, ": -b: requires an argument"));
ok1(parse_args(&argc, &argv, "-bbbb", NULL));
ok1(test_cb_called == 5);
ok1(parse_args(&argc, &argv, "-aabbbb", NULL));
ok1(test_cb_called == 8);
ok1(parse_args(&argc, &argv, "-aabbbb", "-b", "bbb", NULL));
ok1(test_cb_called == 12);
ok1(parse_args(&argc, &argv, "-aabbbb", "--bbb", "bbb", NULL));
ok1(test_cb_called == 16);
ok1(parse_args(&argc, &argv, "-aabbbb", "--bbb=bbb", NULL));
ok1(test_cb_called == 20);
ok1(parse_args(&argc, &argv, "-aacaaa", NULL));
ok1(test_cb_called == 23);
ok1(parse_args(&argc, &argv, "-aacaaa", "-a", NULL));
ok1(test_cb_called == 27);
ok1(parse_args(&argc, &argv, "-aacaaa", "--bbb", "bbb", "-aacaaa",
NULL));
ok1(test_cb_called == 34);
test_cb_called = 0;
reset_options();
/* -- and POSIXLY_CORRECT */
opt_register_noarg("-a|--aaa", test_noarg, NULL, "a");
ok1(parse_args(&argc, &argv, "-a", "--", "-a", NULL));
ok1(test_cb_called == 1);
ok1(argc == 2);
ok1(strcmp(argv[1], "-a") == 0);
ok1(!argv[2]);
unsetenv("POSIXLY_CORRECT");
ok1(parse_args(&argc, &argv, "-a", "somearg", "-a", "--", "-a", NULL));
ok1(test_cb_called == 3);
ok1(argc == 3);
ok1(strcmp(argv[1], "somearg") == 0);
ok1(strcmp(argv[2], "-a") == 0);
ok1(!argv[3]);
setenv("POSIXLY_CORRECT", "1", 1);
ok1(parse_args(&argc, &argv, "-a", "somearg", "-a", "--", "-a", NULL));
ok1(test_cb_called == 4);
ok1(argc == 5);
ok1(strcmp(argv[1], "somearg") == 0);
ok1(strcmp(argv[2], "-a") == 0);
ok1(strcmp(argv[3], "--") == 0);
ok1(strcmp(argv[4], "-a") == 0);
ok1(!argv[5]);
return exit_status();
}
......@@ -12,7 +12,7 @@ CORE_OBJS := tools/ccanlint/ccanlint.o \
ccan/btree/btree.o \
ccan/talloc/talloc.o ccan/noerr/noerr.o \
ccan/read_write_all/read_write_all.o \
ccan/opt/opt.o ccan/opt/usage.o ccan/opt/helpers.o
ccan/opt/opt.o ccan/opt/usage.o ccan/opt/helpers.o ccan/opt/parse.o
OBJS := $(CORE_OBJS) $(TEST_OBJS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment