]> CyberLeo.Net >> Repos - SourceForge/afuse.git/blob - compat/fuse_opt.c
Tidying up for release 0.2.
[SourceForge/afuse.git] / compat / fuse_opt.c
1 /*
2     FUSE: Filesystem in Userspace
3     Copyright (C) 2001-2006  Miklos Szeredi <miklos@szeredi.hu>
4
5     This file is borrowed from sshfs-fuse. It provides an implementation
6     of the FUSE command line parsing functions which only appear in
7     later version of FUSE but are used in afuse.
8     
9     This file can be distributed under the terms of the GNU LGPL.
10     See the file COPYING.LIB
11 */
12
13 #include "fuse_opt.h"
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <assert.h>
19
20 struct fuse_opt_context {
21     void *data;
22     const struct fuse_opt *opt;
23     fuse_opt_proc_t proc;
24     int argctr;
25     int argc;
26     char **argv;
27     struct fuse_args outargs;
28     char *opts;
29     int nonopt;
30 };
31
32 void fuse_opt_free_args(struct fuse_args *args)
33 {
34     if (args && args->argv && args->allocated) {
35         int i;
36         for (i = 0; i < args->argc; i++)
37             free(args->argv[i]);
38         free(args->argv);
39         args->argv = NULL;
40         args->allocated = 0;
41     }
42 }
43
44 static int alloc_failed(void)
45 {
46     fprintf(stderr, "fuse: memory allocation failed\n");
47     return -1;
48 }
49
50 int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
51 {
52     char **newargv;
53     char *newarg;
54
55     assert(!args->argv || args->allocated);
56
57     newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
58     newarg = newargv ? strdup(arg) : NULL;
59     if (!newargv || !newarg)
60         return alloc_failed();
61
62     args->argv = newargv;
63     args->allocated = 1;
64     args->argv[args->argc++] = newarg;
65     args->argv[args->argc] = NULL;
66     return 0;
67 }
68
69 static int next_arg(struct fuse_opt_context *ctx, const char *opt)
70 {
71     if (ctx->argctr + 1 >= ctx->argc) {
72         fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
73         return -1;
74     }
75     ctx->argctr++;
76     return 0;
77 }
78
79 static int add_arg(struct fuse_opt_context *ctx, const char *arg)
80 {
81     return fuse_opt_add_arg(&ctx->outargs, arg);
82 }
83
84 int fuse_opt_add_opt(char **opts, const char *opt)
85 {
86     char *newopts;
87     if (!*opts)
88         newopts = strdup(opt);
89     else {
90         unsigned oldlen = strlen(*opts);
91         newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1);
92         if (newopts) {
93             newopts[oldlen] = ',';
94             strcpy(newopts + oldlen + 1, opt);
95         }
96     }
97     if (!newopts)
98         return alloc_failed();
99
100     *opts = newopts;
101     return 0;
102 }
103
104 static int add_opt(struct fuse_opt_context *ctx, const char *opt)
105 {
106     return fuse_opt_add_opt(&ctx->opts, opt);
107 }
108
109 static int insert_arg(struct fuse_opt_context *ctx, int pos, const char *arg)
110 {
111     assert(pos <= ctx->outargs.argc);
112     if (add_arg(ctx, arg) == -1)
113         return -1;
114
115     if (pos != ctx->outargs.argc - 1) {
116         char *newarg = ctx->outargs.argv[ctx->outargs.argc - 1];
117         memmove(&ctx->outargs.argv[pos + 1], &ctx->outargs.argv[pos],
118                 sizeof(char *) * (ctx->outargs.argc - pos - 1));
119         ctx->outargs.argv[pos] = newarg;
120     }
121     return 0;
122 }
123
124 static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
125                      int iso)
126 {
127     if (ctx->proc) {
128         int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
129         if (res == -1 || !res)
130             return res;
131     }
132     if (iso)
133         return add_opt(ctx, arg);
134     else
135         return add_arg(ctx, arg);
136 }
137
138 static int match_template(const char *t, const char *arg, unsigned *sepp)
139 {
140     int arglen = strlen(arg);
141     const char *sep = strchr(t, '=');
142     sep = sep ? sep : strchr(t, ' ');
143     if (sep && (!sep[1] || sep[1] == '%')) {
144         int tlen = sep - t;
145         if (sep[0] == '=')
146             tlen ++;
147         if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
148             *sepp = sep - t;
149             return 1;
150         }
151     }
152     if (strcmp(t, arg) == 0) {
153         *sepp = 0;
154         return 1;
155     }
156     return 0;
157 }
158
159 static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
160                                        const char *arg, unsigned *sepp)
161 {
162     for (; opt && opt->template; opt++)
163         if (match_template(opt->template, arg, sepp))
164             return opt;
165     return NULL;
166 }
167
168 int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
169 {
170     unsigned dummy;
171     return find_opt(opts, opt, &dummy) ? 1 : 0;
172 }
173
174 static int process_opt_param(void *var, const char *format, const char *param,
175                              const char *arg)
176 {
177     assert(format[0] == '%');
178     if (format[1] == 's') {
179         char *copy = strdup(param);
180         if (!copy)
181             return alloc_failed();
182
183         *(char **) var = copy;
184     } else {
185         if (sscanf(param, format, var) != 1) {
186             fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
187             return -1;
188         }
189     }
190     return 0;
191 }
192
193 static int process_opt(struct fuse_opt_context *ctx,
194                        const struct fuse_opt *opt, unsigned sep,
195                        const char *arg, int iso)
196 {
197     if (opt->offset == -1U) {
198         if (call_proc(ctx, arg, opt->value, iso) == -1)
199             return -1;
200     } else {
201         void *var = ctx->data + opt->offset;
202         if (sep && opt->template[sep + 1]) {
203             const char *param = arg + sep;
204             if (opt->template[sep] == '=')
205                 param ++;
206             if (process_opt_param(var, opt->template + sep + 1,
207                                   param, arg) == -1)
208                 return -1;
209         } else
210             *(int *)var = opt->value;
211     }
212     return 0;
213 }
214
215 static int process_opt_sep_arg(struct fuse_opt_context *ctx,
216                                const struct fuse_opt *opt, unsigned sep,
217                                const char *arg, int iso)
218 {
219     int res;
220     char *newarg;
221     char *param;
222
223     if (next_arg(ctx, arg) == -1)
224         return -1;
225
226     param = ctx->argv[ctx->argctr];
227     newarg = malloc(sep + strlen(param) + 1);
228     if (!newarg)
229         return alloc_failed();
230
231     memcpy(newarg, arg, sep);
232     strcpy(newarg + sep, param);
233     res = process_opt(ctx, opt, sep, newarg, iso);
234     free(newarg);
235
236     return res;
237 }
238
239 static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
240 {
241     unsigned sep;
242     const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
243     if (opt) {
244         for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
245             int res;
246             if (sep && opt->template[sep] == ' ' && !arg[sep])
247                 res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
248             else
249                 res = process_opt(ctx, opt, sep, arg, iso);
250             if (res == -1)
251                 return -1;
252         }
253         return 0;
254     } else
255         return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
256 }
257
258 static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
259 {
260     char *sep;
261
262     do {
263         int res;
264         sep = strchr(opts, ',');
265         if (sep)
266             *sep = '\0';
267         res = process_gopt(ctx, opts, 1);
268         if (res == -1)
269             return -1;
270         opts = sep + 1;
271     } while (sep);
272
273     return 0;
274 }
275
276 static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
277 {
278     int res;
279     char *copy;
280     const char *sep = strchr(opts, ',');
281     if (!sep)
282         return process_gopt(ctx, opts, 1);
283
284     copy = strdup(opts);
285     if (!copy) {
286         fprintf(stderr, "fuse: memory allocation failed\n");
287         return -1;
288     }
289     res = process_real_option_group(ctx, copy);
290     free(copy);
291     return res;
292 }
293
294 static int process_one(struct fuse_opt_context *ctx, const char *arg)
295 {
296     if (ctx->nonopt || arg[0] != '-')
297         return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
298     else if (arg[1] == 'o') {
299         if (arg[2])
300             return process_option_group(ctx, arg + 2);
301         else {
302             if (next_arg(ctx, arg) == -1)
303                 return -1;
304
305             return process_option_group(ctx, ctx->argv[ctx->argctr]);
306         }
307     } else if (arg[1] == '-' && !arg[2]) {
308         if (add_arg(ctx, arg) == -1)
309             return -1;
310         ctx->nonopt = ctx->outargs.argc;
311         return 0;
312     } else
313         return process_gopt(ctx, arg, 0);
314 }
315
316 static int opt_parse(struct fuse_opt_context *ctx)
317 {
318     if (ctx->argc) {
319         if (add_arg(ctx, ctx->argv[0]) == -1)
320             return -1;
321     }
322
323     for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
324         if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
325             return -1;
326
327     if (ctx->opts) {
328         if (insert_arg(ctx, 1, "-o") == -1 ||
329             insert_arg(ctx, 2, ctx->opts) == -1)
330             return -1;
331     }
332     if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc)
333         ctx->outargs.argv[--ctx->outargs.argc] = NULL;
334
335     return 0;
336 }
337
338 int fuse_opt_parse(struct fuse_args *args, void *data,
339                    const struct fuse_opt opts[], fuse_opt_proc_t proc)
340 {
341     int res;
342     struct fuse_opt_context ctx = {
343         .data = data,
344         .opt = opts,
345         .proc = proc,
346     };
347
348     if (!args || !args->argv || !args->argc)
349         return 0;
350
351     ctx.argc = args->argc;
352     ctx.argv = args->argv;
353
354     res = opt_parse(&ctx);
355     if (res != -1) {
356         struct fuse_args tmp = *args;
357         *args = ctx.outargs;
358         ctx.outargs = tmp;
359     }
360     free(ctx.opts);
361     fuse_opt_free_args(&ctx.outargs);
362     return res;
363 }