2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB
10 #include "fuse_misc.h"
17 struct fuse_opt_context
{
19 const struct fuse_opt
*opt
;
24 struct fuse_args outargs
;
29 void fuse_opt_free_args(struct fuse_args
*args
)
32 if (args
->argv
&& args
->allocated
) {
34 for (i
= 0; i
< args
->argc
; i
++)
44 static int alloc_failed(void)
46 fprintf(stderr
, "fuse: memory allocation failed\n");
50 int fuse_opt_add_arg(struct fuse_args
*args
, const char *arg
)
55 assert(!args
->argv
|| args
->allocated
);
59 return alloc_failed();
61 newargv
= realloc(args
->argv
, (args
->argc
+ 2) * sizeof(char *));
64 return alloc_failed();
69 args
->argv
[args
->argc
++] = newarg
;
70 args
->argv
[args
->argc
] = NULL
;
74 static int fuse_opt_insert_arg_common(struct fuse_args
*args
, int pos
,
77 assert(pos
<= args
->argc
);
78 if (fuse_opt_add_arg(args
, arg
) == -1)
81 if (pos
!= args
->argc
- 1) {
82 char *newarg
= args
->argv
[args
->argc
- 1];
83 memmove(&args
->argv
[pos
+ 1], &args
->argv
[pos
],
84 sizeof(char *) * (args
->argc
- pos
- 1));
85 args
->argv
[pos
] = newarg
;
90 int fuse_opt_insert_arg(struct fuse_args
*args
, int pos
, const char *arg
)
92 return fuse_opt_insert_arg_common(args
, pos
, arg
);
95 int fuse_opt_insert_arg_compat(struct fuse_args
*args
, int pos
,
97 int fuse_opt_insert_arg_compat(struct fuse_args
*args
, int pos
, const char *arg
)
99 return fuse_opt_insert_arg_common(args
, pos
, arg
);
102 static int next_arg(struct fuse_opt_context
*ctx
, const char *opt
)
104 if (ctx
->argctr
+ 1 >= ctx
->argc
) {
105 fprintf(stderr
, "fuse: missing argument after `%s'\n", opt
);
112 static int add_arg(struct fuse_opt_context
*ctx
, const char *arg
)
114 return fuse_opt_add_arg(&ctx
->outargs
, arg
);
117 static int add_opt_common(char **opts
, const char *opt
, int esc
)
119 unsigned oldlen
= *opts
? strlen(*opts
) : 0;
120 char *d
= realloc(*opts
, oldlen
+ 1 + strlen(opt
) * 2 + 1);
123 return alloc_failed();
131 for (; *opt
; opt
++) {
132 if (esc
&& (*opt
== ',' || *opt
== '\\'))
141 int fuse_opt_add_opt(char **opts
, const char *opt
)
143 return add_opt_common(opts
, opt
, 0);
146 int fuse_opt_add_opt_escaped(char **opts
, const char *opt
)
148 return add_opt_common(opts
, opt
, 1);
151 static int add_opt(struct fuse_opt_context
*ctx
, const char *opt
)
153 return add_opt_common(&ctx
->opts
, opt
, 1);
156 static int call_proc(struct fuse_opt_context
*ctx
, const char *arg
, int key
,
159 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
)
168 return add_opt(ctx
, arg
);
170 return add_arg(ctx
, arg
);
173 static int match_template(const char *t
, const char *arg
, unsigned *sepp
)
175 int arglen
= strlen(arg
);
176 const char *sep
= strchr(t
, '=');
177 sep
= sep
? sep
: strchr(t
, ' ');
178 if (sep
&& (!sep
[1] || sep
[1] == '%')) {
182 if (arglen
>= tlen
&& strncmp(arg
, t
, tlen
) == 0) {
187 if (strcmp(t
, arg
) == 0) {
194 static const struct fuse_opt
*find_opt(const struct fuse_opt
*opt
,
195 const char *arg
, unsigned *sepp
)
197 for (; opt
&& opt
->templ
; opt
++)
198 if (match_template(opt
->templ
, arg
, sepp
))
203 int fuse_opt_match(const struct fuse_opt
*opts
, const char *opt
)
206 return find_opt(opts
, opt
, &dummy
) ? 1 : 0;
209 static int process_opt_param(void *var
, const char *format
, const char *param
,
212 assert(format
[0] == '%');
213 if (format
[1] == 's') {
214 char *copy
= strdup(param
);
216 return alloc_failed();
218 *(char **) var
= copy
;
220 if (sscanf(param
, format
, var
) != 1) {
221 fprintf(stderr
, "fuse: invalid parameter in option `%s'\n", arg
);
228 static int process_opt(struct fuse_opt_context
*ctx
,
229 const struct fuse_opt
*opt
, unsigned sep
,
230 const char *arg
, int iso
)
232 if (opt
->offset
== -1U) {
233 if (call_proc(ctx
, arg
, opt
->value
, iso
) == -1)
236 void *var
= ctx
->data
+ opt
->offset
;
237 if (sep
&& opt
->templ
[sep
+ 1]) {
238 const char *param
= arg
+ sep
;
239 if (opt
->templ
[sep
] == '=')
241 if (process_opt_param(var
, opt
->templ
+ sep
+ 1,
245 *(int *)var
= opt
->value
;
250 static int process_opt_sep_arg(struct fuse_opt_context
*ctx
,
251 const struct fuse_opt
*opt
, unsigned sep
,
252 const char *arg
, int iso
)
258 if (next_arg(ctx
, arg
) == -1)
261 param
= ctx
->argv
[ctx
->argctr
];
262 newarg
= malloc(sep
+ strlen(param
) + 1);
264 return alloc_failed();
266 memcpy(newarg
, arg
, sep
);
267 strcpy(newarg
+ sep
, param
);
268 res
= process_opt(ctx
, opt
, sep
, newarg
, iso
);
274 static int process_gopt(struct fuse_opt_context
*ctx
, const char *arg
, int iso
)
277 const struct fuse_opt
*opt
= find_opt(ctx
->opt
, arg
, &sep
);
279 for (; opt
; opt
= find_opt(opt
+ 1, arg
, &sep
)) {
281 if (sep
&& opt
->templ
[sep
] == ' ' && !arg
[sep
])
282 res
= process_opt_sep_arg(ctx
, opt
, sep
, arg
,
285 res
= process_opt(ctx
, opt
, sep
, arg
, iso
);
291 return call_proc(ctx
, arg
, FUSE_OPT_KEY_OPT
, iso
);
294 static int process_real_option_group(struct fuse_opt_context
*ctx
, char *opts
)
303 if (*s
== ',' || end
) {
307 res
= process_gopt(ctx
, opts
, 1);
312 if (s
[0] == '\\' && s
[1] != '\0') {
314 if (s
[0] >= '0' && s
[0] <= '3' &&
315 s
[1] >= '0' && s
[1] <= '7' &&
316 s
[2] >= '0' && s
[2] <= '7') {
317 *d
++ = (s
[0] - '0') * 0100 +
318 (s
[1] - '0') * 0010 +
334 static int process_option_group(struct fuse_opt_context
*ctx
, const char *opts
)
337 char *copy
= strdup(opts
);
340 fprintf(stderr
, "fuse: memory allocation failed\n");
343 res
= process_real_option_group(ctx
, copy
);
348 static int process_one(struct fuse_opt_context
*ctx
, const char *arg
)
350 if (ctx
->nonopt
|| arg
[0] != '-')
351 return call_proc(ctx
, arg
, FUSE_OPT_KEY_NONOPT
, 0);
352 else if (arg
[1] == 'o') {
354 return process_option_group(ctx
, arg
+ 2);
356 if (next_arg(ctx
, arg
) == -1)
359 return process_option_group(ctx
,
360 ctx
->argv
[ctx
->argctr
]);
362 } else if (arg
[1] == '-' && !arg
[2]) {
363 if (add_arg(ctx
, arg
) == -1)
365 ctx
->nonopt
= ctx
->outargs
.argc
;
368 return process_gopt(ctx
, arg
, 0);
371 static int opt_parse(struct fuse_opt_context
*ctx
)
374 if (add_arg(ctx
, ctx
->argv
[0]) == -1)
378 for (ctx
->argctr
= 1; ctx
->argctr
< ctx
->argc
; ctx
->argctr
++)
379 if (process_one(ctx
, ctx
->argv
[ctx
->argctr
]) == -1)
383 if (fuse_opt_insert_arg(&ctx
->outargs
, 1, "-o") == -1 ||
384 fuse_opt_insert_arg(&ctx
->outargs
, 2, ctx
->opts
) == -1)
388 /* If option separator ("--") is the last argument, remove it */
389 if (ctx
->nonopt
&& ctx
->nonopt
== ctx
->outargs
.argc
&&
390 strcmp(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1], "--") == 0) {
391 free(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1]);
392 ctx
->outargs
.argv
[--ctx
->outargs
.argc
] = NULL
;
398 int fuse_opt_parse(struct fuse_args
*args
, void *data
,
399 const struct fuse_opt opts
[], fuse_opt_proc_t proc
)
402 struct fuse_opt_context ctx
= {
408 if (!args
|| !args
->argv
|| !args
->argc
)
411 ctx
.argc
= args
->argc
;
412 ctx
.argv
= args
->argv
;
414 res
= opt_parse(&ctx
);
416 struct fuse_args tmp
= *args
;
421 fuse_opt_free_args(&ctx
.outargs
);
425 /* This symbol version was mistakenly added to the version script */
426 FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");