2 libcore: library for basic data structures and algorithms.
3 Copyright (C) 2005 IPD Goos, Universit"at Karlsruhe, Germany
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #if defined(__FreeBSD__)
33 /* Includes to determine user's home directory */
37 #include <sys/types.h>
42 /* maximum length of a path. */
48 #include "lc_common_t.h"
49 #include "lc_opts_t.h"
50 #include "lc_opts_enum.h"
51 #include "lc_parser_t.h"
53 #include "lc_printf.h"
55 #define ERR_STRING "In argument \"%s\": "
59 #define HELP_TEMPL "%-15s %-10s %-45s"
60 #define HELP_TEMPL_VALS HELP_TEMPL " [%s] (%s)"
62 static struct obstack obst;
64 static void set_name(lc_opt_entry_t *ent, const char *name)
67 ent->hash = HASH_STR(name, strlen(name));
70 #define entry_matches(ent,hash_val,str) \
71 ((ent)->hash == hash_val && strcmp((ent)->name, (str)) == 0)
73 #define entries_equal(e1,e2) entry_matches(e1, (e2)->hash, (e2)->name)
75 static lc_opt_err_info_t *set_error(lc_opt_err_info_t *err, int error, const char *arg)
86 int lc_opt_raise_error(const lc_opt_err_info_t *err, lc_opt_error_handler_t *handler,
93 if(err && lc_opt_is_error(err)) {
97 vsnprintf(buf, sizeof(buf), fmt, args);
106 static lc_opt_entry_t *init_entry(lc_opt_entry_t *ent, lc_opt_entry_t *parent,
107 const char *name, const char *desc)
109 const char *copied_name;
110 const char *copied_desc;
112 obstack_grow0(&obst, name, strlen(name));
113 copied_name = obstack_finish(&obst);
114 obstack_grow0(&obst, desc, strlen(desc));
115 copied_desc = obstack_finish(&obst);
117 memset(ent, 0, sizeof(*ent));
118 set_name(ent, copied_name);
119 ent->desc = copied_desc;
120 ent->parent = parent;
124 static lc_opt_entry_t *init_grp(lc_opt_entry_t *ent, lc_opt_err_info_t *err)
127 INIT_LIST_HEAD(&ent->v.grp.grps);
128 INIT_LIST_HEAD(&ent->v.grp.opts);
130 set_error(err, lc_opt_err_none, "");
132 if(ent->parent->is_grp)
133 list_add(&ent->list, &lc_get_grp_special(ent->parent)->grps);
135 set_error(err, lc_opt_err_grp_expected, ent->parent->name);
141 static lc_opt_entry_t *init_opt(lc_opt_entry_t *ent,
143 void *val, size_t length,
144 lc_opt_callback_t *cb,
146 lc_opt_dump_vals_t *dump_vals,
147 lc_opt_err_info_t *err)
149 lc_opt_special_t *s = lc_get_opt_special(ent);
152 set_error(err, lc_opt_err_none, "");
153 list_add(&ent->list, &lc_get_grp_special(ent->parent)->opts);
159 s->dump_vals = dump_vals;
166 lc_opt_entry_t *lc_opt_root_grp(void)
168 static lc_opt_entry_t root_group;
169 static int inited = 0;
175 init_entry(&root_group, NULL, "root", "The root node");
176 init_grp(&root_group, NULL);
182 int lc_opt_grp_is_root(const lc_opt_entry_t *ent)
184 return ent->parent == NULL;
187 static const char *get_type_name(lc_opt_type_t type)
191 #define XXX(t) case lc_opt_type_ ## t: res = #t; break
199 case lc_opt_type_negbit: res = "bit"; break;
200 case lc_opt_type_negboolean: res = "boolean"; break;
209 const char *lc_opt_get_type_name(const lc_opt_entry_t *ent)
211 return get_type_name(lc_get_opt_special(ent)->type);
215 lc_opt_entry_t *lc_opt_find_grp(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err);
216 lc_opt_entry_t *lc_opt_find_opt(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err);
218 lc_opt_entry_t *lc_opt_get_grp(lc_opt_entry_t *parent, const char *name)
220 lc_opt_entry_t *ent = lc_opt_find_grp(parent, name, NULL);
223 ent = obstack_alloc(&obst, sizeof(*ent));
224 init_entry(ent, parent, name, "");
231 lc_opt_entry_t *lc_opt_add_opt(lc_opt_entry_t *parent,
232 const char *name, const char *desc,
233 lc_opt_type_t type, void *value, size_t length,
234 lc_opt_callback_t *cb, lc_opt_dump_t *dump,
235 lc_opt_dump_vals_t *dump_vals,
236 lc_opt_err_info_t *err)
238 lc_opt_entry_t *res = NULL;
241 lc_opt_entry_t *ent = lc_opt_find_opt(parent, name, NULL);
244 res = obstack_alloc(&obst, sizeof(*ent));
245 init_entry(res, parent, name, desc);
246 init_opt(res, type, value, length, cb, dump, dump_vals, err);
248 set_error(err, lc_opt_err_opt_already_there, name);
250 set_error(err, lc_opt_err_grp_expected, name);
256 static lc_opt_entry_t *lc_opt_find_ent(const struct list_head *head, const char *name,
257 int error_to_use, lc_opt_err_info_t *err)
259 lc_opt_entry_t *ent, *found = NULL;
260 int error = error_to_use;
261 unsigned hash = HASH_STR(name, strlen(name));
263 if(!list_empty(head)) {
264 list_for_each_entry(lc_opt_entry_t, ent, head, list) {
265 if(entry_matches(ent, hash, name)) {
266 error = lc_opt_err_none;
273 set_error(err, error, name);
277 lc_opt_entry_t *lc_opt_find_grp(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
279 return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->grps,
280 name, lc_opt_err_grp_not_found, err) : NULL;
283 lc_opt_entry_t *lc_opt_find_opt(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
285 return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->opts,
286 name, lc_opt_err_opt_not_found, err) : NULL;
289 static const lc_opt_entry_t *resolve_up_to_last(const lc_opt_entry_t *root,
290 const char * const *names, int pos, int n, lc_opt_err_info_t *err)
297 ent = lc_opt_find_grp(root, names[pos], err);
298 return ent ? resolve_up_to_last(ent, names, pos + 1, n, err) : NULL;
301 static const char *path_delim = "/.";
303 static lc_opt_entry_t *resolve_up_to_last_str_rec(lc_opt_entry_t *from,
305 const char **last_name)
308 lc_opt_entry_t *res = from;
309 size_t end = strcspn(path, path_delim);
311 if(path[end] != '\0') {
312 /* skip all delimiters */
313 size_t next = strspn(path + end, path_delim);
315 /* copy the part of the path into a buffer */
316 char *buf = malloc((end+1) * sizeof(buf[0]));
317 strncpy(buf, path, end);
320 /* resolve the group and free */
321 from = lc_opt_get_grp(from, buf);
324 res = resolve_up_to_last_str_rec(from, path + end + next, last_name);
327 else if(last_name != NULL) {
334 static lc_opt_entry_t *resolve_up_to_last_str(lc_opt_entry_t *root, const char *path, const char **last_name)
336 size_t next = strspn(path, path_delim);
338 /* if l != 0 we saw deliminators, so we resolve from the root */
340 root = lc_opt_root_grp();
342 return resolve_up_to_last_str_rec(root, path + next, last_name);
345 lc_opt_entry_t *lc_opt_resolve_grp(const lc_opt_entry_t *root,
346 const char * const *names, int n, lc_opt_err_info_t *err)
348 const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
349 return lc_opt_find_grp(grp, names[n - 1], err);
352 lc_opt_entry_t *lc_opt_resolve_opt(const lc_opt_entry_t *root,
353 const char * const *names, int n, lc_opt_err_info_t *err)
355 const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
356 return lc_opt_find_opt(grp, names[n - 1], err);
359 static char *strtolower(char *buf, size_t n, const char *str)
362 for(i = 0; i < n; ++i)
363 buf[i] = tolower(str[i]);
367 int lc_opt_std_cb(UNUSED(const char *name), lc_opt_type_t type, void *data, size_t length, ...)
373 va_start(args, length);
378 case lc_opt_type_bit:
379 integer = va_arg(args, int);
381 *((int *) data) |= length;
383 *((int *) data) &= ~length;
386 case lc_opt_type_negbit:
387 integer = va_arg(args, int);
389 *((int *) data) &= ~length;
391 *((int *) data) |= length;
394 case lc_opt_type_boolean:
395 *((int *) data) = va_arg(args, int);
398 case lc_opt_type_negboolean:
399 *((int *) data) = !va_arg(args, int);
402 case lc_opt_type_string:
403 strncpy(data, va_arg(args, const char *), length);
406 case lc_opt_type_int:
407 *((int *) data) = va_arg(args, int);
410 case lc_opt_type_double:
411 *((double *) data) = va_arg(args, double);
422 int lc_opt_std_dump(char *buf, size_t n, UNUSED(const char *name), lc_opt_type_t type, void *data, UNUSED(size_t length))
428 case lc_opt_type_bit:
429 case lc_opt_type_negbit:
430 res = snprintf(buf, n, "%x", *((int *) data));
432 case lc_opt_type_boolean:
433 case lc_opt_type_negboolean:
434 res = snprintf(buf, n, "%s", *((int *) data) ? "true" : "false");
436 case lc_opt_type_string:
437 strncpy(buf, data, n);
440 case lc_opt_type_int:
441 res = snprintf(buf, n, "%d", *((int *) data));
443 case lc_opt_type_double:
444 res = snprintf(buf, n, "%g", *((double *) data));
460 int lc_opt_bool_dump_vals(char *buf, size_t n, UNUSED(const char *name), UNUSED(lc_opt_type_t type), UNUSED(void *data), UNUSED(size_t length))
462 strncpy(buf, "true, false", n);
466 int lc_opt_occurs(lc_opt_entry_t *opt, const char *value, lc_opt_err_info_t *err)
468 static const struct {
483 int error = lc_opt_err_illegal_format;
484 lc_opt_special_t *s = lc_get_opt_special(opt);
489 } val_storage, *val = &val_storage;
492 set_error(err, lc_opt_err_opt_not_found, "");
497 set_error(err, lc_opt_err_no_callback, "");
504 case lc_opt_type_int:
505 if(sscanf(value, "%i", (int *) val)) {
506 error = lc_opt_err_unknown_value;
507 if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
508 error = lc_opt_err_none;
512 case lc_opt_type_double:
513 if(sscanf(value, "%lf", (double *) val)) {
514 error = lc_opt_err_unknown_value;
515 if (s->cb(opt->name, s->type, s->value, s->length, val->dbl))
516 error = lc_opt_err_none;
520 case lc_opt_type_boolean:
521 case lc_opt_type_negboolean:
522 case lc_opt_type_bit:
523 case lc_opt_type_negbit:
524 strtolower(buf, sizeof(buf), value);
525 for(i = 0; i < LC_ARRSIZE(bool_strings); ++i) {
526 if(strcmp(buf, bool_strings[i].str) == 0) {
527 val->integer = bool_strings[i].val;
528 error = lc_opt_err_none;
533 if (error == lc_opt_err_none) {
534 error = lc_opt_err_unknown_value;
535 if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
536 error = lc_opt_err_none;
541 case lc_opt_type_string:
542 case lc_opt_type_enum:
543 error = lc_opt_err_unknown_value;
544 if (s->cb(opt->name, s->type, s->value, s->length, value))
545 error = lc_opt_err_none;
549 set_error(err, error, value);
550 return error == lc_opt_err_none;
553 char *lc_opt_value_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
555 const lc_opt_special_t *s = lc_get_opt_special(ent);
557 s->dump(buf, len, ent->name, s->type, s->value, s->length);
559 strncpy(buf, "<n/a>", len);
564 char *lc_opt_values_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
566 const lc_opt_special_t *s = lc_get_opt_special(ent);
568 s->dump_vals(buf, len, ent->name, s->type, s->value, s->length);
573 static lc_opt_entry_t *resolve_up_to_last_str(lc_opt_entry_t *root, const char *path, const char **last_name);
575 int lc_opt_add_table(lc_opt_entry_t *root, const lc_opt_table_entry_t *table)
578 lc_opt_err_info_t err;
580 for(i = 0; table[i].name != NULL; ++i) {
582 const lc_opt_table_entry_t *tab = &table[i];
583 lc_opt_entry_t *grp = resolve_up_to_last_str(root, tab->name, &name);
585 lc_opt_add_opt(grp, name, tab->desc, tab->type, tab->value, tab->len, tab->cb, tab->dump, tab->dump_vals, &err);
586 if(err.error != lc_opt_err_none)
593 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)
597 if (!lc_opt_grp_is_root(ent)) {
599 lc_opt_print_grp_path_rec(buf, len, ent->parent, separator, stop_ent);
601 if (l > 0 && l < len-1) {
607 strncat(buf, ent->name, len);
610 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)
614 lc_opt_print_grp_path_rec(buf, len, ent, separator, stop_ent);
619 * dump the option tree.
620 * @param ent starting entity
621 * @param separator separator char
622 * @param stop_ent stop at this entity when dumping the name
623 * @param f output file
625 static void lc_opt_print_help_rec(lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent, FILE *f)
627 lc_grp_special_t *s = lc_get_grp_special(ent);
633 if(!list_empty(&s->opts)) {
634 lc_opt_print_grp_path(grp_name, sizeof(grp_name), ent, separator, stop_ent);
637 fprintf(f, "%s:\n", grp_name);
639 list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
642 lc_opt_value_to_string(value, sizeof(value), e);
643 lc_opt_values_to_string(values, sizeof(values), e);
644 fprintf(f, HELP_TEMPL_VALS "\n", e->name, lc_opt_get_type_name(e), e->desc, value, values);
648 list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
649 lc_opt_print_help_rec(e, separator, stop_ent, f);
654 void lc_opt_print_help(lc_opt_entry_t *ent, FILE *f)
656 fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
657 lc_opt_print_help_rec(ent, '.', NULL, f);
660 void lc_opt_print_help_for_entry(lc_opt_entry_t *ent, char separator, FILE *f)
662 fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
663 lc_opt_print_help_rec(ent, separator, ent, f);
667 static void indent(FILE *f, int n)
670 for(i = 0; i < n; ++i)
674 static void lc_opt_print_tree_lc_opt_indent(lc_opt_entry_t *ent, FILE *f, int level)
677 lc_opt_special_t *s = lc_get_opt_special(ent);
680 fprintf(f, "%c%s(\"%s\"):%s = %s\n", s->is_set ? '+' : '-', ent->name,
681 ent->desc, lc_opt_get_type_name(ent), lc_opt_value_to_string(buf, sizeof(buf), ent));
684 static void lc_opt_print_tree_grp_indent(lc_opt_entry_t *ent, FILE *f, int level)
691 s = lc_get_grp_special(ent);
693 fprintf(f, "/%s\n", ent->name);
695 list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
696 lc_opt_print_tree_grp_indent(e, f, level + 2);
699 list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
700 lc_opt_print_tree_lc_opt_indent(e, f, level + 2);
705 void lc_opt_print_tree(lc_opt_entry_t *ent, FILE *f)
707 lc_opt_print_tree_grp_indent(ent, f, 0);
711 void lc_opt_from_file(const char *filename, FILE *f, lc_opt_error_handler_t *handler)
714 lc_opt_init_parser(filename, handler);
718 int lc_opt_from_single_arg(const lc_opt_entry_t *root,
719 const char *opt_prefix,
720 const char *arg, lc_opt_error_handler_t *handler)
722 const lc_opt_entry_t *grp = root;
724 int n_prefix = opt_prefix ? strlen(opt_prefix) : 0;
728 lc_opt_err_info_t err;
729 char *end, *buf, *eqsign;
731 if(n >= n_prefix && strncmp(opt_prefix, arg, n_prefix) == 0) {
732 arg = arg + n_prefix;
735 * check, if the next character is a @.
736 * This means, that we want to read options
740 size_t n = strcspn(&arg[1], " \t\n");
741 char *fname = alloca(n + 1);
744 strncpy(fname, &arg[1], n);
745 if((f = fopen(fname, "rt")) != NULL) {
746 lc_opt_from_file(fname, f, handler);
748 set_error(&err, lc_opt_err_none, NULL);
752 set_error(&err, lc_opt_err_file_not_found, arg);
754 return !lc_opt_raise_error(&err, handler, "Unable to open file: %s", fname);
757 /* find the next delimiter (the -) and extract the string up to
759 end = strchr(arg, OPT_DELIM);
760 eqsign = strchr(arg, '=');
761 if (eqsign && eqsign < end)
765 * Copy the part of the option into the buffer and add the
768 buf = obstack_copy0(&obst, arg, end - arg);
770 /* Resolve the group inside the group */
771 grp = lc_opt_find_grp(grp, buf, &err);
772 error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
776 /* Find the next option part delimiter. */
778 end = strchr(arg, OPT_DELIM);
779 eqsign = strchr(arg, '=');
780 if (eqsign && eqsign < end)
782 obstack_free(&obst, buf);
789 * Now, we are at the last option part:
790 * --grp1-grp2-...-grpn-opt=value
791 * Check, for the = and evaluate the option string. If the = is
792 * missing, we should have a boolean option, but that is checked
795 end = strchr(arg, '=');
796 buf = obstack_copy0(&obst, arg, end ? end - arg : (int) strlen(arg));
797 opt = lc_opt_find_opt(grp, buf, &err);
798 error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
802 * Now evaluate the parameter of the option (the part after
803 * the =) if it was given.
805 arg = end ? end + 1 : "true";
807 /* Set the value of the option. */
808 lc_opt_occurs(opt, arg, &err);
809 ret = !lc_opt_raise_error(&err, handler, ERR_STRING, arg);
817 int lc_opt_from_argv(const lc_opt_entry_t *root,
818 const char *opt_prefix,
819 int argc, const char *argv[],
820 lc_opt_error_handler_t *handler)
825 for(i = 0; i < argc; ++i) {
826 options_set |= lc_opt_from_single_arg(root, opt_prefix, argv[i], handler);
832 static int opt_arg_type(UNUSED(const lc_arg_occ_t *occ))
834 return lc_arg_type_ptr;
837 static int opt_arg_emit(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *arg)
841 lc_opt_entry_t *opt = arg->v_ptr;
845 switch(occ->conversion) {
847 lc_opt_value_to_string(buf, sizeof(buf), opt);
850 s = lc_opt_get_type_name(opt);
863 res = lc_appendable_snadd(app, s, strlen(s));
868 static const lc_arg_handler_t lc_opt_arg_handler = {
874 /* lc_printf facility for options */
876 const lc_arg_env_t *lc_opt_get_arg_env(void)
878 static lc_arg_env_t *env = NULL;
881 env = lc_arg_new_env();
883 lc_arg_register(env, "opt:value", 'V', &lc_opt_arg_handler);
884 lc_arg_register(env, "opt:type", 'T', &lc_opt_arg_handler);
885 lc_arg_register(env, "opt:desc", 'D', &lc_opt_arg_handler);
886 lc_arg_register(env, "opt:name", 'O', &lc_opt_arg_handler);
892 static int lc_opts_default_error_handler(const char *prefix, const lc_opt_err_info_t *err)
894 fprintf(stderr, "%s: %s; %s\n", prefix, err->msg, err->arg);
898 void lc_opts_init(const char *ini_name, lc_opt_entry_t *root, const char *arg_prefix, int argc, const char **argv)
902 char local_ini_file[MAX_PATH];
903 char home_dir_ini_file[MAX_PATH];
906 strncpy(local_ini_file, ini_name, sizeof(local_ini_file));
907 strncat(local_ini_file, ".ini", sizeof(local_ini_file));
908 local_ini_file[sizeof(local_ini_file) - 1] = '\0';
913 /* ARG: need newer SDK to compile this */
914 SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, path);
915 strncat(path, "\\", sizeof(path));
917 strncpy(home_dir_ini_file, local_ini_file, sizeof(home_dir_ini_file));
918 home_dir_ini_file[sizeof(home_dir_ini_file) - 1] = '\0';
921 struct passwd *entry = getpwuid(getuid());
923 strcpy(path, entry->pw_dir);
924 strncat(path, "/", sizeof(path));
926 snprintf(home_dir_ini_file, sizeof(home_dir_ini_file), ".%src", ini_name);
927 home_dir_ini_file[sizeof(home_dir_ini_file) - 1] = '\0';
929 /* FIXME: some error occured */
930 home_dir_ini_file[0] = '\0';
935 strncat(path, home_dir_ini_file, sizeof(path));
936 path[sizeof(path) - 1] = '\0';
938 /* Process ini file in user's home. */
939 f = fopen(path, "rt");
941 lc_opt_from_file(path, f, lc_opts_default_error_handler);
945 /* Process ini file in current directory. */
946 f = fopen(local_ini_file, "rt");
948 lc_opt_from_file(local_ini_file, f, lc_opts_default_error_handler);
952 /* process arguments from the command line */
953 lc_opt_from_argv(root, arg_prefix, argc, argv, lc_opts_default_error_handler);