Home | History | Annotate | Line # | Download | only in librefuse
refuse_opt.c revision 1.17
      1 /* 	$NetBSD: refuse_opt.c,v 1.17 2016/11/15 00:34:19 pho 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 <stdbool.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <string.h>
     46 
     47 #ifdef FUSE_OPT_DEBUG
     48 #define DPRINTF(x)	do { printf x; } while ( /* CONSTCOND */ 0)
     49 #else
     50 #define DPRINTF(x)
     51 #endif
     52 
     53 enum {
     54 	KEY_HELP,
     55 	KEY_VERBOSE,
     56 	KEY_VERSION
     57 };
     58 
     59 struct fuse_opt_option {
     60 	const struct fuse_opt *fop;
     61 	char *option;
     62 	int key;
     63 	void *data;
     64 };
     65 
     66 static int fuse_opt_popt(struct fuse_opt_option *, const struct fuse_opt *);
     67 
     68 /*
     69  * Public API.
     70  */
     71 
     72 /* ARGSUSED */
     73 int
     74 fuse_opt_add_arg(struct fuse_args *args, const char *arg)
     75 {
     76 	struct fuse_args	*ap;
     77 
     78 	if (args->allocated == 0) {
     79 		ap = fuse_opt_deep_copy_args(args->argc, args->argv);
     80 		args->argv = ap->argv;
     81 		args->argc = ap->argc;
     82 		args->allocated = ap->allocated;
     83 		(void) free(ap);
     84 	} else if (args->allocated == args->argc) {
     85 		void *a;
     86 		int na = args->allocated + 10;
     87 
     88 		if ((a = realloc(args->argv, na * sizeof(*args->argv))) == NULL)
     89 			return -1;
     90 
     91 		args->argv = a;
     92 		args->allocated = na;
     93 	}
     94 	DPRINTF(("%s: arguments passed: [arg:%s]\n", __func__, arg));
     95 	if ((args->argv[args->argc++] = strdup(arg)) == NULL)
     96 		err(1, "fuse_opt_add_arg");
     97 	args->argv[args->argc] = NULL;
     98         return 0;
     99 }
    100 
    101 struct fuse_args *
    102 fuse_opt_deep_copy_args(int argc, char **argv)
    103 {
    104 	struct fuse_args	*ap;
    105 	int			 i;
    106 
    107 	if ((ap = malloc(sizeof(*ap))) == NULL)
    108 		err(1, "_fuse_deep_copy_args");
    109 	/* deep copy args structure into channel args */
    110 	ap->allocated = ((argc / 10) + 1) * 10;
    111 
    112 	if ((ap->argv = calloc((size_t)ap->allocated,
    113 	    sizeof(*ap->argv))) == NULL)
    114 		err(1, "_fuse_deep_copy_args");
    115 
    116 	for (i = 0; i < argc; i++) {
    117 		if ((ap->argv[i] = strdup(argv[i])) == NULL)
    118 			err(1, "_fuse_deep_copy_args");
    119 	}
    120 	ap->argv[ap->argc = i] = NULL;
    121 	return ap;
    122 }
    123 
    124 void
    125 fuse_opt_free_args(struct fuse_args *ap)
    126 {
    127 	int	i;
    128 
    129 	for (i = 0; i < ap->argc; i++) {
    130 		free(ap->argv[i]);
    131 	}
    132 	free(ap->argv);
    133 	ap->argv = NULL;
    134 	ap->allocated = ap->argc = 0;
    135 }
    136 
    137 /* ARGSUSED */
    138 int
    139 fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
    140 {
    141 	int	i;
    142 	int	na;
    143 	void   *a;
    144 
    145 	DPRINTF(("%s: arguments passed: [pos=%d] [arg=%s]\n",
    146 	    __func__, pos, arg));
    147 	if (args->argv == NULL) {
    148 		na = 10;
    149 		a = malloc(na * sizeof(*args->argv));
    150 	} else {
    151 		na = args->allocated + 10;
    152 		a = realloc(args->argv, na * sizeof(*args->argv));
    153 	}
    154 	if (a == NULL) {
    155 		warn("fuse_opt_insert_arg");
    156 		return -1;
    157 	}
    158 	args->argv = a;
    159 	args->allocated = na;
    160 
    161 	for (i = args->argc++; i > pos; --i) {
    162 		args->argv[i] = args->argv[i - 1];
    163 	}
    164 	if ((args->argv[pos] = strdup(arg)) == NULL)
    165 		err(1, "fuse_opt_insert_arg");
    166 	args->argv[args->argc] = NULL;
    167 	return 0;
    168 }
    169 
    170 static int add_opt(char **opts, const char *opt, bool escape)
    171 {
    172 	const size_t orig_len = *opts == NULL ? 0 : strlen(*opts);
    173 	char* buf = realloc(*opts, orig_len + 1 + strlen(opt) * 2 + 1);
    174 
    175 	if (buf == NULL) {
    176 		return -1;
    177 	}
    178 	*opts = buf;
    179 
    180 	if (orig_len > 0) {
    181 		buf += orig_len;
    182 		*buf++ = ',';
    183 	}
    184 
    185 	for (; *opt; opt++) {
    186 		if (escape && (*opt == ',' || *opt == '\\')) {
    187 			*buf++ = '\\';
    188 		}
    189 		*buf++ = *opt;
    190 	}
    191 	*buf = '\0';
    192 
    193 	return 0;
    194 }
    195 
    196 int fuse_opt_add_opt(char **opts, const char *opt)
    197 {
    198 	DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n",
    199 	    __func__, *opts, opt));
    200 	return add_opt(opts, opt, false);
    201 }
    202 
    203 int fuse_opt_add_opt_escaped(char **opts, const char *opt)
    204 {
    205 	DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n",
    206 	    __func__, *opts, opt));
    207 	return add_opt(opts, opt, true);
    208 }
    209 
    210 static bool match_templ(const char *templ, const char *opt, size_t *sep_idx)
    211 {
    212 	const char *sep = strpbrk(templ, "= ");
    213 
    214 	if (sep != NULL && (sep[1] == '\0' || sep[1] == '%')) {
    215 		const size_t cmp_len =
    216 			sep[0] == '=' ? sep - templ + 1 : sep - templ;
    217 
    218 		if (strlen(opt) >= cmp_len && strncmp(templ, opt, cmp_len) == 0) {
    219 			if (sep_idx != NULL)
    220 				*sep_idx = sep - templ;
    221 			return true;
    222 		}
    223 		else {
    224 			return false;
    225 		}
    226 	}
    227 	else {
    228 		if (strcmp(templ, opt) == 0) {
    229 			if (sep_idx != NULL)
    230 				*sep_idx = 0;
    231 			return true;
    232 		}
    233 		else {
    234 			return false;
    235 		}
    236 	}
    237 }
    238 
    239 static const struct fuse_opt *
    240 find_opt(const struct fuse_opt *opts, const char *opt, size_t *sep_idx)
    241 {
    242 	for (; opts != NULL && opts->templ != NULL; opts++) {
    243 		if (match_templ(opts->templ, opt, sep_idx))
    244 			return opts;
    245 	}
    246 	return NULL;
    247 }
    248 
    249 /*
    250  * Returns 1 if opt was matched with any option from opts,
    251  * otherwise returns 0.
    252  */
    253 int
    254 fuse_opt_match(const struct fuse_opt *opts, const char *opt)
    255 {
    256 	return find_opt(opts, opt, NULL) != NULL ? 1 : 0;
    257 }
    258 
    259 /*
    260  * Returns 0 if foo->option was matched with any option from opts,
    261  * and sets the following on match:
    262  *
    263  * 	* foo->key is set to the foo->fop->value if offset == -1.
    264  * 	* foo->fop points to the matched struct opts.
    265  *
    266  * otherwise returns 1.
    267  */
    268 static int
    269 fuse_opt_popt(struct fuse_opt_option *foo, const struct fuse_opt *opts)
    270 {
    271 	int i, found = 0;
    272 	char *match;
    273 
    274 	if (!foo->option) {
    275 		(void)fprintf(stderr, "fuse: missing argument after -o\n");
    276 		return 1;
    277 	}
    278 	/*
    279 	 * iterate over argv and opts to see
    280 	 * if there's a match with any template.
    281 	 */
    282 	for (match = strtok(foo->option, ",");
    283 	     match; match = strtok(NULL, ",")) {
    284 
    285 		DPRINTF(("%s: specified option='%s'\n", __func__, match));
    286 		found = 0;
    287 
    288 		for (i = 0; opts && opts->templ; opts++, i++) {
    289 
    290 			DPRINTF(("%s: opts->templ='%s' opts->offset=%d "
    291 			    "opts->value=%d\n", __func__, opts->templ,
    292 			    opts->offset, opts->value));
    293 
    294 			/* option is ok */
    295 			if (strcmp(match, opts->templ) == 0) {
    296 				DPRINTF(("%s: option matched='%s'\n",
    297 				    __func__, match));
    298 				found++;
    299 				/*
    300 				 * our fop pointer now points
    301 				 * to the matched struct opts.
    302 				 */
    303 				foo->fop = opts;
    304 				/*
    305 				 * assign default key value, necessary for
    306 				 * KEY_HELP, KEY_VERSION and KEY_VERBOSE.
    307 				 */
    308 				if (foo->fop->offset == -1)
    309 					foo->key = foo->fop->value;
    310 				/* reset counter */
    311 				opts -= i;
    312 				break;
    313 			}
    314 		}
    315 		/* invalid option */
    316 		if (!found) {
    317 			(void)fprintf(stderr, "fuse: '%s' is not a "
    318 			    "valid option\n", match);
    319 			return 1;
    320 		}
    321 	}
    322 
    323 	return 0;
    324 }
    325 
    326 /* ARGSUSED1 */
    327 int
    328 fuse_opt_parse(struct fuse_args *args, void *data,
    329         const struct fuse_opt *opts, fuse_opt_proc_t proc)
    330 {
    331 	struct fuse_opt_option foo;
    332 	char *buf;
    333 	int i, rv = 0;
    334 
    335 	if (!args || !args->argv || !args->argc || !proc)
    336 		return 0;
    337 
    338 	foo.data = data;
    339 	if (args->argc == 1)
    340 		return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args);
    341 
    342 	/* the real loop to process the arguments */
    343 	for (i = 1; i < args->argc; i++) {
    344 
    345 		/* assign current argv string */
    346 		foo.option = buf = args->argv[i];
    347 
    348 		/* argvn != -foo... */
    349 		if (buf[0] != '-') {
    350 
    351 			foo.key = FUSE_OPT_KEY_NONOPT;
    352 			rv = proc(foo.data, foo.option, foo.key, args);
    353 			if (rv != 0)
    354 				break;
    355 
    356 		/* -o was specified... */
    357 		} else if (buf[0] == '-' && buf[1] == 'o') {
    358 
    359 			/* -oblah,foo... */
    360 			if (buf[2]) {
    361 				/* skip -o */
    362 				foo.option = args->argv[i] + 2;
    363 			/* -o blah,foo... */
    364 			} else {
    365 				/*
    366 			 	 * skip current argv and pass to the
    367 			 	 * next one to parse the options.
    368 				 */
    369 				++i;
    370 				foo.option = args->argv[i];
    371 			}
    372 
    373 			rv = fuse_opt_popt(&foo, opts);
    374 			if (rv != 0)
    375 				break;
    376 
    377 		/* help/version/verbose argument */
    378 		} else if (buf[0] == '-' && buf[1] != 'o') {
    379 			/*
    380 			 * check if the argument matches
    381 			 * with any template in opts.
    382 			 */
    383 			rv = fuse_opt_popt(&foo, opts);
    384 			if (rv != 0) {
    385 				break;
    386 			} else {
    387 				DPRINTF(("%s: foo.fop->templ='%s' "
    388 			    	    "foo.fop->offset: %d "
    389 			    	    "foo.fop->value: %d\n",
    390 			    	    __func__, foo.fop->templ,
    391 			    	    foo.fop->offset, foo.fop->value));
    392 
    393 				/* argument needs to be discarded */
    394 				if (foo.key == FUSE_OPT_KEY_DISCARD) {
    395 					rv = 1;
    396 					break;
    397 				}
    398 
    399 				/* process help/version argument */
    400 				if (foo.key != KEY_VERBOSE &&
    401 				    foo.key != FUSE_OPT_KEY_KEEP) {
    402 					rv = proc(foo.data, foo.option,
    403 				    		  foo.key, args);
    404 					break;
    405 				} else {
    406 					/* process verbose argument */
    407 					rv = proc(foo.data, foo.option,
    408 						       foo.key, args);
    409 					if (rv != 0)
    410 						break;
    411 				}
    412 			}
    413 		/* unknown option, how could that happen? */
    414 		} else {
    415 			DPRINTF(("%s: unknown option\n", __func__));
    416 			rv = 1;
    417 			break;
    418 		}
    419 	}
    420 
    421 	return rv;
    422 }
    423