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
);
57 newargv
= realloc(args
->argv
, (args
->argc
+ 2) * sizeof(char *));
58 newarg
= newargv
? strdup(arg
) : NULL
;
59 if (!newargv
|| !newarg
)
60 return alloc_failed();
64 args
->argv
[args
->argc
++] = newarg
;
65 args
->argv
[args
->argc
] = NULL
;
69 static int fuse_opt_insert_arg_common(struct fuse_args
*args
, int pos
,
72 assert(pos
<= args
->argc
);
73 if (fuse_opt_add_arg(args
, arg
) == -1)
76 if (pos
!= args
->argc
- 1) {
77 char *newarg
= args
->argv
[args
->argc
- 1];
78 memmove(&args
->argv
[pos
+ 1], &args
->argv
[pos
],
79 sizeof(char *) * (args
->argc
- pos
- 1));
80 args
->argv
[pos
] = newarg
;
85 int fuse_opt_insert_arg(struct fuse_args
*args
, int pos
, const char *arg
)
87 return fuse_opt_insert_arg_common(args
, pos
, arg
);
90 int fuse_opt_insert_arg_compat(struct fuse_args
*args
, int pos
,
92 int fuse_opt_insert_arg_compat(struct fuse_args
*args
, int pos
, const char *arg
)
94 return fuse_opt_insert_arg_common(args
, pos
, arg
);
97 static int next_arg(struct fuse_opt_context
*ctx
, const char *opt
)
99 if (ctx
->argctr
+ 1 >= ctx
->argc
) {
100 fprintf(stderr
, "fuse: missing argument after `%s'\n", opt
);
107 static int add_arg(struct fuse_opt_context
*ctx
, const char *arg
)
109 return fuse_opt_add_arg(&ctx
->outargs
, arg
);
112 static int add_opt_common(char **opts
, const char *opt
, int esc
)
114 unsigned oldlen
= *opts
? strlen(*opts
) : 0;
115 char *d
= realloc(*opts
, oldlen
+ 1 + strlen(opt
) * 2 + 1);
118 return alloc_failed();
126 for (; *opt
; opt
++) {
127 if (esc
&& (*opt
== ',' || *opt
== '\\'))
136 int fuse_opt_add_opt(char **opts
, const char *opt
)
138 return add_opt_common(opts
, opt
, 0);
141 int fuse_opt_add_opt_escaped(char **opts
, const char *opt
)
143 return add_opt_common(opts
, opt
, 1);
146 static int add_opt(struct fuse_opt_context
*ctx
, const char *opt
)
148 return add_opt_common(&ctx
->opts
, opt
, 1);
151 static int call_proc(struct fuse_opt_context
*ctx
, const char *arg
, int key
,
154 if (key
== FUSE_OPT_KEY_DISCARD
)
157 if (key
!= FUSE_OPT_KEY_KEEP
&& ctx
->proc
) {
158 int res
= ctx
->proc(ctx
->data
, arg
, key
, &ctx
->outargs
);
159 if (res
== -1 || !res
)
163 return add_opt(ctx
, arg
);
165 return add_arg(ctx
, arg
);
168 static int match_template(const char *t
, const char *arg
, unsigned *sepp
)
170 int arglen
= strlen(arg
);
171 const char *sep
= strchr(t
, '=');
172 sep
= sep
? sep
: strchr(t
, ' ');
173 if (sep
&& (!sep
[1] || sep
[1] == '%')) {
177 if (arglen
>= tlen
&& strncmp(arg
, t
, tlen
) == 0) {
182 if (strcmp(t
, arg
) == 0) {
189 static const struct fuse_opt
*find_opt(const struct fuse_opt
*opt
,
190 const char *arg
, unsigned *sepp
)
192 for (; opt
&& opt
->templ
; opt
++)
193 if (match_template(opt
->templ
, arg
, sepp
))
198 int fuse_opt_match(const struct fuse_opt
*opts
, const char *opt
)
201 return find_opt(opts
, opt
, &dummy
) ? 1 : 0;
204 static int process_opt_param(void *var
, const char *format
, const char *param
,
207 assert(format
[0] == '%');
208 if (format
[1] == 's') {
209 char *copy
= strdup(param
);
211 return alloc_failed();
213 *(char **) var
= copy
;
215 if (sscanf(param
, format
, var
) != 1) {
216 fprintf(stderr
, "fuse: invalid parameter in option `%s'\n", arg
);
223 static int process_opt(struct fuse_opt_context
*ctx
,
224 const struct fuse_opt
*opt
, unsigned sep
,
225 const char *arg
, int iso
)
227 if (opt
->offset
== -1U) {
228 if (call_proc(ctx
, arg
, opt
->value
, iso
) == -1)
231 void *var
= ctx
->data
+ opt
->offset
;
232 if (sep
&& opt
->templ
[sep
+ 1]) {
233 const char *param
= arg
+ sep
;
234 if (opt
->templ
[sep
] == '=')
236 if (process_opt_param(var
, opt
->templ
+ sep
+ 1,
240 *(int *)var
= opt
->value
;
245 static int process_opt_sep_arg(struct fuse_opt_context
*ctx
,
246 const struct fuse_opt
*opt
, unsigned sep
,
247 const char *arg
, int iso
)
253 if (next_arg(ctx
, arg
) == -1)
256 param
= ctx
->argv
[ctx
->argctr
];
257 newarg
= malloc(sep
+ strlen(param
) + 1);
259 return alloc_failed();
261 memcpy(newarg
, arg
, sep
);
262 strcpy(newarg
+ sep
, param
);
263 res
= process_opt(ctx
, opt
, sep
, newarg
, iso
);
269 static int process_gopt(struct fuse_opt_context
*ctx
, const char *arg
, int iso
)
272 const struct fuse_opt
*opt
= find_opt(ctx
->opt
, arg
, &sep
);
274 for (; opt
; opt
= find_opt(opt
+ 1, arg
, &sep
)) {
276 if (sep
&& opt
->templ
[sep
] == ' ' && !arg
[sep
])
277 res
= process_opt_sep_arg(ctx
, opt
, sep
, arg
,
280 res
= process_opt(ctx
, opt
, sep
, arg
, iso
);
286 return call_proc(ctx
, arg
, FUSE_OPT_KEY_OPT
, iso
);
289 static int process_real_option_group(struct fuse_opt_context
*ctx
, char *opts
)
298 if (*s
== ',' || end
) {
302 res
= process_gopt(ctx
, opts
, 1);
307 if (s
[0] == '\\' && s
[1] != '\0')
317 static int process_option_group(struct fuse_opt_context
*ctx
, const char *opts
)
320 char *copy
= strdup(opts
);
323 fprintf(stderr
, "fuse: memory allocation failed\n");
326 res
= process_real_option_group(ctx
, copy
);
331 static int process_one(struct fuse_opt_context
*ctx
, const char *arg
)
333 if (ctx
->nonopt
|| arg
[0] != '-')
334 return call_proc(ctx
, arg
, FUSE_OPT_KEY_NONOPT
, 0);
335 else if (arg
[1] == 'o') {
337 return process_option_group(ctx
, arg
+ 2);
339 if (next_arg(ctx
, arg
) == -1)
342 return process_option_group(ctx
,
343 ctx
->argv
[ctx
->argctr
]);
345 } else if (arg
[1] == '-' && !arg
[2]) {
346 if (add_arg(ctx
, arg
) == -1)
348 ctx
->nonopt
= ctx
->outargs
.argc
;
351 return process_gopt(ctx
, arg
, 0);
354 static int opt_parse(struct fuse_opt_context
*ctx
)
357 if (add_arg(ctx
, ctx
->argv
[0]) == -1)
361 for (ctx
->argctr
= 1; ctx
->argctr
< ctx
->argc
; ctx
->argctr
++)
362 if (process_one(ctx
, ctx
->argv
[ctx
->argctr
]) == -1)
366 if (fuse_opt_insert_arg(&ctx
->outargs
, 1, "-o") == -1 ||
367 fuse_opt_insert_arg(&ctx
->outargs
, 2, ctx
->opts
) == -1)
371 /* If option separator ("--") is the last argument, remove it */
372 if (ctx
->nonopt
&& ctx
->nonopt
== ctx
->outargs
.argc
&&
373 strcmp(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1], "--") == 0) {
374 free(ctx
->outargs
.argv
[ctx
->outargs
.argc
- 1]);
375 ctx
->outargs
.argv
[--ctx
->outargs
.argc
] = NULL
;
381 int fuse_opt_parse(struct fuse_args
*args
, void *data
,
382 const struct fuse_opt opts
[], fuse_opt_proc_t proc
)
385 struct fuse_opt_context ctx
= {
391 if (!args
|| !args
->argv
|| !args
->argc
)
394 ctx
.argc
= args
->argc
;
395 ctx
.argv
= args
->argv
;
397 res
= opt_parse(&ctx
);
399 struct fuse_args tmp
= *args
;
404 fuse_opt_free_args(&ctx
.outargs
);
408 /* This symbol version was mistakenly added to the version script */
409 FUSE_SYMVER(".symver fuse_opt_insert_arg_compat,fuse_opt_insert_arg@FUSE_2.5");