virtiofsd: add --rlimit-nofile=NUM option
[qemu/ar7.git] / tools / virtiofsd / fuse_opt.c
blob28922361a26f6fa0da304fd1869b26791d879322
1 /*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 * Implementation of option parsing routines (dealing with `struct
6 * fuse_args`).
8 * This program can be distributed under the terms of the GNU LGPLv2.
9 * See the file COPYING.LIB
12 #include "qemu/osdep.h"
13 #include "fuse_opt.h"
14 #include "fuse_i.h"
15 #include "fuse_misc.h"
17 #include <assert.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 struct fuse_opt_context {
23 void *data;
24 const struct fuse_opt *opt;
25 fuse_opt_proc_t proc;
26 int argctr;
27 int argc;
28 char **argv;
29 struct fuse_args outargs;
30 char *opts;
31 int nonopt;
34 void fuse_opt_free_args(struct fuse_args *args)
36 if (args) {
37 if (args->argv && args->allocated) {
38 int i;
39 for (i = 0; i < args->argc; i++) {
40 free(args->argv[i]);
42 free(args->argv);
44 args->argc = 0;
45 args->argv = NULL;
46 args->allocated = 0;
50 static int alloc_failed(void)
52 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
53 return -1;
56 int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
58 char **newargv;
59 char *newarg;
61 assert(!args->argv || args->allocated);
63 newarg = strdup(arg);
64 if (!newarg) {
65 return alloc_failed();
68 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
69 if (!newargv) {
70 free(newarg);
71 return alloc_failed();
74 args->argv = newargv;
75 args->allocated = 1;
76 args->argv[args->argc++] = newarg;
77 args->argv[args->argc] = NULL;
78 return 0;
81 static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
82 const char *arg)
84 assert(pos <= args->argc);
85 if (fuse_opt_add_arg(args, arg) == -1) {
86 return -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;
95 return 0;
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);
107 return -1;
109 ctx->argctr++;
110 return 0;
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);
123 if (!d) {
124 return alloc_failed();
127 *opts = d;
128 if (oldlen) {
129 d += oldlen;
130 *d++ = ',';
133 for (; *opt; opt++) {
134 if (esc && (*opt == ',' || *opt == '\\')) {
135 *d++ = '\\';
137 *d++ = *opt;
139 *d = '\0';
141 return 0;
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,
160 int iso)
162 if (key == FUSE_OPT_KEY_DISCARD) {
163 return 0;
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) {
169 return res;
172 if (iso) {
173 return add_opt(ctx, arg);
174 } else {
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] == '%')) {
185 int tlen = sep - t;
186 if (sep[0] == '=') {
187 tlen++;
189 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
190 *sepp = sep - t;
191 return 1;
194 if (strcmp(t, arg) == 0) {
195 *sepp = 0;
196 return 1;
198 return 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)) {
206 return opt;
209 return NULL;
212 int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
214 unsigned dummy;
215 return find_opt(opts, opt, &dummy) ? 1 : 0;
218 static int process_opt_param(void *var, const char *format, const char *param,
219 const char *arg)
221 assert(format[0] == '%');
222 if (format[1] == 's') {
223 char **s = var;
224 char *copy = strdup(param);
225 if (!copy) {
226 return alloc_failed();
229 free(*s);
230 *s = copy;
231 } else {
232 if (sscanf(param, format, var) != 1) {
233 fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n",
234 arg);
235 return -1;
238 return 0;
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) {
246 return -1;
248 } else {
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] == '=') {
253 param++;
255 if (process_opt_param(var, opt->templ + sep + 1, param, arg) ==
256 -1) {
257 return -1;
259 } else {
260 *(int *)var = opt->value;
263 return 0;
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)
270 int res;
271 char *newarg;
272 char *param;
274 if (next_arg(ctx, arg) == -1) {
275 return -1;
278 param = ctx->argv[ctx->argctr];
279 newarg = malloc(sep + strlen(param) + 1);
280 if (!newarg) {
281 return alloc_failed();
284 memcpy(newarg, arg, sep);
285 strcpy(newarg + sep, param);
286 res = process_opt(ctx, opt, sep, newarg, iso);
287 free(newarg);
289 return res;
292 static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
294 unsigned sep;
295 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
296 if (opt) {
297 for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
298 int res;
299 if (sep && opt->templ[sep] == ' ' && !arg[sep]) {
300 res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
301 } else {
302 res = process_opt(ctx, opt, sep, arg, iso);
304 if (res == -1) {
305 return -1;
308 return 0;
309 } else {
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)
316 char *s = opts;
317 char *d = s;
318 int end = 0;
320 while (!end) {
321 if (*s == '\0') {
322 end = 1;
324 if (*s == ',' || end) {
325 int res;
327 *d = '\0';
328 res = process_gopt(ctx, opts, 1);
329 if (res == -1) {
330 return -1;
332 d = opts;
333 } else {
334 if (s[0] == '\\' && s[1] != '\0') {
335 s++;
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 +
339 (s[2] - '0');
340 s += 2;
341 } else {
342 *d++ = *s;
344 } else {
345 *d++ = *s;
348 s++;
351 return 0;
354 static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
356 int res;
357 char *copy = strdup(opts);
359 if (!copy) {
360 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
361 return -1;
363 res = process_real_option_group(ctx, copy);
364 free(copy);
365 return res;
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') {
373 if (arg[2]) {
374 return process_option_group(ctx, arg + 2);
375 } else {
376 if (next_arg(ctx, arg) == -1) {
377 return -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) {
384 return -1;
386 ctx->nonopt = ctx->outargs.argc;
387 return 0;
388 } else {
389 return process_gopt(ctx, arg, 0);
393 static int opt_parse(struct fuse_opt_context *ctx)
395 if (ctx->argc) {
396 if (add_arg(ctx, ctx->argv[0]) == -1) {
397 return -1;
401 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) {
402 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) {
403 return -1;
407 if (ctx->opts) {
408 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
409 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) {
410 return -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;
421 return 0;
424 int fuse_opt_parse(struct fuse_args *args, void *data,
425 const struct fuse_opt opts[], fuse_opt_proc_t proc)
427 int res;
428 struct fuse_opt_context ctx = {
429 .data = data,
430 .opt = opts,
431 .proc = proc,
434 if (!args || !args->argv || !args->argc) {
435 return 0;
438 ctx.argc = args->argc;
439 ctx.argv = args->argv;
441 res = opt_parse(&ctx);
442 if (res != -1) {
443 struct fuse_args tmp = *args;
444 *args = ctx.outargs;
445 ctx.outargs = tmp;
447 free(ctx.opts);
448 fuse_opt_free_args(&ctx.outargs);
449 return res;