1 /* $NetBSD: refuse_opt.c,v 1.14 2009/01/19 09:56:06 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Juan Romero Pardines. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /* 29 * TODO: 30 * * -oblah,foo... works, but the options are not enabled. 31 * * -ofoo=%s (accepts a string) or -ofoo=%u (int) is not 32 * supported for now. 33 * * void *data: how is it used? I think it's used to enable 34 * options or pass values for the matching options. 35 */ 36 37 #include <sys/types.h> 38 39 #include <err.h> 40 #include <fuse.h> 41 #include <fuse_opt.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 #ifdef FUSE_OPT_DEBUG 47 #define DPRINTF(x) do { printf x; } while ( /* CONSTCOND */ 0) 48 #else 49 #define DPRINTF(x) 50 #endif 51 52 enum { 53 KEY_HELP, 54 KEY_VERBOSE, 55 KEY_VERSION 56 }; 57 58 struct fuse_opt_option { 59 const struct fuse_opt *fop; 60 char *option; 61 int key; 62 void *data; 63 }; 64 65 static int fuse_opt_popt(struct fuse_opt_option *, const struct fuse_opt *); 66 67 /* 68 * Public API. 69 * 70 * The following functions always return 0: 71 * 72 * int fuse_opt_add_opt(char **, const char *); 73 * 74 * We implement the next ones: 75 * 76 * int fuse_opt_add_arg(struct fuse_args *, const char *); 77 * void fuse_opt_free_args(struct fuse_args *); 78 * int fuse_opt_insert_arg(struct fuse_args *, const char *); 79 * int fuse_opt_match(const struct fuse_opt *, const char *); 80 * int fuse_opt_parse(struct fuse_args *, void *, 81 * const struct fuse_opt *, fuse_opt_proc_t); 82 * 83 */ 84 85 /* ARGSUSED */ 86 int 87 fuse_opt_add_arg(struct fuse_args *args, const char *arg) 88 { 89 struct fuse_args *ap; 90 91 if (args->allocated == 0) { 92 ap = fuse_opt_deep_copy_args(args->argc, args->argv); 93 args->argv = ap->argv; 94 args->argc = ap->argc; 95 args->allocated = ap->allocated; 96 (void) free(ap); 97 } else if (args->allocated == args->argc) { 98 void *a; 99 int na = args->allocated + 10; 100 101 if ((a = realloc(args->argv, na * sizeof(*args->argv))) == NULL) 102 return -1; 103 104 args->argv = a; 105 args->allocated = na; 106 } 107 DPRINTF(("%s: arguments passed: [arg:%s]\n", __func__, arg)); 108 if ((args->argv[args->argc++] = strdup(arg)) == NULL) 109 err(1, "fuse_opt_add_arg"); 110 args->argv[args->argc] = NULL; 111 return 0; 112 } 113 114 struct fuse_args * 115 fuse_opt_deep_copy_args(int argc, char **argv) 116 { 117 struct fuse_args *ap; 118 int i; 119 120 if ((ap = malloc(sizeof(*ap))) == NULL) 121 err(1, "_fuse_deep_copy_args"); 122 /* deep copy args structure into channel args */ 123 ap->allocated = ((argc / 10) + 1) * 10; 124 125 if ((ap->argv = calloc((size_t)ap->allocated, 126 sizeof(*ap->argv))) == NULL) 127 err(1, "_fuse_deep_copy_args"); 128 129 for (i = 0; i < argc; i++) { 130 if ((ap->argv[i] = strdup(argv[i])) == NULL) 131 err(1, "_fuse_deep_copy_args"); 132 } 133 ap->argv[ap->argc = i] = NULL; 134 return ap; 135 } 136 137 void 138 fuse_opt_free_args(struct fuse_args *ap) 139 { 140 int i; 141 142 for (i = 0; i < ap->argc; i++) { 143 free(ap->argv[i]); 144 } 145 free(ap->argv); 146 ap->argv = NULL; 147 ap->allocated = ap->argc = 0; 148 } 149 150 /* ARGSUSED */ 151 int 152 fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) 153 { 154 int i; 155 int na; 156 void *a; 157 158 DPRINTF(("%s: arguments passed: [pos=%d] [arg=%s]\n", 159 __func__, pos, arg)); 160 if (args->argv == NULL) { 161 na = 10; 162 a = malloc(na * sizeof(*args->argv)); 163 } else { 164 na = args->allocated + 10; 165 a = realloc(args->argv, na * sizeof(*args->argv)); 166 } 167 if (a == NULL) { 168 warn("fuse_opt_insert_arg"); 169 return -1; 170 } 171 args->argv = a; 172 args->allocated = na; 173 174 for (i = args->argc++; i > pos; --i) { 175 args->argv[i] = args->argv[i - 1]; 176 } 177 if ((args->argv[pos] = strdup(arg)) == NULL) 178 err(1, "fuse_opt_insert_arg"); 179 args->argv[args->argc] = NULL; 180 return 0; 181 } 182 183 /* ARGSUSED */ 184 int fuse_opt_add_opt(char **opts, const char *opt) 185 { 186 DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n", 187 __func__, *opts, opt)); 188 return 0; 189 } 190 191 /* 192 * Returns 0 if opt was matched with any option from opts, 193 * otherwise returns 1. 194 */ 195 int 196 fuse_opt_match(const struct fuse_opt *opts, const char *opt) 197 { 198 while (opts++) { 199 if (strcmp(opt, opts->templ) == 0) 200 return 0; 201 } 202 203 return 1; 204 } 205 206 /* 207 * Returns 0 if foo->option was matched with any option from opts, 208 * and sets the following on match: 209 * 210 * * foo->key is set to the foo->fop->value if offset == -1. 211 * * foo->fop points to the matched struct opts. 212 * 213 * otherwise returns 1. 214 */ 215 static int 216 fuse_opt_popt(struct fuse_opt_option *foo, const struct fuse_opt *opts) 217 { 218 int i, found = 0; 219 char *match; 220 221 if (!foo->option) { 222 (void)fprintf(stderr, "fuse: missing argument after -o\n"); 223 return 1; 224 } 225 /* 226 * iterate over argv and opts to see 227 * if there's a match with any template. 228 */ 229 for (match = strtok(foo->option, ","); 230 match; match = strtok(NULL, ",")) { 231 232 DPRINTF(("%s: specified option='%s'\n", __func__, match)); 233 found = 0; 234 235 for (i = 0; opts && opts->templ; opts++, i++) { 236 237 DPRINTF(("%s: opts->templ='%s' opts->offset=%d " 238 "opts->value=%d\n", __func__, opts->templ, 239 opts->offset, opts->value)); 240 241 /* option is ok */ 242 if (strcmp(match, opts->templ) == 0) { 243 DPRINTF(("%s: option matched='%s'\n", 244 __func__, match)); 245 found++; 246 /* 247 * our fop pointer now points 248 * to the matched struct opts. 249 */ 250 foo->fop = opts; 251 /* 252 * assign default key value, necessary for 253 * KEY_HELP, KEY_VERSION and KEY_VERBOSE. 254 */ 255 if (foo->fop->offset == -1) 256 foo->key = foo->fop->value; 257 /* reset counter */ 258 opts -= i; 259 break; 260 } 261 } 262 /* invalid option */ 263 if (!found) { 264 (void)fprintf(stderr, "fuse: '%s' is not a " 265 "valid option\n", match); 266 return 1; 267 } 268 } 269 270 return 0; 271 } 272 273 /* ARGSUSED1 */ 274 int 275 fuse_opt_parse(struct fuse_args *args, void *data, 276 const struct fuse_opt *opts, fuse_opt_proc_t proc) 277 { 278 struct fuse_opt_option foo; 279 char *buf; 280 int i, rv = 0; 281 282 if (!args || !args->argv || !args->argc || !proc) 283 return 0; 284 285 if (args->argc == 1) 286 return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args); 287 288 /* the real loop to process the arguments */ 289 for (i = 1; i < args->argc; i++) { 290 291 /* assign current argv string */ 292 foo.option = buf = args->argv[i]; 293 294 /* argvn != -foo... */ 295 if (buf[0] != '-') { 296 297 foo.key = FUSE_OPT_KEY_NONOPT; 298 rv = proc(foo.data, foo.option, foo.key, args); 299 if (rv != 0) 300 break; 301 302 /* -o was specified... */ 303 } else if (buf[0] == '-' && buf[1] == 'o') { 304 305 /* -oblah,foo... */ 306 if (buf[2]) { 307 /* skip -o */ 308 foo.option = args->argv[i] + 2; 309 /* -o blah,foo... */ 310 } else { 311 /* 312 * skip current argv and pass to the 313 * next one to parse the options. 314 */ 315 ++i; 316 foo.option = args->argv[i]; 317 } 318 319 rv = fuse_opt_popt(&foo, opts); 320 if (rv != 0) 321 break; 322 323 /* help/version/verbose argument */ 324 } else if (buf[0] == '-' && buf[1] != 'o') { 325 /* 326 * check if the argument matches 327 * with any template in opts. 328 */ 329 rv = fuse_opt_popt(&foo, opts); 330 if (rv != 0) { 331 break; 332 } else { 333 DPRINTF(("%s: foo.fop->templ='%s' " 334 "foo.fop->offset: %d " 335 "foo.fop->value: %d\n", 336 __func__, foo.fop->templ, 337 foo.fop->offset, foo.fop->value)); 338 339 /* argument needs to be discarded */ 340 if (foo.key == FUSE_OPT_KEY_DISCARD) { 341 rv = 1; 342 break; 343 } 344 345 /* process help/version argument */ 346 if (foo.key != KEY_VERBOSE && 347 foo.key != FUSE_OPT_KEY_KEEP) { 348 rv = proc(foo.data, foo.option, 349 foo.key, args); 350 break; 351 } else { 352 /* process verbose argument */ 353 rv = proc(foo.data, foo.option, 354 foo.key, args); 355 if (rv != 0) 356 break; 357 } 358 } 359 /* unknown option, how could that happen? */ 360 } else { 361 DPRINTF(("%s: unknown option\n", __func__)); 362 rv = 1; 363 break; 364 } 365 } 366 367 return rv; 368 } 369