added option parsing
diff --git a/ChangeLog b/ChangeLog
index 86a4365..f71b18d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2005-12-09  Miklos Szeredi <miklos@szeredi.hu>
+
+	* libfuse: added option parsing interface, defined in
+	<fuse_opt.h>.
+
 2005-12-07  Miklos Szeredi <miklos@szeredi.hu>
 
 	* Return EIO for file operations (read, write, fsync, flush) on
diff --git a/include/Makefile.am b/include/Makefile.am
index 41d9a81..81d2661 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -7,7 +7,8 @@
 	fuse_compat.h		\
 	fuse_common.h		\
 	fuse_lowlevel.h		\
-	fuse_lowlevel_compat.h
+	fuse_lowlevel_compat.h	\
+	fuse_opt.h
 
 include_HEADERS = old/fuse.h
 
diff --git a/include/fuse_opt.h b/include/fuse_opt.h
new file mode 100644
index 0000000..528ec9d
--- /dev/null
+++ b/include/fuse_opt.h
@@ -0,0 +1,212 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2005  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+
+#ifndef _FUSE_OPT_H_
+#define _FUSE_OPT_H_
+
+/* This file defines the option parsing interface of FUSE */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Special 'offset' value.  In case of a match, the processing
+ * function will be called with 'value' as the key
+ */
+#define FUSE_OPT_OFFSET_KEY -1U
+
+/**
+ * Option description
+ *
+ * This structure describes a single option, and and action associated
+ * with it, in case it matches.
+ *
+ * More than one such match may occur, in which case the action for
+ * each match is executed.
+ *
+ * There are three possible actions in case of a match:
+ *
+ * i) An integer (int or unsigned) variable determined by 'offset' is
+ *    set to 'value'
+ *
+ * ii) The processing function is called, with 'value' as the key
+ *
+ * iii) An integer (any) or string (char *) variable determined by
+ *    'offset' is set to the value of an option parameter
+ *
+ * 'offset' should normally be either set to
+ *
+ *  - 'offsetof(struct foo, member)'  actions i) and iii)
+ *
+ *  - FUSE_OPT_OFFSET_KEY             action ii)
+ *
+ * The 'offsetof()' macro is defined in the <stddef.h> header.
+ *
+ * The template determines which options match, and also have an
+ * effect on the action.  Normally the action is either i) or ii), but
+ * if a format is present in the template, then action iii) is
+ * performed.
+ *
+ * The types of templates are:
+ *
+ * 1) "-x", "-foo", "--foo", "--foo-bar", etc.  These match only
+ *   themselves.  Invalid values are "--" and anything beginning
+ *   with "-o"
+ *
+ * 2) "foo", "foo-bar", etc.  These match "-ofoo", "-ofoo-bar" or
+ *    the relevant option in a comma separated option list
+ *
+ * 3) "bar=", "--foo=", etc.  These are variations of 1) and 2)
+ *    which have a parameter
+ *
+ * 4) "bar=%s", "--foo=%lu", etc.  Same matching as above but perform
+ *    action iii).
+ *
+ * 5) "-x ", etc.  Matches either "-xparam" or "-x param" as
+ *    two separate arguments
+ *
+ * 6) "-x %s", etc.  Combination of 4) and 5)
+ *
+ * If the format is "%s", memory is allocated for the string unlike
+ * with scanf().
+ */
+struct fuse_opt {
+    /** Matching template and optional parameter formatting */
+    const char *template;
+
+    /**
+     * Offset of variable within 'data' parameter of fuse_opt_parse()
+     * or FUSE_OPT_OFFSET_KEY
+     */
+    unsigned long offset;
+
+    /**
+     * Value to set the variable to, or to be passed as 'key' to the
+     * processing function.  Ignored if template a format
+     */
+    int value;
+};
+
+/**
+ * Last option.  An array of 'struct fuse_opt' must end with a NULL
+ * template value
+ */
+#define FUSE_OPT_END { .template = NULL }
+
+
+/**
+ * Key value passed to the processing function if an option did not
+ * match any templated
+ */
+#define FUSE_OPT_KEY_OPT     -1
+
+/**
+ * Key value passed to the processing function for all non-options
+ *
+ * Non-options are the arguments beginning with a charater other than
+ * '-' or all arguments after the special '--' option
+ */
+#define FUSE_OPT_KEY_NONOPT  -2
+
+/**
+ * Processing function
+ *
+ * This function is called if
+ *    - option did not match any 'struct fuse_opt'
+ *    - argument is a non-option
+ *    - option did match and offset was set to FUSE_OPT_OFFSET_KEY
+ *
+ * The 'arg' parameter will always contain the whole argument or
+ * option including the parameter if exists.  A two-argument option
+ * ("-x foo") is always converted to single arguemnt option of the
+ * form "-xfoo" before this function is called.
+ *
+ * Options of the form '-ofoo' are passed to this function without the
+ * '-o' prefix.
+ *
+ * The return value of this function determines whether this argument
+ * is to be inserted into the output argument vector, or discarded.
+ *
+ * @param data is the user data passed to the fuse_opt_parse() function
+ * @param arg is the whole argument or option
+ * @param key determines why the processing function was called
+ * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
+ */
+typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key);
+
+/**
+ * Option parsing function
+ *
+ * If 'argv' is NULL, the values pointed by argcout and argvout will
+ * be used as input
+ *
+ * A NULL 'opts' is the same as an 'opts' array containing a single
+ * end marker
+ *
+ * If 'proc' is NULL, then any non-matching options will cause an
+ * error to be returned
+ *
+ * If argvout is NULL, then any output arguments are discarded
+ *
+ * If argcout is NULL, then the output argument count is not stored
+ *
+ * @param argc is the input argument count
+ * @param argv is the input argument vector, may be NULL
+ * @param data is the user data
+ * @param opts is the option description array, may be NULL
+ * @param proc is the processing function, may be NULL
+ * @param argcout is pointer to output argument count, may be NULL
+ * @param argvout is pointer to output argument vector, may be NULL
+ * @return -1 on error, 0 on success
+ */
+int fuse_opt_parse(int argc, char *argv[], void *data,
+                   const struct fuse_opt opts[], fuse_opt_proc_t proc,
+                   int *argcout, char **argvout[]);
+
+/**
+ * Add an option to a comma separated option list
+ *
+ * @param opts is a pointer to an option list, may point to a NULL value
+ * @param opt is the option to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_opt(char **opts, const char *opt);
+
+/**
+ * Add an argument to a NULL terminated argument vector
+ *
+ * @param argcp is a pointer to argument count
+ * @param argvp is a pointer to argument vector
+ * @param arg is the new argument to add
+ * @return -1 on allocation error, 0 on success
+ */
+int fuse_opt_add_arg(int *argcp, char **argvp[], const char *arg);
+
+/**
+ * Free argument vector
+ *
+ * @param args is the argument vector
+ */
+void fuse_opt_free_args(char *args[]);
+
+
+/**
+ * Check if an option matches
+ *
+ * @param opts is the option description array
+ * @param opt is the option to match
+ * @return 1 if a match is found, 0 if not
+ */
+int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FUSE_OPT_H_ */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 0bd952e..2af57b9 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -16,6 +16,7 @@
 	fuse_loop_mt.c		\
 	fuse_lowlevel.c		\
 	fuse_mt.c		\
