* <<ex_structure,The structure>>
* <<ex_load,Loading>>
- <<deep,Getting deeper>>
+ * <<conf_multi,Arrays and lists>>
* <<reload,Reloading configuration>>
* <<custom_parser,Creating custom parsers>>
* <<hooks,Hooks>>
* <<conf_macros,Convenience macros>>
* <<alloc,Memory allocation>>
* <<journal,Undo journal>>
+ * <<declare,Section declaration>>
* <<bparser,Parsers for basic types>>
- <<getopt_h,ucw/getopt.h>>
* <<conf_load,Safe configuration loading>>
values work as default, if nothing else is loaded. The hw_config()
structure assigns the variables to configuration names. The hw_init()
function (because of the `CONSTRUCTOR` macro) is run before main()
-is called and it plugs in the whole section to the parser.
+is called and it plugs in the whole section to the parser (alternatively,
+you can call @cf_declare_section() at the start of your main()).
You can plug in as many configuration sections as you like, from
various places across your code.
[[ex_load]]
Loading of the values
~~~~~~~~~~~~~~~~~~~~~
-You need to parse the command line arguments and load the
-configuration. You can do it in a similar way to this example.
+Suppose you need to parse the command line arguments and load the
+configuration. Then @cf_getopt() is there for you: it works like
+the the traditional @getopt() from the C library, but it also handles
+configuration files.
#include <ucw/lib.h>
#include <ucw/conf.h>
#include <ucw/getopt.h>
- static byte short_opts[] = CF_SHORT_OPTS "v";
+ static char short_opts[] = CF_SHORT_OPTS "v";
static struct option long_opts[] = {
CF_LONG_OPTS
{ "verbose", 0, 0, 'v' },
{ NULL, 0, 0, 0 }
};
- int verbose;
+ static int verbose;
int main(int argc, char *argv[]) {
cf_def_file = "default.cf";
case 'v': verbose = 1; break;
default: fprintf("Unknown option %c\n", opt); return 1;
}
+ }
The `short_opts` and `long_opts` variables describe the command line
arguments. Notice the `CF_SHORT_OPTS` and `CF_LONG_OPTS` macros. They
-add options for the configuration parser. These options are handled
-internally by @cf_getopt(). It loads the configuration before it starts
-giving you your program's options.
+add the `-S` and `-C` options for the configuration parser as described
+in <<config:>>. These options are handled internally by @cf_getopt().
-See documentation of unix getopt_long() function.
+You can rely on the configuration files having been loaded before the
+first of your program's options is parsed.
[[deep]]
Getting deeper
Since the configuration system is somehow complicated, this part gives
you a little overview of what you can find and where.
+[[conf_multi]]
+Arrays and lists
+~~~~~~~~~~~~~~~~
+
+It is sometime needed to have multiple items of the same type. There
+are three ways to do that:
+
+*Static arrays*::
+ An array with fixed maximum length. You provide
+ the length and already allocated array which is filled with items.
+ The configuration may contain less than the maximum length items.
++
+For example, you can have an static array of five unsigned integers:
++
+ static uns array[] = { 1, 2, 3, 4, 5 };
++
+ static struct cf_section section = {
+ CF_ITEMS {
+ CF_UNS_ARY("array", array, 5),
+ CF_END
+ }
+ };
+
+*Dynamic arrays*::
+ Similar to static array, but you provide pointer
+ to pointer to the given item (eg. if you want dynamic array of
+ integers, you give `**int`). The parser allocates an array of needed
+ size. You can use the <<def_DARY_LEN,`DARY_LEN`>> macro to find out
+ the number of elements actually loaded.
++
+If you want dynamic array of strings, you would use:
++
+ static char *array[];
++
+ static struct cf_section section = {
+ CF_ITEMS {
+ CF_STRING_DYN("array", &array, CF_ANY_NUM),
+ CF_END
+ }
+ };
+
+*Lists*::
+ Linked lists based on <<clist:>>. You provide description
+ of single node and pointer to the
+ <<clist:struct_clist,`struct clist`>> variable. All the nodes will
+ be created dynamically and put there.
++
+First element of your structure must be <<clist:type_cnode,`cnode`>>.
++
+The first example is list of strings and uses <<clist:simple,simple
+lists>>:
++
+ static struct clist list;
++
+ static struct cf_section section = {
+ CF_ITEMS {
+ CF_LIST("list", &list, &cf_string_list_config),
+ CF_END
+ }
+ };
++
+Another example, describing how to create more complicated list node
+than just a string can be found at the <<def_CF_TYPE,`CF_TYPE`>> macro.
+
[[reload]]
Reloading configuration
~~~~~~~~~~~~~~~~~~~~~~~
The parser needs to support <<journal,journaling>>. To accomplish that,
you have to use the <<alloc,configuration mempool>> for memory allocation.
-Furthermore, you need to call @cf_journal_block() before you change
-the configuration (eg. before you save the parsed value to the destination
-variable). You can use <<def_CF_JOURNAL_VAR,`CF_JOURNAL_VAR`>> macro
-instead if it is a simple variable.
Now, you need a function with the same signature as
<<type_cf_parser1,`cf_parser1`>>. Parse the first parameter (the
-string), call @cf_journal_block() on the second parameter and store
-the data there. You may want to write a dumper function, with
-signature of <<type_cf_dumper1,`cf_dumper1`>> (needed for debug
-dumps).
+string) and store the data in the second parameter. You may want to
+write a dumper function, with signature of
+<<type_cf_dumper1,`cf_dumper1`>> (needed for debug dumps).
-Fill in a <<struct_cf_user_type,structure cf_user_type>> and use the
+Fill in a structure <<struct_cf_user_type,cf_user_type>> and use the
new data type in your configuration description with
-<<def_CF_USER,`CF_USER` macro>>.
+<<def_CF_USER,`CF_USER`>> macro as its @t parameter.
+
+You do not need to call @cf_journal_block() on the variable you store
+the result. It is true you change it, but it was stored to journal
+before your parser function was called.
[[hooks]]
Hooks