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"
18 struct fuse_opt_context
{
20 const struct fuse_opt
*opt
;
25 struct fuse_args outargs
;
30 void fuse_opt_free_args(struct fuse_args
*args
)
33 if (args
->argv
&& args
->allocated
) {
35 for (i
= 0; i
< args
->argc
; i
++) {
46 static int alloc_failed(void)
48 fuse_log(FUSE_LOG_ERR
, "fuse: memory allocation failed\n");
52 int fuse_opt_add_arg(struct fuse_args
*args
, const char *arg
)
57 assert(!args
->argv
|| args
->allocated
);
61 return alloc_failed();
64 newargv
= realloc(args
->argv
, (args
->argc
+ 2) * sizeof(char *));
67 return alloc_failed();
72 args
->argv
[args
->argc
++] = newarg
;
73 args
->argv
[args
->argc
] = NULL
;
77 static int fuse_opt_insert_arg_common(struct fuse_args
*args
, int pos
,
80 assert(pos
<= args
->argc
);
81 if (fuse_opt_add_arg(args
, arg
) == -1) {
85 if (pos
!= args
->argc
- 1) {
86 char *newarg
= args
->argv
[args
->argc
- 1];
87 memmove(&args
->argv
[pos
+ 1], &args
->argv
[pos
],
88 sizeof(char *) * (args
->argc
- pos
- 1));
89 args
->argv
[pos
] = newarg
;
94 int fuse_opt_insert_arg(struct fuse_args
*args
, int pos
, const char *arg
)
96 return fuse_opt_insert_arg_common(args
, pos
, arg
);
99 static int next_arg(struct fuse_opt_context
*ctx
, const char *opt
)
101 if (ctx
->argctr
+ 1 >= ctx
->argc
) {
102 fuse_log(FUSE_LOG_ERR
, "fuse: missing argument after `%s'\n", opt
);
109 static int add_arg(struct fuse_opt_context
*ctx
, const char *arg
)
111 return fuse_opt_add_arg(&ctx
->outargs
, arg
);
114 static int add_opt_common(char **opts
, const char *opt
, int esc
)
116 unsigned oldlen
= *opts
? strlen(*opts
) : 0;
117 char *d
= realloc(*opts
, oldlen
+ 1 + strlen(opt
) * 2 + 1);
120 return alloc_failed();
129 for (; *opt
; opt
++) {
130 if (esc
&& (*opt
== ',' || *opt
== '\\')) {
140 int fuse_opt_add_opt(char **opts
, const char *opt
)
142 return add_opt_common(opts
, opt
, 0);
145 int fuse_opt_add_opt_escaped(char **opts
, const char *opt
)
147 return add_opt_common(opts
, opt
, 1);
150 static int add_opt(struct fuse_opt_context
*ctx
, const char *opt
)
152 return add_opt_common(&ctx
->opts
, opt
, 1);
155 static int call_proc(struct fuse_opt_context
*ctx
, const char *arg
, int key
,
158 if (key
== FUSE_OPT_KEY_DISCARD
) {
162 if (key
!= FUSE_OPT_KEY_KEEP
&& ctx
->proc
) {
163 int res
= ctx
->proc(ctx
->data
, arg
, key
, &ctx
->outargs
);
164 if (res
== -1 || !res
) {
169 return add_opt(ctx
, arg
);
171 return add_arg(ctx
, arg
);
175 static int match_template(const char *t
, const char *arg
, unsigned *sepp
)
177 int arglen
= strlen(arg
);
178 const char *sep
= strchr(t
, '=');
179 sep
= sep
? sep
: strchr(t
, ' ');
180 if (sep
&& (!sep
[1] || sep
[1] == '%')) {
185 if (arglen
>= tlen
&& strncmp(arg
, t
, tlen
) == 0) {
190 if (strcmp(t
, arg
) == 0) {
197 static const struct fuse_opt
*find_opt(const struct fuse_opt
*opt
,
198 const char *arg
, unsigned *sepp
)
200 for (; opt
&& opt
->templ
; opt
++) {
201 if (match_template(opt
->templ
, arg
, sepp
)) {
208 int fuse_opt_match(const struct fuse_opt
*opts
, const char *opt
)
211 return find_opt(opts
, opt
, &dummy
) ? 1 : 0;
214 static int process_opt_param(void *var
, const char *format
, const char *param
,
217 assert(format
[0] == '%');
218 if (format
[1] == 's') {
220 char *copy
= strdup(param
);
222 return alloc_failed();
228 if (sscanf(param
, format
, var
) != 1) {
229 fuse_log(FUSE_LOG_ERR
, "fuse: invalid parameter in option `%s'\n",
237 static int process_opt(struct fuse_opt_context
*ctx
, const struct fuse_opt
*opt
,
238 unsigned sep
, const char *arg
, int iso
)
240 if (opt
->offset
== -1U) {
241 if (call_proc(ctx
, arg
, opt
->value
, iso
) == -1) {
245 void *var
= (char *)ctx
->data
+ opt
->offset
;
246 if (sep
&& opt
->templ
[sep
+ 1]) {
247 const char *param
= arg
+ sep
;
248 if (opt
->templ
[sep
] == '=') {
251 if (process_opt_param(var
, opt
->templ
+ sep
+ 1, param
, arg
) ==
256 *(int *)var
= opt
->value
;
262 static int process_opt_sep_arg(struct fuse_opt_context
*ctx
,
263 const struct fuse_opt
*opt
, unsigned sep
,
264 const char *arg
, int iso
)
270 if (next_arg(ctx
, arg
) == -1) {
274 param
= ctx
->argv
[ctx
->argctr
];
275 newarg
= malloc(sep
+ strlen(param
) + 1);
277 return alloc_failed();
280 memcpy(newarg
, arg
, sep
);
281 strcpy(newarg
+ sep
, param
);
282 res
= process_opt(ctx
, opt
, sep
, newarg
, iso
);
288 static int process_gopt(struct fuse_opt_context
*ctx
, const char *arg
, int iso
)
291 const struct fuse_opt
*opt
= find_opt(ctx
->opt
, arg
, &sep
);
293 for (; opt
; opt
= find_opt(opt
+ 1, arg
, &sep
)) {
295 if (sep
&& opt
->templ
[sep
] == ' ' && !arg
[sep
]) {
296 res
= process_opt_sep_arg(ctx
, opt
, sep
, arg
, iso
);
298 res
= process_opt(ctx
, opt
, sep
, arg
, iso
);
306 return call_proc(ctx
, arg
, FUSE_OPT_KEY_OPT
, iso
);
310 static int process_real_option_group(struct fuse_opt_context
*ctx
, char *opts
)
320 if (*s
== ',' || end
) {
324 res
= process_gopt(ctx
, opts
, 1);
330 if (s
[0] == '\\' && s
[1] != '\0') {
332 if (s
[0] >= '0' && s
[0] <= '3' && s
[1] >= '0' && s
[1] <= '7' &&
333 s
[2] >= '0' && s
[2] <= '7') {
334 *d
++ = (s
[0] - '0') * 0100 + (s
[1] - '0') * 0010 +
350 static int process_option_group(struct fuse_opt_context
*ctx
, const char *opts
)
353 char *copy
= strdup(opts
);
356 fuse_log(FUSE_LOG_ERR
, "fuse: memory allocation failed\n");
359 res
= process_real_option_group(ctx
, copy
);
364 static int process_one(struct fuse_opt_context
*ctx
, const char *arg
)
366 if (ctx
->nonopt
|| arg
[0] != '-') {
367 return call_proc(ctx
, arg
, FUSE_OPT_KEY_NONOPT
, 0);
368 } else if (arg
[1] == 'o') {
370 return process_option_group(ctx
, arg
+ 2);
372 if (next_arg(ctx
, arg
) == -1) {
376 return process_option_group(ctx
, ctx
->argv
[ctx
->argctr
]);
378 } else if (arg
[1] == '-' && !arg
[2]) {
379 if (add_arg(ctx
, arg
) == -1) {
382 ctx
->nonopt
= ctx
->outargs
.argc
;
385 return process_gopt(ctx
, arg
, 0);
389 static int opt_parse(struct fuse_opt_context
*ctx
)
392 if (add_arg(ctx
, ctx
->argv
[0]) == -1) {
397 for (ctx
->argctr
= 1; ctx
->argctr
< ctx
->argc
; ctx
->argctr
++) {
398 if (process_one(ctx
, ctx
->argv
[ctx
->argctr
]) == -1) {
404 if (fuse_opt_insert_arg(&ctx
->outargs
, 1, "-o") == -1 ||
405 fuse_opt_insert_arg(&ctx
->outargs
, 2, ctx
->opts
) == -1) {
410 /* If option separator ("--") is the last argument, remove it */
411 if (ctx
->nonopt
&& ctx
->nonopt
== ctx
->outargs
.argc
&&
412 strcmp(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1], "--") == 0) {
413 free(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1]);
414 ctx
->outargs
.argv
[--ctx
->outargs
.argc
] = NULL
;
420 int fuse_opt_parse(struct fuse_args
*args
, void *data
,
421 const struct fuse_opt opts
[], fuse_opt_proc_t proc
)
424 struct fuse_opt_context ctx
= {
430 if (!args
|| !args
->argv
|| !args
->argc
) {
434 ctx
.argc
= args
->argc
;
435 ctx
.argv
= args
->argv
;
437 res
= opt_parse(&ctx
);
439 struct fuse_args tmp
= *args
;
444 fuse_opt_free_args(&ctx
.outargs
);