lc_opts.c 19.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
  libcore: library for basic data structures and algorithms.
  Copyright (C) 2005  IPD Goos, Universit"at Karlsruhe, Germany

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
Matthias Braun's avatar
Matthias Braun committed
19
#include "config.h"
20
21
22

#include <stdio.h>
#include <stdarg.h>
23
#include <stdlib.h>
24
25
26
27
28
29
30
#include <string.h>
#include <ctype.h>

#include "lc_opts_t.h"
#include "lc_opts_enum.h"
#include "hashptr.h"
#include "lc_printf.h"
31
#include "xmalloc.h"
32
#include "obst.h"
33
34
35
36
37

#define ERR_STRING "In argument \"%s\": "

#define OPT_DELIM '-'

38
39
#define HELP_TEMPL         "%-15s %-10s %-45s"
#define HELP_TEMPL_VALS    HELP_TEMPL " [%s] (%s)"
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

static struct obstack obst;

static void set_name(lc_opt_entry_t *ent, const char *name)
{
	ent->name = name;
	ent->hash = HASH_STR(name, strlen(name));
}

#define entry_matches(ent,hash_val,str) \
	((ent)->hash == hash_val && strcmp((ent)->name, (str)) == 0)

#define entries_equal(e1,e2) entry_matches(e1, (e2)->hash, (e2)->name)

static lc_opt_err_info_t *set_error(lc_opt_err_info_t *err, int error, const char *arg)
{
56
	if (err) {
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
		err->error = error;
		err->msg = "";
		err->arg = arg;
	}

	return err;
}

int lc_opt_raise_error(const lc_opt_err_info_t *err, lc_opt_error_handler_t *handler,
		const char *fmt, ...)
{
	va_list args;
	int res = 0;

	va_start(args, fmt);
72
	if (err && lc_opt_is_error(err)) {
73
		res = 1;
74
		if (handler) {
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
			char buf[256];
			vsnprintf(buf, sizeof(buf), fmt, args);
			handler(buf, err);
		}
	}
	va_end(args);

	return res;
}

static lc_opt_entry_t *init_entry(lc_opt_entry_t *ent, lc_opt_entry_t *parent,
		const char *name, const char *desc)
{
	const char *copied_name;
	const char *copied_desc;

	obstack_grow0(&obst, name, strlen(name));
92
	copied_name = (char*)obstack_finish(&obst);
93
	obstack_grow0(&obst, desc, strlen(desc));
94
	copied_desc = (char*)obstack_finish(&obst);
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109

	memset(ent, 0, sizeof(*ent));
	set_name(ent, copied_name);
	ent->desc = copied_desc;
	ent->parent = parent;
	return ent;
}

static lc_opt_entry_t *init_grp(lc_opt_entry_t *ent, lc_opt_err_info_t *err)
{
	ent->is_grp = 1;
	INIT_LIST_HEAD(&ent->v.grp.grps);
	INIT_LIST_HEAD(&ent->v.grp.opts);

	set_error(err, lc_opt_err_none, "");
110
111
	if (ent->parent) {
		if (ent->parent->is_grp)
112
			list_add_tail(&ent->list, &lc_get_grp_special(ent->parent)->grps);
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
		else
			set_error(err, lc_opt_err_grp_expected, ent->parent->name);
	}

	return ent;
}

static lc_opt_entry_t *init_opt(lc_opt_entry_t *ent,
								lc_opt_type_t type,
								void *val, size_t length,
								lc_opt_callback_t *cb,
								lc_opt_dump_t *dump,
								lc_opt_dump_vals_t *dump_vals,
								lc_opt_err_info_t *err)
{
	lc_opt_special_t *s = lc_get_opt_special(ent);

	ent->is_grp = 0;
	set_error(err, lc_opt_err_none, "");
132
	list_add_tail(&ent->list, &lc_get_grp_special(ent->parent)->opts);
133

134
135
136
	s->type      = type;
	s->value     = val;
	s->cb        = cb;
137
138
	s->dump      = dump;
	s->dump_vals = dump_vals;
139
	s->length    = length;
140
141
142
143
144
145
146
147
148
149

	return ent;
}


