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