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