lc_opt_entry_t *lc_opt_root_grp(void)
{
	static lc_opt_entry_t root_group;
	static int inited = 0;

150
	if (!inited) {
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
		obstack_init(&obst);
		inited = 1;

		init_entry(&root_group, NULL, "root", "The root node");
		init_grp(&root_group, NULL);
	}

	return &root_group;
}

int lc_opt_grp_is_root(const lc_opt_entry_t *ent)
{
	return ent->parent == NULL;
}

static const char *get_type_name(lc_opt_type_t type)
{
	const char *res;

#define XXX(t) case lc_opt_type_ ## t: res = #t; break
171
	switch (type) {
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
		XXX(enum);
		XXX(bit);
		XXX(int);
		XXX(double);
		XXX(boolean);
		XXX(string);
		case lc_opt_type_negbit:     res = "bit";     break;
		case lc_opt_type_negboolean: res = "boolean"; break;
		default:
		res = "<none>";
	}
#undef XXX

	return res;
}

const char *lc_opt_get_type_name(const lc_opt_entry_t *ent)
{
	return get_type_name(lc_get_opt_special(ent)->type);
}

lc_opt_entry_t *lc_opt_get_grp(lc_opt_entry_t *parent, const char *name)
{
	lc_opt_entry_t *ent = lc_opt_find_grp(parent, name, NULL);

197
	if (!ent) {
198
		ent = OALLOC(&obst, lc_opt_entry_t);
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
		init_entry(ent, parent, name, "");
		init_grp(ent, NULL);
	}

	return ent;
}

lc_opt_entry_t *lc_opt_add_opt(lc_opt_entry_t *parent,
							   const char *name, const char *desc,
							   lc_opt_type_t type, void *value, size_t length,
							   lc_opt_callback_t *cb, lc_opt_dump_t *dump,
							   lc_opt_dump_vals_t *dump_vals,
							   lc_opt_err_info_t *err)
{
	lc_opt_entry_t *res = NULL;

215
	if (parent->is_grp) {
216
217
		lc_opt_entry_t *ent = lc_opt_find_opt(parent, name, NULL);

218
		if (!ent) {
219
			res = OALLOC(&obst, lc_opt_entry_t);
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
			init_entry(res, parent, name, desc);
			init_opt(res, type, value, length, cb, dump, dump_vals, err);
		} else
			set_error(err, lc_opt_err_opt_already_there, name);
	} else
		set_error(err, lc_opt_err_grp_expected, name);

	return res;
}


static lc_opt_entry_t *lc_opt_find_ent(const struct list_head *head, const char *name,
		int error_to_use, lc_opt_err_info_t *err)
{
	lc_opt_entry_t *ent, *found = NULL;
	int error = error_to_use;
	unsigned hash = HASH_STR(name, strlen(name));

238
	if (!list_empty(head)) {
239
		list_for_each_entry(lc_opt_entry_t, ent, head, list) {
240
			if (entry_matches(ent, hash, name)) {
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
				error = lc_opt_err_none;
				found = ent;
				break;
			}
		}
	}

	set_error(err, error, name);
	return found;
}

lc_opt_entry_t *lc_opt_find_grp(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
{
	return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->grps,
			name, lc_opt_err_grp_not_found, err) : NULL;
}

lc_opt_entry_t *lc_opt_find_opt(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
{
	return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->opts,
			name, lc_opt_err_opt_not_found, err) : NULL;
}

static const lc_opt_entry_t *resolve_up_to_last(const lc_opt_entry_t *root,
		const char * const *names, int pos, int n, lc_opt_err_info_t *err)
{
	lc_opt_entry_t *ent;

269
	if (pos == n)
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
		return root;

	ent = lc_opt_find_grp(root, names[pos], err);
	return ent ? resolve_up_to_last(ent, names, pos + 1, n, err) : NULL;
}

static const char *path_delim = "/.";

static lc_opt_entry_t *resolve_up_to_last_str_rec(lc_opt_entry_t *from,
														const char *path,
														const char **last_name)
{

	lc_opt_entry_t *res = from;
	size_t end          = strcspn(path, path_delim);

286
	if (path[end] != '\0') {
287
288
289
290
		/* skip all delimiters */
		size_t next = strspn(path + end, path_delim);

		/* copy the part of the path into a buffer */
291
		char *buf = (char*)malloc((end+1) * sizeof(buf[0]));
292
293
294
295
296
297
298
299
300
301
		strncpy(buf, path, end);
		buf[end] = '\0';

		/* resolve the group and free */
		from = lc_opt_get_grp(from, buf);
		free(buf);

		res = resolve_up_to_last_str_rec(from, path + end + next, last_name);
	}

302
	else if (last_name != NULL) {
303
304
305
306
307
308
309
310
311
312
313
		*last_name = path;
	}

	return res;
}

