summaryrefslogtreecommitdiff
path: root/lib/librefuse
diff options
context:
space:
mode:
authorpho <pho@NetBSD.org>2016-11-16 16:11:42 +0000
committerpho <pho@NetBSD.org>2016-11-16 16:11:42 +0000
commitf71ba78dc6faf9e1e87f13435b811d6d7b8e53fe (patch)
tree2551da823b852b3d420734429f8975e0a3448539 /lib/librefuse
parent4db8e394a5d16f34425f4a91fb1e257f8d375602 (diff)
Major rework of fuse_opt_parse(3) so that it supports all the functionality of the original function
Diffstat (limited to 'lib/librefuse')
-rw-r--r--lib/librefuse/TODO4
-rw-r--r--lib/librefuse/refuse_opt.c445
2 files changed, 277 insertions, 172 deletions
diff --git a/lib/librefuse/TODO b/lib/librefuse/TODO
index 918d38b1f1a..1bd1f5a2ef4 100644
--- a/lib/librefuse/TODO
+++ b/lib/librefuse/TODO
@@ -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
diff --git a/lib/librefuse/refuse_opt.c b/lib/librefuse/refuse_opt.c
index 895ecc2c526..866ece6c035 100644
--- a/lib/librefuse/refuse_opt.c
+++ b/lib/librefuse/refuse_opt.c
@@ -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 (*i + 1 >= args->argc) {
+ (void)fprintf(stderr, "fuse: missing argument"
+ " after '%s'\n", args->argv[*i]);
+ return -1;
+ }
+ else {
+ *i += 1;
+ return 0;
+ }
+}
+
+/* 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;
- if (!args || !args->argv || !args->argc || !proc)
+ *(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;
- /* the real loop to process the arguments */
- for (i = 1; i < args->argc; i++) {
+ /* 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];
+ }
+ }
- /* 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;
}