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