static lc_opt_entry_t *resolve_up_to_last_str(lc_opt_entry_t *root, const char *path, const char **last_name)
{
	size_t next = strspn(path, path_delim);

	/* if l != 0 we saw deliminators, so we resolve from the root */
314
	if (next > 0)
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
		root = lc_opt_root_grp();

	return resolve_up_to_last_str_rec(root, path + next, last_name);
}

lc_opt_entry_t *lc_opt_resolve_grp(const lc_opt_entry_t *root,
		const char * const *names, int n, lc_opt_err_info_t *err)
{
	const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
	return lc_opt_find_grp(grp, names[n - 1], err);
}

lc_opt_entry_t *lc_opt_resolve_opt(const lc_opt_entry_t *root,
		const char * const *names, int n, lc_opt_err_info_t *err)
{
	const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
	return lc_opt_find_opt(grp, names[n - 1], err);
}

static char *strtolower(char *buf, size_t n, const char *str)
{
	unsigned i;
337
	for (i = 0; i < n; ++i)
338
339
340
341
		buf[i] = tolower(str[i]);
	return buf;
}

342
int lc_opt_std_cb(const char *name, lc_opt_type_t type, void *data, size_t length, ...)
343
344
345
346
{
	va_list args;
	int res = 0;
	int integer;
347
	(void) name;
348
349
350

	va_start(args, length);

351
	if (data) {
352
		res = 1;
353
		switch (type) {
354
355
		case lc_opt_type_bit:
			integer = va_arg(args, int);
356
			if (integer)
357
				*(unsigned*)data |= length;
358
			else
359
				*(unsigned*)data &= ~length;
360
361
362
363
			break;

		case lc_opt_type_negbit:
			integer = va_arg(args, int);
364
			if (integer)
365
				*(unsigned*)data &= ~length;
366
			else
367
				*(unsigned*)data |= length;
368
369
370
371
372
373
374
375
376
377
378
			break;

		case lc_opt_type_boolean:
			*((int *) data) = va_arg(args, int);
			break;

		case lc_opt_type_negboolean:
			*((int *) data) = !va_arg(args, int);
			break;

		case lc_opt_type_string:
379
			strncpy((char*)data, va_arg(args, const char *), length);
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
			break;

		case lc_opt_type_int:
			*((int *) data) = va_arg(args, int);
			break;

		case lc_opt_type_double:
			*((double *) data) = va_arg(args, double);
			break;
		default:
			res = 0;
		}
	}

	va_end(args);
	return res;
}

398
int lc_opt_std_dump(char *buf, size_t n, const char *name, lc_opt_type_t type, void *data, size_t length)
399
400
{
	int res;
401
402
	(void) name;
	(void) length;
403

404
405
	if (data) {
		switch (type) {
406
407
		case lc_opt_type_bit:
		case lc_opt_type_negbit:
408
			res = snprintf(buf, n, "%x", *((unsigned *) data));
409
410
411
412
413
414
			break;
		case lc_opt_type_boolean:
		case lc_opt_type_negboolean:
			res = snprintf(buf, n, "%s", *((int *) data) ? "true" : "false");
			break;
		case lc_opt_type_string:
415
			strncpy(buf, (const char*)data, n);
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
			res = n;
			break;
		case lc_opt_type_int:
			res = snprintf(buf, n, "%d", *((int *) data));
			break;
		case lc_opt_type_double:
			res = snprintf(buf, n, "%g", *((double *) data));
			break;
		default:
			strncpy(buf, "", n);
			res = 0;
		}
	}

	else {
		strncpy(buf, "", n);
		res = 0;
	}

	return res;
}

438
int lc_opt_bool_dump_vals(char *buf, size_t n, const char *name, lc_opt_type_t type, void *data, size_t length)
439
{
440
441
442
443
	(void) name;
	(void) type;
	(void) data;
	(void) length;
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
	strncpy(buf, "true, false", n);
	return n;
}

