xref: /netbsd-src/lib/librefuse/refuse_opt.c (revision c68139a4b6daa43e46772ed422785053da5037b9)
1*c68139a4Spho /* 	$NetBSD: refuse_opt.c,v 1.23 2022/01/22 07:58:32 pho Exp $	*/
2c7b91b59Sxtraeme 
3c7b91b59Sxtraeme /*-
4c7b91b59Sxtraeme  * Copyright (c) 2007 Juan Romero Pardines.
5c7b91b59Sxtraeme  * All rights reserved.
6c7b91b59Sxtraeme  *
7c7b91b59Sxtraeme  * Redistribution and use in source and binary forms, with or without
8c7b91b59Sxtraeme  * modification, are permitted provided that the following conditions
9c7b91b59Sxtraeme  * are met:
10c7b91b59Sxtraeme  * 1. Redistributions of source code must retain the above copyright
11c7b91b59Sxtraeme  *    notice, this list of conditions and the following disclaimer.
12c7b91b59Sxtraeme  * 2. Redistributions in binary form must reproduce the above copyright
13c7b91b59Sxtraeme  *    notice, this list of conditions and the following disclaimer in the
14c7b91b59Sxtraeme  *    documentation and/or other materials provided with the distribution.
15c7b91b59Sxtraeme  *
16c7b91b59Sxtraeme  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17c7b91b59Sxtraeme  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18c7b91b59Sxtraeme  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19c7b91b59Sxtraeme  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20c7b91b59Sxtraeme  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21c7b91b59Sxtraeme  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22c7b91b59Sxtraeme  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23c7b91b59Sxtraeme  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24c7b91b59Sxtraeme  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25c7b91b59Sxtraeme  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26c7b91b59Sxtraeme  */
27c7b91b59Sxtraeme 
2802a45a4aSpooka #include <sys/types.h>
2902a45a4aSpooka 
30c0df99a1Schristos #include <err.h>
311f6f9b83Spho #include <fuse_internal.h>
3202a45a4aSpooka #include <fuse_opt.h>
33111da591Spho #include <stdbool.h>
3402a45a4aSpooka #include <stdio.h>
3502a45a4aSpooka #include <stdlib.h>
3602a45a4aSpooka #include <string.h>
37c7b91b59Sxtraeme 
38c7b91b59Sxtraeme #ifdef FUSE_OPT_DEBUG
39bffaa1dfSagc #define DPRINTF(x)	do { printf x; } while ( /* CONSTCOND */ 0)
40c7b91b59Sxtraeme #else
41c7b91b59Sxtraeme #define DPRINTF(x)
42c7b91b59Sxtraeme #endif
43c7b91b59Sxtraeme 
44c7b91b59Sxtraeme /*
45c7b91b59Sxtraeme  * Public API.
46c7b91b59Sxtraeme  */
47c7b91b59Sxtraeme 
48950c7ae2Sagc /* ARGSUSED */
49c7b91b59Sxtraeme int
fuse_opt_add_arg(struct fuse_args * args,const char * arg)50c7b91b59Sxtraeme fuse_opt_add_arg(struct fuse_args *args, const char *arg)
51c7b91b59Sxtraeme {
52426de305Sagc 	struct fuse_args	*ap;
53426de305Sagc 
54426de305Sagc 	if (args->allocated == 0) {
5519468d62Schristos 		ap = fuse_opt_deep_copy_args(args->argc, args->argv);
56426de305Sagc 		args->argv = ap->argv;
57426de305Sagc 		args->argc = ap->argc;
58426de305Sagc 		args->allocated = ap->allocated;
59426de305Sagc 		(void) free(ap);
60426de305Sagc 	} else if (args->allocated == args->argc) {
61c0df99a1Schristos 		int na = args->allocated + 10;
62c0df99a1Schristos 
63*c68139a4Spho 		if (reallocarr(&args->argv, (size_t)na, sizeof(*args->argv)) != 0)
64c0df99a1Schristos 			return -1;
65c0df99a1Schristos 
66c0df99a1Schristos 		args->allocated = na;
67426de305Sagc 	}
68c7b91b59Sxtraeme 	DPRINTF(("%s: arguments passed: [arg:%s]\n", __func__, arg));
69c0df99a1Schristos 	if ((args->argv[args->argc++] = strdup(arg)) == NULL)
70c0df99a1Schristos 		err(1, "fuse_opt_add_arg");
7119468d62Schristos 	args->argv[args->argc] = NULL;
72426de305Sagc         return 0;
73c7b91b59Sxtraeme }
74c7b91b59Sxtraeme 
7519468d62Schristos struct fuse_args *
fuse_opt_deep_copy_args(int argc,char ** argv)7619468d62Schristos fuse_opt_deep_copy_args(int argc, char **argv)
77c7b91b59Sxtraeme {
7819468d62Schristos 	struct fuse_args	*ap;
796bb2880eSlukem 	int			 i;
8019468d62Schristos 
8119468d62Schristos 	if ((ap = malloc(sizeof(*ap))) == NULL)
8219468d62Schristos 		err(1, "_fuse_deep_copy_args");
8319468d62Schristos 	/* deep copy args structure into channel args */
8419468d62Schristos 	ap->allocated = ((argc / 10) + 1) * 10;
8519468d62Schristos 
8619468d62Schristos 	if ((ap->argv = calloc((size_t)ap->allocated,
8719468d62Schristos 	    sizeof(*ap->argv))) == NULL)
8819468d62Schristos 		err(1, "_fuse_deep_copy_args");
8919468d62Schristos 
9019468d62Schristos 	for (i = 0; i < argc; i++) {
9119468d62Schristos 		if ((ap->argv[i] = strdup(argv[i])) == NULL)
9219468d62Schristos 			err(1, "_fuse_deep_copy_args");
9319468d62Schristos 	}
9419468d62Schristos 	ap->argv[ap->argc = i] = NULL;
9519468d62Schristos 	return ap;
9619468d62Schristos }
9719468d62Schristos 
9819468d62Schristos void
fuse_opt_free_args(struct fuse_args * ap)9919468d62Schristos fuse_opt_free_args(struct fuse_args *ap)
10019468d62Schristos {
101fd862595Spho 	if (ap) {
102fd862595Spho 		if (ap->allocated) {
10319468d62Schristos 			int	i;
10419468d62Schristos 			for (i = 0; i < ap->argc; i++) {
10519468d62Schristos 				free(ap->argv[i]);
10619468d62Schristos 			}
10719468d62Schristos 			free(ap->argv);
108fd862595Spho 		}
10919468d62Schristos 		ap->argv = NULL;
11019468d62Schristos 		ap->allocated = ap->argc = 0;
111c7b91b59Sxtraeme 	}
112fd862595Spho }
113c7b91b59Sxtraeme 
114950c7ae2Sagc /* ARGSUSED */
115c7b91b59Sxtraeme int
fuse_opt_insert_arg(struct fuse_args * args,int pos,const char * arg)116c7b91b59Sxtraeme fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
117c7b91b59Sxtraeme {
118426de305Sagc 	int	i;
119c0df99a1Schristos 	int	na;
120426de305Sagc 
121950c7ae2Sagc 	DPRINTF(("%s: arguments passed: [pos=%d] [arg=%s]\n",
122950c7ae2Sagc 	    __func__, pos, arg));
123c0df99a1Schristos 	if (args->argv == NULL) {
124c0df99a1Schristos 		na = 10;
125c0df99a1Schristos 	} else {
126c0df99a1Schristos 		na = args->allocated + 10;
127c0df99a1Schristos 	}
128*c68139a4Spho 	if (reallocarr(&args->argv, (size_t)na, sizeof(*args->argv)) != 0) {
129c0df99a1Schristos 		warn("fuse_opt_insert_arg");
130c0df99a1Schristos 		return -1;
131c0df99a1Schristos 	}
132c0df99a1Schristos 	args->allocated = na;
133c0df99a1Schristos 
134426de305Sagc 	for (i = args->argc++; i > pos; --i) {
135426de305Sagc 		args->argv[i] = args->argv[i - 1];
136426de305Sagc 	}
137c0df99a1Schristos 	if ((args->argv[pos] = strdup(arg)) == NULL)
138c0df99a1Schristos 		err(1, "fuse_opt_insert_arg");
13919468d62Schristos 	args->argv[args->argc] = NULL;
140426de305Sagc 	return 0;
141c7b91b59Sxtraeme }
142c7b91b59Sxtraeme 
add_opt(char ** opts,const char * opt,bool escape)143111da591Spho static int add_opt(char **opts, const char *opt, bool escape)
144111da591Spho {
145111da591Spho 	const size_t orig_len = *opts == NULL ? 0 : strlen(*opts);
146111da591Spho 	char* buf = realloc(*opts, orig_len + 1 + strlen(opt) * 2 + 1);
147111da591Spho 
148111da591Spho 	if (buf == NULL) {
149111da591Spho 		return -1;
150111da591Spho 	}
151111da591Spho 	*opts = buf;
152111da591Spho 
153111da591Spho 	if (orig_len > 0) {
154111da591Spho 		buf += orig_len;
155111da591Spho 		*buf++ = ',';
156111da591Spho 	}
157111da591Spho 
158111da591Spho 	for (; *opt; opt++) {
159111da591Spho 		if (escape && (*opt == ',' || *opt == '\\')) {
160111da591Spho 			*buf++ = '\\';
161111da591Spho 		}
162111da591Spho 		*buf++ = *opt;
163111da591Spho 	}
164111da591Spho 	*buf = '\0';
165111da591Spho 
166111da591Spho 	return 0;
167111da591Spho }
168111da591Spho 
fuse_opt_add_opt(char ** opts,const char * opt)169c7b91b59Sxtraeme int fuse_opt_add_opt(char **opts, const char *opt)
170c7b91b59Sxtraeme {
171c7b91b59Sxtraeme 	DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n",
172c7b91b59Sxtraeme 	    __func__, *opts, opt));
173111da591Spho 	return add_opt(opts, opt, false);
174111da591Spho }
175111da591Spho 
fuse_opt_add_opt_escaped(char ** opts,const char * opt)176111da591Spho int fuse_opt_add_opt_escaped(char **opts, const char *opt)
177111da591Spho {
178111da591Spho 	DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n",
179111da591Spho 	    __func__, *opts, opt));
180111da591Spho 	return add_opt(opts, opt, true);
181c7b91b59Sxtraeme }
182c7b91b59Sxtraeme 
match_templ(const char * templ,const char * opt,ssize_t * sep_idx)183*c68139a4Spho static bool match_templ(const char *templ, const char *opt, ssize_t *sep_idx)
1845ae81a5bSpho {
1855ae81a5bSpho 	const char *sep = strpbrk(templ, "= ");
1865ae81a5bSpho 
1875ae81a5bSpho 	if (sep != NULL && (sep[1] == '\0' || sep[1] == '%')) {
1885ae81a5bSpho 		const size_t cmp_len =
189*c68139a4Spho 			(size_t)(sep[0] == '=' ? sep - templ + 1 : sep - templ);
1905ae81a5bSpho 
1915ae81a5bSpho 		if (strlen(opt) >= cmp_len && strncmp(templ, opt, cmp_len) == 0) {
1925ae81a5bSpho 			if (sep_idx != NULL)
193*c68139a4Spho 				*sep_idx = (ssize_t)(sep - templ);
1945ae81a5bSpho 			return true;
1955ae81a5bSpho 		}
1965ae81a5bSpho 		else {
1975ae81a5bSpho 			return false;
1985ae81a5bSpho 		}
1995ae81a5bSpho 	}
2005ae81a5bSpho 	else {
2015ae81a5bSpho 		if (strcmp(templ, opt) == 0) {
2025ae81a5bSpho 			if (sep_idx != NULL)
203fd862595Spho 				*sep_idx = -1;
2045ae81a5bSpho 			return true;
2055ae81a5bSpho 		}
2065ae81a5bSpho 		else {
2075ae81a5bSpho 			return false;
2085ae81a5bSpho 		}
2095ae81a5bSpho 	}
2105ae81a5bSpho }
2115ae81a5bSpho 
2125ae81a5bSpho static const struct fuse_opt *
find_opt(const struct fuse_opt * opts,const char * opt,ssize_t * sep_idx)213*c68139a4Spho find_opt(const struct fuse_opt *opts, const char *opt, ssize_t *sep_idx)
2145ae81a5bSpho {
2155ae81a5bSpho 	for (; opts != NULL && opts->templ != NULL; opts++) {
2165ae81a5bSpho 		if (match_templ(opts->templ, opt, sep_idx))
2175ae81a5bSpho 			return opts;
2185ae81a5bSpho 	}
2195ae81a5bSpho 	return NULL;
2205ae81a5bSpho }
2215ae81a5bSpho 
222c7b91b59Sxtraeme /*
2235ae81a5bSpho  * Returns 1 if opt was matched with any option from opts,
2245ae81a5bSpho  * otherwise returns 0.
225c7b91b59Sxtraeme  */
226c7b91b59Sxtraeme int
fuse_opt_match(const struct fuse_opt * opts,const char * opt)227c7b91b59Sxtraeme fuse_opt_match(const struct fuse_opt *opts, const char *opt)
228c7b91b59Sxtraeme {
2295ae81a5bSpho 	return find_opt(opts, opt, NULL) != NULL ? 1 : 0;
230c7b91b59Sxtraeme }
231c7b91b59Sxtraeme 
call_proc(fuse_opt_proc_t proc,void * data,const char * arg,int key,struct fuse_args * outargs,bool is_opt)232fd862595Spho static int call_proc(fuse_opt_proc_t proc, void* data,
233fd862595Spho 		const char* arg, int key, struct fuse_args *outargs, bool is_opt)
234c7b91b59Sxtraeme {
235fd862595Spho 	if (key == FUSE_OPT_KEY_DISCARD)
236fd862595Spho 		return 0;
237c7b91b59Sxtraeme 
238fd862595Spho 	if (key != FUSE_OPT_KEY_KEEP && proc != NULL) {
239fd862595Spho 		const int rv = proc(data, arg, key, outargs);
240fd862595Spho 
241fd862595Spho 		if (rv == -1 || /* error   */
242fd862595Spho 			rv ==  0    /* discard */)
243fd862595Spho 			return rv;
244c7b91b59Sxtraeme 	}
245c7b91b59Sxtraeme 
246fd862595Spho 	if (is_opt) {
247fd862595Spho 		/* Do we already have "-o" at the beginning of outargs? */
248fd862595Spho 		if (outargs->argc >= 3 && strcmp(outargs->argv[1], "-o") == 0) {
249fd862595Spho 			/* Append the option to the comma-separated list. */
250fd862595Spho 			if (fuse_opt_add_opt_escaped(&outargs->argv[2], arg) == -1)
251fd862595Spho 				return -1;
252fd862595Spho 		}
253fd862595Spho 		else {
254fd862595Spho 			/* Insert -o arg at the beginning. */
255fd862595Spho 			if (fuse_opt_insert_arg(outargs, 1, "-o") == -1)
256fd862595Spho 				return -1;
257fd862595Spho 			if (fuse_opt_insert_arg(outargs, 2, arg) == -1)
258fd862595Spho 				return -1;
259c7b91b59Sxtraeme 		}
260c7b91b59Sxtraeme 	}
261fd862595Spho 	else {
262fd862595Spho 		if (fuse_opt_add_arg(outargs, arg) == -1)
263fd862595Spho 			return -1;
264c7b91b59Sxtraeme 	}
265c7b91b59Sxtraeme 
266426de305Sagc 	return 0;
267c7b91b59Sxtraeme }
268c7b91b59Sxtraeme 
269fd862595Spho /* Skip the current argv if possible. */
next_arg(const struct fuse_args * args,int * i)270fd862595Spho static int next_arg(const struct fuse_args *args, int *i)
271fd862595Spho {
272fd862595Spho 	if (*i + 1 >= args->argc) {
273fd862595Spho 		(void)fprintf(stderr, "fuse: missing argument"
274fd862595Spho 				" after '%s'\n", args->argv[*i]);
275fd862595Spho 		return -1;
276fd862595Spho 	}
277fd862595Spho 	else {
278fd862595Spho 		*i += 1;
279fd862595Spho 		return 0;
280fd862595Spho 	}
281fd862595Spho }
282fd862595Spho 
283fd862595Spho /* Parse a single argument with a matched template. */
284fd862595Spho static int
parse_matched_arg(const char * arg,struct fuse_args * outargs,const struct fuse_opt * opt,ssize_t sep_idx,void * data,fuse_opt_proc_t proc,bool is_opt)285fd862595Spho parse_matched_arg(const char* arg, struct fuse_args *outargs,
286*c68139a4Spho 		const struct fuse_opt* opt, ssize_t sep_idx, void* data,
287fd862595Spho 		fuse_opt_proc_t proc, bool is_opt)
288fd862595Spho {
289f970914eSpho 	if (opt->offset == -1) {
290fd862595Spho 		/* The option description does not want any variables to be
291fd862595Spho 		 * updated.*/
292fd862595Spho 		if (call_proc(proc, data, arg, opt->value, outargs, is_opt) == -1)
293fd862595Spho 			return -1;
294fd862595Spho 	}
295fd862595Spho 	else {
296fd862595Spho 		void *var = (char*)data + opt->offset;
297fd862595Spho 
298fd862595Spho 		if (sep_idx > 0 && opt->templ[sep_idx + 1] == '%') {
299fd862595Spho 			/* "foo=%y" or "-x %y" */
300fd862595Spho 			const char* param =
301fd862595Spho 				opt->templ[sep_idx] == '=' ? &arg[sep_idx + 1] : &arg[sep_idx];
302fd862595Spho 
303fd862595Spho 			if (opt->templ[sep_idx + 2] == 's') {
304fd862595Spho 				char* dup = strdup(param);
305fd862595Spho 				if (dup == NULL)
306fd862595Spho 					return -1;
307fd862595Spho 
308fd862595Spho 				*(char **)var = dup;
309fd862595Spho 			}
310fd862595Spho 			else {
311fd862595Spho 				/* The format string is not a literal. We all know
312fd862595Spho 				 * this is a bad idea but it's exactly what fuse_opt
313fd862595Spho 				 * wants to do... */
314fd862595Spho #pragma GCC diagnostic push
315fd862595Spho #pragma GCC diagnostic ignored "-Wformat-nonliteral"
316fd862595Spho 				if (sscanf(param, &opt->templ[sep_idx + 1], var) == -1) {
317fd862595Spho #pragma GCC diagnostic pop
318fd862595Spho 					(void)fprintf(stderr, "fuse: '%s' is not a "
319fd862595Spho 								"valid parameter for option '%.*s'\n",
320*c68139a4Spho 								param, (int)sep_idx, opt->templ);
321fd862595Spho 					return -1;
322fd862595Spho 				}
323fd862595Spho 			}
324fd862595Spho 		}
325fd862595Spho 		else {
326fd862595Spho 			/* No parameter format is given. */
327fd862595Spho 			*(int *)var = opt->value;
328fd862595Spho 		}
329fd862595Spho 	}
330fd862595Spho 	return 0;
331fd862595Spho }
332fd862595Spho 
333fd862595Spho /* Parse a single argument with matching templates. */
334fd862595Spho 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)335fd862595Spho parse_arg(struct fuse_args* args, int *argi, const char* arg,
336fd862595Spho 		struct fuse_args *outargs, void *data,
337fd862595Spho 		const struct fuse_opt *opts, fuse_opt_proc_t proc, bool is_opt)
338fd862595Spho {
339*c68139a4Spho 	ssize_t sep_idx;
340fd862595Spho 	const struct fuse_opt *opt = find_opt(opts, arg, &sep_idx);
341fd862595Spho 
342fd862595Spho 	if (opt) {
343fd862595Spho 		/* An argument can match to multiple templates. Process them
344fd862595Spho 		 * all. */
345fd862595Spho 		for (; opt != NULL && opt->templ != NULL;
346fd862595Spho 			opt = find_opt(++opt, arg, &sep_idx)) {
347fd862595Spho 
348fd862595Spho 			if (sep_idx > 0 && opt->templ[sep_idx] == ' ' &&
349fd862595Spho 				arg[sep_idx] == '\0') {
350fd862595Spho 				/* The template "-x %y" requests a separate
351fd862595Spho 				 * parameter "%y". Try to find one. */
352fd862595Spho 				char *new_arg;
353fd862595Spho 				int rv;
354fd862595Spho 
355fd862595Spho 				if (next_arg(args, argi) == -1)
356fd862595Spho 					return -1;
357fd862595Spho 
358fd862595Spho 				/* ...but processor callbacks expect a concatenated
359fd862595Spho 				 * argument "-xfoo". */
360*c68139a4Spho 				if ((new_arg = malloc((size_t)sep_idx +
361fd862595Spho 									strlen(args->argv[*argi]) + 1)) == NULL)
362fd862595Spho 					return -1;
363fd862595Spho 
364*c68139a4Spho 				strncpy(new_arg, arg, (size_t)sep_idx); /* -x */
365fd862595Spho 				strcpy(new_arg + sep_idx, args->argv[*argi]); /* foo */
366fd862595Spho 				rv = parse_matched_arg(new_arg, outargs, opt, sep_idx,
367fd862595Spho 									data, proc, is_opt);
368fd862595Spho 				free(new_arg);
369fd862595Spho 
370fd862595Spho 				if (rv == -1)
371fd862595Spho 					return -1;
372fd862595Spho 			}
373fd862595Spho 			else {
374fd862595Spho 				int rv;
375fd862595Spho 				rv = parse_matched_arg(arg, outargs, opt, sep_idx,
376fd862595Spho 									data, proc, is_opt);
377fd862595Spho 				if (rv == -1)
378fd862595Spho 					return -1;
379fd862595Spho 			}
380fd862595Spho 		}
381fd862595Spho 		return 0;
382fd862595Spho 	}
383fd862595Spho 	else {
384fd862595Spho 		/* No templates matched to it so just invoke the callback. */
385fd862595Spho 		return call_proc(proc, data, arg, FUSE_OPT_KEY_OPT, outargs, is_opt);
386fd862595Spho 	}
387fd862595Spho }
388fd862595Spho 
389fd862595Spho /* Parse a comma-separated list of options which possibly has escaped
390fd862595Spho  * characters. */
391fd862595Spho 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)392fd862595Spho parse_opts(struct fuse_args *args, int *argi, const char* arg,
393fd862595Spho 		struct fuse_args *outargs, void *data,
394fd862595Spho 		const struct fuse_opt *opts, fuse_opt_proc_t proc)
395fd862595Spho {
396fd862595Spho 	char *opt;
397fd862595Spho 	size_t i, opt_len = 0;
398fd862595Spho 
399fd862595Spho 	/* An unescaped option can never be longer than the original
400fd862595Spho 	 * list. */
401fd862595Spho 	if ((opt = malloc(strlen(arg) + 1)) == NULL)
402fd862595Spho 		return -1;
403fd862595Spho 
404fd862595Spho 	for (i = 0; i < strlen(arg); i++) {
405fd862595Spho 		if (arg[i] == ',') {
406fd862595Spho 			opt[opt_len] = '\0';
407fd862595Spho 			if (parse_arg(args, argi, opt, outargs,
408fd862595Spho 						data, opts, proc, true) == -1) {
409fd862595Spho 				free(opt);
410fd862595Spho 				return -1;
411fd862595Spho 			}
412fd862595Spho 			/* Start a new option. */
413fd862595Spho 			opt_len = 0;
414fd862595Spho 		}
415fd862595Spho 		else if (arg[i] == '\\' && arg[i+1] != '\0') {
416fd862595Spho 			/* Unescape it. */
417fd862595Spho 			opt[opt_len++] = arg[i+1];
418fd862595Spho 			i++;
419fd862595Spho 		}
420fd862595Spho 		else {
421fd862595Spho 			opt[opt_len++] = arg[i];
422fd862595Spho 		}
423fd862595Spho 	}
424fd862595Spho 
425fd862595Spho 	/* Parse the last option here. */
426fd862595Spho 	opt[opt_len] = '\0';
427fd862595Spho 	if (parse_arg(args, argi, opt, outargs, data, opts, proc, true) == -1) {
428fd862595Spho 		free(opt);
429fd862595Spho 		return -1;
430fd862595Spho 	}
431fd862595Spho 
432fd862595Spho 	free(opt);
433fd862595Spho 	return 0;
434fd862595Spho }
435fd862595Spho 
436fd862595Spho static int
parse_all(struct fuse_args * args,struct fuse_args * outargs,void * data,const struct fuse_opt * opts,fuse_opt_proc_t proc)437fd862595Spho parse_all(struct fuse_args *args, struct fuse_args *outargs, void *data,
438fd862595Spho 		const struct fuse_opt *opts, fuse_opt_proc_t proc)
439fd862595Spho {
440fd862595Spho 	bool nonopt = false; /* Have we seen the "--" marker? */
441fd862595Spho 	int i;
442fd862595Spho 
443fd862595Spho 	/* The first argument, the program name, is implicitly
444fd862595Spho 	 * FUSE_OPT_KEY_KEEP. */
445fd862595Spho 	if (args->argc > 0) {
446fd862595Spho 		if (fuse_opt_add_arg(outargs, args->argv[0]) == -1)
447fd862595Spho 			return -1;
448fd862595Spho 	}
449fd862595Spho 
450fd862595Spho 	/* the real loop to process the arguments */
451fd862595Spho 	for (i = 1; i < args->argc; i++) {
452fd862595Spho 		const char *arg = args->argv[i];
453fd862595Spho 
454fd862595Spho 		/* argvn != -foo... */
455fd862595Spho 		if (nonopt || arg[0] != '-') {
456fd862595Spho 			if (call_proc(proc, data, arg, FUSE_OPT_KEY_NONOPT,
457fd862595Spho 						outargs, false) == -1)
458fd862595Spho 				return -1;
459fd862595Spho 		}
460fd862595Spho 		/* -o or -ofoo */
461fd862595Spho 		else if (arg[1] == 'o') {
462fd862595Spho 			/* -oblah,foo... */
463fd862595Spho 			if (arg[2] != '\0') {
464fd862595Spho 				/* skip -o */
465fd862595Spho 				if (parse_opts(args, &i, arg + 2, outargs,
466fd862595Spho 							data, opts, proc) == -1)
467fd862595Spho 					return -1;
468fd862595Spho 			}
469fd862595Spho 			/* -o blah,foo... */
470fd862595Spho 			else {
471fd862595Spho 				if (next_arg(args, &i) == -1)
472fd862595Spho 					return -1;
473fd862595Spho 				if (parse_opts(args, &i, args->argv[i], outargs,
474fd862595Spho 							data, opts, proc) == -1)
475fd862595Spho 					return -1;
476fd862595Spho 			}
477fd862595Spho 		}
478fd862595Spho 		/* -- */
479fd862595Spho 		else if (arg[1] == '-' && arg[2] == '\0') {
480fd862595Spho 			if (fuse_opt_add_arg(outargs, arg) == -1)
481fd862595Spho 				return -1;
482fd862595Spho 			nonopt = true;
483fd862595Spho 		}
484fd862595Spho 		/* -foo */
485fd862595Spho 		else {
486fd862595Spho 			if (parse_arg(args, &i, arg, outargs,
487fd862595Spho 						data, opts, proc, false) == -1)
488fd862595Spho 				return -1;
489fd862595Spho 		}
490fd862595Spho 	}
491fd862595Spho 
492fd862595Spho 	/* The "--" marker at the last of outargs should be removed */
493fd862595Spho 	if (nonopt && strcmp(outargs->argv[outargs->argc - 1], "--") == 0) {
494fd862595Spho 		free(outargs->argv[outargs->argc - 1]);
495fd862595Spho 		outargs->argv[--outargs->argc] = NULL;
496fd862595Spho 	}
497fd862595Spho 
498fd862595Spho 	return 0;
499fd862595Spho }
500fd862595Spho 
501c7b91b59Sxtraeme int
fuse_opt_parse(struct fuse_args * args,void * data,const struct fuse_opt * opts,fuse_opt_proc_t proc)502c7b91b59Sxtraeme fuse_opt_parse(struct fuse_args *args, void *data,
503c7b91b59Sxtraeme 		const struct fuse_opt *opts, fuse_opt_proc_t proc)
504c7b91b59Sxtraeme {
505fd862595Spho 	struct fuse_args outargs = FUSE_ARGS_INIT(0, NULL);
506fd862595Spho 	int rv;
507c7b91b59Sxtraeme 
508fd862595Spho 	if (!args || !args->argv || !args->argc)
509c7b91b59Sxtraeme 		return 0;
510c7b91b59Sxtraeme 
511fd862595Spho 	rv = parse_all(args, &outargs, data, opts, proc);
512fd862595Spho 	if (rv != -1) {
513fd862595Spho 		/* Succeeded. Swap the outargs and args. */
514fd862595Spho 		struct fuse_args tmp = *args;
515fd862595Spho 		*args = outargs;
516fd862595Spho 		outargs = tmp;
517c7b91b59Sxtraeme 	}
518c7b91b59Sxtraeme 
519fd862595Spho 	fuse_opt_free_args(&outargs);
520c7b91b59Sxtraeme 	return rv;
521c7b91b59Sxtraeme }
522