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 }
#define FUSE_OPT_KEY_KEEP
Definition: fuse_opt.h:145
int argc
Definition: fuse_opt.h:111
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
Definition: fuse_opt.c:94
unsigned long offset
Definition: fuse_opt.h:85
int(* fuse_opt_proc_t)(void *data, const char *arg, int key, struct fuse_args *outargs)
Definition: fuse_opt.h:180
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
int allocated
Definition: fuse_opt.h:117
int value
Definition: fuse_opt.h:91
int fuse_opt_add_opt(char **opts, const char *opt)
Definition: fuse_opt.c:138
char ** argv
Definition: fuse_opt.h:114
int fuse_opt_match(const struct fuse_opt opts[], const char *opt)
#define FUSE_OPT_KEY_OPT
Definition: fuse_opt.h:129
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
Definition: fuse_opt.c:54
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition: fuse_opt.c:143
#define FUSE_OPT_KEY_DISCARD
Definition: fuse_opt.h:153
#define FUSE_OPT_KEY_NONOPT
Definition: fuse_opt.h:137
const char * templ
Definition: fuse_opt.h:79
void fuse_opt_free_args(struct fuse_args *args)
Definition: fuse_opt.c:33