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