~james-w/+junk/fuse-ubuntu-upstream

« back to all changes in this revision

Viewing changes to lib/fuse_opt.c

  • Committer: James Westby
  • Date: 2008-05-16 12:58:06 UTC
  • Revision ID: jw+debian@jameswestby.net-20080516125806-ij1d8h7ihcwsv3ck
Tags: upstream-debian-2.5.1
Import upstream from fuse_2.5.1.orig.tar.gz

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
16
struct 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
 
 
28
void 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
 
 
40
static int alloc_failed(void)
 
41
{
 
42
    fprintf(stderr, "fuse: memory allocation failed\n");
 
43
    return -1;
 
44
}
 
45
 
 
46
int 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
 
 
65
static 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
 
 
75
static int add_arg(struct fuse_opt_context *ctx, const char *arg)
 
76
{
 
77
    return fuse_opt_add_arg(&ctx->outargs, arg);
 
78
}
 
79
 
 
80
int 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
 
 
100
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
 
101
{
 
102
    return fuse_opt_add_opt(&ctx->opts, opt);
 
103
}
 
104
 
 
105
static 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
 
 
120
static 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
 
 
134
static 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
 
 
155
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
 
156
                                       const char *arg, unsigned *sepp)
 
157
{
 
158
    for (; opt && opt->templ; opt++)
 
159
        if (match_template(opt->templ, arg, sepp))
 
160
            return opt;
 
161
    return NULL;
 
162
}
 
163
 
 
164
int 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
 
 
170
static 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
 
 
189
static 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->templ[sep + 1]) {
 
199
            const char *param = arg + sep;
 
200
            if (opt->templ[sep] == '=')
 
201
                param ++;
 
202
            if (process_opt_param(var, opt->templ + sep + 1,
 
203
                                  param, arg) == -1)
 
204
                return -1;
 
205
        } else
 
206
            *(int *)var = opt->value;
 
207
    }
 
208
    return 0;
 
209
}
 
210
 
 
211
static 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
 
 
235
static 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->templ[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
 
 
254
static 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
 
 
272
static 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
 
 
290
static 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
 
 
312
static 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
        free(ctx->outargs.argv[ctx->outargs.argc - 1]);
 
330
        ctx->outargs.argv[--ctx->outargs.argc] = NULL;
 
331
    }
 
332
 
 
333
    return 0;
 
334
}
 
335
 
 
336
int fuse_opt_parse(struct fuse_args *args, void *data,
 
337
                   const struct fuse_opt opts[], fuse_opt_proc_t proc)
 
338
{
 
339
    int res;
 
340
    struct fuse_opt_context ctx = {
 
341
        .data = data,
 
342
        .opt = opts,
 
343
        .proc = proc,
 
344
    };
 
345
 
 
346
    if (!args || !args->argv || !args->argc)
 
347
        return 0;
 
348
 
 
349
    ctx.argc = args->argc;
 
350
    ctx.argv = args->argv;
 
351
 
 
352
    res = opt_parse(&ctx);
 
353
    if (res != -1) {
 
354
        struct fuse_args tmp = *args;
 
355
        *args = ctx.outargs;
 
356
        ctx.outargs = tmp;
 
357
    }
 
358
    free(ctx.opts);
 
359
    fuse_opt_free_args(&ctx.outargs);
 
360
    return res;
 
361
}