+	fuse_opt.c		\
 	fuse_session.c		\
 	helper.c		\
 	$(mount_source)
diff --git a/lib/fuse.c b/lib/fuse.c
index 8733164..cd5307c 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -12,10 +12,12 @@
 
 #include "fuse_i.h"
 #include "fuse_lowlevel.h"
+#include "fuse_opt.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stddef.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <limits.h>
@@ -25,45 +27,29 @@
 #include <sys/param.h>
 #include <sys/uio.h>
 
-/* FUSE flags: */
-
-/** Enable debugging output */
-#define FUSE_DEBUG       (1 << 1)
-
-/** If a file is removed but it's still open, don't hide the file but
-    remove it immediately */
-#define FUSE_HARD_REMOVE (1 << 2)
-
-/** Use st_ino field in getattr instead of generating inode numbers  */
-#define FUSE_USE_INO     (1 << 3)
-
-/** Make a best effort to fill in inode number in a readdir **/
-#define FUSE_READDIR_INO (1 << 5)
-
-/** Ignore file mode supplied by the filesystem, and create one based
-    on the 'umask' option */
-#define FUSE_SET_MODE (1 << 6)
-
-/** Ignore st_uid supplied by the filesystem and set it based on the
-    'uid' option*/
-#define FUSE_SET_UID (1 << 7)
-
-/** Ignore st_gid supplied by the filesystem and set it based on the
-    'gid' option*/
-#define FUSE_SET_GID (1 << 8)
-
-/** Bypass the page cache for read and write operations  */
-#define FUSE_DIRECT_IO (1 << 9)
-
-/** If the FUSE_KERNEL_CACHE flag is given, then cached data will not
-    be flushed on open */
-#define FUSE_KERNEL_CACHE (1 << 10)
-
 #define FUSE_MAX_PATH 4096
 
+struct fuse_config {
+    char *llopts;
+    unsigned int uid;
+    unsigned int gid;
+    unsigned int  umask;
+    double entry_timeout;
+    double negative_timeout;
+    double attr_timeout;
+    int debug;
+    int hard_remove;
+    int use_ino;
+    int readdir_ino;
+    int set_mode;
+    int set_uid;
+    int set_gid;
+    int direct_io;
+    int kernel_cache;
+};
+
 struct fuse {
     struct fuse_session *se;
-    int flags;
     struct fuse_operations op;
     int compat;
     struct node **name_table;
@@ -76,12 +62,7 @@
     pthread_mutex_t lock;
     pthread_rwlock_t tree_lock;
     void *user_data;
-    unsigned int uid;
-    unsigned int gid;
-    unsigned int  umask;
-    double entry_timeout;
-    double negative_timeout;
-    double attr_timeout;
+    struct fuse_config conf;
 };
 
 struct node {
@@ -231,7 +212,7 @@
 
 static void delete_node(struct fuse *f, struct node *node)
 {
-    if (f->flags & FUSE_DEBUG) {
+    if (f->conf.debug) {
         printf("delete: %llu\n", (unsigned long long) node->nodeid);
         fflush(stdout);
     }
@@ -422,14 +403,14 @@
 
 static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
 {
-    if (!(f->flags & FUSE_USE_INO))
+    if (!f->conf.use_ino)
         stbuf->st_ino = nodeid;
-    if (f->flags & FUSE_SET_MODE)
-        stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->umask);
-    if (f->flags & FUSE_SET_UID)
-        stbuf->st_uid = f->uid;
-    if (f->flags & FUSE_SET_GID)
-        stbuf->st_gid = f->gid;
+    if (f->conf.set_mode)
+        stbuf->st_mode = (stbuf->st_mode & S_IFMT) | (0777 & ~f->conf.umask);
+    if (f->conf.set_uid)
+        stbuf->st_uid = f->conf.uid;
+    if (f->conf.set_gid)
+        stbuf->st_gid = f->conf.gid;
 }
 
 static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
@@ -525,10 +506,10 @@
         else {
             e->ino = node->nodeid;
             e->generation = node->generation;
-            e->entry_timeout = f->entry_timeout;
-            e->attr_timeout = f->attr_timeout;
+            e->entry_timeout = f->conf.entry_timeout;
+            e->attr_timeout = f->conf.attr_timeout;
             set_stat(f, e->ino, &e->attr);
-            if (f->flags & FUSE_DEBUG) {
+            if (f->conf.debug) {
                 printf("   NODEID: %lu\n", (unsigned long) e->ino);
                 fflush(stdout);
             }
@@ -607,16 +588,16 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path_name(f, parent, name);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("LOOKUP %s\n", path);
             fflush(stdout);
         }
         err = -ENOSYS;
         if (f->op.getattr) {
             err = lookup_path(f, parent, name, path, &e, NULL);
-            if (err == -ENOENT && f->negative_timeout != 0.0) {
+            if (err == -ENOENT && f->conf.negative_timeout != 0.0) {
                 e.ino = 0;
-                e.entry_timeout = f->negative_timeout;
+                e.entry_timeout = f->conf.negative_timeout;
                 err = 0;
             }
         }
@@ -629,7 +610,7 @@
 static void fuse_forget(fuse_req_t req, fuse_ino_t ino, unsigned long nlookup)
 {
     struct fuse *f = req_fuse(req);
-    if (f->flags & FUSE_DEBUG) {
+    if (f->conf.debug) {
         printf("FORGET %llu/%lu\n", (unsigned long long) ino, nlookup);
         fflush(stdout);
     }
@@ -659,7 +640,7 @@
     pthread_rwlock_unlock(&f->tree_lock);
     if (!err) {
         set_stat(f, ino, &buf);
-        fuse_reply_attr(req, &buf, f->attr_timeout);
+        fuse_reply_attr(req, &buf, f->conf.attr_timeout);
     } else
         reply_err(req, err);
 }
@@ -747,7 +728,7 @@
     pthread_rwlock_unlock(&f->tree_lock);
     if (!err) {
         set_stat(f, ino, &buf);
-        fuse_reply_attr(req, &buf, f->attr_timeout);
+        fuse_reply_attr(req, &buf, f->conf.attr_timeout);
     } else
         reply_err(req, err);
 }
@@ -762,7 +743,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("ACCESS %s 0%o\n", path, mask);
             fflush(stdout);
         }
@@ -811,7 +792,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path_name(f, parent, name);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("MKNOD %s\n", path);
             fflush(stdout);
         }
@@ -850,7 +831,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path_name(f, parent, name);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("MKDIR %s\n", path);
             fflush(stdout);
         }
@@ -876,13 +857,13 @@
     pthread_rwlock_wrlock(&f->tree_lock);
     path = get_path_name(f, parent, name);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("UNLINK %s\n", path);
             fflush(stdout);
         }
         err = -ENOSYS;
         if (f->op.unlink) {
-            if (!(f->flags & FUSE_HARD_REMOVE) && is_open(f, parent, name))
+            if (!f->conf.hard_remove && is_open(f, parent, name))
                 err = hide_node(f, path, parent, name);
             else {
                 err = f->op.unlink(path);
@@ -906,7 +887,7 @@
     pthread_rwlock_wrlock(&f->tree_lock);
     path = get_path_name(f, parent, name);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("RMDIR %s\n", path);
             fflush(stdout);
         }
@@ -934,7 +915,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path_name(f, parent, name);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("SYMLINK %s\n", path);
             fflush(stdout);
         }
