source: FTPfs/curlftpfs-0.9.1/compat/fuse_opt.c @ 10

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