int lc_opt_occurs(lc_opt_entry_t *opt, const char *value, lc_opt_err_info_t *err)
{
	static const struct {
		const char *str;
		int val;
	} bool_strings[] = {
		{ "yes", 1 },
		{ "true", 1 },
		{ "on", 1 },
		{ "1", 1 },
		{ "no", 0 },
		{ "false", 0 },
		{ "off", 0 },
		{ "0", 0 },
	};

	unsigned i;
	int error = lc_opt_err_illegal_format;
	lc_opt_special_t *s = lc_get_opt_special(opt);
	char buf[16];
	union {
		int integer;
		double dbl;
	} val_storage, *val = &val_storage;

473
	if (!opt) {
474
475
476
477
		set_error(err, lc_opt_err_opt_not_found, "");
		return 0;
	}

478
	if (!s->cb) {
479
480
481
482
483
484
		set_error(err, lc_opt_err_no_callback, "");
		return 0;
	}

	s->is_set = 1;

485
	switch (s->type) {
486
		case lc_opt_type_int:
487
			if (sscanf(value, "%i", (int *) val)) {
488
489
490
491
492
493
494
				error = lc_opt_err_unknown_value;
				if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
					error = lc_opt_err_none;
			}
			break;

		case lc_opt_type_double:
495
			if (sscanf(value, "%lf", (double *) val)) {
496
497
498
499
500
501
502
503
504
505
506
				error = lc_opt_err_unknown_value;
				if (s->cb(opt->name, s->type, s->value, s->length, val->dbl))
					error = lc_opt_err_none;
			}
			break;

		case lc_opt_type_boolean:
		case lc_opt_type_negboolean:
		case lc_opt_type_bit:
		case lc_opt_type_negbit:
				strtolower(buf, sizeof(buf), value);
507
508
				for (i = 0; i < LC_ARRSIZE(bool_strings); ++i) {
					if (strcmp(buf, bool_strings[i].str) == 0) {
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
						val->integer = bool_strings[i].val;
						error = lc_opt_err_none;
						break;
					}
				}

				if (error == lc_opt_err_none) {
					error = lc_opt_err_unknown_value;
					if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
						error = lc_opt_err_none;
				}

			break;

		case lc_opt_type_string:
		case lc_opt_type_enum:
			error = lc_opt_err_unknown_value;
			if (s->cb(opt->name, s->type, s->value, s->length, value))
				error = lc_opt_err_none;
			break;
529
530
		case lc_opt_type_invalid:
			abort();
531
532
533
534
535
536
537
538
539
	}

	set_error(err, error, value);
	return error == lc_opt_err_none;
}

char *lc_opt_value_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
{
	const lc_opt_special_t *s = lc_get_opt_special(ent);
540
	if (s->dump)
541
542
543
544
545
546
547
		s->dump(buf, len, ent->name, s->type, s->value, s->length);
	else
		strncpy(buf, "<n/a>", len);

	return buf;
}

548
static char *lc_opt_values_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
549
550
{
	const lc_opt_special_t *s = lc_get_opt_special(ent);
551
	if (s->dump_vals)
552
553
554
555
556
557
558
559
560
561
		s->dump_vals(buf, len, ent->name, s->type, s->value, s->length);

	return buf;
}

int lc_opt_add_table(lc_opt_entry_t *root, const lc_opt_table_entry_t *table)
{
	int i, res = 0;
	lc_opt_err_info_t err;

562
	for (i = 0; table[i].name != NULL; ++i) {
563
564
565
566
567
		const char *name;
		const lc_opt_table_entry_t *tab = &table[i];
		lc_opt_entry_t *grp = resolve_up_to_last_str(root, tab->name, &name);

		lc_opt_add_opt(grp, name, tab->desc, tab->type, tab->value, tab->len, tab->cb, tab->dump, tab->dump_vals, &err);
568
		if (err.error != lc_opt_err_none)
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
			res = 1;
	}

	return res;
}

