xref: /minix3/lib/librefuse/refuse_opt.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1 /* 	$NetBSD: refuse_opt.c,v 1.15 2011/03/01 11:23:42 soda 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
fuse_opt_add_arg(struct fuse_args * args,const char * arg)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 *
fuse_opt_deep_copy_args(int argc,char ** argv)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
fuse_opt_free_args(struct fuse_args * ap)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
fuse_opt_insert_arg(struct fuse_args * args,int pos,const char * arg)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 */
fuse_opt_add_opt(char ** opts,const char * opt)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
fuse_opt_match(const struct fuse_opt * opts,const char * opt)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
fuse_opt_popt(struct fuse_opt_option * foo,const struct fuse_opt * opts)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
fuse_opt_parse(struct fuse_args * args,void * data,const struct fuse_opt * opts,fuse_opt_proc_t proc)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 	foo.data = data;
286 	if (args->argc == 1)
287 		return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args);
288 
289 	/* the real loop to process the arguments */
290 	for (i = 1; i < args->argc; i++) {
291 
292 		/* assign current argv string */
293 		foo.option = buf = args->argv[i];
294 
295 		/* argvn != -foo... */
296 		if (buf[0] != '-') {
297 
298 			foo.key = FUSE_OPT_KEY_NONOPT;
299 			rv = proc(foo.data, foo.option, foo.key, args);
300 			if (rv != 0)
301 				break;
302 
303 		/* -o was specified... */
304 		} else if (buf[0] == '-' && buf[1] == 'o') {
305 
306 			/* -oblah,foo... */
307 			if (buf[2]) {
308 				/* skip -o */
309 				foo.option = args->argv[i] + 2;
310 			/* -o blah,foo... */
311 			} else {
312 				/*
313 			 	 * skip current argv and pass to the
314 			 	 * next one to parse the options.
315 				 */
316 				++i;
317 				foo.option = args->argv[i];
318 			}
319 
320 			rv = fuse_opt_popt(&foo, opts);
321 			if (rv != 0)
322 				break;
323 
324 		/* help/version/verbose argument */
325 		} else if (buf[0] == '-' && buf[1] != 'o') {
326 			/*
327 			 * check if the argument matches
328 			 * with any template in opts.
329 			 */
330 			rv = fuse_opt_popt(&foo, opts);
331 			if (rv != 0) {
332 				break;
333 			} else {
334 				DPRINTF(("%s: foo.fop->templ='%s' "
335 			    	    "foo.fop->offset: %d "
336 			    	    "foo.fop->value: %d\n",
337 			    	    __func__, foo.fop->templ,
338 			    	    foo.fop->offset, foo.fop->value));
339 
340 				/* argument needs to be discarded */
341 				if (foo.key == FUSE_OPT_KEY_DISCARD) {
342 					rv = 1;
343 					break;
344 				}
345 
346 				/* process help/version argument */
347 				if (foo.key != KEY_VERBOSE &&
348 				    foo.key != FUSE_OPT_KEY_KEEP) {
349 					rv = proc(foo.data, foo.option,
350 				    		  foo.key, args);
351 					break;
352 				} else {
353 					/* process verbose argument */
354 					rv = proc(foo.data, foo.option,
355 						       foo.key, args);
356 					if (rv != 0)
357 						break;
358 				}
359 			}
360 		/* unknown option, how could that happen? */
361 		} else {
362 			DPRINTF(("%s: unknown option\n", __func__));
363 			rv = 1;
364 			break;
365 		}
366 	}
367 
368 	return rv;
369 }
370