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