static void lc_opt_print_grp_path_rec(char *buf, size_t len, const lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent)
{
	if (ent == stop_ent)
		return;
	if (!lc_opt_grp_is_root(ent)) {
		size_t l;
		lc_opt_print_grp_path_rec(buf, len, ent->parent, separator, stop_ent);
		l = strlen(buf);
		if (l > 0 && l < len-1) {
			buf[l]     = separator;
			buf[l + 1] = '\0';
		}
	}

589
	strncat(buf, ent->name, len-1);
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
}

static char *lc_opt_print_grp_path(char *buf, size_t len, const lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent)
{
	if (len > 0)
		buf[0] = '\0';
	lc_opt_print_grp_path_rec(buf, len, ent, separator, stop_ent);
	return buf;
}

/**
 * dump the option tree.
 * @param ent        starting entity
 * @param separator  separator char
 * @param stop_ent   stop at this entity when dumping the name
 * @param f          output file
 */
static void lc_opt_print_help_rec(lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent, FILE *f)
{
	lc_grp_special_t *s = lc_get_grp_special(ent);
	char grp_name[512];
	char value[256];
612
	char values[512];
613
614
	lc_opt_entry_t *e;

615
	if (!list_empty(&s->opts)) {
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
		lc_opt_print_grp_path(grp_name, sizeof(grp_name), ent, separator, stop_ent);
		fputc('\n', f);
		if (grp_name[0])
			fprintf(f, "%s:\n", grp_name);

		list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
			value[0]  = '\0';
			values[0] = '\0';
			lc_opt_value_to_string(value, sizeof(value), e);
			lc_opt_values_to_string(values, sizeof(values), e);
			fprintf(f, HELP_TEMPL_VALS "\n", e->name, lc_opt_get_type_name(e), e->desc, value, values);
		}
	}

	list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
		lc_opt_print_help_rec(e, separator, stop_ent, f);
	}

}

void lc_opt_print_help(lc_opt_entry_t *ent, FILE *f)
{
	fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
	lc_opt_print_help_rec(ent, '.', NULL, f);
}

void lc_opt_print_help_for_entry(lc_opt_entry_t *ent, char separator, FILE *f)
{
	fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
	lc_opt_print_help_rec(ent, separator, ent, f);
}


static void indent(FILE *f, int n)
{
	int i;
652
	for (i = 0; i < n; ++i)
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
		fputc(' ', f);
}

static void lc_opt_print_tree_lc_opt_indent(lc_opt_entry_t *ent, FILE *f, int level)
{
	char buf[256];
	lc_opt_special_t *s = lc_get_opt_special(ent);

	indent(f, level);
	fprintf(f, "%c%s(\"%s\"):%s = %s\n", s->is_set ? '+' : '-', ent->name,
			ent->desc, lc_opt_get_type_name(ent), lc_opt_value_to_string(buf, sizeof(buf), ent));
}

static void lc_opt_print_tree_grp_indent(lc_opt_entry_t *ent, FILE *f, int level)
{
	lc_grp_special_t *s;

670
	if (ent->is_grp) {
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
		lc_opt_entry_t *e;

		s = lc_get_grp_special(ent);
		indent(f, level);
		fprintf(f, "/%s\n", ent->name);

		list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
			lc_opt_print_tree_grp_indent(e, f, level + 2);
		}

		list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
			lc_opt_print_tree_lc_opt_indent(e, f, level + 2);
		}
	}
}

void lc_opt_print_tree(lc_opt_entry_t *ent, FILE *f)
{
	lc_opt_print_tree_grp_indent(ent, f, 0);
}

692
693
694
695
696
static int lc_opts_default_error_handler(const char *prefix, const lc_opt_err_info_t *err)
{
	fprintf(stderr, "%s: %s; %s\n", prefix, err->msg, err->arg);
	return 0;
}
697
698
699
700
701
702

