Major rework of fuse_opt_parse(3) so that it supports all the functionality of the original function

This commit is contained in:
pho 2016-11-16 16:11:42 +00:00
parent a1889144f5
commit fd86259521
3 changed files with 542 additions and 179 deletions

View File

@ -1,9 +1,8 @@
$NetBSD: TODO,v 1.3 2007/05/03 21:02:54 agc Exp $
$NetBSD: TODO,v 1.4 2016/11/16 16:11:42 pho Exp $
To Do
=====
address all XXX
implement all fuse_opt
implement proper lookup (pending some libpuffs stuff)
support fuse_mt (i.e. worker threads, but that'll probably be smarter
to do inside of libpuffs)
@ -23,3 +22,4 @@ special directory handling in open()
Finish off manual page
fuse_setup
fuse_teardown
fuse_opt

View File

@ -1,4 +1,4 @@
/* $NetBSD: refuse_opt.c,v 1.17 2016/11/15 00:34:19 pho Exp $ */
/* $NetBSD: refuse_opt.c,v 1.18 2016/11/16 16:11:42 pho Exp $ */
/*-
* Copyright (c) 2007 Juan Romero Pardines.
@ -25,15 +25,6 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* TODO:
* * -oblah,foo... works, but the options are not enabled.
* * -ofoo=%s (accepts a string) or -ofoo=%u (int) is not
* supported for now.
* * void *data: how is it used? I think it's used to enable
* options or pass values for the matching options.
*/
#include <sys/types.h>
#include <err.h>
@ -50,21 +41,6 @@
#define DPRINTF(x)
#endif
enum {
KEY_HELP,
KEY_VERBOSE,
KEY_VERSION
};
struct fuse_opt_option {
const struct fuse_opt *fop;
char *option;
int key;
void *data;
};
static int fuse_opt_popt(struct fuse_opt_option *, const struct fuse_opt *);
/*
* Public API.
*/
@ -124,14 +100,17 @@ fuse_opt_deep_copy_args(int argc, char **argv)
void
fuse_opt_free_args(struct fuse_args *ap)
{
int i;
for (i = 0; i < ap->argc; i++) {
free(ap->argv[i]);
if (ap) {
if (ap->allocated) {
int i;
for (i = 0; i < ap->argc; i++) {
free(ap->argv[i]);
}
free(ap->argv);
}
ap->argv = NULL;
ap->allocated = ap->argc = 0;
}
free(ap->argv);
ap->argv = NULL;
ap->allocated = ap->argc = 0;
}
/* ARGSUSED */
@ -207,7 +186,7 @@ int fuse_opt_add_opt_escaped(char **opts, const char *opt)
return add_opt(opts, opt, true);
}
static bool match_templ(const char *templ, const char *opt, size_t *sep_idx)
static bool match_templ(const char *templ, const char *opt, int *sep_idx)
{
const char *sep = strpbrk(templ, "= ");
@ -227,7 +206,7 @@ static bool match_templ(const char *templ, const char *opt, size_t *sep_idx)
else {
if (strcmp(templ, opt) == 0) {
if (sep_idx != NULL)
*sep_idx = 0;
*sep_idx = -1;
return true;
}
else {
@ -237,7 +216,7 @@ static bool match_templ(const char *templ, const char *opt, size_t *sep_idx)
}
static const struct fuse_opt *
find_opt(const struct fuse_opt *opts, const char *opt, size_t *sep_idx)
find_opt(const struct fuse_opt *opts, const char *opt, int *sep_idx)
{
for (; opts != NULL && opts->templ != NULL; opts++) {
if (match_templ(opts->templ, opt, sep_idx))
@ -256,167 +235,293 @@ fuse_opt_match(const struct fuse_opt *opts, const char *opt)
return find_opt(opts, opt, NULL) != NULL ? 1 : 0;
}
/*
* Returns 0 if foo->option was matched with any option from opts,
* and sets the following on match:
*
* * foo->key is set to the foo->fop->value if offset == -1.
* * foo->fop points to the matched struct opts.
*
* otherwise returns 1.
*/
static int
fuse_opt_popt(struct fuse_opt_option *foo, const struct fuse_opt *opts)
static int call_proc(fuse_opt_proc_t proc, void* data,
const char* arg, int key, struct fuse_args *outargs, bool is_opt)
{
int i, found = 0;
char *match;
if (!foo->option) {
(void)fprintf(stderr, "fuse: missing argument after -o\n");
return 1;
if (key == FUSE_OPT_KEY_DISCARD)
return 0;
if (key != FUSE_OPT_KEY_KEEP && proc != NULL) {
const int rv = proc(data, arg, key, outargs);
if (rv == -1 || /* error */
rv == 0 /* discard */)
return rv;
}
/*
* iterate over argv and opts to see
* if there's a match with any template.
*/
for (match = strtok(foo->option, ",");
match; match = strtok(NULL, ",")) {
DPRINTF(("%s: specified option='%s'\n", __func__, match));
found = 0;
for (i = 0; opts && opts->templ; opts++, i++) {
DPRINTF(("%s: opts->templ='%s' opts->offset=%d "
"opts->value=%d\n", __func__, opts->templ,
opts->offset, opts->value));
/* option is ok */
if (strcmp(match, opts->templ) == 0) {
DPRINTF(("%s: option matched='%s'\n",
__func__, match));
found++;
/*
* our fop pointer now points
* to the matched struct opts.
*/
foo->fop = opts;
/*
* assign default key value, necessary for
* KEY_HELP, KEY_VERSION and KEY_VERBOSE.
*/
if (foo->fop->offset == -1)
foo->key = foo->fop->value;
/* reset counter */
opts -= i;
break;
}
if (is_opt) {
/* Do we already have "-o" at the beginning of outargs? */
if (outargs->argc >= 3 && strcmp(outargs->argv[1], "-o") == 0) {
/* Append the option to the comma-separated list. */
if (fuse_opt_add_opt_escaped(&outargs->argv[2], arg) == -1)
return -1;
}
/* invalid option */
if (!found) {
(void)fprintf(stderr, "fuse: '%s' is not a "
"valid option\n", match);
return 1;
else {
/* Insert -o arg at the beginning. */
if (fuse_opt_insert_arg(outargs, 1, "-o") == -1)
return -1;
if (fuse_opt_insert_arg(outargs, 2, arg) == -1)
return -1;
}
}
else {
if (fuse_opt_add_arg(outargs, arg) == -1)
return -1;
}
return 0;
}
/* ARGSUSED1 */
int
fuse_opt_parse(struct fuse_args *args, void *data,
const struct fuse_opt *opts, fuse_opt_proc_t proc)
/* Skip the current argv if possible. */
static int next_arg(const struct fuse_args *args, int *i)
{
struct fuse_opt_option foo;
char *buf;
int i, rv = 0;
if (!args || !args->argv || !args->argc || !proc)
if (*i + 1 >= args->argc) {
(void)fprintf(stderr, "fuse: missing argument"
" after '%s'\n", args->argv[*i]);
return -1;
}
else {
*i += 1;
return 0;
}
}
foo.data = data;
if (args->argc == 1)
return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args);
/* Parse a single argument with a matched template. */
static int
parse_matched_arg(const char* arg, struct fuse_args *outargs,
const struct fuse_opt* opt, int sep_idx, void* data,
fuse_opt_proc_t proc, bool is_opt)
{
if (opt->offset == -1) {
/* The option description does not want any variables to be
* updated.*/
if (call_proc(proc, data, arg, opt->value, outargs, is_opt) == -1)
return -1;
}
else {
void *var = (char*)data + opt->offset;
/* the real loop to process the arguments */
for (i = 1; i < args->argc; i++) {
if (sep_idx > 0 && opt->templ[sep_idx + 1] == '%') {
/* "foo=%y" or "-x %y" */
const char* param =
opt->templ[sep_idx] == '=' ? &arg[sep_idx + 1] : &arg[sep_idx];
/* assign current argv string */
foo.option = buf = args->argv[i];
if (opt->templ[sep_idx + 2] == 's') {
char* dup = strdup(param);
if (dup == NULL)
return -1;
/* argvn != -foo... */
if (buf[0] != '-') {
foo.key = FUSE_OPT_KEY_NONOPT;
rv = proc(foo.data, foo.option, foo.key, args);
if (rv != 0)
break;
/* -o was specified... */
} else if (buf[0] == '-' && buf[1] == 'o') {
/* -oblah,foo... */
if (buf[2]) {
/* skip -o */
foo.option = args->argv[i] + 2;
/* -o blah,foo... */
} else {
/*
* skip current argv and pass to the
* next one to parse the options.
*/
++i;
foo.option = args->argv[i];
*(char **)var = dup;
}
rv = fuse_opt_popt(&foo, opts);
if (rv != 0)
break;
/* help/version/verbose argument */
} else if (buf[0] == '-' && buf[1] != 'o') {
/*
* check if the argument matches
* with any template in opts.
*/
rv = fuse_opt_popt(&foo, opts);
if (rv != 0) {
break;
} else {
DPRINTF(("%s: foo.fop->templ='%s' "
"foo.fop->offset: %d "
"foo.fop->value: %d\n",
__func__, foo.fop->templ,
foo.fop->offset, foo.fop->value));
/* argument needs to be discarded */
if (foo.key == FUSE_OPT_KEY_DISCARD) {
rv = 1;
break;
}
/* process help/version argument */
if (foo.key != KEY_VERBOSE &&
foo.key != FUSE_OPT_KEY_KEEP) {
rv = proc(foo.data, foo.option,
foo.key, args);
break;
} else {
/* process verbose argument */
rv = proc(foo.data, foo.option,
foo.key, args);
if (rv != 0)
break;
else {
/* The format string is not a literal. We all know
* this is a bad idea but it's exactly what fuse_opt
* wants to do... */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
if (sscanf(param, &opt->templ[sep_idx + 1], var) == -1) {
#pragma GCC diagnostic pop
(void)fprintf(stderr, "fuse: '%s' is not a "
"valid parameter for option '%.*s'\n",
param, sep_idx, opt->templ);
return -1;
}
}
/* unknown option, how could that happen? */
} else {
DPRINTF(("%s: unknown option\n", __func__));
rv = 1;
break;
}
else {
/* No parameter format is given. */
*(int *)var = opt->value;
}
}
return 0;
}
/* Parse a single argument with matching templates. */
static int
parse_arg(struct fuse_args* args, int *argi, const char* arg,
struct fuse_args *outargs, void *data,
const struct fuse_opt *opts, fuse_opt_proc_t proc, bool is_opt)
{
int sep_idx;
const struct fuse_opt *opt = find_opt(opts, arg, &sep_idx);
if (opt) {
/* An argument can match to multiple templates. Process them
* all. */
for (; opt != NULL && opt->templ != NULL;
opt = find_opt(++opt, arg, &sep_idx)) {
if (sep_idx > 0 && opt->templ[sep_idx] == ' ' &&
arg[sep_idx] == '\0') {
/* The template "-x %y" requests a separate
* parameter "%y". Try to find one. */
char *new_arg;
int rv;
if (next_arg(args, argi) == -1)
return -1;
/* ...but processor callbacks expect a concatenated
* argument "-xfoo". */
if ((new_arg = malloc(sep_idx +
strlen(args->argv[*argi]) + 1)) == NULL)
return -1;
strncpy(new_arg, arg, sep_idx); /* -x */
strcpy(new_arg + sep_idx, args->argv[*argi]); /* foo */
rv = parse_matched_arg(new_arg, outargs, opt, sep_idx,
data, proc, is_opt);
free(new_arg);
if (rv == -1)
return -1;
}
else {
int rv;
rv = parse_matched_arg(arg, outargs, opt, sep_idx,
data, proc, is_opt);
if (rv == -1)
return -1;
}
}
return 0;
}
else {
/* No templates matched to it so just invoke the callback. */
return call_proc(proc, data, arg, FUSE_OPT_KEY_OPT, outargs, is_opt);
}
}
/* Parse a comma-separated list of options which possibly has escaped
* characters. */
static int
parse_opts(struct fuse_args *args, int *argi, const char* arg,
struct fuse_args *outargs, void *data,
const struct fuse_opt *opts, fuse_opt_proc_t proc)
{
char *opt;
size_t i, opt_len = 0;
/* An unescaped option can never be longer than the original
* list. */
if ((opt = malloc(strlen(arg) + 1)) == NULL)
return -1;
for (i = 0; i < strlen(arg); i++) {
if (arg[i] == ',') {
opt[opt_len] = '\0';
if (parse_arg(args, argi, opt, outargs,
data, opts, proc, true) == -1) {
free(opt);
return -1;
}
/* Start a new option. */
opt_len = 0;
}
else if (arg[i] == '\\' && arg[i+1] != '\0') {
/* Unescape it. */
opt[opt_len++] = arg[i+1];
i++;
}
else {
opt[opt_len++] = arg[i];
}
}
/* Parse the last option here. */
opt[opt_len] = '\0';
if (parse_arg(args, argi, opt, outargs, data, opts, proc, true) == -1) {
free(opt);
return -1;
}
free(opt);
return 0;
}
static int
parse_all(struct fuse_args *args, struct fuse_args *outargs, void *data,
const struct fuse_opt *opts, fuse_opt_proc_t proc)
{
bool nonopt = false; /* Have we seen the "--" marker? */
int i;
/* The first argument, the program name, is implicitly
* FUSE_OPT_KEY_KEEP. */
if (args->argc > 0) {
if (fuse_opt_add_arg(outargs, args->argv[0]) == -1)
return -1;
}
/* the real loop to process the arguments */
for (i = 1; i < args->argc; i++) {
const char *arg = args->argv[i];
/* argvn != -foo... */
if (nonopt || arg[0] != '-') {
if (call_proc(proc, data, arg, FUSE_OPT_KEY_NONOPT,
outargs, false) == -1)
return -1;
}
/* -o or -ofoo */
else if (arg[1] == 'o') {
/* -oblah,foo... */
if (arg[2] != '\0') {
/* skip -o */
if (parse_opts(args, &i, arg + 2, outargs,
data, opts, proc) == -1)
return -1;
}
/* -o blah,foo... */
else {
if (next_arg(args, &i) == -1)
return -1;
if (parse_opts(args, &i, args->argv[i], outargs,
data, opts, proc) == -1)
return -1;
}
}
/* -- */
else if (arg[1] == '-' && arg[2] == '\0') {
if (fuse_opt_add_arg(outargs, arg) == -1)
return -1;
nonopt = true;
}
/* -foo */
else {
if (parse_arg(args, &i, arg, outargs,
data, opts, proc, false) == -1)
return -1;
}
}
/* The "--" marker at the last of outargs should be removed */
if (nonopt && strcmp(outargs->argv[outargs->argc - 1], "--") == 0) {
free(outargs->argv[outargs->argc - 1]);
outargs->argv[--outargs->argc] = NULL;
}
return 0;
}
int
fuse_opt_parse(struct fuse_args *args, void *data,
const struct fuse_opt *opts, fuse_opt_proc_t proc)
{
struct fuse_args outargs = FUSE_ARGS_INIT(0, NULL);
int rv;
if (!args || !args->argv || !args->argc)
return 0;
rv = parse_all(args, &outargs, data, opts, proc);
if (rv != -1) {
/* Succeeded. Swap the outargs and args. */
struct fuse_args tmp = *args;
*args = outargs;
outargs = tmp;
}
fuse_opt_free_args(&outargs);
return rv;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: t_refuse_opt.c,v 1.5 2016/11/15 10:05:22 martin Exp $ */
/* $NetBSD: t_refuse_opt.c,v 1.6 2016/11/16 16:11:42 pho Exp $ */
/*-
* Copyright (c) 2016 The NetBSD Foundation, Inc.
@ -26,7 +26,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: t_refuse_opt.c,v 1.5 2016/11/15 10:05:22 martin Exp $");
__RCSID("$NetBSD: t_refuse_opt.c,v 1.6 2016/11/16 16:11:42 pho Exp $");
#define _KERNTYPES
#include <machine/types.h>
@ -148,6 +148,260 @@ ATF_TC_BODY(t_fuse_opt_match, tc)
ATF_CHECK(fuse_opt_match(o6, "bar" ) == 0);
}
struct foofs_config {
int number;
char *string;
char* nonopt;
};
#define FOOFS_OPT(t, p, v) { t, offsetof(struct foofs_config, p), v }
static struct fuse_opt foofs_opts[] = {
FOOFS_OPT("number=%i" , number, 0),
FOOFS_OPT("-n %i" , number, 0),
FOOFS_OPT("string=%s" , string, 0),
FOOFS_OPT("number1" , number, 1),
FOOFS_OPT("number2" , number, 2),
FOOFS_OPT("--number=three", number, 3),
FOOFS_OPT("--number=four" , number, 4),
FUSE_OPT_END
};
static int foo_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs) {
struct foofs_config *config = data;
if (key == FUSE_OPT_KEY_NONOPT && config->nonopt == NULL) {
config->nonopt = strdup(arg);
return 0;
}
else {
return 1;
}
}
ATF_TC(t_fuse_opt_parse_null_args);
ATF_TC_HEAD(t_fuse_opt_parse_null_args, tc)
{
atf_tc_set_md_var(tc, "descr", "NULL args means an empty arguments vector");
}
ATF_TC_BODY(t_fuse_opt_parse_null_args, tc)
{
struct foofs_config config;
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(NULL, &config, NULL, NULL) == 0);
ATF_CHECK_EQ(config.number, 0);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_EQ(config.nonopt, NULL);
}
ATF_TC(t_fuse_opt_parse_null_opts);
ATF_TC_HEAD(t_fuse_opt_parse_null_opts, tc)
{
atf_tc_set_md_var(tc, "descr", "NULL opts means an opts array which only has FUSE_OPT_END");
}
ATF_TC_BODY(t_fuse_opt_parse_null_opts, tc)
{
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
struct foofs_config config;
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "number=1,string=foo"));
RZ(fuse_opt_add_arg(&args, "bar"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, NULL, NULL) == 0);
ATF_CHECK_EQ(config.number, 0);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_EQ(config.nonopt, NULL);
ATF_CHECK_EQ(args.argc, 4);
ATF_CHECK_STREQ(args.argv[0], "foofs");
ATF_CHECK_STREQ(args.argv[1], "-o");
ATF_CHECK_STREQ(args.argv[2], "number=1,string=foo");
ATF_CHECK_STREQ(args.argv[3], "bar");
}
ATF_TC(t_fuse_opt_parse_null_proc);
ATF_TC_HEAD(t_fuse_opt_parse_null_proc, tc)
{
atf_tc_set_md_var(tc, "descr", "NULL proc means a processor function always returning 1,"
" i.e. keep the argument");
}
ATF_TC_BODY(t_fuse_opt_parse_null_proc, tc)
{
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
struct foofs_config config;
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "number=1,string=foo"));
RZ(fuse_opt_add_arg(&args, "bar"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, NULL) == 0);
ATF_CHECK_EQ(config.number, 1);
ATF_CHECK_STREQ(config.string, "foo");
ATF_CHECK_EQ(config.nonopt, NULL);
ATF_CHECK_EQ(args.argc, 2);
ATF_CHECK_STREQ(args.argv[0], "foofs");
ATF_CHECK_STREQ(args.argv[1], "bar");
}
ATF_TC(t_fuse_opt_parse);
ATF_TC_HEAD(t_fuse_opt_parse, tc)
{
atf_tc_set_md_var(tc, "descr", "Check that fuse_opt_parse(3) fully works");
}
ATF_TC_BODY(t_fuse_opt_parse, tc)
{
struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
struct foofs_config config;
/* Standard form */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "number=1,string=foo"));
RZ(fuse_opt_add_arg(&args, "bar"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 1);
ATF_CHECK_STREQ(config.string, "foo");
ATF_CHECK_STREQ(config.nonopt, "bar");
ATF_CHECK_EQ(args.argc, 1);
ATF_CHECK_STREQ(args.argv[0], "foofs");
/* Concatenated -o */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-onumber=1,unknown,string=foo"));
RZ(fuse_opt_add_arg(&args, "bar"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 1);
ATF_CHECK_STREQ(config.string, "foo");
ATF_CHECK_STREQ(config.nonopt, "bar");
ATF_CHECK_EQ(args.argc, 3);
ATF_CHECK_STREQ(args.argv[0], "foofs");
ATF_CHECK_STREQ(args.argv[1], "-o");
ATF_CHECK_STREQ(args.argv[2], "unknown");
/* Sparse -o */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "bar"));
RZ(fuse_opt_add_arg(&args, "baz"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "number=1"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "unknown"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "string=foo"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 1);
ATF_CHECK_STREQ(config.string, "foo");
ATF_CHECK_STREQ(config.nonopt, "bar");
ATF_CHECK_EQ(args.argc, 4);
ATF_CHECK_STREQ(args.argv[0], "foofs");
ATF_CHECK_STREQ(args.argv[1], "-o");
ATF_CHECK_STREQ(args.argv[2], "unknown");
ATF_CHECK_STREQ(args.argv[3], "baz");
/* Separate -n %i */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-n"));
RZ(fuse_opt_add_arg(&args, "3"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 3);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_EQ(config.nonopt, NULL);
ATF_CHECK_EQ(args.argc, 1);
ATF_CHECK_STREQ(args.argv[0], "foofs");
/* Concatenated -n %i */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-n3"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 3);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_EQ(config.nonopt, NULL);
ATF_CHECK_EQ(args.argc, 1);
ATF_CHECK_STREQ(args.argv[0], "foofs");
/* -o constant */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "-o"));
RZ(fuse_opt_add_arg(&args, "number2"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 2);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_EQ(config.nonopt, NULL);
ATF_CHECK_EQ(args.argc, 1);
ATF_CHECK_STREQ(args.argv[0], "foofs");
/* -x constant */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "--number=four"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 4);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_EQ(config.nonopt, NULL);
ATF_CHECK_EQ(args.argc, 1);
ATF_CHECK_STREQ(args.argv[0], "foofs");
/* end-of-options "--" marker */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "--"));
RZ(fuse_opt_add_arg(&args, "-onumber=1"));
RZ(fuse_opt_add_arg(&args, "-ostring=foo"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 0);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_STREQ(config.nonopt, "-onumber=1");
ATF_CHECK_EQ(args.argc, 3);
ATF_CHECK_STREQ(args.argv[0], "foofs");
ATF_CHECK_STREQ(args.argv[1], "--");
ATF_CHECK_STREQ(args.argv[2], "-ostring=foo");
/* The "--" marker at the last of outargs should be removed */
fuse_opt_free_args(&args);
RZ(fuse_opt_add_arg(&args, "foofs"));
RZ(fuse_opt_add_arg(&args, "--"));
RZ(fuse_opt_add_arg(&args, "-onumber=1"));
memset(&config, 0, sizeof(config));
ATF_CHECK(fuse_opt_parse(&args, &config, foofs_opts, foo_opt_proc) == 0);
ATF_CHECK_EQ(config.number, 0);
ATF_CHECK_EQ(config.string, NULL);
ATF_CHECK_STREQ(config.nonopt, "-onumber=1");
ATF_CHECK_EQ(args.argc, 1);
ATF_CHECK_STREQ(args.argv[0], "foofs");
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, t_fuse_opt_add_arg);
@ -155,6 +409,10 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, t_fuse_opt_add_opt);
ATF_TP_ADD_TC(tp, t_fuse_opt_add_opt_escaped);
ATF_TP_ADD_TC(tp, t_fuse_opt_match);
ATF_TP_ADD_TC(tp, t_fuse_opt_parse_null_args);
ATF_TP_ADD_TC(tp, t_fuse_opt_parse_null_opts);
ATF_TP_ADD_TC(tp, t_fuse_opt_parse_null_proc);
ATF_TP_ADD_TC(tp, t_fuse_opt_parse);
return atf_no_error();
}