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 
set_default_choice(struct isl_arg * arg,void * opt)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 
set_default_flags(struct isl_arg * arg,void * opt)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 
set_default_bool(struct isl_arg * arg,void * opt)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 
set_default_child(struct isl_arg * arg,void * opt)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(1, arg->u.child.child->options_size);
50 		*(void **)(((char *)opt) + arg->offset) = child;
51 	}
52 
53 	if (child)
54 		isl_args_set_defaults(arg->u.child.child, child);
55 }
56 
set_default_user(struct isl_arg * arg,void * opt)57 static void set_default_user(struct isl_arg *arg, void *opt)
58 {
59 	arg->u.user.init(((char *)opt) + arg->offset);
60 }
61 
set_default_int(struct isl_arg * arg,void * opt)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 
set_default_long(struct isl_arg * arg,void * opt)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 
set_default_ulong(struct isl_arg * arg,void * opt)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 
set_default_str(struct isl_arg * arg,void * opt)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(arg->u.str.default_value);
82 	*(const char **)(((char *)opt) + arg->offset) = str;
83 }
84 
set_default_str_list(struct isl_arg * arg,void * opt)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 
isl_args_set_defaults(struct isl_args * args,void * opt)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(&args->args[i], opt);
99 			break;
100 		case isl_arg_flags:
101 			set_default_flags(&args->args[i], opt);
102 			break;
103 		case isl_arg_bool:
104 			set_default_bool(&args->args[i], opt);
105 			break;
106 		case isl_arg_child:
107 			set_default_child(&args->args[i], opt);
108 			break;
109 		case isl_arg_user:
110 			set_default_user(&args->args[i], opt);
111 			break;
112 		case isl_arg_int:
113 			set_default_int(&args->args[i], opt);
114 			break;
115 		case isl_arg_long:
116 			set_default_long(&args->args[i], opt);
117 			break;
118 		case isl_arg_ulong:
119 			set_default_ulong(&args->args[i], opt);
120 			break;
121 		case isl_arg_arg:
122 		case isl_arg_str:
123 			set_default_str(&args->args[i], opt);
124 			break;
125 		case isl_arg_str_list:
126 			set_default_str_list(&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 
free_child(struct isl_arg * arg,void * opt)139 static void free_child(struct isl_arg *arg, void *opt)
140 {
141 	if (arg->offset == ISL_ARG_OFFSET_NONE)
142 		free_args(arg->u.child.child->args, opt);
143 	else
144 		isl_args_free(arg->u.child.child,
145 			    *(void **)(((char *)opt) + arg->offset));
146 }
147 
free_str_list(struct isl_arg * arg,void * opt)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(list[i]);
156 	free(list);
157 }
158 
free_user(struct isl_arg * arg,void * opt)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 
free_args(struct isl_arg * arg,void * opt)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[i], opt);
173 			break;
174 		case isl_arg_arg:
175 		case isl_arg_str:
176 			free(*(char **)(((char *)opt) + arg[i].offset));
177 			break;
178 		case isl_arg_str_list:
179 			free_str_list(&arg[i], opt);
180 			break;
181 		case isl_arg_user:
182 			free_user(&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 
isl_args_free(struct isl_args * args,void * opt)199 void isl_args_free(struct isl_args *args, void *opt)
200 {
201 	if (!opt)
202 		return;
203 
204 	free_args(args->args, opt);
205 
206 	free(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  */
add_prefix(struct isl_prefixes * prefixes,const char * prefix)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, "too many prefixes\n");
233 		exit(EXIT_FAILURE);
234 	}
235 	prefixes->len[prefixes->n] = strlen(prefix);
236 	prefixes->prefix[prefixes->n] = prefix;
237 	prefixes->n++;
238 
239 	return n;
240 }
241 
242 /* Drop all prefixes starting at "first".
243  */
drop_prefix(struct isl_prefixes * prefixes,int first)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  */
print_prefixes(struct isl_prefixes * prefixes)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("%s-", prefixes->prefix[i]);
261 		len += strlen(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  */
skip_prefixes(const char * name,struct isl_prefixes * prefixes,int * first)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(name, prefix, len) == 0 && name[len] == '-') {
281 			name += len + 1;
282 			if (first)
283 				*first = i + 1;
284 		}
285 	}
286 
287 	return name;
288 }
289 
print_arg_help(struct isl_arg * decl,struct isl_prefixes * prefixes,int no)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("  -%c", decl->short_name);
297 		return 4;
298 	}
299 
300 	if (decl->short_name) {
301 		printf("  -%c, --", decl->short_name);
302 		len += 8;
303 	} else if (decl->flags & ISL_ARG_SINGLE_DASH) {
304 		printf("  -");
305 		len += 3;
306 	} else {
307 		printf("      --");
308 		len += 8;
309 	}
310 
311 	if (no) {
312 		printf("no-");
313 		len += 3;
314 	}
315 	len += print_prefixes(prefixes);
316 	printf("%s", decl->long_name);
317 	len += strlen(decl->long_name);
318 
319 	while ((++decl)->type == isl_arg_alias) {
320 		printf(", --");
321 		len += 4;
322 		if (no) {
323 			printf("no-");
324 			len += 3;
325 		}
326 		printf("%s", decl->long_name);
327 		len += strlen(decl->long_name);
328 	}
329 
330 	return len;
331 }
332 
isl_memrchr(const void * s,int c,size_t n)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 
wrap_msg(const char * s,int indent,int pos)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("\n%*s", indent, "");
349 	else
350 		printf("%*s", indent - pos, "");
351 
352 	len = strlen(s);
353 	while (len > wrap_len) {
354 		const char *space = isl_memrchr(s, ' ', wrap_len);
355 		int l;
356 
357 		if (!space)
358 			space = strchr(s + wrap_len, ' ');
359 		if (!space)
360 			break;
361 		l = space - s;
362 		printf("%.*s", l, s);
363 		s = space + 1;
364 		len -= l + 1;
365 		printf("\n%*s", indent, "");
366 	}
367 
368 	printf("%s", s);
369 	return len;
370 }
371 
print_help_msg(struct isl_arg * decl,int pos)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(decl->help_msg, 30, pos);
378 }
379 
print_default(struct isl_arg * decl,const char * def,int pos)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(default_prefix) + strlen(def) + strlen(default_suffix);
387 
388 	if (!decl->help_msg) {
389 		if (pos >= 29)
390 			printf("\n%30s", "");
391 		else
392 			printf("%*s", 30 - pos, "");
393 	} else {
394 		if (pos + len >= 48)
395 			printf("\n%30s", "");
396 		else
397 			printf(" ");
398 	}
399 	printf("%s%s%s", default_prefix, def, default_suffix);
400 }
401 
print_default_choice(struct isl_arg * decl,void * opt,int pos)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, s, pos);
416 }
417 
print_choice_help(struct isl_arg * decl,struct isl_prefixes * prefixes,void * opt)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, 0);
425 	printf("=");
426 	pos++;
427 
428 	for (i = 0; decl->u.choice.choice[i].name; ++i) {
429 		if (i) {
430 			printf("|");
431 			pos++;
432 		}
433 		printf("%s", decl->u.choice.choice[i].name);
434 		pos += strlen(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("\n");
441 }
442 
print_default_flags(struct isl_arg * decl,void * opt,int pos)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(default_prefix) + strlen(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(decl->u.flags.flags[i].name);
456 
457 	if (!decl->help_msg) {
458 		if (pos >= 29)
459 			printf("\n%30s", "");
460 		else
461 			printf("%*s", 30 - pos, "");
462 	} else {
463 		if (pos + len >= 48)
464 			printf("\n%30s", "");
465 		else
466 			printf(" ");
467 	}
468 	printf("%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(",");
475 			printf("%s", decl->u.flags.flags[i].name);
476 			first = 0;
477 		}
478 
479 	printf("%s", default_suffix);
480 }
481 
print_flags_help(struct isl_arg * decl,struct isl_prefixes * prefixes,void * opt)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, 0);
489 	printf("=");
490 	pos++;
491 
492 	for (i = 0; decl->u.flags.flags[i].name; ++i) {
493 		if (i) {
494 			printf(",");
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("|");
502 				pos++;
503 			}
504 			printf("%s", decl->u.flags.flags[j].name);
505 			pos += strlen(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("\n");
514 }
515 
print_bool_help(struct isl_arg * decl,struct isl_prefixes * prefixes,void * opt)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, no ? "yes" : "no", pos);
526 	printf("\n");
527 }
528 
print_argument_name(struct isl_arg * decl,const char * name,int pos)529 static int print_argument_name(struct isl_arg *decl, const char *name, int pos)
530 {
531 	printf("%c<%s>", decl->long_name ? '=' : ' ', name);
532 	return pos + 3 + strlen(name);
533 }
534 
print_int_help(struct isl_arg * decl,struct isl_prefixes * prefixes,void * opt)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, 0);
542 	pos = print_argument_name(decl, decl->argument_name, pos);
543 	pos = print_help_msg(decl, pos);
544 	snprintf(val, sizeof(val), "%d", *p);
545 	print_default(decl, val, pos);
546 	printf("\n");
547 }
548 
print_long_help(struct isl_arg * decl,struct isl_prefixes * prefixes,void * opt)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, 0);
555 	if (*p != decl->u.l.default_selected) {
556 		printf("[");
557 		pos++;
558 	}
559 	printf("=long");
560 	pos += 5;
561 	if (*p != decl->u.l.default_selected) {
562 		printf("]");
563 		pos++;
564 	}
565 	print_help_msg(decl, pos);
566 	printf("\n");
567 }
568 
print_ulong_help(struct isl_arg * decl,struct isl_prefixes * prefixes)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, 0);
574 	printf("=ulong");
575 	pos += 6;
576 	print_help_msg(decl, pos);
577 	printf("\n");
578 }
579 
print_str_help(struct isl_arg * decl,struct isl_prefixes * prefixes,void * opt)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, 0);
587 	pos = print_argument_name(decl, a, pos);
588 	pos = print_help_msg(decl, pos);
589 	if (*p)
590 		print_default(decl, *p, pos);
591 	printf("\n");
592 }
593 
print_str_list_help(struct isl_arg * decl,struct isl_prefixes * prefixes)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, 0);
600 	pos = print_argument_name(decl, a, pos);
601 	pos = print_help_msg(decl, pos);
602 	printf("\n");
603 }
604 
print_help(struct isl_arg * arg,struct isl_prefixes * prefixes,void * opt)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(&arg[i], prefixes, opt);
617 			any = 1;
618 			break;
619 		case isl_arg_choice:
620 			print_choice_help(&arg[i], prefixes, opt);
621 			any = 1;
622 			break;
623 		case isl_arg_bool:
624 			print_bool_help(&arg[i], prefixes, opt);
625 			any = 1;
626 			break;
627 		case isl_arg_int:
628 			print_int_help(&arg[i], prefixes, opt);
629 			any = 1;
630 			break;
631 		case isl_arg_long:
632 			print_long_help(&arg[i], prefixes, opt);
633 			any = 1;
634 			break;
635 		case isl_arg_ulong:
636 			print_ulong_help(&arg[i], prefixes);
637 			any = 1;
638 			break;
639 		case isl_arg_str:
640 			print_str_help(&arg[i], prefixes, opt);
641 			any = 1;
642 			break;
643 		case isl_arg_str_list:
644 			print_str_list_help(&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("\n");
669 		if (arg[i].help_msg)
670 			printf(" %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, arg[i].long_name);
676 		print_help(arg[i].u.child.child->args, prefixes, child);
677 		drop_prefix(prefixes, first);
678 		any = 1;
679 	}
680 }
681 
prog_name(const char * prog)682 static const char *prog_name(const char *prog)
683 {
684 	const char *slash;
685 
686 	slash = strrchr(prog, '/');
687 	if (slash)
688 		prog = slash + 1;
689 	if (strncmp(prog, "lt-", 3) == 0)
690 		prog += 3;
691 
692 	return prog;
693 }
694 
any_version(struct isl_arg * decl)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[i].u.child.child->args))
705 				return 1;
706 			break;
707 		default:
708 			break;
709 		}
710 	}
711 
712 	return 0;
713 }
714 
print_help_and_exit(struct isl_arg * arg,const char * prog,void * opt)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("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(" %s", arg[i].argument_name);
726 
727 	printf("\n\n");
728 
729 	print_help(arg, &prefixes, opt);
730 	printf("\n");
731 	if (any_version(arg))
732 		printf("  -V, --version\n");
733 	print_bool_help(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(arg[i].help_msg, 0, 0);
739 		printf("\n");
740 	}
741 
742 	exit(0);
743 }
744 
match_long_name(struct isl_arg * decl,const char * start,const char * end)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(decl->long_name) &&
750 		    !strncmp(start, decl->long_name, end - start))
751 			return 1;
752 	} while ((++decl)->type == isl_arg_alias);
753 
754 	return 0;
755 }
756 
skip_dash_dash(struct isl_arg * decl,const char * arg)757 static const char *skip_dash_dash(struct isl_arg *decl, const char *arg)
758 {
759 	if (!strncmp(arg, "--", 2))
760 		return arg + 2;
761 	if ((decl->flags & ISL_ARG_SINGLE_DASH) && arg[0] == '-')
762 		return arg + 1;
763 	return NULL;
764 }
765 
skip_name(struct isl_arg * decl,const char * arg,struct isl_prefixes * prefixes,int need_argument,int * has_argument)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(name, '=');
788 	if (need_argument && !equal)
789 		return NULL;
790 
791 	if (has_argument)
792 		*has_argument = !!equal;
793 	end = equal ? equal : name + strlen(name);
794 
795 	name = skip_prefixes(name, prefixes, NULL);
796 
797 	if (!match_long_name(decl, name, end))
798 		return NULL;
799 
800 	return equal ? equal + 1 : end;
801 }
802 
parse_choice_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &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(choice, 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 
set_flag(struct isl_arg * decl,unsigned * val,const char * flag,size_t len)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(flag, decl->u.flags.flags[i].name, 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 
parse_flags_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &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(flags, ',')) != NULL) {
884 		if (!set_flag(decl, &val, flags, comma - flags))
885 			return 0;
886 		flags = comma + 1;
887 	}
888 	if (!set_flag(decl, &val, flags, strlen(flags)))
889 		return 0;
890 
891 	*(unsigned *)(((char *)opt) + decl->offset) = val;
892 
893 	return has_argument ? 1 : 2;
894 }
895 
parse_bool_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, NULL)) {
904 		if ((decl->flags & ISL_ARG_BOOL_ARG) && arg[1]) {
905 			char *endptr;
906 			int val = strtol(arg[1], &endptr, 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[0]);
927 	if (!name)
928 		return 0;
929 
930 	next_prefix = 0;
931 	name = skip_prefixes(name, prefixes, &next_prefix);
932 
933 	if (strncmp(name, "no-", 3))
934 		return 0;
935 	name += 3;
936 
937 	name = skip_prefixes(name, prefixes, &next_prefix);
938 
939 	if (match_long_name(decl, name, name + strlen(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 
parse_str_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &has_argument);
959 	if (!s)
960 		return 0;
961 
962 	if (has_argument) {
963 		free(*p);
964 		*p = strdup(s);
965 		return 1;
966 	}
967 
968 	if (arg[1]) {
969 		free(*p);
970 		*p = strdup(arg[1]);
971 		return 2;
972 	}
973 
974 	return 0;
975 }
976 
isl_arg_str_list_append(struct isl_arg * decl,void * opt,const char * s)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(list, (*n + 1) * sizeof(char *));
984 	if (!list)
985 		return -1;
986 	*(char ***)(((char *) opt) + decl->offset) = list;
987 	list[*n] = strdup(s);
988 	(*n)++;
989 	return 0;
990 }
991 
parse_str_list_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &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, arg[1]);
1009 		return 2;
1010 	}
1011 
1012 	return 0;
1013 }
1014 
parse_int_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &has_argument);
1024 	if (!val)
1025 		return 0;
1026 
1027 	if (has_argument) {
1028 		*p = atoi(val);
1029 		return 1;
1030 	}
1031 
1032 	if (arg[1]) {
1033 		int i = strtol(arg[1], &endptr, 0);
1034 		if (*endptr == '\0') {
1035 			*p = i;
1036 			return 2;
1037 		}
1038 	}
1039 
1040 	return 0;
1041 }
1042 
parse_long_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &has_argument);
1052 	if (!val)
1053 		return 0;
1054 
1055 	if (has_argument) {
1056 		long l = strtol(val, NULL, 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(arg[1], &endptr, 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 
parse_ulong_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[0], prefixes, 0, &has_argument);
1092 	if (!val)
1093 		return 0;
1094 
1095 	if (has_argument) {
1096 		*p = strtoul(val, NULL, 0);
1097 		return 1;
1098 	}
1099 
1100 	if (arg[1]) {
1101 		unsigned long ul = strtoul(arg[1], &endptr, 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 
parse_child_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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, decl->long_name);
1126 	parsed = parse_option(decl->u.child.child->args, arg, prefixes, child);
1127 	drop_prefix(prefixes, first);
1128 
1129 	return parsed;
1130 }
1131 
parse_option(struct isl_arg * decl,char ** arg,struct isl_prefixes * prefixes,void * opt)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[i], arg,
1142 							prefixes, opt);
1143 			break;
1144 		case isl_arg_flags:
1145 			parsed = parse_flags_option(&decl[i], arg,
1146 							prefixes, opt);
1147 			break;
1148 		case isl_arg_int:
1149 			parsed = parse_int_option(&decl[i], arg, prefixes, opt);
1150 			break;
1151 		case isl_arg_long:
1152 			parsed = parse_long_option(&decl[i], arg,
1153 							prefixes, opt);
1154 			break;
1155 		case isl_arg_ulong:
1156 			parsed = parse_ulong_option(&decl[i], arg,
1157 							prefixes, opt);
1158 			break;
1159 		case isl_arg_bool:
1160 			parsed = parse_bool_option(&decl[i], arg,
1161 							prefixes, opt);
1162 			break;
1163 		case isl_arg_str:
1164 			parsed = parse_str_option(&decl[i], arg, prefixes, opt);
1165 			break;
1166 		case isl_arg_str_list:
1167 			parsed = parse_str_list_option(&decl[i], arg, prefixes,
1168 							opt);
1169 			break;
1170 		case isl_arg_child:
1171 			parsed = parse_child_option(&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 
print_version(struct isl_arg * decl)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[i].u.child.child->args);
1200 			break;
1201 		default:
1202 			break;
1203 		}
1204 	}
1205 }
1206 
print_version_and_exit(struct isl_arg * decl)1207 static void print_version_and_exit(struct isl_arg *decl)
1208 {
1209 	print_version(decl);
1210 
1211 	exit(0);
1212 }
1213 
drop_argument(int argc,char ** argv,int drop,int n)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 
n_arg(struct isl_arg * arg)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 
next_arg(struct isl_arg * arg,int a)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  */
check_help(struct isl_args * args,char * arg,char * prog,void * opt,unsigned flags)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(arg, "--help") == 0 || strcmp(arg, "-h") == 0)
1253 		print_help_and_exit(args->args, prog, opt);
1254 }
1255 
isl_args_parse(struct isl_args * args,int argc,char ** argv,void * opt,unsigned flags)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(args->args);
1266 
1267 	for (i = 1; i < argc; ++i) {
1268 		if ((strcmp(argv[i], "--version") == 0 ||
1269 		     strcmp(argv[i], "-V") == 0) && any_version(args->args))
1270 			print_version_and_exit(args->args);
1271 	}
1272 
1273 	while (argc > 1 + skip) {
1274 		int parsed;
1275 		if (argv[1 + skip][0] != '-') {
1276 			a = next_arg(args->args, a);
1277 			if (a >= 0) {
1278 				char **p;
1279 				p = (char **)(((char *)opt)+args->args[a].offset);
1280 				free(*p);
1281 				*p = strdup(argv[1 + skip]);
1282 				argc = drop_argument(argc, argv, 1 + skip, 1);
1283 				--n;
1284 			} else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) {
1285 				fprintf(stderr, "%s: extra argument: %s\n",
1286 					    prog_name(argv[0]), argv[1 + skip]);
1287 				exit(-1);
1288 			} else
1289 				++skip;
1290 			continue;
1291 		}
1292 		check_help(args, argv[1 + skip], argv[0], opt, flags);
1293 		parsed = parse_option(args->args, &argv[1 + skip],
1294 					&prefixes, opt);
1295 		if (parsed)
1296 			argc = drop_argument(argc, argv, 1 + skip, parsed);
1297 		else if (ISL_FL_ISSET(flags, ISL_ARG_ALL)) {
1298 			fprintf(stderr, "%s: unrecognized option: %s\n",
1299 					prog_name(argv[0]), argv[1 + skip]);
1300 			exit(-1);
1301 		} else
1302 			++skip;
1303 	}
1304 
1305 	if (n > 0) {
1306 		fprintf(stderr, "%s: expecting %d more argument(s)\n",
1307 				prog_name(argv[0]), n);
1308 		exit(-1);
1309 	}
1310 
1311 	return argc;
1312 }
1313