@@ -964,14 +945,14 @@
     if (oldpath != NULL) {
         newpath = get_path_name(f, newdir, newname);
         if (newpath != NULL) {
-            if (f->flags & FUSE_DEBUG) {
+            if (f->conf.debug) {
                 printf("RENAME %s -> %s\n", oldpath, newpath);
                 fflush(stdout);
             }
             err = -ENOSYS;
             if (f->op.rename) {
                 err = 0;
-                if (!(f->flags & FUSE_HARD_REMOVE) &&
+                if (!f->conf.hard_remove &&
                     is_open(f, newdir, newname))
                     err = hide_node(f, newpath, newdir, newname);
                 if (!err) {
@@ -1003,7 +984,7 @@
     if (oldpath != NULL) {
         newpath =  get_path_name(f, newparent, newname);
         if (newpath != NULL) {
-            if (f->flags & FUSE_DEBUG) {
+            if (f->conf.debug) {
                 printf("LINK %s\n", newpath);
                 fflush(stdout);
             }
@@ -1037,7 +1018,7 @@
         if (f->op.create && f->op.getattr) {
             err = f->op.create(path, mode, fi);
             if (!err) {
-                if (f->flags & FUSE_DEBUG) {
+                if (f->conf.debug) {
                     printf("CREATE[%llu] flags: 0x%x %s\n",
                            (unsigned long long) fi->fh, fi->flags, path);
                     fflush(stdout);
@@ -1057,9 +1038,9 @@
     }
 
     if (!err) {
-        if (f->flags & FUSE_DIRECT_IO)
+        if (f->conf.direct_io)
             fi->direct_io = 1;
-        if (f->flags & FUSE_KERNEL_CACHE)
+        if (f->conf.kernel_cache)
             fi->keep_cache = 1;
 
         pthread_mutex_lock(&f->lock);
@@ -1096,15 +1077,15 @@
             err = fuse_do_open(f, path, fi);
     }
     if (!err) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("OPEN[%llu] flags: 0x%x\n", (unsigned long long) fi->fh,
                    fi->flags);
             fflush(stdout);
         }
 
-        if (f->flags & FUSE_DIRECT_IO)
+        if (f->conf.direct_io)
             fi->direct_io = 1;
-        if (f->flags & FUSE_KERNEL_CACHE)
+        if (f->conf.kernel_cache)
             fi->keep_cache = 1;
 
         pthread_mutex_lock(&f->lock);
@@ -1143,7 +1124,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("READ[%llu] %u bytes from %llu\n",
                    (unsigned long long) fi->fh, size, off);
             fflush(stdout);
@@ -1157,7 +1138,7 @@
     pthread_rwlock_unlock(&f->tree_lock);
 
     if (res >= 0) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("   READ[%llu] %u bytes\n", (unsigned long long) fi->fh,
                    res);
             fflush(stdout);
@@ -1182,7 +1163,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("WRITE%s[%llu] %u bytes to %llu\n",
                    fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh,
                    size, off);
@@ -1197,7 +1178,7 @@
     pthread_rwlock_unlock(&f->tree_lock);
 
     if (res >= 0) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("   WRITE%s[%llu] %u bytes\n",
                    fi->writepage ? "PAGE" : "", (unsigned long long) fi->fh,
                    res);
@@ -1221,7 +1202,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("FLUSH[%llu]\n", (unsigned long long) fi->fh);
             fflush(stdout);
         }
@@ -1251,7 +1232,7 @@
 
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
-    if (f->flags & FUSE_DEBUG) {
+    if (f->conf.debug) {
         printf("RELEASE[%llu] flags: 0x%x\n", (unsigned long long) fi->fh,
                fi->flags);
         fflush(stdout);
@@ -1280,7 +1261,7 @@
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
     if (path != NULL) {
-        if (f->flags & FUSE_DEBUG) {
+        if (f->conf.debug) {
             printf("FSYNC[%llu]\n", (unsigned long long) fi->fh);
             fflush(stdout);
         }
@@ -1375,9 +1356,9 @@
         stbuf.st_ino = (ino_t) -1;
     }
 
-    if (!(dh->fuse->flags & FUSE_USE_INO)) {
+    if (!dh->fuse->conf.use_ino) {
         stbuf.st_ino = (ino_t) -1;
-        if (dh->fuse->flags & FUSE_READDIR_INO) {
+        if (dh->fuse->conf.readdir_ino) {
             struct node *node;
             pthread_mutex_lock(&dh->fuse->lock);
             node = lookup_node(dh->fuse, dh->nodeid, name);
@@ -1824,97 +1805,39 @@
     fuse_getcontext = func;
 }
 
-static int begins_with(const char *s, const char *beg)
+static int fuse_lib_opt_proc(void *data, const char *arg, int key)
 {
-    if (strncmp(s, beg, strlen(beg)) == 0)
-        return 1;
-    else
-        return 0;
+    struct fuse_config *conf = data;
+    (void) key;
+    return fuse_opt_add_opt(&conf->llopts, arg);
 }
 
+#define FUSE_LIB_OPT(t, p, v) { t, offsetof(struct fuse_config, p), v }
+
+static const struct fuse_opt fuse_lib_opts[] = {
+    { "debug", FUSE_OPT_OFFSET_KEY, 0 },
+    FUSE_LIB_OPT("debug",                 debug, 1),
+    FUSE_LIB_OPT("hard_remove",           hard_remove, 1),
+    FUSE_LIB_OPT("use_ino",               use_ino, 1),
+    FUSE_LIB_OPT("readdir_ino",           readdir_ino, 1),
+    FUSE_LIB_OPT("direct_io",             direct_io, 1),
+    FUSE_LIB_OPT("kernel_cache",          kernel_cache, 1),
+    FUSE_LIB_OPT("umask=",                set_mode, 1),
+    FUSE_LIB_OPT("umask=%o",              umask, 0),
+    FUSE_LIB_OPT("uid=",                  set_uid, 1),
+    FUSE_LIB_OPT("uid=%d",                uid, 0),
+    FUSE_LIB_OPT("gid=",                  set_gid, 1),
+    FUSE_LIB_OPT("gid=%d",                gid, 0),
+    FUSE_LIB_OPT("entry_timeout=%lf",     entry_timeout, 0),
+    FUSE_LIB_OPT("attr_timeout=%lf",      attr_timeout, 0),
+    FUSE_LIB_OPT("negative_timeout=%lf",  negative_timeout, 0),
+    FUSE_OPT_END
+};
+
 int fuse_is_lib_option(const char *opt)
 {
-    if (fuse_lowlevel_is_lib_option(opt) ||
-        strcmp(opt, "debug") == 0 ||
-        strcmp(opt, "hard_remove") == 0 ||
-        strcmp(opt, "use_ino") == 0 ||
-        strcmp(opt, "allow_root") == 0 ||
-        strcmp(opt, "readdir_ino") == 0 ||
-        strcmp(opt, "direct_io") == 0 ||
-        strcmp(opt, "kernel_cache") == 0 ||
-        begins_with(opt, "umask=") ||
-        begins_with(opt, "uid=") ||
-        begins_with(opt, "gid=") ||
-        begins_with(opt, "entry_timeout=") ||
-        begins_with(opt, "attr_timeout=") ||
-        begins_with(opt, "negative_timeout="))
-        return 1;
-    else
-        return 0;
-}
-
-static int parse_lib_opts(struct fuse *f, const char *opts, char **llopts)
-{
-    if (opts) {
-        char *xopts = strdup(opts);
-        char *s = xopts;
-        char *opt;
-        char *d = xopts;
-
-        if (xopts == NULL) {
-            fprintf(stderr, "fuse: memory allocation failed\n");
-            return -1;
-        }
-
-        while((opt = strsep(&s, ","))) {
-            if (fuse_lowlevel_is_lib_option(opt)) {
-                size_t optlen = strlen(opt);
-                if (strcmp(opt, "debug") == 0)
-                    f->flags |= FUSE_DEBUG;
-                memmove(d, opt, optlen);
-                d += optlen;
-                *d++ = ',';
-            } else if (strcmp(opt, "hard_remove") == 0)
-                f->flags |= FUSE_HARD_REMOVE;
-            else if (strcmp(opt, "use_ino") == 0)
-                f->flags |= FUSE_USE_INO;
-            else if (strcmp(opt, "readdir_ino") == 0)
-                f->flags |= FUSE_READDIR_INO;
-            else if (strcmp(opt, "direct_io") == 0)
-                f->flags |= FUSE_DIRECT_IO;
-            else if (strcmp(opt, "kernel_cache") == 0)
-                f->flags |= FUSE_KERNEL_CACHE;
-            else if (sscanf(opt, "umask=%o", &f->umask) == 1)
-                f->flags |= FUSE_SET_MODE;
-            else if (sscanf(opt, "uid=%u", &f->uid) == 1)
-                f->flags |= FUSE_SET_UID;
-            else if(sscanf(opt, "gid=%u", &f->gid) == 1)
-                f->flags |= FUSE_SET_GID;
-            else if (sscanf(opt, "entry_timeout=%lf", &f->entry_timeout) == 1)
-                /* nop */;
-            else if (sscanf(opt, "attr_timeout=%lf", &f->attr_timeout) == 1)
-                /* nop */;
-            else if (sscanf(opt, "negative_timeout=%lf",
-                            &f->negative_timeout) == 1)
-                /* nop */;
-            else
-                fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt);
-        }
-        if (d != xopts) {
-            d[-1] = '\0';
-            *llopts = xopts;
-        }
-        else
-            free(xopts);
-    }
-#ifdef __FreeBSD__
-    /*
-     * In FreeBSD, we always use these settings as inode numbers are needed to
-     * make getcwd(3) work.
-     */
-    f->flags |= FUSE_READDIR_INO;
-#endif
-    return 0;
+    return fuse_lowlevel_is_lib_option(opt) ||
+        fuse_opt_match(fuse_lib_opts, opt);
 }
 
 struct fuse *fuse_new_common(int fd, const char *opts,
@@ -1924,7 +1847,6 @@
     struct fuse_chan *ch;
     struct fuse *f;
     struct node *root;
-    char *llopts = NULL;
 
     if (sizeof(struct fuse_operations) < op_size) {
         fprintf(stderr, "fuse: warning: library too old, some operations may not not work\n");
@@ -1937,15 +1859,28 @@
         goto out;
     }
 
-    f->entry_timeout = 1.0;
-    f->attr_timeout = 1.0;
-    f->negative_timeout = 0.0;
+    f->conf.entry_timeout = 1.0;
+    f->conf.attr_timeout = 1.0;
+    f->conf.negative_timeout = 0.0;
 
-    if (parse_lib_opts(f, opts, &llopts) == -1)
-        goto out_free;
+    if (opts) {
+        const char *argv[] = { "", "-o", opts, NULL };
+        if (fuse_opt_parse(3, (char **) argv, &f->conf,
+                           fuse_lib_opts, fuse_lib_opt_proc, NULL, NULL) == -1)
+            goto out_free;
+    }
 
-    f->se = fuse_lowlevel_new(llopts, &fuse_path_ops, sizeof(fuse_path_ops), f);
-    free(llopts);
+#ifdef __FreeBSD__
+    /*
+     * In FreeBSD, we always use these settings as inode numbers are needed to
+     * make getcwd(3) work.
+     */
+    f->flags |= FUSE_READDIR_INO;
+#endif
+
+    f->se = fuse_lowlevel_new(f->conf.llopts, &fuse_path_ops,
+                              sizeof(fuse_path_ops), f);
+    free(f->conf.llopts);
     if (f->se == NULL)
         goto out_free;
 
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 0787368..370e320 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -9,10 +9,12 @@
 #include <config.h>
 #include "fuse_lowlevel.h"
 #include "fuse_kernel.h"
+#include "fuse_opt.h"
 
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
 #include <unistd.h>
 #include <limits.h>
 #include <errno.h>
@@ -31,8 +33,8 @@
 #define MIN_BUFFER_SIZE (MIN_SYMLINK + HEADER_OVERHEAD)
 
 struct fuse_ll {
-    unsigned int debug : 1;
-    unsigned int allow_root : 1;
+    int debug;
+    int allow_root;
     struct fuse_lowlevel_ops op;
     int got_init;
     void *userdata;
@@ -917,38 +919,15 @@
     }
 }
 
+static struct fuse_opt fuse_ll_opts[] = {
+    { "debug", offsetof(struct fuse_ll, debug), 1 },
+    { "allow_root", offsetof(struct fuse_ll, allow_root), 1 },
+    FUSE_OPT_END
+};
+
 int fuse_lowlevel_is_lib_option(const char *opt)
 {
-    if (strcmp(opt, "debug") == 0 ||
-        strcmp(opt, "allow_root") == 0)
-        return 1;
-    else
-        return 0;
-}
-
-static int parse_ll_opts(struct fuse_ll *f, const char *opts)
-{
-    if (opts) {
-        char *xopts = strdup(opts);
-        char *s = xopts;
-        char *opt;
-
-        if (xopts == NULL) {
-            fprintf(stderr, "fuse: memory allocation failed\n");
-            return -1;
-        }
-
-        while((opt = strsep(&s, ","))) {
-            if (strcmp(opt, "debug") == 0)
-                f->debug = 1;
-            else if (strcmp(opt, "allow_root") == 0)
-                f->allow_root = 1;
-            else
-                fprintf(stderr, "fuse: warning: unknown option `%s'\n", opt);
-        }
-        free(xopts);
-    }
-    return 0;
+    return fuse_opt_match(fuse_ll_opts, opt);
 }
 
 static void fuse_ll_destroy(void *data)
@@ -983,8 +962,12 @@
         goto out;
     }
 
-    if (parse_ll_opts(f, opts) == -1)
-        goto out_free;
+    if (opts) {
+        const char *argv[] = { "", "-o", opts, NULL };
+        if (fuse_opt_parse(3, (char **) argv, f, fuse_ll_opts, NULL,
+                           NULL, NULL) == -1)
+            goto out_free;
+    }
 
     memcpy(&f->op, op, op_size);
     f->owner = getuid();
diff --git a/lib/fuse_opt.c b/lib/fuse_opt.c
new file mode 100644
index 0000000..f49e85d
--- /dev/null
+++ b/lib/fuse_opt.c
@@ -0,0 +1,363 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
+
+    This program can be distributed under the terms of the GNU LGPL.
+    See the file COPYING.LIB
+*/
+
+#include "fuse_opt.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+struct fuse_opt_context {
+    void *data;
+    const struct fuse_opt *opt;
+    fuse_opt_proc_t proc;
+    int argctr;
+    int argc;
+    char **argv;
+    int argcout;
+    char **argvout;
+    char *opts;
+    int nonopt;
+};
+
+void fuse_opt_free_args(char *args[])
+{
+    int i;
+
+    if (args) {
+        for (i = 0; args[i]; i++)
+            free(args[i]);
+        free(args);
+    }
+}
+
+static int alloc_failed(void)
+{
+    fprintf(stderr, "fuse: memory allocation failed\n");
+    return -1;
+}
+
+int fuse_opt_add_arg(int *argcp, char **argvp[], const char *arg)
+{
+    char **newargv = realloc(*argvp, (*argcp + 2) * sizeof(char *));
+    char *newarg = newargv ? strdup(arg) : NULL;
+    if (!newargv || !newarg)
+        return alloc_failed();
+
+    newargv[(*argcp)++] = newarg;
+    newargv[*argcp] = NULL;
+    *argvp = newargv;
+    return 0;
+}
+
+static int next_arg(struct fuse_opt_context *ctx, const char *opt)
+{
+    if (ctx->argctr + 1 >= ctx->argc) {
+        fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
+        return -1;
+    }
+    ctx->argctr++;
+    return 0;
+}
+
+static int add_arg(struct fuse_opt_context *ctx, const char *arg)
+{
+    return fuse_opt_add_arg(&ctx->argcout, &ctx->argvout, arg);
+}
+
+int fuse_opt_add_opt(char **opts, const char *opt)
+{
+    char *newopts;
+    if (!*opts)
+        newopts = strdup(opt);
+    else {
+        unsigned oldlen = strlen(*opts);
+        newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1);
+        if (newopts) {
+            newopts[oldlen] = ',';
+            strcpy(newopts + oldlen + 1, opt);
+        }
+    }
+    if (!newopts)
+        return alloc_failed();
+
+    *opts = newopts;
+    return 0;
+}
+
+static int add_opt(struct fuse_opt_context *ctx, const char *opt)
+{
+    return fuse_opt_add_opt(&ctx->opts, opt);
+}
+
+static int insert_arg(struct fuse_opt_context *ctx, int pos, const char *arg)
+{
+    assert(pos <= ctx->argcout);
+    if (add_arg(ctx, arg) == -1)
+        return -1;
+
+    if (pos != ctx->argcout - 1) {
+        char *newarg = ctx->argvout[ctx->argcout - 1];
+        memmove(&ctx->argvout[pos+1], &ctx->argvout[pos], 
+                sizeof(char *) * (ctx->argcout - pos - 1));
+        ctx->argvout[pos] = newarg;
+    }
+    return 0;
+}
+
+static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
+                     int iso)
+{
+    int res;
+
+    if (!ctx->proc) {
+        fprintf(stderr, "fuse: unknown option `%s'\n", arg);
+        return -1;
+    }
+
+    res = ctx->proc(ctx->data, arg, key);
+    if (res == -1 || !res)
+        return res;
+
+    if (iso)
+        return add_opt(ctx, arg);
+    else
+        return add_arg(ctx, arg);
+}
+
+static int match_template(const char *t, const char *arg, unsigned *sepp)
+{
+    int arglen = strlen(arg);
+    const char *sep = strchr(t, '=');
+    sep = sep ? sep : strchr(t, ' ');
+    if (sep && (!sep[1] || sep[1] == '%')) {
+        int tlen = sep - t;
+        if (sep[0] == '=')
+            tlen ++;
+        if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
+            *sepp = sep - t;
+            return 1;
+        }
+    }
+    if (strcmp(t, arg) == 0) {
+        *sepp = 0;
+        return 1;
+    }
+    return 0;
+}
+
+static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
+                                       const char *arg, unsigned *sepp)
+{
+    for (; opt && opt->template; opt++)
+        if (match_template(opt->template, arg, sepp))
+            return opt;
+    return NULL;
+}
+
+int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
+{
+    unsigned dummy;
+    return find_opt(opts, opt, &dummy) ? 1 : 0;
+}
+
+static int process_opt_param(void *var, const char *format, const char *param,
+                             const char *arg)
+{
+    assert(format[0] == '%');
+    if (format[1] == 's') {
+        char *copy = strdup(param);
+        if (!copy)
+            return alloc_failed();
+
+        *(char **) var = copy;
+    } else {
+        if (sscanf(param, format, var) != 1) {
+            fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int process_opt(struct fuse_opt_context *ctx,
+                       const struct fuse_opt *opt, unsigned sep,
+                       const char *arg, int iso)
+{
+    if (opt->offset == FUSE_OPT_OFFSET_KEY) {
+        if (call_proc(ctx, arg, opt->value, iso) == -1)
+            return -1;
+    } else {
+        void *var = ctx->data + opt->offset;
+        if (sep && opt->template[sep + 1]) {
+            const char *param = arg + sep;
+            if (opt->template[sep] == '=')
+                param ++;
+            if (process_opt_param(var, opt->template + sep + 1,
+                                  param, arg) == -1)
+                return -1;
+        } else
+            *(int *)var = opt->value;
+    }
+    return 0;
+}
+
+static int process_opt_sep_arg(struct fuse_opt_context *ctx,
+                               const struct fuse_opt *opt, unsigned sep,
+                               const char *arg, int iso)
+{
+    int res;
+    char *newarg;
+    char *param;
+
+    if (next_arg(ctx, arg) == -1)
+        return -1;
+
+    param = ctx->argv[ctx->argctr];
+    newarg = malloc(sep + strlen(param) + 1);
+    if (!newarg)
+        return alloc_failed();
+
+    memcpy(newarg, arg, sep);
+    strcpy(newarg + sep, param);
+    res = process_opt(ctx, opt, sep, newarg, iso);
+    free(newarg);
+
+    return res;
+}
+
+static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
+{
+    unsigned sep;
+    const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
+    if (opt) {
+        for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
+            int res;
+            if (sep && opt->template[sep] == ' ' && !arg[sep])
+                res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
+            else
+                res = process_opt(ctx, opt, sep, arg, iso);
+            if (res == -1)
+                return -1;
+        }
+        return 0;
+    } else
+        return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
+}
+
+static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
+{
+    char *sep;
+
+    do {
+        int res;
+        sep = strchr(opts, ',');
+        if (sep)
+            *sep = '\0';
+        res = process_gopt(ctx, opts, 1);
+        if (res == -1)
+            return -1;
+        opts = sep + 1;
+    } while (sep);
+
+    return 0;
+}
+
+static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
+{
+    int res;
+    char *copy;
+    const char *sep = strchr(opts, ',');
+    if (!sep)
+        return process_gopt(ctx, opts, 1);
+
+    copy = strdup(opts);
+    if (!copy) {
+        fprintf(stderr, "fuse: memory allocation failed\n");
+        return -1;
+    }
+    res = process_real_option_group(ctx, copy);
+    free(copy);
+    return res;
+}
+
+static int process_one(struct fuse_opt_context *ctx, const char *arg)
+{
+    if (ctx->nonopt || arg[0] != '-')
+        return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
+    else if (arg[1] == 'o') {
+        if (arg[2])
+            return process_option_group(ctx, arg + 2);
+        else {
+            if (next_arg(ctx, arg) == -1)
+                return -1;
+
+            return process_option_group(ctx, ctx->argv[ctx->argctr]);
+        }
+    } else if (arg[1] == '-' && !arg[2]) {
+        if (add_arg(ctx, arg) == -1)
+            return -1;
+        ctx->nonopt = 1;
+        return 0;
+    } else
+        return process_gopt(ctx, arg, 0);
+}
+
+static int opt_parse(struct fuse_opt_context *ctx)
+{
+    if (ctx->argc) {
+        if (add_arg(ctx, ctx->argv[0]) == -1)
+            return -1;
+    }
+
+    for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
+        if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
+            return -1;
+
+    if (ctx->opts) {
+        if (insert_arg(ctx, 1, "-o") == -1 ||
+            insert_arg(ctx, 2, ctx->opts) == -1)
+            return -1;
+    }
+    return 0;
+}
+
+int fuse_opt_parse(int argc, char *argv[], void *data,
+                   const struct fuse_opt opts[], fuse_opt_proc_t proc,
+                   int *argcout, char **argvout[])
+{
+    int res;
+    struct fuse_opt_context ctx = {
+        .argc = argv ? argc : *argcout,
+        .argv = argv ? argv : *argvout,
+        .data = data,
+        .opt = opts,
+        .proc = proc,
+        .argcout = 0,
+        .argvout = NULL,
+        .opts = NULL,
+        .nonopt = 0,
+    };
+
+    res = opt_parse(&ctx);
+    if (!argv)
+        fuse_opt_free_args(ctx.argv);
+    free(ctx.opts);
+    if (res == -1)
+        fuse_opt_free_args(ctx.argvout);
+    else {
+        if (argcout)
+            *argcout = ctx.argcout;
+        if (argvout)
+            *argvout = ctx.argvout;
+        else
+            fuse_opt_free_args(ctx.argvout);
+    }
+    return res;
+}
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index f3f4e21..e610b88 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -67,6 +67,11 @@
 		fuse_main_real_compat22;
 		fuse_new;
 		fuse_new_compat22;
+		fuse_opt_parse;
+		fuse_opt_add_opt;
+		fuse_opt_add_arg;
+		fuse_opt_free_args;
+		fuse_opt_match;
 		fuse_reply_create;
 		fuse_reply_open;
 		fuse_reply_open_compat;
diff --git a/lib/helper.c b/lib/helper.c
index bb8243c..1f99c2f 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -7,9 +7,11 @@
 */
 
 #include "fuse_i.h"
+#include "fuse_opt.h"
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
 #include <unistd.h>
 #include <string.h>
 #include <limits.h>
@@ -52,12 +54,6 @@
             );
 }
 
-static void invalid_option(const char *argv[], int argctr)
-{
-    fprintf(stderr, "fuse: invalid option: %s\n\n", argv[argctr]);
-    fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
-}
-
 static void exit_handler(int sig)
 {
     (void) sig;
@@ -99,187 +95,140 @@
     return 0;
 }
 
-static int opt_member(const char *opts, const char *opt)
+enum  {
+    KEY_HELP,
+    KEY_HELP_NOHEADER,
+    KEY_DEBUG,
+    KEY_KERN,
+    KEY_ALLOW_ROOT,
+    KEY_RO,
+};
+
+struct helper_opts {
+    const char *progname;
+    int singlethread;
+    int foreground;
+    int allow_other;
+    int allow_root;
+    int fsname;
+    char *kernel_opts;
+    char *lib_opts;
+    char *mountpoint;
+};
+
+#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
+#define FUSE_HELPER_KEY(t, k)    { t, FUSE_OPT_OFFSET_KEY, k }
+
+static const struct fuse_opt fuse_helper_opts[] = {
+    FUSE_HELPER_OPT("-d",                       foreground),
+    FUSE_HELPER_OPT("debug",                    foreground),
+    FUSE_HELPER_OPT("-f",			foreground),
+    FUSE_HELPER_OPT("-s",			singlethread),
+    FUSE_HELPER_OPT("allow_other",              allow_other),
+    FUSE_HELPER_OPT("allow_root",               allow_root),
+    FUSE_HELPER_OPT("fsname=",                  fsname),
+
+    FUSE_HELPER_KEY("-h",                       KEY_HELP),
+    FUSE_HELPER_KEY("--help",                   KEY_HELP),
+    FUSE_HELPER_KEY("-ho",                      KEY_HELP_NOHEADER),
+    FUSE_HELPER_KEY("-d",                       KEY_DEBUG),
+    FUSE_HELPER_KEY("debug",                    KEY_DEBUG),
+    FUSE_HELPER_KEY("allow_other",              KEY_KERN),
+    FUSE_HELPER_KEY("allow_root",               KEY_ALLOW_ROOT),
+    FUSE_HELPER_KEY("nonempty",                 KEY_KERN),
+    FUSE_HELPER_KEY("default_permissions",      KEY_KERN),
+    FUSE_HELPER_KEY("fsname=",                  KEY_KERN),
+    FUSE_HELPER_KEY("large_read",               KEY_KERN),
+    FUSE_HELPER_KEY("max_read=",                KEY_KERN),
+    FUSE_HELPER_KEY("-r",                       KEY_RO),
+    FUSE_HELPER_KEY("ro",                       KEY_KERN),
+    FUSE_HELPER_KEY("rw",                       KEY_KERN),
+    FUSE_HELPER_KEY("suid",                     KEY_KERN),
+    FUSE_HELPER_KEY("nosuid",                   KEY_KERN),
+    FUSE_HELPER_KEY("dev",                      KEY_KERN),
+    FUSE_HELPER_KEY("nodev",                    KEY_KERN),
+    FUSE_HELPER_KEY("exec",                     KEY_KERN),
+    FUSE_HELPER_KEY("noexec",                   KEY_KERN),
+    FUSE_HELPER_KEY("async",                    KEY_KERN),
+    FUSE_HELPER_KEY("sync",                     KEY_KERN),
+    FUSE_HELPER_KEY("atime",                    KEY_KERN),
+    FUSE_HELPER_KEY("noatime",                  KEY_KERN),
+    FUSE_OPT_END
+};
+
+static int fuse_helper_opt_proc(void *data, const char *arg, int key)
 {
-    const char *e, *s = opts;
-    int optlen = strlen(opt);
-    for (s = opts; s; s = e + 1) {
-        if(!(e = strchr(s, ',')))
+    struct helper_opts *hopts = data;
+
+    switch (key) {
+    case KEY_HELP:
+    case KEY_HELP_NOHEADER:
+        usage(key == KEY_HELP ? hopts->progname : NULL);
+        exit(1);
+
+    case FUSE_OPT_KEY_OPT:
+        return fuse_opt_add_opt(&hopts->lib_opts, arg);
+
+    case FUSE_OPT_KEY_NONOPT:
+        if (hopts->mountpoint)
             break;
-        if (e - s == optlen && strncmp(s, opt, optlen) == 0)
-            return 1;
+
+        return fuse_opt_add_opt(&hopts->mountpoint, arg);
+
+    case KEY_DEBUG:
+        return fuse_opt_add_opt(&hopts->lib_opts, "debug");
+
+    case KEY_ALLOW_ROOT:
+        if (fuse_opt_add_opt(&hopts->kernel_opts, "allow_other") == -1 ||
+            fuse_opt_add_opt(&hopts->lib_opts, "allow_root") == -1)
+            return -1;
+        return 0;
+
+    case KEY_RO:
+        arg = "ro";
+        /* fall through */
+
+    case KEY_KERN:
+        return fuse_opt_add_opt(&hopts->kernel_opts, arg);
     }
-    return (s && strcmp(s, opt) == 0);
+
+    fprintf(stderr, "fuse: invalid option `%s'\n", arg);
+    return -1;
 }
 
-static int add_option_to(const char *opt, char **optp)
+static int fuse_parse_cmdline(int argc, const char *argv[],
+                              struct helper_opts *hopts)
 {
-    unsigned len = strlen(opt);
-    if (*optp) {
-        unsigned oldlen = strlen(*optp);
-        *optp = (char *) realloc(*optp, oldlen + 1 + len + 1);
-        if (*optp == NULL)
-            return -1;
-        (*optp)[oldlen] = ',';
-        strcpy(*optp + oldlen + 1, opt);
-    } else {
-        *optp = (char *) malloc(len + 1);
-        if (*optp == NULL)
-            return -1;
-        strcpy(*optp, opt);
-    }
-    return 0;
-}
+    int res;
 
-static int add_options(char **lib_optp, char **kernel_optp, const char *opts)
-{
-    char *xopts = strdup(opts);
-    char *s = xopts;
-    char *opt;
-    int has_allow_other = 0;
-    int has_allow_root = 0;
-
-    if (xopts == NULL) {
-        fprintf(stderr, "fuse: memory allocation failed\n");
+    hopts->progname = argv[0];
+    res = fuse_opt_parse(argc, (char **) argv, hopts, fuse_helper_opts,
+                         fuse_helper_opt_proc, NULL, NULL);
+    if (res == -1)
         return -1;
-    }
 
-    while((opt = strsep(&s, ",")) != NULL) {
-        int res;
-        if (fuse_is_lib_option(opt)) {
-            res = add_option_to(opt, lib_optp);
-            /* Compatibility hack */
-            if (strcmp(opt, "allow_root") == 0 && res != -1) {
-                has_allow_root = 1;
-                res = add_option_to("allow_other", kernel_optp);
-            }
-        }
-        else {
-            res = add_option_to(opt, kernel_optp);
-            if (strcmp(opt, "allow_other") == 0)
-                has_allow_other = 1;
-        }
-        if (res == -1) {
-            fprintf(stderr, "fuse: memory allocation failed\n");
-            return -1;
-        }
-    }
-    if (has_allow_other && has_allow_root) {
+    if (hopts->allow_other && hopts->allow_root) {
         fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
         return -1;
     }
-    free(xopts);
-    return 0;
-}
 
-static int fuse_parse_cmdline(int argc, const char *argv[], char **kernel_opts,
-                              char **lib_opts, char **mountpoint,
-                              int *multithreaded, int *background)
-{
-    int res;
-    int argctr;
-    const char *basename;
-    char *fsname_opt;
+    if (!hopts->fsname) {
+        char *fsname_opt;
+        const char *basename = strrchr(argv[0], '/');
+        if (basename == NULL)
+            basename = argv[0];
+        else if (basename[1] != '\0')
+            basename++;
 
-    *kernel_opts = NULL;
-    *lib_opts = NULL;
-    *mountpoint = NULL;
-    *multithreaded = 1;
-    *background = 1;
-
-    basename = strrchr(argv[0], '/');
-    if (basename == NULL)
-        basename = argv[0];
-    else if (basename[1] != '\0')
-        basename++;
-
-    fsname_opt = (char *) malloc(strlen(basename) + 64);
-    if (fsname_opt == NULL) {
-        fprintf(stderr, "fuse: memory allocation failed\n");
-        return -1;
-    }
-    sprintf(fsname_opt, "fsname=%s", basename);
-    res = add_options(lib_opts, kernel_opts, fsname_opt);
-    free(fsname_opt);
-    if (res == -1)
-        goto err;
-
-    for (argctr = 1; argctr < argc; argctr ++) {
-        if (argv[argctr][0] == '-') {
-            if (strlen(argv[argctr]) == 2)
-                switch (argv[argctr][1]) {
-                case 'o':
-                    if (argctr + 1 == argc || argv[argctr+1][0] == '-') {
-                        fprintf(stderr, "missing option after -o\n");
-                        fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
-                        goto err;
-                    }
-                    argctr ++;
-                    res = add_options(lib_opts, kernel_opts, argv[argctr]);
-                    if (res == -1)
-                        goto err;
-                    break;
-
-                case 'd':
-                    res = add_options(lib_opts, kernel_opts, "debug");
-                    if (res == -1)
-                        goto err;
-                    break;
-
-                case 'r':
-                    res = add_options(lib_opts, kernel_opts, "ro");
-                    if (res == -1)
-                        goto err;
-                    break;
-
-                case 'f':
-                    *background = 0;
-                    break;
-
-                case 's':
-                    *multithreaded = 0;
-                    break;
-
-                case 'h':
-                    usage(argv[0]);
-                    goto err;
-
-                default:
-                    invalid_option(argv, argctr);
-                    goto err;
-                }
-            else {
-                if (argv[argctr][1] == 'o') {
-                    res = add_options(lib_opts, kernel_opts, &argv[argctr][2]);
-                    if (res == -1)
-                        goto err;
-                } else if(strcmp(argv[argctr], "-ho") == 0) {
-                    usage(NULL);
-                    goto err;
-                } else {
-                    invalid_option(argv, argctr);
-                    goto err;
-                }
-            }
-        } else if (*mountpoint == NULL) {
-            *mountpoint = strdup(argv[argctr]);
-            if (*mountpoint == NULL) {
-                fprintf(stderr, "fuse: memory allocation failed\n");
-                goto err;
-            }
+        fsname_opt = (char *) malloc(strlen(basename) + 64);
+        if (fsname_opt == NULL) {
+            fprintf(stderr, "fuse: memory allocation failed\n");
+            return -1;
         }
-        else {
-            invalid_option(argv, argctr);
-            goto err;
-        }
+        sprintf(fsname_opt, "fsname=%s", basename);
+        fuse_opt_add_opt(&hopts->kernel_opts, fsname_opt);
     }
     return 0;
-
- err:
-    free(*kernel_opts);
-    free(*lib_opts);
-    free(*mountpoint);
-    return -1;
 }
 
 static struct fuse *fuse_setup_common(int argc, char *argv[],
@@ -291,9 +240,7 @@
                                       int compat)
 {
     struct fuse *fuse;
-    int background;
-    char *kernel_opts;
-    char *lib_opts;
+    struct helper_opts hopts;
     int res;
 
     if (fuse_instance != NULL) {
@@ -301,21 +248,20 @@
         return NULL;
     }
 
-    res = fuse_parse_cmdline(argc, (const char **) argv, &kernel_opts,
-                             &lib_opts, mountpoint, multithreaded,
-                             &background);
+    memset(&hopts, 0, sizeof(hopts));
+    res = fuse_parse_cmdline(argc, (const char **) argv, &hopts);
     if (res == -1)
-        return NULL;
+        goto err_free;
 
-    *fd = fuse_mount(*mountpoint, kernel_opts);
+    *fd = fuse_mount(hopts.mountpoint, hopts.kernel_opts);
     if (*fd == -1)
         goto err_free;
 
-    fuse = fuse_new_common(*fd, lib_opts, op, op_size, compat);
+    fuse = fuse_new_common(*fd, hopts.lib_opts, op, op_size, compat);
     if (fuse == NULL)
         goto err_unmount;
 
-    if (background && !opt_member(lib_opts, "debug")) {
+    if (!hopts.foreground) {
         res = daemon(0, 0);
         if (res == -1) {
             perror("fuse: failed to daemonize program\n");
@@ -327,19 +273,21 @@
     if (res == -1)
         goto err_destroy;
 
+    *mountpoint = hopts.mountpoint;
+    *multithreaded = !hopts.singlethread;
     fuse_instance = fuse;
-    free(kernel_opts);
-    free(lib_opts);
+    free(hopts.kernel_opts);
+    free(hopts.lib_opts);
     return fuse;
 
  err_destroy:
     fuse_destroy(fuse);
  err_unmount:
-    fuse_unmount(*mountpoint);
+    fuse_unmount(hopts.mountpoint);
  err_free:
-    free(kernel_opts);
-    free(lib_opts);
-    free(*mountpoint);
+    free(hopts.mountpoint);
+    free(hopts.kernel_opts);
+    free(hopts.lib_opts);
     return NULL;
 }