Module Name: src Committed By: pho Date: Wed Nov 16 16:11:42 UTC 2016
Modified Files: src/lib/librefuse: TODO refuse_opt.c src/tests/lib/librefuse: t_refuse_opt.c Log Message: Major rework of fuse_opt_parse(3) so that it supports all the functionality of the original function To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/lib/librefuse/TODO cvs rdiff -u -r1.17 -r1.18 src/lib/librefuse/refuse_opt.c cvs rdiff -u -r1.5 -r1.6 src/tests/lib/librefuse/t_refuse_opt.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/librefuse/TODO diff -u src/lib/librefuse/TODO:1.3 src/lib/librefuse/TODO:1.4 --- src/lib/librefuse/TODO:1.3 Thu May 3 21:02:54 2007 +++ src/lib/librefuse/TODO Wed Nov 16 16:11:42 2016 @@ -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 Index: src/lib/librefuse/refuse_opt.c diff -u src/lib/librefuse/refuse_opt.c:1.17 src/lib/librefuse/refuse_opt.c:1.18 --- src/lib/librefuse/refuse_opt.c:1.17 Tue Nov 15 00:34:19 2016 +++ src/lib/librefuse/refuse_opt.c Wed Nov 16 16:11:42 2016 @@ -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 * 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 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 *temp 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 *temp } 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 *op 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; - } - /* - * 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 (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; + } + + 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 (*i + 1 >= args->argc) { + (void)fprintf(stderr, "fuse: missing argument" + " after '%s'\n", args->argv[*i]); + return -1; + } + else { + *i += 1; + return 0; + } +} - if (!args || !args->argv || !args->argc || !proc) +/* 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; + + 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]; + + if (opt->templ[sep_idx + 2] == 's') { + char* dup = strdup(param); + if (dup == NULL) + return -1; + + *(char **)var = dup; + } + 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; + } + } + } + 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); + } +} - foo.data = data; - if (args->argc == 1) - return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args); +/* 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; - /* the real loop to process the arguments */ - for (i = 1; i < args->argc; i++) { + 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]; + } + } - /* assign current argv string */ - foo.option = buf = args->argv[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; + } - /* argvn != -foo... */ - if (buf[0] != '-') { + 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; - foo.key = FUSE_OPT_KEY_NONOPT; - rv = proc(foo.data, foo.option, foo.key, args); - if (rv != 0) - break; + /* 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; + } - /* -o was specified... */ - } else if (buf[0] == '-' && buf[1] == 'o') { + /* 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 (buf[2]) { + if (arg[2] != '\0') { /* skip -o */ - foo.option = args->argv[i] + 2; + if (parse_opts(args, &i, arg + 2, outargs, + data, opts, proc) == -1) + return -1; + } /* -o blah,foo... */ - } else { - /* - * skip current argv and pass to the - * next one to parse the options. - */ - ++i; - foo.option = args->argv[i]; + 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; + } + } - 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; - } + /* 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; + } - /* 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; - } - } - /* unknown option, how could that happen? */ - } else { - DPRINTF(("%s: unknown option\n", __func__)); - rv = 1; - break; - } + 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; } Index: src/tests/lib/librefuse/t_refuse_opt.c diff -u src/tests/lib/librefuse/t_refuse_opt.c:1.5 src/tests/lib/librefuse/t_refuse_opt.c:1.6 --- src/tests/lib/librefuse/t_refuse_opt.c:1.5 Tue Nov 15 10:05:22 2016 +++ src/tests/lib/librefuse/t_refuse_opt.c Wed Nov 16 16:11:42 2016 @@ -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(); }