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