refuse_opt.c revision 1.6 1 /* $NetBSD: refuse_opt.c,v 1.6 2007/04/17 00:20:52 xtraeme 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
44 #ifdef FUSE_OPT_DEBUG
45 #define DPRINTF(x) do { printf x; } while ( /* CONSTCOND */ 0)
46 #else
47 #define DPRINTF(x)
48 #endif
49
50 enum {
51 KEY_HELP,
52 KEY_VERBOSE,
53 KEY_VERSION
54 };
55
56 struct fuse_opt_option {
57 const struct fuse_opt *fop;
58 char *option;
59 int key;
60 void *data;
61 };
62
63 static int fuse_opt_popt(struct fuse_opt_option *, const struct fuse_opt *);
64
65 /*
66 * Public API.
67 *
68 * The following functions always return 0:
69 *
70 * int fuse_opt_add_arg(struct fuse_args *, const char *);
71 * int fuse_opt_add_opt(char **, const char *);
72 * void fuse_opt_free_args(struct fuse_args *);
73 * int fuse_opt_insert_arg(struct fuse_args *, const char *);
74 *
75 * We implement the next ones:
76 *
77 * int fuse_opt_match(const struct fuse_opt *, const char *);
78 * int fuse_opt_parse(struct fuse_args *, void *,
79 * const struct fuse_opt *, fuse_opt_proc_t);
80 *
81 */
82
83 /* function to perform a deep copy of the args structure */
84 static struct fuse_args *
85 deep_copy_args(int argc, char **argv)
86 {
87 struct fuse_args *ap;
88
89 NEW(struct fuse_args, ap, "deep_copy_args", return NULL);
90 /* deep copy args structure into channel args */
91 ap->allocated = ((argc / 10) + 1) * 10;
92 NEWARRAY(char *, ap->argv, ap->allocated, "fuse_mount", return NULL);
93 for (ap->argc = 0 ; ap->argc < argc ; ap->argc++) {
94 ap->argv[ap->argc] = strdup(argv[ap->argc]);
95 }
96 return ap;
97 }
98
99 /* ARGSUSED */
100 int
101 fuse_opt_add_arg(struct fuse_args *args, const char *arg)
102 {
103 struct fuse_args *ap;
104
105 if (args->allocated == 0) {
106 ap = deep_copy_args(args->argc, args->argv);
107 args->argv = ap->argv;
108 args->argc = ap->argc;
109 args->allocated = ap->allocated;
110 (void) free(ap);
111 } else if (args->allocated == args->argc) {
112 args->allocated += 10;
113 RENEW(char *, args->argv, args->allocated,
114 "fuse_opt_add_arg", return 1);
115 }
116 DPRINTF(("%s: arguments passed: [arg:%s]\n", __func__, arg));
117 args->argv[args->argc++] = strdup(arg);
118 return 0;
119 }
120
121 /* ARGSUSED */
122 void
123 fuse_opt_free_args(struct fuse_args *args)
124 {
125 int i;
126
127 for (i = 0 ; i < args->argc ; i++) {
128 FREE(args->argv[i]);
129 }
130 FREE(args->argv);
131 args->allocated = args->argc = 0;
132 }
133
134 /* ARGSUSED */
135 int
136 fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
137 {
138 int i;
139
140 DPRINTF(("%s: arguments passed: [pos=%d] [arg=%s]\n",
141 __func__, pos, arg));
142 ALLOC(char *, args->argv, args->allocated, args->argc, 10, 10,
143 "fuse_opt_insert_org", return 1);
144 for (i = args->argc++ ; i > pos ; --i) {
145 args->argv[i] = args->argv[i - 1];
146 }
147 args->argv[pos] = strdup(arg);
148 return 0;
149 }
150
151 /* ARGSUSED */
152 int fuse_opt_add_opt(char **opts, const char *opt)
153 {
154 DPRINTF(("%s: arguments passed: [opts=%s] [opt=%s]\n",
155 __func__, *opts, opt));
156 return 0;
157 }
158
159 /*
160 * Returns 0 if opt was matched with any option from opts,
161 * otherwise returns 1.
162 */
163 int
164 fuse_opt_match(const struct fuse_opt *opts, const char *opt)
165 {
166 while (opts++) {
167 if (strcmp(opt, opts->templ) == 0)
168 return 0;
169 }
170
171 return 1;
172 }
173
174 /*
175 * Returns 0 if foo->option was matched with any option from opts,
176 * and sets the following on match:
177 *
178 * * foo->key is set to the foo->fop->value if offset == -1.
179 * * foo->fop points to the matched struct opts.
180 *
181 * otherwise returns 1.
182 */
183 static int
184 fuse_opt_popt(struct fuse_opt_option *foo, const struct fuse_opt *opts)
185 {
186 int i, found = 0;
187 char *match;
188
189 if (!foo->option) {
190 (void)fprintf(stderr, "fuse: missing argument after -o\n");
191 return 1;
192 }
193 /*
194 * iterate over argv and opts to see
195 * if there's a match with any template.
196 */
197 for (match = strtok(foo->option, ",");
198 match; match = strtok(NULL, ",")) {
199
200 DPRINTF(("%s: specified option='%s'\n", __func__, match));
201 found = 0;
202
203 for (i = 0; opts && opts->templ; opts++, i++) {
204
205 DPRINTF(("%s: opts->templ='%s' opts->offset=%d "
206 "opts->value=%d\n", __func__, opts->templ,
207 opts->offset, opts->value));
208
209 /* option is ok */
210 if (strcmp(match, opts->templ) == 0) {
211 DPRINTF(("%s: option matched='%s'\n",
212 __func__, match));
213 found++;
214 /*
215 * our fop pointer now points
216 * to the matched struct opts.
217 */
218 foo->fop = opts;
219 /*
220 * assign default key value, necessary for
221 * KEY_HELP, KEY_VERSION and KEY_VERBOSE.
222 */
223 if (foo->fop->offset == -1)
224 foo->key = foo->fop->value;
225 /* reset counter */
226 opts -= i;
227 break;
228 }
229 }
230 /* invalid option */
231 if (!found) {
232 (void)fprintf(stderr, "fuse: '%s' is not a "
233 "valid option\n", match);
234 return 1;
235 }
236 }
237
238 return 0;
239 }
240
241 /* ARGSUSED1 */
242 int
243 fuse_opt_parse(struct fuse_args *args, void *data,
244 const struct fuse_opt *opts, fuse_opt_proc_t proc)
245 {
246 struct fuse_opt_option foo;
247 char *buf;
248 int i, rv = 0;
249
250 if (!args || !args->argv || !args->argc || !proc)
251 return 0;
252
253 if (args->argc == 1)
254 return proc(foo.data, *args->argv, FUSE_OPT_KEY_OPT, args);
255
256 /* the real loop to process the arguments */
257 for (i = 1; i < args->argc; i++) {
258
259 /* assign current argv string */
260 foo.option = buf = args->argv[i];
261
262 /* argvn != -foo... */
263 if (buf[0] != '-') {
264
265 foo.key = FUSE_OPT_KEY_NONOPT;
266 rv = proc(foo.data, foo.option, foo.key, args);
267 if (rv != 0)
268 break;
269
270 /* -o was specified... */
271 } else if (buf[0] == '-' && buf[1] == 'o') {
272
273 /* -oblah,foo... */
274 if (buf[2]) {
275 /* skip -o */
276 foo.option = args->argv[i] + 2;
277 /* -o blah,foo... */
278 } else {
279 /*
280 * skip current argv and pass to the
281 * next one to parse the options.
282 */
283 ++i;
284 foo.option = args->argv[i];
285 }
286
287 rv = fuse_opt_popt(&foo, opts);
288 if (rv != 0)
289 break;
290
291 /* help/version/verbose argument */
292 } else if (buf[0] == '-' && buf[1] != 'o') {
293 /*
294 * check if the argument matches
295 * with any template in opts.
296 */
297 rv = fuse_opt_popt(&foo, opts);
298 if (rv != 0) {
299 break;
300 } else {
301 DPRINTF(("%s: foo.fop->templ='%s' "
302 "foo.fop->offset: %d "
303 "foo.fop->value: %d\n",
304 __func__, foo.fop->templ,
305 foo.fop->offset, foo.fop->value));
306
307 /* argument needs to be discarded */
308 if (foo.key == FUSE_OPT_KEY_DISCARD) {
309 rv = 1;
310 break;
311 }
312
313 /* process help/version argument */
314 if (foo.key != KEY_VERBOSE &&
315 foo.key != FUSE_OPT_KEY_KEEP) {
316 rv = proc(foo.data, foo.option,
317 foo.key, args);
318 break;
319 } else {
320 /* process verbose argument */
321 rv = proc(foo.data, foo.option,
322 foo.key, args);
323 if (rv != 0)
324 break;
325 }
326 }
327 /* unknown option, how could that happen? */
328 } else {
329 DPRINTF(("%s: unknown option\n", __func__));
330 rv = 1;
331 break;
332 }
333 }
334
335 return rv;
336 }
337