libfuse

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 "config.h"
13 #include "fuse_opt.h"
14 #include "fuse_misc.h"
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <assert.h>
20
21 struct fuse_opt_context {
22 void *data;
23 const struct fuse_opt *opt;
24 fuse_opt_proc_t proc;
25 int argctr;
26 int argc;
27 char **argv;
28 struct fuse_args outargs;
29 char *opts;
30 int nonopt;
31 };
32
33 void fuse_opt_free_args(struct fuse_args *args)
34 {
35 if (args) {
36 if (args->argv && args->allocated) {
37 int i;
38 for (i = 0; i < args->argc; i++)
39 free(args->argv[i]);
40 free(args->argv);
41 }
42 args->argc = 0;
43 args->argv = NULL;
44 args->allocated = 0;
45 }
46 }
47
48 static int alloc_failed(void)
49 {
50 fprintf(stderr, "fuse: memory allocation failed\n");
51 return -1;
52 }
53
54 int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
55 {
56 char **newargv;
57 char *newarg;
58
59 assert(!args->argv || args->allocated);
60
61 newarg = strdup(arg);
62 if (!newarg)
63 return alloc_failed();
64
65 newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
66 if (!newargv) {
67 free(newarg);
68 return alloc_failed();
69 }
70
71 args->argv = newargv;
72 args->allocated = 1;
73 args->argv[args->argc++] = newarg;
74 args->argv[args->argc] = NULL;
75 return 0;
76 }
77
78 static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos,
79 const char *arg)
80 {
81 assert(pos <= args->argc);
82 if (fuse_opt_add_arg(args, arg) == -1)
83 return -1;
84
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;
90 }
91 return 0;
92 }
93
94 int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
95 {
96 return fuse_opt_insert_arg_common(args, pos, arg);
97 }
98
99 static int next_arg(struct fuse_opt_context *ctx, const char *opt)
100 {
101 if (ctx->argctr + 1 >= ctx->argc) {
102 fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
103 return -1;
104 }
105 ctx->argctr++;
106 return 0;
107 }
108
109 static int add_arg(struct fuse_opt_context *ctx, const char *arg)
110 {
111 return fuse_opt_add_arg(&ctx->outargs, arg);
112 }
113
114 static int add_opt_common(char **opts, const char *opt, int esc)
115 {
116 unsigned oldlen = *opts ? strlen(*opts) : 0;
117 char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1);
118
119 if (!d)
120 return alloc_failed();
121
122 *opts = d;
123 if (oldlen) {
124 d += oldlen;
125 *d++ = ',';
126 }
127
128 for (; *opt; opt++) {
129 if (esc && (*opt == ',' || *opt == '\\'))
130 *d++ = '\\';
131 *d++ = *opt;
132 }
133 *d = '\0';
134
135 return 0;
136 }
137
138 int fuse_opt_add_opt(char **opts, const char *opt)
139 {
140 return add_opt_common(opts, opt, 0);
141 }
142
143 int fuse_opt_add_opt_escaped(char **opts, const char *opt)
144 {
145 return add_opt_common(opts, opt, 1);
146 }
147
148 static int add_opt(struct fuse_opt_context *ctx, const char *opt)
149 {
150 return add_opt_common(&ctx->opts, opt, 1);
151 }
152
153 static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
154 int iso)
155 {
156 if (key == FUSE_OPT_KEY_DISCARD)
157 return 0;
158
159 if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
160 int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
161 if (res == -1 || !res)
162 return res;
163 }
164 if (iso)
165 return add_opt(ctx, arg);
166 else
167 return add_arg(ctx, arg);
168 }
169
170 static int match_template(const char *t, const char *arg, unsigned *sepp)
171 {
172 int arglen = strlen(arg);
173 const char *sep = strchr(t, '=');
174 sep = sep ? sep : strchr(t, ' ');
175 if (sep && (!sep[1] || sep[1] == '%')) {
176 int tlen = sep - t;
177 if (sep[0] == '=')
178 tlen ++;
179 if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
180 *sepp = sep - t;
181 return 1;
182 }
183 }
184 if (strcmp(t, arg) == 0) {
185 *sepp = 0;
186 return 1;
187 }
188 return 0;
189 }
190
191 static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
192 const char *arg, unsigned *sepp)
193 {
194 for (; opt && opt->templ; opt++)
195 if (match_template(opt->templ, arg, sepp))
196 return opt;
197 return NULL;
198 }
199
200 int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
201 {
202 unsigned dummy;
203 return find_opt(opts, opt, &dummy) ? 1 : 0;
204 }
205
206 static int process_opt_param(void *var, const char *format, const char *param,
207 const char *arg)
208 {
209 assert(format[0] == '%');
210 if (format[1] == 's') {
211 char **s = var;
212 char *copy = strdup(param);
213 if (!copy)
214 return alloc_failed();
215
216 free(*s);
217 *s = copy;
218 } else {
219 if (sscanf(param, format, var) != 1) {
220 fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
221 return -1;
222 }
223 }
224 return 0;
225 }
226
227 static int process_opt(struct fuse_opt_context *ctx,
228 const struct fuse_opt *opt, unsigned sep,
229 const char *arg, int iso)
230 {
231 if (opt->offset == -1U) {
232 if (call_proc(ctx, arg, opt->value, iso) == -1)
233 return -1;
234 } else {
235 void *var = (char *)ctx->data + opt->offset;
236 if (sep && opt->templ[sep + 1]) {
237 const char *param = arg + sep;
238 if (opt->templ[sep] == '=')
239 param ++;
240 if (process_opt_param(var, opt->templ + sep + 1,
241 param, arg) == -1)
242 return -1;
243 } else
244 *(int *)var = opt->value;
245 }
246 return 0;
247 }
248
249 static int process_opt_sep_arg(struct fuse_opt_context *ctx,
250 const struct fuse_opt *opt, unsigned sep,
251 const char *arg, int iso)
252 {
253 int res;
254 char *newarg;
255 char *param;
256
257 if (next_arg(ctx, arg) == -1)
258 return -1;
259
260 param = ctx->argv[ctx->argctr];
261 newarg = malloc(sep + strlen(param) + 1);
262 if (!newarg)
263 return alloc_failed();
264
265 memcpy(newarg, arg, sep);
266 strcpy(newarg + sep, param);
267 res = process_opt(ctx, opt, sep, newarg, iso);
268 free(newarg);
269
270 return res;
271 }
272
273 static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
274 {
275 unsigned sep;
276 const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
277 if (opt) {
278 for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
279 int res;
280 if (sep && opt->templ[sep] == ' ' && !arg[sep])
281 res = process_opt_sep_arg(ctx, opt, sep, arg,
282 iso);
283 else
284 res = process_opt(ctx, opt, sep, arg, iso);
285 if (res == -1)
286 return -1;
287 }
288 return 0;
289 } else
290 return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
291 }
292
293 static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
294 {
295 char *s = opts;
296 char *d = s;
297 int end = 0;
298
299 while (!end) {
300 if (*s == '\0')
301 end = 1;
302 if (*s == ',' || end) {
303 int res;
304
305 *d = '\0';
306 res = process_gopt(ctx, opts, 1);
307 if (res == -1)
308 return -1;
309 d = opts;
310 } else {
311 if (s[0] == '\\' && s[1] != '\0') {
312 s++;
313 if (s[0] >= '0' && s[0] <= '3' &&
314 s[1] >= '0' && s[1] <= '7' &&
315 s[2] >= '0' && s[2] <= '7') {
316 *d++ = (s[0] - '0') * 0100 +
317 (s[1] - '0') * 0010 +
318 (s[2] - '0');
319 s += 2;
320 } else {
321 *d++ = *s;
322 }
323 } else {
324 *d++ = *s;
325 }
326 }
327 s++;
328 }
329
330 return 0;
331 }
332
333 static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
334 {
335 int res;
336 char *copy = strdup(opts);
337
338 if (!copy) {
339 fprintf(stderr, "fuse: memory allocation failed\n");
340 return -1;
341 }
342 res = process_real_option_group(ctx, copy);
343 free(copy);
344 return res;
345 }
346
347 static int process_one(struct fuse_opt_context *ctx, const char *arg)
348 {
349 if (ctx->nonopt || arg[0] != '-')
350 return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
351 else if (arg[1] == 'o') {
352 if (arg[2])
353 return process_option_group(ctx, arg + 2);
354 else {
355 if (next_arg(ctx, arg) == -1)
356 return -1;
357
358 return process_option_group(ctx,
359 ctx->argv[ctx->argctr]);
360 }
361 } else if (arg[1] == '-' && !arg[2]) {
362 if (add_arg(ctx, arg) == -1)
363 return -1;
364 ctx->nonopt = ctx->outargs.argc;
365 return 0;
366 } else
367 return process_gopt(ctx, arg, 0);
368 }
369
370 static int opt_parse(struct fuse_opt_context *ctx)
371 {
372 if (ctx->argc) {
373 if (add_arg(ctx, ctx->argv[0]) == -1)
374 return -1;
375 }
376
377 for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
378 if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
379 return -1;
380
381 if (ctx->opts) {
382 if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
383 fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
384 return -1;
385 }
386
387 /* If option separator ("--") is the last argument, remove it */
388 if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc &&
389 strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) {
390 free(ctx->outargs.argv[ctx->outargs.argc - 1]);
391 ctx->outargs.argv[--ctx->outargs.argc] = NULL;
392 }
393
394 return 0;
395 }
396
397 int fuse_opt_parse(struct fuse_args *args, void *data,
398 const struct fuse_opt opts[], fuse_opt_proc_t proc)
399 {
400 int res;
401 struct fuse_opt_context ctx = {
402 .data = data,
403 .opt = opts,
404 .proc = proc,
405 };
406
407 if (!args || !args->argv || !args->argc)
408 return 0;
409
410 ctx.argc = args->argc;
411 ctx.argv = args->argv;
412
413 res = opt_parse(&ctx);
414 if (res != -1) {
415 struct fuse_args tmp = *args;
416 *args = ctx.outargs;
417 ctx.outargs = tmp;
418 }
419 free(ctx.opts);
420 fuse_opt_free_args(&ctx.outargs);
421 return res;
422 }
FUSE_OPT_KEY_KEEP
#define FUSE_OPT_KEY_KEEP
Definition: fuse_opt.h:145
fuse_args::argc
int argc
Definition: fuse_opt.h:111
fuse_opt_insert_arg
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition: fuse_opt.c:94
fuse_opt::offset
unsigned long offset
Definition: fuse_opt.h:85
fuse_opt_proc_t
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition: fuse_opt.h:180
fuse_opt_parse
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:397
fuse_args::allocated
int allocated
Definition: fuse_opt.h:117
fuse_opt::value
int value
Definition: fuse_opt.h:91
fuse_opt_add_opt
int fuse_opt_add_opt(char **opts, const char *opt)
Definition: fuse_opt.c:138
fuse_args::argv
char ** argv
Definition: fuse_opt.h:114
fuse_opt_match
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
FUSE_OPT_KEY_OPT
#define FUSE_OPT_KEY_OPT
Definition: fuse_opt.h:129
fuse_opt_add_arg
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition: fuse_opt.c:54
fuse_opt_add_opt_escaped
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition: fuse_opt.c:143
FUSE_OPT_KEY_DISCARD
#define FUSE_OPT_KEY_DISCARD
Definition: fuse_opt.h:153
fuse_opt
Definition: fuse_opt.h:77
FUSE_OPT_KEY_NONOPT
#define FUSE_OPT_KEY_NONOPT
Definition: fuse_opt.h:137
fuse_args
Definition: fuse_opt.h:109
fuse_opt::templ
const char * templ
Definition: fuse_opt.h:79
fuse_opt_free_args
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:33