1 | /* |
2 | * Copyright 2008-2009 Katholieke Universiteit Leuven |
3 | * |
4 | * Use of this software is governed by the MIT license |
5 | * |
6 | * Written by Sven Verdoolaege, K.U.Leuven, Departement |
7 | * Computerwetenschappen, Celestijnenlaan 200A, B-3001 Leuven, Belgium |
8 | */ |
9 | |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | |
14 | #include <isl/arg.h> |
15 | #include <isl/ctx.h> |
16 | #include <isl_config.h> |
17 | |
18 | static struct isl_arg help_arg[] = { |
19 | ISL_ARG_PHANTOM_BOOL('h', "help" , NULL, "print this help, then exit" ) |
20 | { isl_arg_end } |
21 | }; |
22 | |
23 | static void set_default_choice(struct isl_arg *arg, void *opt) |
24 | { |
25 | if (arg->offset == ISL_ARG_OFFSET_NONE) |
26 | return; |
27 | *(unsigned *)(((char *)opt) + arg->offset) = arg->u.choice.default_value; |
28 | } |
29 | |
30 | static void set_default_flags(struct isl_arg *arg, void *opt) |
31 | { |
32 | *(unsigned *)(((char *)opt) + arg->offset) = arg->u.flags.default_value; |
33 | } |
34 | |
35 | static void set_default_bool(struct isl_arg *arg, void *opt) |
36 | { |
37 | if (arg->offset == ISL_ARG_OFFSET_NONE) |
38 | return; |
39 | *(unsigned *)(((char *)opt) + arg->offset) = arg->u.b.default_value; |
40 | } |
41 | |
42 | static void set_default_child(struct isl_arg *arg, void *opt) |
43 | { |
44 | void *child; |
45 | |
46 | if (arg->offset == ISL_ARG_OFFSET_NONE) |
47 | child = opt; |
48 | else { |
49 | child = calloc(nmemb: 1, size: arg->u.child.child->options_size); |
50 | *(void **)(((char *)opt) + arg->offset) = child; |
51 | } |
52 | |
53 | if (child) |
54 | isl_args_set_defaults(args: arg->u.child.child, opt: child); |
55 | } |
56 | |
57 | static void set_default_user(struct isl_arg *arg, void *opt) |
58 | { |
59 | arg->u.user.init(((char *)opt) + arg->offset); |
60 | } |
61 | |
62 | static void set_default_int(struct isl_arg *arg, void *opt) |
63 | { |
64 | *(int *)(((char *)opt) + arg->offset) = arg->u.i.default_value; |
65 | } |
66 | |
67 | static void set_default_long(struct isl_arg *arg, void *opt) |
68 | { |
69 | *(long *)(((char *)opt) + arg->offset) = arg->u.l.default_value; |
70 | } |
71 | |
72 | static void set_default_ulong(struct isl_arg *arg, void *opt) |
73 | { |
74 | *(unsigned long *)(((char *)opt) + arg->offset) = arg->u.ul.default_value; |
75 | } |
76 | |
77 | static void set_default_str(struct isl_arg *arg, void *opt) |
78 | { |
79 | const char *str = NULL; |
80 | if (arg->u.str.default_value) |
81 | str = strdup(s: arg->u.str.default_value); |
82 | *(const char **)(((char *)opt) + arg->offset) = str; |
83 | } |
84 | |
85 | static void set_default_str_list(struct isl_arg *arg, void *opt) |
86 | { |
87 | *(const char ***)(((char *) opt) + arg->offset) = NULL; |
88 | *(int *)(((char *) opt) + arg->u.str_list.offset_n) = 0; |
89 | } |
90 | |
91 | void isl_args_set_defaults(struct isl_args *args, void *opt) |
92 | { |
93 | int i; |
94 | |
95 | for (i = 0; args->args[i].type != isl_arg_end; ++i) { |
96 | switch (args->args[i].type) { |
97 | case isl_arg_choice: |
98 | set_default_choice(arg: &args->args[i], opt); |
99 | break; |
100 | case isl_arg_flags: |
101 | set_default_flags(arg: &args->args[i], opt); |
102 | break; |
103 | case isl_arg_bool: |
104 | set_default_bool(arg: &args->args[i], opt); |
105 | break; |
106 | case isl_arg_child: |
107 | set_default_child(arg: &args->args[i], opt); |
108 | break; |
109 | case isl_arg_user: |
110 | set_default_user(arg: &args->args[i], opt); |
111 | break; |
112 | case isl_arg_int: |
113 | set_default_int(arg: &args->args[i], opt); |
114 | break; |
115 | case isl_arg_long: |
116 | set_default_long(arg: &args->args[i], opt); |
117 | break; |
118 | case isl_arg_ulong: |
119 | set_default_ulong(arg: &args->args[i], opt); |
120 | break; |
121 | case isl_arg_arg: |
122 | case isl_arg_str: |
123 | set_default_str(arg: &args->args[i], opt); |
124 | break; |
125 | case isl_arg_str_list: |
126 | set_default_str_list(arg: &args->args[i], opt); |
127 | break; |
128 | case isl_arg_alias: |
129 | case isl_arg_footer: |
130 | case isl_arg_version: |
131 | case isl_arg_end: |
132 | break; |
133 | } |
134 | } |
135 | } |
136 | |
137 | static void free_args(struct isl_arg *arg, void *opt); |
138 | |
139 | static void free_child(struct isl_arg *arg, void *opt) |
140 | { |
141 | if (arg->offset == ISL_ARG_OFFSET_NONE) |
142 | free_args(arg: arg->u.child.child->args, opt); |
143 | else |
144 | isl_args_free(args: arg->u.child.child, |
145 | opt: *(void **)(((char *)opt) + arg->offset)); |
146 | } |
147 | |
148 | static void free_str_list(struct isl_arg *arg, void *opt) |
149 | { |
150 | int i; |
151 | int n = *(int *)(((char *) opt) + arg->u.str_list.offset_n); |
152 | char **list = *(char ***)(((char *) opt) + arg->offset); |
153 | |
154 | for (i = 0; i < n; ++i) |
155 | free(ptr: list[i]); |
156 | free(ptr: list); |
157 | } |
158 | |
159 | static void free_user(struct isl_arg *arg, void *opt) |
160 | { |
161 | if (arg->u.user.clear) |
162 | arg->u.user.clear(((char *)opt) + arg->offset); |
163 | } |
164 | |
165 | static void free_args(struct isl_arg *arg, void *opt) |
166 | { |
167 | int i; |
168 | |
169 | for (i = 0; arg[i].type != isl_arg_end; ++i) { |
170 | switch (arg[i].type) { |
171 | case isl_arg_child: |
172 | free_child(arg: &arg[i], opt); |
173 | break; |
174 | case isl_arg_arg: |
175 | case isl_arg_str: |
176 | free(ptr: *(char **)(((char *)opt) + arg[i].offset)); |
177 | break; |
178 | case isl_arg_str_list: |
179 | free_str_list(arg: &arg[i], opt); |
180 | break; |
181 | case isl_arg_user: |
182 | free_user(arg: &arg[i], opt); |
183 | break; |
184 | case isl_arg_alias: |
185 | case isl_arg_bool: |
186 | case isl_arg_choice: |
187 | case isl_arg_flags: |
188 | case isl_arg_int: |
189 | case isl_arg_long: |
190 | case isl_arg_ulong: |
191 | case isl_arg_version: |
192 | case isl_arg_footer: |
193 | case isl_arg_end: |
194 | break; |
195 | } |
196 | } |
197 | } |
198 | |
199 | void isl_args_free(struct isl_args *args, void *opt) |
200 | { |
201 | if (!opt) |
202 | return; |
203 | |
204 | free_args(arg: args->args, opt); |
205 | |
206 | free(ptr: opt); |
207 | } |
208 | |
209 | /* Data structure for collecting the prefixes of ancestor nodes. |
210 | * |
211 | * n is the number of prefixes. |
212 | * prefix[i] for i < n is a prefix of an ancestor. |
213 | * len[i] for i < n is the length of prefix[i]. |
214 | */ |
215 | struct isl_prefixes { |
216 | int n; |
217 | const char *prefix[10]; |
218 | size_t len[10]; |
219 | }; |
220 | |
221 | /* Add "prefix" to the list of prefixes and return the updated |
222 | * number of prefixes. |
223 | */ |
224 | static int add_prefix(struct isl_prefixes *prefixes, const char *prefix) |
225 | { |
226 | int n = prefixes->n; |
227 | |
228 | if (!prefix) |
229 | return n; |
230 | |
231 | if (prefixes->n >= 10) { |
232 | fprintf(stderr, format: "too many prefixes\n" ); |
233 | exit(EXIT_FAILURE); |
234 | } |
235 | prefixes->len[prefixes->n] = strlen(s: prefix); |
236 | prefixes->prefix[prefixes->n] = prefix; |
237 | prefixes->n++; |
238 | |
239 | return n; |
240 | } |
241 | |
242 | /* Drop all prefixes starting at "first". |
243 | */ |
244 | static void drop_prefix(struct isl_prefixes *prefixes, int first) |
245 | { |
246 | prefixes->n = first; |
247 | } |
248 | |
249 | /* Print the prefixes in "prefixes". |
250 | */ |
251 | static int print_prefixes(struct isl_prefixes *prefixes) |
252 | { |
253 | int i; |
254 | int len = 0; |
255 | |
256 | if (!prefixes) |
257 | return 0; |
258 | |
259 | for (i = 0; i < prefixes->n; ++i) { |
260 | printf(format: "%s-" , prefixes->prefix[i]); |
261 | len += strlen(s: prefixes->prefix[i]) + 1; |
262 | } |
263 | |
264 | return len; |
265 | } |
266 | |
267 | /* Check if "name" starts with one or more of the prefixes in "prefixes", |
268 | * starting at *first. If so, advance the pointer beyond the prefixes |
269 | * and return the updated pointer. Additionally, update *first to |
270 | * the index after the last prefix found. |
271 | */ |
272 | static const char *skip_prefixes(const char *name, |
273 | struct isl_prefixes *prefixes, int *first) |
274 | { |
275 | int i; |
276 | |
277 | for (i = first ? *first : 0; i < prefixes->n; ++i) { |
278 | size_t len = prefixes->len[i]; |
279 | const char *prefix = prefixes->prefix[i]; |
280 | if (strncmp(s1: name, s2: prefix, n: len) == 0 && name[len] == '-') { |
281 | name += len + 1; |
282 | if (first) |
283 | *first = i + 1; |
284 | } |
285 | } |
286 | |
287 | return name; |
288 | } |
289 | |
290 | static int print_arg_help(struct isl_arg *decl, struct isl_prefixes *prefixes, |
291 | int no) |
292 | { |
293 | int len = 0; |
294 | |
295 | if (!decl->long_name) { |
296 | printf(format: " -%c" , decl->short_name); |
297 | return 4; |
298 | } |
299 | |
300 | if (decl->short_name) { |
301 | printf(format: " -%c, --" , decl->short_name); |
302 | len += 8; |
303 | } else if (decl->flags & ISL_ARG_SINGLE_DASH) { |
304 | printf(format: " -" ); |
305 | len += 3; |
306 | } else { |
307 | printf(format: " --" ); |
308 | len += 8; |
309 | } |
310 | |
311 | if (no) { |
312 | printf(format: "no-" ); |
313 | len += 3; |
314 | } |
315 | len += print_prefixes(prefixes); |
316 | printf(format: "%s" , decl->long_name); |
317 | len += strlen(s: decl->long_name); |
318 | |
319 | while ((++decl)->type == isl_arg_alias) { |
320 | printf(format: ", --" ); |
321 | len += 4; |
322 | if (no) { |
323 | printf(format: "no-" ); |
324 | len += 3; |
325 | } |
326 | printf(format: "%s" , decl->long_name); |
327 | len += strlen(s: decl->long_name); |
328 | } |
329 | |
330 | return len; |
331 | } |
332 | |
333 | const void *isl_memrchr(const void *s, int c, size_t n) |
334 | { |
335 | const char *p = s; |
336 | while (n-- > 0) |
337 | if (p[n] == c) |
338 | return p + n; |
339 | return NULL; |
340 | } |
341 | |
342 | static int wrap_msg(const char *s, int indent, int pos) |
343 | { |
344 | int len; |
345 | int wrap_len = 75 - indent; |
346 | |
347 | if (pos + 1 >= indent) |
348 | printf(format: "\n%*s" , indent, "" ); |
349 | else |
350 | printf(format: "%*s" , indent - pos, "" ); |
351 | |
352 | len = strlen(s: s); |
353 | while (len > wrap_len) { |
354 | const char *space = isl_memrchr(s, c: ' ', n: wrap_len); |
355 | int l; |
356 | |
357 | if (!space) |
358 | space = strchr(s: s + wrap_len, c: ' '); |
359 | if (!space) |
360 | break; |
361 | l = space - s; |
362 | printf(format: "%.*s" , l, s); |
363 | s = space + 1; |
364 | len -= l + 1; |
365 | printf(format: "\n%*s" , indent, "" ); |
366 | } |
367 | |
368 | printf(format: "%s" , s); |
369 | return len; |
370 | } |
371 | |
372 | static int print_help_msg(struct isl_arg *decl, int pos) |
373 | { |
374 | if (!decl->help_msg) |
375 | return pos; |
376 | |
377 | return wrap_msg(s: decl->help_msg, indent: 30, pos); |
378 | } |
379 | |
380 | static void print_default(struct isl_arg *decl, const char *def, int pos) |
381 | { |
382 | const char *default_prefix = "[default: " ; |
383 | const char *default_suffix = "]" ; |
384 | int len; |
385 | |
386 | len = strlen(s: default_prefix) + strlen(s: def) + strlen(s: default_suffix); |
387 | |
388 | if (!decl->help_msg) { |
389 | if (pos >= 29) |
390 | printf(format: "\n%30s" , "" ); |
391 | else |
392 | printf(format: "%*s" , 30 - pos, "" ); |
393 | } else { |
394 | if (pos + len >= 48) |
395 | printf(format: "\n%30s" , "" ); |
396 | else |
397 | printf(format: " " ); |
398 | } |
399 | printf(format: "%s%s%s" , default_prefix, def, default_suffix); |
400 | } |
401 | |
402 | static void print_default_choice(struct isl_arg *decl, void *opt, int pos) |
403 | { |
404 | int i; |
405 | const char *s = "none" ; |
406 | unsigned *p; |
407 | |
408 | p = (unsigned *)(((char *) opt) + decl->offset); |
409 | for (i = 0; decl->u.choice.choice[i].name; ++i) |
410 | if (decl->u.choice.choice[i].value == *p) { |
411 | s = decl->u.choice.choice[i].name; |
412 | break; |
413 | } |
414 | |
415 | print_default(decl, def: s, pos); |
416 | } |
417 | |
418 | static void print_choice_help(struct isl_arg *decl, |
419 | struct isl_prefixes *prefixes, void *opt) |
420 | { |
421 | int i; |
422 | int pos; |
423 | |
424 | pos = print_arg_help(decl, prefixes, no: 0); |
425 | printf(format: "=" ); |
426 | pos++; |
427 | |
428 | for (i = 0; decl->u.choice.choice[i].name; ++i) { |
429 | if (i) { |
430 | printf(format: "|" ); |
431 | pos++; |
432 | } |
433 | printf(format: "%s" , decl->u.choice.choice[i].name); |
434 | pos += strlen(s: decl->u.choice.choice[i].name); |
435 | } |
436 | |
437 | pos = print_help_msg(decl, pos); |
438 | print_default_choice(decl, opt, pos); |
439 | |
440 | printf(format: "\n" ); |
441 | } |
442 | |
443 | static void print_default_flags(struct isl_arg *decl, void *opt, int pos) |
444 | { |
445 | int i, first; |
446 | const char *default_prefix = "[default: " ; |
447 | const char *default_suffix = "]" ; |
448 | int len = strlen(s: default_prefix) + strlen(s: default_suffix); |
449 | unsigned *p; |
450 | |
451 | p = (unsigned *)(((char *) opt) + decl->offset); |
452 | for (i = 0; decl->u.flags.flags[i].name; ++i) |
453 | if ((*p & decl->u.flags.flags[i].mask) == |
454 | decl->u.flags.flags[i].value) |
455 | len += strlen(s: decl->u.flags.flags[i].name); |
456 | |
457 | if (!decl->help_msg) { |
458 | if (pos >= 29) |
459 | printf(format: "\n%30s" , "" ); |
460 | else |
461 | printf(format: "%*s" , 30 - pos, "" ); |
462 | } else { |
463 | if (pos + len >= 48) |
464 | printf(format: "\n%30s" , "" ); |
465 | else |
466 | printf(format: " " ); |
467 | } |
468 | printf(format: "%s" , default_prefix); |
469 | |
470 | for (first = 1, i = 0; decl->u.flags.flags[i].name; ++i) |
471 | if ((*p & decl->u.flags.flags[i].mask) == |
472 | decl->u.flags.flags[i].value) { |
473 | if (!first) |
474 | printf(format: "," ); |
475 | printf(format: "%s" , decl->u.flags.flags[i].name); |
476 | first = 0; |
477 | } |
478 | |
479 | printf(format: "%s" , default_suffix); |
480 | } |
481 | |
482 | static void print_flags_help(struct isl_arg *decl, |
483 | struct isl_prefixes *prefixes, void *opt) |
484 | { |
485 | int i, j; |
486 | int pos; |
487 | |
488 | pos = print_arg_help(decl, prefixes, no: 0); |
489 | printf(format: "=" ); |
490 | pos++; |
491 | |
492 | for (i = 0; decl->u.flags.flags[i].name; ++i) { |
493 | if (i) { |
494 | printf(format: "," ); |
495 | pos++; |
496 | } |
497 | for (j = i; |
498 | decl->u.flags.flags[j].mask == decl->u.flags.flags[i].mask; |
499 | ++j) { |
500 | if (j != i) { |
501 | printf(format: "|" ); |
502 | pos++; |
503 | } |
504 | printf(format: "%s" , decl->u.flags.flags[j].name); |
505 | pos += strlen(s: decl->u.flags.flags[j].name); |
506 | } |
507 | i = j - 1; |
508 | } |
509 | |
510 | pos = print_help_msg(decl, pos); |
511 | print_default_flags(decl, opt, pos); |
512 | |
513 | printf(format: "\n" ); |
514 | } |
515 | |
516 | static void print_bool_help(struct isl_arg *decl, |
517 | struct isl_prefixes *prefixes, void *opt) |
518 | { |
519 | int pos; |
520 | unsigned *p = opt ? (unsigned *)(((char *) opt) + decl->offset) : NULL; |
521 | int no = p ? *p == 1 : 0; |
522 | pos = print_arg_help(decl, prefixes, no); |
523 | pos = print_help_msg(decl, pos); |
524 | if (decl->offset != ISL_ARG_OFFSET_NONE) |
525 | print_default(decl, def: no ? "yes" : "no" , pos); |
526 | printf(format: "\n" ); |
527 | } |
528 | |
529 | static int print_argument_name(struct isl_arg *decl, const char *name, int pos) |
530 | { |
531 | printf(format: "%c<%s>" , decl->long_name ? '=' : ' ', name); |
532 | return pos + 3 + strlen(s: name); |
533 | } |
534 | |
535 | static void print_int_help(struct isl_arg *decl, |
536 | struct isl_prefixes *prefixes, void *opt) |
537 | { |
538 | int pos; |
539 | char val[20]; |
540 | int *p = (int *)(((char *) opt) + decl->offset); |
541 | pos = print_arg_help(decl, prefixes, no: 0); |
542 | pos = print_argument_name(decl, name: decl->argument_name, pos); |
543 | pos = print_help_msg(decl, pos); |
544 | snprintf(s: val, maxlen: sizeof(val), format: "%d" , *p); |
545 | print_default(decl, def: val, pos); |
546 | printf(format: "\n" ); |
547 | } |
548 | |
549 | static void print_long_help(struct isl_arg *decl, |
550 | struct isl_prefixes *prefixes, void *opt) |
551 | { |
552 | int pos; |
553 | long *p = (long *)(((char *) opt) + decl->offset); |
554 | pos = print_arg_help(decl, prefixes, no: 0); |
555 | if (*p != decl->u.l.default_selected) { |
556 | printf(format: "[" ); |
557 | pos++; |
558 | } |
559 | printf(format: "=long" ); |
560 | pos += 5; |
561 | if (*p != decl->u.l.default_selected) { |
562 | printf(format: "]" ); |
563 | pos++; |
564 | } |
565 | print_help_msg(decl, pos); |
566 | printf(format: "\n" ); |
567 | } |
568 | |
569 | static void print_ulong_help(struct isl_arg *decl, |
570 | struct isl_prefixes *prefixes) |
571 | { |
572 | int pos; |
573 | pos = print_arg_help(decl, prefixes, no: 0); |
574 | printf(format: "=ulong" ); |
575 | pos += 6; |
576 | print_help_msg(decl, pos); |
577 | printf(format: "\n" ); |
578 | } |
579 | |
580 | static void print_str_help(struct isl_arg *decl, |
581 | struct isl_prefixes *prefixes, void *opt) |
582 | { |
583 | int pos; |
584 | const char *a = decl->argument_name ? decl->argument_name : "string" ; |
585 | const char **p = (const char **)(((char *) opt) + decl->offset); |
586 | pos = print_arg_help(decl, prefixes, no: 0); |
587 | pos = print_argument_name(decl, name: a, pos); |
588 | pos = print_help_msg(decl, pos); |
589 | if (*p) |
590 | print_default(decl, def: *p, pos); |
591 | printf(format: "\n" ); |
592 | } |
593 | |
594 | static void print_str_list_help(struct isl_arg *decl, |
595 | struct isl_prefixes *prefixes) |
596 | { |
597 | int pos; |
598 | const char *a = decl->argument_name ? decl->argument_name : "string" ; |
599 | pos = print_arg_help(decl, prefixes, no: 0); |
600 | pos = print_argument_name(decl, name: a, pos); |
601 | pos = print_help_msg(decl, pos); |
602 | printf(format: "\n" ); |
603 | } |
604 | |
605 | static void print_help(struct isl_arg *arg, |
606 | struct isl_prefixes *prefixes, void *opt) |
607 | { |
608 | int i; |
609 | int any = 0; |
610 | |
611 | for (i = 0; arg[i].type != isl_arg_end; ++i) { |
612 | if (arg[i].flags & ISL_ARG_HIDDEN) |
613 | continue; |
614 | switch (arg[i].type) { |
615 | case isl_arg_flags: |
616 | print_flags_help(decl: &arg[i], prefixes, opt); |
617 | any = 1; |
618 | break; |
619 | case isl_arg_choice: |
620 | print_choice_help(decl: &arg[i], prefixes, opt); |
621 | any = 1; |
622 | break; |
623 | case isl_arg_bool: |
624 | print_bool_help(decl: &arg[i], prefixes, opt); |
625 | any = 1; |
626 | break; |
627 | case isl_arg_int: |
628 | print_int_help(decl: &arg[i], prefixes, opt); |
629 | any = 1; |
630 | break; |
631 | case isl_arg_long: |
632 | print_long_help(decl: &arg[i], prefixes, opt); |
633 | any = 1; |
634 | break; |
635 | case isl_arg_ulong: |
636 | print_ulong_help(decl: &arg[i], prefixes); |
637 | any = 1; |
638 | break; |
639 | case isl_arg_str: |
640 | print_str_help(decl: &arg[i], prefixes, opt); |
641 | any = 1; |
642 | break; |
643 | case isl_arg_str_list: |
644 | print_str_list_help(decl: &arg[i], prefixes); |
645 | any = 1; |
646 | break; |
647 | case isl_arg_alias: |
648 | case isl_arg_version: |
649 | case isl_arg_arg: |
650 | case isl_arg_footer: |
651 | case isl_arg_child: |
652 | case isl_arg_user: |
653 | case isl_arg_end: |
654 | break; |
655 | } |
656 | } |
657 | |
658 | for (i = 0; arg[i].type != isl_arg_end; ++i) { |
659 | void *child; |
660 | int first; |
661 | |
662 | if (arg[i].type != isl_arg_child) |
663 | continue; |
664 | if (arg[i].flags & ISL_ARG_HIDDEN) |
665 | continue; |
666 | |
667 | if (any) |
668 | printf(format: "\n" ); |
669 | if (arg[i].help_msg) |
670 | printf(format: " %s\n" , arg[i].help_msg); |
671 | if (arg[i].offset == ISL_ARG_OFFSET_NONE) |
672 | child = opt; |
673 | else |
674 | child = *(void **)(((char *) opt) + arg[i].offset); |
675 | first = add_prefix(prefixes, prefix: arg[i].long_name); |
676 | print_help(arg: arg[i].u.child.child->args, prefixes, opt: child); |
677 | drop_prefix(prefixes, first); |
678 | any = 1; |
679 | } |
680 | } |
681 | |
682 | static const char *prog_name(const char *prog) |
683 | { |
684 | const char *slash; |
685 | |
686 | slash = strrchr(s: prog, c: '/'); |
687 | if (slash) |
688 | prog = slash + 1; |
689 | if (strncmp(s1: prog, s2: "lt-" , n: 3) == 0) |
690 | prog += 3; |
691 | |
692 | return prog; |
693 | } |
694 | |
695 | static int any_version(struct isl_arg *decl) |
696 | { |
697 | int i; |
698 | |
699 | for (i = 0; decl[i].type != isl_arg_end; ++i) { |
700 | switch (decl[i].type) { |
701 | case isl_arg_version: |
702 | return 1; |
703 | case isl_arg_child: |
704 | if (any_version(decl: decl[i].u.child.child->args)) |
705 | return 1; |
706 | break; |
707 | default: |
708 | break; |
709 | } |
710 | } |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static void print_help_and_exit(struct isl_arg *arg, const char *prog, |
716 | void *opt) |
717 | { |
718 | int i; |
719 | struct isl_prefixes prefixes = { 0 }; |
720 | |
721 | printf(format: "Usage: %s [OPTION...]" , prog_name(prog)); |
722 | |
723 | for (i = 0; arg[i].type != isl_arg_end; ++i) |
724 | if (arg[i].type == isl_arg_arg) |
725 | printf(format: " %s" , arg[i].argument_name); |
726 | |
727 | printf(format: "\n\n" ); |
728 | |
729 | print_help(arg, prefixes: &prefixes, opt); |
730 | printf(format: "\n" ); |
731 | if (any_version(decl: arg)) |
732 | printf(format: " -V, --version\n" ); |
733 | print_bool_help(decl: help_arg, NULL, NULL); |
734 | |
735 | for (i = 0; arg[i].type != isl_arg_end; ++i) { |
736 | if (arg[i].type != isl_arg_footer) |
737 | continue; |
738 | wrap_msg(s: arg[i].help_msg, indent: 0, pos: 0); |
739 | printf(format: "\n" ); |
740 | } |
741 | |
742 | exit(status: 0); |
743 | } |
744 | |
745 | static int match_long_name(struct isl_arg *decl, |
746 | const char *start, const char *end) |
747 | { |
748 | do { |
749 | if (end - start == strlen(s: decl->long_name) && |
750 | !strncmp(s1: start, s2: decl->long_name, n: end - start)) |
751 | return 1; |
752 | } while ((++decl)->type == isl_arg_alias); |
753 | |
754 | return 0; |
755 | } |
756 | |
757 | static const char *skip_dash_dash(struct isl_arg *decl, const char *arg) |
758 | { |
759 | if (!strncmp(s1: arg, s2: "--" , n: 2)) |
760 | return arg + 2; |
761 | if ((decl->flags & ISL_ARG_SINGLE_DASH) && arg[0] == '-') |
762 | return arg + 1; |
763 | return NULL; |
764 | } |
765 | |
766 | static const char *skip_name(struct isl_arg *decl, const char *arg, |
767 | struct isl_prefixes *prefixes, int need_argument, int *has_argument) |
768 | { |
769 | const char *equal; |
770 | const char *name; |
771 | const char *end; |
772 | |
773 | if (arg[0] == '-' && arg[1] && arg[1] == decl->short_name) { |
774 | if (need_argument && !arg[2]) |
775 | return NULL; |
776 | if (has_argument) |
777 | *has_argument = arg[2] != '\0'; |
778 | return arg + 2; |
779 | } |
780 | if (!decl->long_name) |
781 | return NULL; |
782 | |
783 | name = skip_dash_dash(decl, arg); |
784 | if (!name) |
785 | return NULL; |
786 | |
787 | equal = strchr(s: name, c: '='); |
788 | if (need_argument && !equal) |
789 | return NULL; |
790 | |
791 | if (has_argument) |
792 | *has_argument = !!equal; |
793 | end = equal ? equal : name + strlen(s: name); |
794 | |
795 | name = skip_prefixes(name, prefixes, NULL); |
796 | |
797 | if (!match_long_name(decl, start: name, end)) |
798 | return NULL; |
799 | |
800 | return equal ? equal + 1 : end; |
801 | } |
802 | |
803 | static int parse_choice_option(struct isl_arg *decl, char **arg, |
804 | struct isl_prefixes *prefixes, void *opt) |
805 | { |
806 | int i; |
807 | int has_argument; |
808 | const char *choice; |
809 | |
810 | choice = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
811 | if (!choice) |
812 | return 0; |
813 | |
814 | if (!has_argument && (!arg[1] || arg[1][0] == '-')) { |
815 | unsigned u = decl->u.choice.default_selected; |
816 | if (decl->offset != ISL_ARG_OFFSET_NONE) |
817 | *(unsigned *)(((char *)opt) + decl->offset) = u; |
818 | if (decl->u.choice.set) |
819 | decl->u.choice.set(opt, u); |
820 | |
821 | return 1; |
822 | } |
823 | |
824 | if (!has_argument) |
825 | choice = arg[1]; |
826 | |
827 | for (i = 0; decl->u.choice.choice[i].name; ++i) { |
828 | unsigned u; |
829 | |
830 | if (strcmp(s1: choice, s2: decl->u.choice.choice[i].name)) |
831 | continue; |
832 | |
833 | u = decl->u.choice.choice[i].value; |
834 | if (decl->offset != ISL_ARG_OFFSET_NONE) |
835 | *(unsigned *)(((char *)opt) + decl->offset) = u; |
836 | if (decl->u.choice.set) |
837 | decl->u.choice.set(opt, u); |
838 | |
839 | return has_argument ? 1 : 2; |
840 | } |
841 | |
842 | return 0; |
843 | } |
844 | |
845 | static int set_flag(struct isl_arg *decl, unsigned *val, const char *flag, |
846 | size_t len) |
847 | { |
848 | int i; |
849 | |
850 | for (i = 0; decl->u.flags.flags[i].name; ++i) { |
851 | if (strncmp(s1: flag, s2: decl->u.flags.flags[i].name, n: len)) |
852 | continue; |
853 | |
854 | *val &= ~decl->u.flags.flags[i].mask; |
855 | *val |= decl->u.flags.flags[i].value; |
856 | |
857 | return 1; |
858 | } |
859 | |
860 | return 0; |
861 | } |
862 | |
863 | static int parse_flags_option(struct isl_arg *decl, char **arg, |
864 | struct isl_prefixes *prefixes, void *opt) |
865 | { |
866 | int has_argument; |
867 | const char *flags; |
868 | const char *comma; |
869 | unsigned val; |
870 | |
871 | flags = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
872 | if (!flags) |
873 | return 0; |
874 | |
875 | if (!has_argument && !arg[1]) |
876 | return 0; |
877 | |
878 | if (!has_argument) |
879 | flags = arg[1]; |
880 | |
881 | val = 0; |
882 | |
883 | while ((comma = strchr(s: flags, c: ',')) != NULL) { |
884 | if (!set_flag(decl, val: &val, flag: flags, len: comma - flags)) |
885 | return 0; |
886 | flags = comma + 1; |
887 | } |
888 | if (!set_flag(decl, val: &val, flag: flags, len: strlen(s: flags))) |
889 | return 0; |
890 | |
891 | *(unsigned *)(((char *)opt) + decl->offset) = val; |
892 | |
893 | return has_argument ? 1 : 2; |
894 | } |
895 | |
896 | static int parse_bool_option(struct isl_arg *decl, char **arg, |
897 | struct isl_prefixes *prefixes, void *opt) |
898 | { |
899 | const char *name; |
900 | unsigned *p = (unsigned *)(((char *)opt) + decl->offset); |
901 | int next_prefix; |
902 | |
903 | if (skip_name(decl, arg: arg[0], prefixes, need_argument: 0, NULL)) { |
904 | if ((decl->flags & ISL_ARG_BOOL_ARG) && arg[1]) { |
905 | char *endptr; |
906 | int val = strtol(nptr: arg[1], endptr: &endptr, base: 0); |
907 | if (*endptr == '\0' && (val == 0 || val == 1)) { |
908 | if (decl->offset != ISL_ARG_OFFSET_NONE) |
909 | *p = val; |
910 | if (decl->u.b.set) |
911 | decl->u.b.set(opt, val); |
912 | return 2; |
913 | } |
914 | } |
915 | if (decl->offset != ISL_ARG_OFFSET_NONE) |
916 | *p = 1; |
917 | if (decl->u.b.set) |
918 | decl->u.b.set(opt, 1); |
919 | |
920 | return 1; |
921 | } |
922 | |
923 | if (!decl->long_name) |
924 | return 0; |
925 | |
926 | name = skip_dash_dash(decl, arg: arg[0]); |
927 | if (!name) |
928 | return 0; |
929 | |
930 | next_prefix = 0; |
931 | name = skip_prefixes(name, prefixes, first: &next_prefix); |
932 | |
933 | if (strncmp(s1: name, s2: "no-" , n: 3)) |
934 | return 0; |
935 | name += 3; |
936 | |
937 | name = skip_prefixes(name, prefixes, first: &next_prefix); |
938 | |
939 | if (match_long_name(decl, start: name, end: name + strlen(s: name))) { |
940 | if (decl->offset != ISL_ARG_OFFSET_NONE) |
941 | *p = 0; |
942 | if (decl->u.b.set) |
943 | decl->u.b.set(opt, 0); |
944 | |
945 | return 1; |
946 | } |
947 | |
948 | return 0; |
949 | } |
950 | |
951 | static int parse_str_option(struct isl_arg *decl, char **arg, |
952 | struct isl_prefixes *prefixes, void *opt) |
953 | { |
954 | int has_argument; |
955 | const char *s; |
956 | char **p = (char **)(((char *)opt) + decl->offset); |
957 | |
958 | s = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
959 | if (!s) |
960 | return 0; |
961 | |
962 | if (has_argument) { |
963 | free(ptr: *p); |
964 | *p = strdup(s: s); |
965 | return 1; |
966 | } |
967 | |
968 | if (arg[1]) { |
969 | free(ptr: *p); |
970 | *p = strdup(s: arg[1]); |
971 | return 2; |
972 | } |
973 | |
974 | return 0; |
975 | } |
976 | |
977 | static int isl_arg_str_list_append(struct isl_arg *decl, void *opt, |
978 | const char *s) |
979 | { |
980 | int *n = (int *)(((char *) opt) + decl->u.str_list.offset_n); |
981 | char **list = *(char ***)(((char *) opt) + decl->offset); |
982 | |
983 | list = realloc(ptr: list, size: (*n + 1) * sizeof(char *)); |
984 | if (!list) |
985 | return -1; |
986 | *(char ***)(((char *) opt) + decl->offset) = list; |
987 | list[*n] = strdup(s: s); |
988 | (*n)++; |
989 | return 0; |
990 | } |
991 | |
992 | static int parse_str_list_option(struct isl_arg *decl, char **arg, |
993 | struct isl_prefixes *prefixes, void *opt) |
994 | { |
995 | int has_argument; |
996 | const char *s; |
997 | |
998 | s = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
999 | if (!s) |
1000 | return 0; |
1001 | |
1002 | if (has_argument) { |
1003 | isl_arg_str_list_append(decl, opt, s); |
1004 | return 1; |
1005 | } |
1006 | |
1007 | if (arg[1]) { |
1008 | isl_arg_str_list_append(decl, opt, s: arg[1]); |
1009 | return 2; |
1010 | } |
1011 | |
1012 | return 0; |
1013 | } |
1014 | |
1015 | static int parse_int_option(struct isl_arg *decl, char **arg, |
1016 | struct isl_prefixes *prefixes, void *opt) |
1017 | { |
1018 | int has_argument; |
1019 | const char *val; |
1020 | char *endptr; |
1021 | int *p = (int *)(((char *)opt) + decl->offset); |
1022 | |
1023 | val = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
1024 | if (!val) |
1025 | return 0; |
1026 | |
1027 | if (has_argument) { |
1028 | *p = atoi(nptr: val); |
1029 | return 1; |
1030 | } |
1031 | |
1032 | if (arg[1]) { |
1033 | int i = strtol(nptr: arg[1], endptr: &endptr, base: 0); |
1034 | if (*endptr == '\0') { |
1035 | *p = i; |
1036 | return 2; |
1037 | } |
1038 | } |
1039 | |
1040 | return 0; |
1041 | } |
1042 | |
1043 | static int parse_long_option(struct isl_arg *decl, char **arg, |
1044 | struct isl_prefixes *prefixes, void *opt) |
1045 | { |
1046 | int has_argument; |
1047 | const char *val; |
1048 | char *endptr; |
1049 | long *p = (long *)(((char *)opt) + decl->offset); |
1050 | |
1051 | val = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
1052 | if (!val) |
1053 | return 0; |
1054 | |
1055 | if (has_argument) { |
1056 | long l = strtol(nptr: val, NULL, base: 0); |
1057 | *p = l; |
1058 | if (decl->u.l.set) |
1059 | decl->u.l.set(opt, l); |
1060 | return 1; |
1061 | } |
1062 | |
1063 | if (arg[1]) { |
1064 | long l = strtol(nptr: arg[1], endptr: &endptr, base: 0); |
1065 | if (*endptr == '\0') { |
1066 | *p = l; |
1067 | if (decl->u.l.set) |
1068 | decl->u.l.set(opt, l); |
1069 | return 2; |
1070 | } |
1071 | } |
1072 | |
1073 | if (decl->u.l.default_value != decl->u.l.default_selected) { |
1074 | *p = decl->u.l.default_selected; |
1075 | if (decl->u.l.set) |
1076 | decl->u.l.set(opt, decl->u.l.default_selected); |
1077 | return 1; |
1078 | } |
1079 | |
1080 | return 0; |
1081 | } |
1082 | |
1083 | static int parse_ulong_option(struct isl_arg *decl, char **arg, |
1084 | struct isl_prefixes *prefixes, void *opt) |
1085 | { |
1086 | int has_argument; |
1087 | const char *val; |
1088 | char *endptr; |
1089 | unsigned long *p = (unsigned long *)(((char *)opt) + decl->offset); |
1090 | |
1091 | val = skip_name(decl, arg: arg[0], prefixes, need_argument: 0, has_argument: &has_argument); |
1092 | if (!val) |
1093 | return 0; |
1094 | |
1095 | if (has_argument) { |
1096 | *p = strtoul(nptr: val, NULL, base: 0); |
1097 | return 1; |
1098 | } |
1099 | |
1100 | if (arg[1]) { |
1101 | unsigned long ul = strtoul(nptr: arg[1], endptr: &endptr, base: 0); |
1102 | if (*endptr == '\0') { |
1103 | *p = ul; |
1104 | return 2; |
1105 | } |
1106 | } |
1107 | |
1108 | return 0; |
1109 | } |
1110 | |
1111 | static int parse_option(struct isl_arg *decl, char **arg, |
1112 | struct isl_prefixes *prefixes, void *opt); |
1113 | |
1114 | static int parse_child_option(struct isl_arg *decl, char **arg, |
1115 | struct isl_prefixes *prefixes, void *opt) |
1116 | { |
1117 | void *child; |
1118 | int first, parsed; |
1119 | |
1120 | if (decl->offset == ISL_ARG_OFFSET_NONE) |
1121 | child = opt; |
1122 | else |
1123 | child = *(void **)(((char *)opt) + decl->offset); |
1124 | |
1125 | first = add_prefix(prefixes, prefix: decl->long_name); |
1126 | parsed = parse_option(decl: decl->u.child.child->args, arg, prefixes, opt: child); |
1127 | drop_prefix(prefixes, first); |
1128 | |
1129 | return parsed; |
1130 | } |
1131 | |
1132 | static int parse_option(struct isl_arg *decl, char **arg, |
1133 | struct isl_prefixes *prefixes, void *opt) |
1134 | { |
1135 | int i; |
1136 | |
1137 | for (i = 0; decl[i].type != isl_arg_end; ++i) { |
1138 | int parsed = 0; |
1139 | switch (decl[i].type) { |
1140 | case isl_arg_choice: |
1141 | parsed = parse_choice_option(decl: &decl[i], arg, |
1142 | prefixes, opt); |
1143 | break; |
1144 | case isl_arg_flags: |
1145 | parsed = parse_flags_option(decl: &decl[i], arg, |
1146 | prefixes, opt); |
1147 | break; |
1148 | case isl_arg_int: |
1149 | parsed = parse_int_option(decl: &decl[i], arg, prefixes, opt); |
1150 | break; |
1151 | case isl_arg_long: |
1152 | parsed = parse_long_option(decl: &decl[i], arg, |
1153 | prefixes, opt); |
1154 | break; |
1155 | case isl_arg_ulong: |
1156 | parsed = parse_ulong_option(decl: &decl[i], arg, |
1157 | prefixes, opt); |
1158 | break; |
1159 | case isl_arg_bool: |
1160 | parsed = parse_bool_option(decl: &decl[i], arg, |
1161 | prefixes, opt); |
1162 | break; |
1163 | case isl_arg_str: |
1164 | parsed = parse_str_option(decl: &decl[i], arg, prefixes, opt); |
1165 | break; |
1166 | case isl_arg_str_list: |
1167 | parsed = parse_str_list_option(decl: &decl[i], arg, prefixes, |
1168 | opt); |
1169 | break; |
1170 | case isl_arg_child: |
1171 | parsed = parse_child_option(decl: &decl[i], arg, |
1172 | prefixes, opt); |
1173 | break; |
1174 | case isl_arg_alias: |
1175 | case isl_arg_arg: |
1176 | case isl_arg_footer: |
1177 | case isl_arg_user: |
1178 | case isl_arg_version: |
1179 | case isl_arg_end: |
1180 | break; |
1181 | } |
1182 | if (parsed) |
1183 | return parsed; |
1184 | } |
1185 | |
1186 | return 0; |
1187 | } |
1188 | |
1189 | static void print_version(struct isl_arg *decl) |
1190 | { |
1191 | int i; |
1192 | |
1193 | for (i = 0; decl[i].type != isl_arg_end; ++i) { |
1194 | switch (decl[i].type) { |
1195 | case isl_arg_version: |
1196 | decl[i].u.version.print_version(); |
1197 | break; |
1198 | case isl_arg_child: |
1199 | print_version(decl: decl[i].u.child.child->args); |
1200 | break; |
1201 | default: |
1202 | break; |
1203 | } |
1204 | } |
1205 | } |
1206 | |
1207 | static void print_version_and_exit(struct isl_arg *decl) |
1208 | { |
1209 | print_version(decl); |
1210 | |
1211 | exit(status: 0); |
1212 | } |
1213 | |
1214 | static int drop_argument(int argc, char **argv, int drop, int n) |
1215 | { |
1216 | for (; drop + n < argc; ++drop) |
1217 | argv[drop] = argv[drop + n]; |
1218 | |
1219 | return argc - n; |
1220 | } |
1221 | |
1222 | static int n_arg(struct isl_arg *arg) |
1223 | { |
1224 | int i; |
1225 | int n_arg = 0; |
1226 | |
1227 | for (i = 0; arg[i].type != isl_arg_end; ++i) |
1228 | if (arg[i].type == isl_arg_arg) |
1229 | n_arg++; |
1230 | |
1231 | return n_arg; |
1232 | } |
1233 | |
1234 | static int next_arg(struct isl_arg *arg, int a) |
1235 | { |
1236 | for (++a; arg[a].type != isl_arg_end; ++a) |
1237 | if (arg[a].type == isl_arg_arg) |
1238 | return a; |
1239 | |
1240 | return -1; |
1241 | } |
1242 | |
1243 | /* Unless ISL_ARG_SKIP_HELP is set, check if "arg" is |
1244 | * equal to "--help" or "-h" and if so call print_help_and_exit. |
1245 | */ |
1246 | static void check_help(struct isl_args *args, char *arg, char *prog, void *opt, |
1247 | unsigned flags) |
1248 | { |
1249 | if (ISL_FL_ISSET(flags, ISL_ARG_SKIP_HELP)) |
1250 | return; |
1251 | |
1252 | if (strcmp(s1: arg, s2: "--help" ) == 0 || strcmp(s1: arg, s2: "-h" ) == 0) |
1253 | print_help_and_exit(arg: args->args, prog, opt); |
1254 | } |
1255 | |
1256 | int isl_args_parse(struct isl_args *args, int argc, char **argv, void *opt, |
1257 | unsigned flags) |
1258 | { |
1259 | int a = -1; |
1260 | int skip = 0; |
1261 | int i; |
1262 | int n; |
1263 | struct isl_prefixes prefixes = { 0 }; |
1264 | |
1265 | n = n_arg(arg: args->args); |
1266 | |
1267 | for (i = 1; i < argc; ++i) { |
1268 | if ((strcmp(s1: argv[i], s2: "--version" ) == 0 || |
1269 | strcmp(s1: argv[i], s2: "-V" ) == 0) && any_version(decl: args->args)) |
1270 | print_version_and_exit(decl: args->args); |
1271 | } |
1272 | |
1273 | while (argc > 1 + skip) { |
1274 | int parsed; |
1275 | if (argv[1 + skip][0] != '-') { |
1276 | a = next_arg(arg: args->args, a); |
1277 | if (a >= 0) { |
1278 | char **p; |
1279 | p = (char **)(((char *)opt)+args->args[a].offset); |
1280 | free(ptr: *p); |
1281 | *p = strdup(s: argv[1 + skip]); |
1282 | argc = drop_argument(argc, argv, drop: 1 + skip, n: 1); |
1283 | --n; |
1284 | } else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) { |
1285 | fprintf(stderr, format: "%s: extra argument: %s\n" , |
1286 | prog_name(prog: argv[0]), argv[1 + skip]); |
1287 | exit(status: -1); |
1288 | } else |
1289 | ++skip; |
1290 | continue; |
1291 | } |
1292 | check_help(args, arg: argv[1 + skip], prog: argv[0], opt, flags); |
1293 | parsed = parse_option(decl: args->args, arg: &argv[1 + skip], |
1294 | prefixes: &prefixes, opt); |
1295 | if (parsed) |
1296 | argc = drop_argument(argc, argv, drop: 1 + skip, n: parsed); |
1297 | else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) { |
1298 | fprintf(stderr, format: "%s: unrecognized option: %s\n" , |
1299 | prog_name(prog: argv[0]), argv[1 + skip]); |
1300 | exit(status: -1); |
1301 | } else |
1302 | ++skip; |
1303 | } |
1304 | |
1305 | if (n > 0) { |
1306 | fprintf(stderr, format: "%s: expecting %d more argument(s)\n" , |
1307 | prog_name(prog: argv[0]), n); |
1308 | exit(status: -1); |
1309 | } |
1310 | |
1311 | return argc; |
1312 | } |
1313 | |