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