int lc_opt_from_single_arg(const lc_opt_entry_t *root,
						   const char *opt_prefix,
						   const char *arg, lc_opt_error_handler_t *handler)
{
	const lc_opt_entry_t *grp = root;
703
704
	size_t n                  = strlen(arg);
	size_t n_prefix           = opt_prefix ? strlen(opt_prefix) : 0;
705
706
707
708
	int error                 = 0;
	int ret                   = 0;

	lc_opt_err_info_t err;
709
	const char *end, *eqsign;
710

711
	if (n >= n_prefix && strncmp(opt_prefix, arg, n_prefix) == 0) {
712
713
714
715
		arg = arg + n_prefix;

		/* find the next delimiter (the -) and extract the string up to
		 * there. */
716
717
718
719
		end    = strchr(arg, OPT_DELIM);
		eqsign = strchr(arg, '=');
		if (eqsign && eqsign < end)
			end = NULL;
720
		while (end != NULL) {
721
722
723
724
			/*
			 * Copy the part of the option into the buffer and add the
			 * finalizing zero.
			 */
725
			char *buf = (char*)obstack_copy0(&obst, arg, end - arg);
726
727
728
729

			/* Resolve the group inside the group */
			grp = lc_opt_find_grp(grp, buf, &err);
			error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
730
			if (error)
731
732
733
734
				break;

			/* Find the next option part delimiter. */
			arg = end + 1;
735
736
737
738
			end    = strchr(arg, OPT_DELIM);
			eqsign = strchr(arg, '=');
			if (eqsign && eqsign < end)
				end = NULL;
739
740
741
			obstack_free(&obst, buf);
		}

742
		if (!error) {
743
			lc_opt_entry_t *opt;
744
			char           *buf;
745
746
747
748
749
750
751
752
753

			/*
			 * Now, we are at the last option part:
			 * --grp1-grp2-...-grpn-opt=value
			 * Check, for the = and evaluate the option string. If the = is
			 * missing, we should have a boolean option, but that is checked
			 * later.
			 */
			end = strchr(arg, '=');
754
			buf = (char*)obstack_copy0(&obst, arg, end ? end - arg : (int) strlen(arg));
755
756
757
			opt = lc_opt_find_opt(grp, buf, &err);
			error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);

758
			if (!error) {
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
				/*
				 * Now evaluate the parameter of the option (the part after
				 * the =) if it was given.
				 */
				arg = end ? end + 1 : "true";

				/* Set the value of the option. */
				lc_opt_occurs(opt, arg, &err);
				ret = !lc_opt_raise_error(&err, handler, ERR_STRING, arg);
			}
		}
	}

	return ret;
}

int lc_opt_from_argv(const lc_opt_entry_t *root,
					 const char *opt_prefix,
					 int argc, const char *argv[],
					 lc_opt_error_handler_t *handler)
{
	int i;
	int options_set = 0;

783
784
785
	if (handler == NULL)
		handler = lc_opts_default_error_handler;

786
	for (i = 0; i < argc; ++i) {
787
788
789
790
791
792
		options_set |= lc_opt_from_single_arg(root, opt_prefix, argv[i], handler);
	}

	return options_set;
}

793
static int opt_arg_type(const lc_arg_occ_t *occ)
794
{
795
	(void) occ;
796
797
798
799
800
801
802
	return lc_arg_type_ptr;
}

static int opt_arg_emit(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *arg)
{
	char buf[256];

803
	lc_opt_entry_t *opt = (lc_opt_entry_t*)arg->v_ptr;
804
805
	const char     *s   = buf;
	size_t          res = 0;
806

807
	switch (occ->conversion) {
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
	case 'V':
		lc_opt_value_to_string(buf, sizeof(buf), opt);
		break;
	case 'T':
		s = lc_opt_get_type_name(opt);
		break;
	case 'D':
		s = opt->desc;
		break;
	case 'O':
		s = opt->name;
		break;
	default:
		s = NULL;
	}

824
	if (s)
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
		res = lc_appendable_snadd(app, s, strlen(s));

	return res;
}

static const lc_arg_handler_t lc_opt_arg_handler = {
	opt_arg_type,
	opt_arg_emit
};


/* lc_printf facility for options */

const lc_arg_env_t *lc_opt_get_arg_env(void)
{
	static lc_arg_env_t *env = NULL;

842
	if (!env) {
843
844
845
846
847
848
849
850
851
852
		env = lc_arg_new_env();

		lc_arg_register(env, "opt:value", 'V', &lc_opt_arg_handler);
		lc_arg_register(env, "opt:type",  'T', &lc_opt_arg_handler);
		lc_arg_register(env, "opt:desc",  'D', &lc_opt_arg_handler);
		lc_arg_register(env, "opt:name",  'O', &lc_opt_arg_handler);
	}

	return env;
}