2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 * Implementation of option parsing routines (dealing with `struct
8 * This program can be distributed under the terms of the GNU LGPLv2.
9 * See the file COPYING.LIB
12 #include "qemu/osdep.h"
15 #include "fuse_misc.h"
22 struct fuse_opt_context
{
24 const struct fuse_opt
*opt
;
29 struct fuse_args outargs
;
34 void fuse_opt_free_args(struct fuse_args
*args
)
37 if (args
->argv
&& args
->allocated
) {
39 for (i
= 0; i
< args
->argc
; i
++) {
50 static int alloc_failed(void)
52 fuse_log(FUSE_LOG_ERR
, "fuse: memory allocation failed\n");
56 int fuse_opt_add_arg(struct fuse_args
*args
, const char *arg
)
61 assert(!args
->argv
|| args
->allocated
);
65 return alloc_failed();
68 newargv
= realloc(args
->argv
, (args
->argc
+ 2) * sizeof(char *));
71 return alloc_failed();
76 args
->argv
[args
->argc
++] = newarg
;
77 args
->argv
[args
->argc
] = NULL
;
81 static int fuse_opt_insert_arg_common(struct fuse_args
*args
, int pos
,
84 assert(pos
<= args
->argc
);
85 if (fuse_opt_add_arg(args
, arg
) == -1) {
89 if (pos
!= args
->argc
- 1) {
90 char *newarg
= args
->argv
[args
->argc
- 1];
91 memmove(&args
->argv
[pos
+ 1], &args
->argv
[pos
],
92 sizeof(char *) * (args
->argc
- pos
- 1));
93 args
->argv
[pos
] = newarg
;
98 int fuse_opt_insert_arg(struct fuse_args
*args
, int pos
, const char *arg
)
100 return fuse_opt_insert_arg_common(args
, pos
, arg
);
103 static int next_arg(struct fuse_opt_context
*ctx
, const char *opt
)
105 if (ctx
->argctr
+ 1 >= ctx
->argc
) {
106 fuse_log(FUSE_LOG_ERR
, "fuse: missing argument after `%s'\n", opt
);
113 static int add_arg(struct fuse_opt_context
*ctx
, const char *arg
)
115 return fuse_opt_add_arg(&ctx
->outargs
, arg
);
118 static int add_opt_common(char **opts
, const char *opt
, int esc
)
120 unsigned oldlen
= *opts
? strlen(*opts
) : 0;
121 char *d
= realloc(*opts
, oldlen
+ 1 + strlen(opt
) * 2 + 1);
124 return alloc_failed();
133 for (; *opt
; opt
++) {
134 if (esc
&& (*opt
== ',' || *opt
== '\\')) {
144 int fuse_opt_add_opt(char **opts
, const char *opt
)
146 return add_opt_common(opts
, opt
, 0);
149 int fuse_opt_add_opt_escaped(char **opts
, const char *opt
)
151 return add_opt_common(opts
, opt
, 1);
154 static int add_opt(struct fuse_opt_context
*ctx
, const char *opt
)
156 return add_opt_common(&ctx
->opts
, opt
, 1);
159 static int call_proc(struct fuse_opt_context
*ctx
, const char *arg
, int key
,
162 if (key
== FUSE_OPT_KEY_DISCARD
) {
166 if (key
!= FUSE_OPT_KEY_KEEP
&& ctx
->proc
) {
167 int res
= ctx
->proc(ctx
->data
, arg
, key
, &ctx
->outargs
);
168 if (res
== -1 || !res
) {
173 return add_opt(ctx
, arg
);
175 return add_arg(ctx
, arg
);
179 static int match_template(const char *t
, const char *arg
, unsigned *sepp
)
181 int arglen
= strlen(arg
);
182 const char *sep
= strchr(t
, '=');
183 sep
= sep
? sep
: strchr(t
, ' ');
184 if (sep
&& (!sep
[1] || sep
[1] == '%')) {
189 if (arglen
>= tlen
&& strncmp(arg
, t
, tlen
) == 0) {
194 if (strcmp(t
, arg
) == 0) {
201 static const struct fuse_opt
*find_opt(const struct fuse_opt
*opt
,
202 const char *arg
, unsigned *sepp
)
204 for (; opt
&& opt
->templ
; opt
++) {
205 if (match_template(opt
->templ
, arg
, sepp
)) {
212 int fuse_opt_match(const struct fuse_opt
*opts
, const char *opt
)
215 return find_opt(opts
, opt
, &dummy
) ? 1 : 0;
218 static int process_opt_param(void *var
, const char *format
, const char *param
,
221 assert(format
[0] == '%');
222 if (format
[1] == 's') {
224 char *copy
= strdup(param
);
226 return alloc_failed();
232 if (sscanf(param
, format
, var
) != 1) {
233 fuse_log(FUSE_LOG_ERR
, "fuse: invalid parameter in option `%s'\n",
241 static int process_opt(struct fuse_opt_context
*ctx
, const struct fuse_opt
*opt
,
242 unsigned sep
, const char *arg
, int iso
)
244 if (opt
->offset
== -1U) {
245 if (call_proc(ctx
, arg
, opt
->value
, iso
) == -1) {
249 void *var
= (char *)ctx
->data
+ opt
->offset
;
250 if (sep
&& opt
->templ
[sep
+ 1]) {
251 const char *param
= arg
+ sep
;
252 if (opt
->templ
[sep
] == '=') {
255 if (process_opt_param(var
, opt
->templ
+ sep
+ 1, param
, arg
) ==
260 *(int *)var
= opt
->value
;
266 static int process_opt_sep_arg(struct fuse_opt_context
*ctx
,
267 const struct fuse_opt
*opt
, unsigned sep
,
268 const char *arg
, int iso
)
274 if (next_arg(ctx
, arg
) == -1) {
278 param
= ctx
->argv
[ctx
->argctr
];
279 newarg
= malloc(sep
+ strlen(param
) + 1);
281 return alloc_failed();
284 memcpy(newarg
, arg
, sep
);
285 strcpy(newarg
+ sep
, param
);
286 res
= process_opt(ctx
, opt
, sep
, newarg
, iso
);
292 static int process_gopt(struct fuse_opt_context
*ctx
, const char *arg
, int iso
)
295 const struct fuse_opt
*opt
= find_opt(ctx
->opt
, arg
, &sep
);
297 for (; opt
; opt
= find_opt(opt
+ 1, arg
, &sep
)) {
299 if (sep
&& opt
->templ
[sep
] == ' ' && !arg
[sep
]) {
300 res
= process_opt_sep_arg(ctx
, opt
, sep
, arg
, iso
);
302 res
= process_opt(ctx
, opt
, sep
, arg
, iso
);
310 return call_proc(ctx
, arg
, FUSE_OPT_KEY_OPT
, iso
);
314 static int process_real_option_group(struct fuse_opt_context
*ctx
, char *opts
)
324 if (*s
== ',' || end
) {
328 res
= process_gopt(ctx
, opts
, 1);
334 if (s
[0] == '\\' && s
[1] != '\0') {
336 if (s
[0] >= '0' && s
[0] <= '3' && s
[1] >= '0' && s
[1] <= '7' &&
337 s
[2] >= '0' && s
[2] <= '7') {
338 *d
++ = (s
[0] - '0') * 0100 + (s
[1] - '0') * 0010 +
354 static int process_option_group(struct fuse_opt_context
*ctx
, const char *opts
)
357 char *copy
= strdup(opts
);
360 fuse_log(FUSE_LOG_ERR
, "fuse: memory allocation failed\n");
363 res
= process_real_option_group(ctx
, copy
);
368 static int process_one(struct fuse_opt_context
*ctx
, const char *arg
)
370 if (ctx
->nonopt
|| arg
[0] != '-') {
371 return call_proc(ctx
, arg
, FUSE_OPT_KEY_NONOPT
, 0);
372 } else if (arg
[1] == 'o') {
374 return process_option_group(ctx
, arg
+ 2);
376 if (next_arg(ctx
, arg
) == -1) {
380 return process_option_group(ctx
, ctx
->argv
[ctx
->argctr
]);
382 } else if (arg
[1] == '-' && !arg
[2]) {
383 if (add_arg(ctx
, arg
) == -1) {
386 ctx
->nonopt
= ctx
->outargs
.argc
;
389 return process_gopt(ctx
, arg
, 0);
393 static int opt_parse(struct fuse_opt_context
*ctx
)
396 if (add_arg(ctx
, ctx
->argv
[0]) == -1) {
401 for (ctx
->argctr
= 1; ctx
->argctr
< ctx
->argc
; ctx
->argctr
++) {
402 if (process_one(ctx
, ctx
->argv
[ctx
->argctr
]) == -1) {
408 if (fuse_opt_insert_arg(&ctx
->outargs
, 1, "-o") == -1 ||
409 fuse_opt_insert_arg(&ctx
->outargs
, 2, ctx
->opts
) == -1) {
414 /* If option separator ("--") is the last argument, remove it */
415 if (ctx
->nonopt
&& ctx
->nonopt
== ctx
->outargs
.argc
&&
416 strcmp(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1], "--") == 0) {
417 free(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1]);
418 ctx
->outargs
.argv
[--ctx
->outargs
.argc
] = NULL
;
424 int fuse_opt_parse(struct fuse_args
*args
, void *data
,
425 const struct fuse_opt opts
[], fuse_opt_proc_t proc
)
428 struct fuse_opt_context ctx
= {
434 if (!args
|| !args
->argv
|| !args
->argc
) {
438 ctx
.argc
= args
->argc
;
439 ctx
.argv
= args
->argv
;
441 res
= opt_parse(&ctx
);
443 struct fuse_args tmp
= *args
;
448 fuse_opt_free_args(&ctx
.outargs
);