qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

fuse_opt.c (10822B)


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