Doing our own parsing lost a few lines of code, too.
Our coverage is over 99% now.
*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>
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)) {
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] == '-') {
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);
}
}
}
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, ...)
--- /dev/null
+/* 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;
+}
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 */
--- /dev/null
+#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();
+}
#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[])
/* --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;
#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)
{
#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();
}
#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[])
#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>...",
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();
}
#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)
{
const char *myname = argv[0];
- plan_tests(148);
+ plan_tests(215);
/* Simple short arg.*/
opt_register_noarg("-a", test_noarg, NULL, "All");
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;
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();
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();
}
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)