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();
 }

Reply via email to