disas: Clean up CPUDebug initialization
[qemu.git] / tools / virtiofsd / fuse_opt.c
1 /*
2 * FUSE: Filesystem in Userspace
3 * Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4 *
5 * Implementation of option parsing routines (dealing with `struct
6 * fuse_args`).
7 *
8 * This program can be distributed under the terms of the GNU LGPLv2.
9 * See the file COPYING.LIB
10 */
11
12 #include "qemu/osdep.h"
13 #include "fuse_opt.h"
14 #include "fuse_i.h"
15 #include "fuse_misc.h"
16
17 #include <assert.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
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;
32 };
33
34 void fuse_opt_free_args(struct fuse_args *args)
35 {
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]);
41 }
42 free(args->argv);
43 }
44 args->argc = 0;
45 args->argv = NULL;
46 args->allocated = 0;
47 }
48 }
49
50 static int alloc_failed(void)
51 {
52 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
53 return -1;
54 }
55
56 int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
57 {
58 char **newargv;
59 char *newarg;
60
61 assert(!args->argv || args->allocated);
62
63 newarg = strdup(arg);
64 if (!newarg) {
65 return alloc_failed();
66 }
67
68 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
69 if (!newargv) {
70 free(newarg);
71 return alloc_failed();
72 }
73
74 args->argv = newargv;
75 args->allocated = 1;
76 args->argv[args->argc++] = newarg;
77 args->argv[args->argc] = NULL;
78 return 0;
79 }
80
81 static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
82 const char *arg)
83 {
84 assert(pos <= args->argc);
85 if (fuse_opt_add_arg(args, arg) == -1) {
86 return -1;
87 }
88
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;
94 }
95 return 0;
96 }
97
98 int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
99 {
100 return fuse_opt_insert_arg_common(args, pos, arg);
101 }
102
103 static int next_arg(struct fuse_opt_context *ctx, const char *opt)
104 {
105 if (ctx->argctr + 1 >= ctx->argc) {
106 fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt);
107 return -1;
108 }
109 ctx->argctr++;
110 return 0;
111 }
112
113 static int add_arg(struct fuse_opt_context *ctx, const char *arg)
114 {
115 return fuse_opt_add_arg(&ctx->outargs, arg);
116 }
117
118 static int add_opt_common(char **opts, const char *opt, int esc)
119 {
120 unsigned oldlen = *opts ? strlen(*opts) : 0;
121 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
122
123 if (!d) {
124 return alloc_failed();
125 }
126
127 *opts = d;
128 if (oldlen) {
129 d += oldlen;
130 *d++ = ',';
131 }
132
133 for (; *opt; opt++) {
134 if (esc && (*opt == ',' || *opt == '\\')) {
135 *d++ = '\\';
136 }
137 *d++ = *opt;
138 }
139 *d = '\0';
140
141 return 0;
142 }
143
144 int fuse_opt_add_opt(char **opts, const char *opt)
145 {
146 return add_opt_common(opts, opt, 0);
147 }
148
149 int fuse_opt_add_opt_escaped(char **opts, const char *opt)
150 {
151 return add_opt_common(opts, opt, 1);
152 }
153
154 static int add_opt(struct fuse_opt_context *ctx, const char *opt)
155 {
156 return add_opt_common(&ctx->opts, opt, 1);
157 }
158
159 static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
160 int iso)
161 {
162 if (key == FUSE_OPT_KEY_DISCARD) {
163 return 0;
164 }
165
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;
170 }
171 }
172 if (iso) {
173 return add_opt(ctx, arg);
174 } else {
175 return add_arg(ctx, arg);
176 }
177 }
178
179 static int match_template(const char *t, const char *arg, unsigned *sepp)
180 {
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++;
188 }
189 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
190 *sepp = sep - t;
191 return 1;
192 }
193 }
194 if (strcmp(t, arg) == 0) {
195 *sepp = 0;
196 return 1;
197 }
198 return 0;
199 }
200
201 static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
202 const char *arg, unsigned *sepp)
203 {
204 for (; opt && opt->templ; opt++) {
205 if (match_template(opt->templ, arg, sepp)) {
206 return opt;
207 }
208 }
209 return NULL;
210 }
211
212 int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
213 {
214 unsigned dummy;
215 return find_opt(opts, opt, &dummy) ? 1 : 0;
216 }
217
218 static int process_opt_param(void *var, const char *format, const char *param,
219 const char *arg)
220 {
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();
227 }
228
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;
236 }
237 }
238 return 0;
239 }
240
241 static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt,
242 unsigned sep, const char *arg, int iso)
243 {
244 if (opt->offset == -1U) {
245 if (call_proc(ctx, arg, opt->value, iso) == -1) {
246 return -1;
247 }
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++;
254 }
255 if (process_opt_param(var, opt->templ + sep + 1, param, arg) ==
256 -1) {
257 return -1;
258 }
259 } else {
260 *(int *)var = opt->value;
261 }
262 }
263 return 0;
264 }
265
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)
269 {
270 int res;
271 char *newarg;
272 char *param;
273
274 if (next_arg(ctx, arg) == -1) {
275 return -1;
276 }
277
278 param = ctx->argv[ctx->argctr];
279 newarg = malloc(sep + strlen(param) + 1);
280 if (!newarg) {
281 return alloc_failed();
282 }
283
284 memcpy(newarg, arg, sep);
285 strcpy(newarg + sep, param);
286 res = process_opt(ctx, opt, sep, newarg, iso);
287 free(newarg);
288
289 return res;
290 }
291
292 static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
293 {
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);
303 }
304 if (res == -1) {
305 return -1;
306 }
307 }
308 return 0;
309 } else {
310 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
311 }
312 }
313
314 static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
315 {
316 char *s = opts;
317 char *d = s;
318 int end = 0;
319
320 while (!end) {
321 if (*s == '\0') {
322 end = 1;
323 }
324 if (*s == ',' || end) {
325 int res;
326
327 *d = '\0';
328 res = process_gopt(ctx, opts, 1);
329 if (res == -1) {
330 return -1;
331 }
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;
343 }
344 } else {
345 *d++ = *s;
346 }
347 }
348 s++;
349 }
350
351 return 0;
352 }
353
354 static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
355 {
356 int res;
357 char *copy = strdup(opts);
358
359 if (!copy) {
360 fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n");
361 return -1;
362 }
363 res = process_real_option_group(ctx, copy);
364 free(copy);
365 return res;
366 }
367
368 static int process_one(struct fuse_opt_context *ctx, const char *arg)
369 {
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;
378 }
379
380 return process_option_group(ctx, ctx->argv[ctx->argctr]);
381 }
382 } else if (arg[1] == '-' && !arg[2]) {
383 if (add_arg(ctx, arg) == -1) {
384 return -1;
385 }
386 ctx->nonopt = ctx->outargs.argc;
387 return 0;
388 } else {
389 return process_gopt(ctx, arg, 0);
390 }
391 }
392
393 static int opt_parse(struct fuse_opt_context *ctx)
394 {
395 if (ctx->argc) {
396 if (add_arg(ctx, ctx->argv[0]) == -1) {
397 return -1;
398 }
399 }
400
401 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) {
402 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) {
403 return -1;
404 }
405 }
406
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;
411 }
412 }
413
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;
419 }
420
421 return 0;
422 }
423
424 int fuse_opt_parse(struct fuse_args *args, void *data,
425 const struct fuse_opt opts[], fuse_opt_proc_t proc)
426 {
427 int res;
428 struct fuse_opt_context ctx = {
429 .data = data,
430 .opt = opts,
431 .proc = proc,
432 };
433
434 if (!args || !args->argv || !args->argc) {
435 return 0;
436 }
437
438 ctx.argc = args->argc;
439 ctx.argv = args->argv;
440
441 res = opt_parse(&ctx);
442 if (res != -1) {
443 struct fuse_args tmp = *args;
444 *args = ctx.outargs;
445 ctx.outargs = tmp;
446 }
447 free(ctx.opts);
448 fuse_opt_free_args(&ctx.outargs);
449 return res;
450 }