# libConfuse tutorial

### Martin Hedenfalk

## Introducing libConfuse in an existing program

Consider this simple program:

    01   #include <stdio.h>
    02
    03   int main(void)
    04   {
    05       printf("Hello, World!\n");
    06       return 0;
    07   }
    08

Simple enough, but we want to extend the program so we can greet others. Maybe we don't want to greet the whole world, just our neighbour. We use libConfuse to let the user decide whom to greet.

    01   #include <stdio.h>
    02   #include <confuse.h>
    03
    04   int main(void)
    05   {
    06       cfg_opt_t opts[] =
    07       {
    08           CFG_STR("target", "World", CFGF_NONE),
    09           CFG_END()
    10       };
    11       cfg_t *cfg;
    12
    13       cfg = cfg_init(opts, CFGF_NONE);
    14       cfg_parse(cfg, "hello.conf");
    15
    16       printf("Hello, %s!\n", cfg_getstr(cfg, "target"));
    17
    18       cfg_free(cfg);
    19       return 0;
    20   }
    21

All programs using libConfuse must first include the `confuse.h` header file. This is done on line 2.

On line 6 - 10, the options that should be recognized are defined in an array of cfg_opt_t structs. This is passed to the `cfg_init` function on line 13. The resulting cfg_t context is used by `cfg_parse()`, which reads the configuration file "hello.conf". When reading the configuration file, only options defined in the array of options passed to `cfg_init()` are recognized.

The friendly greeting is now replaced with a parameter read from the configuration file. The value of the `target` option is retrieved with `cfg_getstr(cfg, "target")`.

Lets take a look at the configuration file hello.conf:

    # this is the configuration file for the hello program

    target = "Neighbour"

Here, the target option is set to the string value "Neighbour". What if the configuration file was empty or didn't exist? Then the default value for the `target` option would be used. When we initialized our options, the second parameter to the `CFG_STR()` macro specified the default value. Thus, if no `target` option was specified in the configuration file, the hello program would have printed the standard greeting "Hello, World".

### Environment variables in values

What else can we do in the configuration file? We can set the value to an environment variable:

    target = ${USER}

This results in the hello program greeting the user who runs it. On some systems, the USER variable might not be available, so we want to specify a default value in those cases:

    target = ${USER:-User}

Now, if the USER environment variable is unset, the string "User" will be used instead.

## Other types of options

Of course, not only strings can be specified in the configuration file. libConfuse can parse strings, integers, booleans and floating point values. These are the fundamental values, and they are all also available as lists. We'll talk more about lists in the next chapter.

The macros used to initialize a string, integer, boolean and a float is, respectively, `CFG_STR()`, `CFG_INT()`, `CFG_BOOL()` and `CFG_FLOAT()`. All macros take three parameters: the name of the option, a default value and flags. To retrieve the values, use `cfg_getstr()`, `cfg_getint()`, `cfg_getbool()` or `cfg_getfloat()`.

Let's introduce an integer option that tells us how many times to print the greeting:

    01   #include <stdio.h>
    02   #include <confuse.h>
    03
    04   int main(void)
    05   {
    06       cfg_opt_t opts[] =
    07       {
    08           CFG_STR("target", "World", CFGF_NONE),
    09           CFG_INT("repeat", 1, CFGF_NONE),
    10           CFG_END()
    11       };
    12       cfg_t *cfg;
    13       int repeat;
    14
    15       cfg = cfg_init(opts, CFGF_NONE);
    16       cfg_parse(cfg, "hello.conf");
    17
    18       repeat = cfg_getint(cfg, "repeat");
    19       while(repeat--)
    20           printf("Hello, %s!\n", cfg_getstr(cfg, "target"));
    21
    22       cfg_free(cfg);
    23       return 0;
    24   }
    25

Here we have used the `CFG_INT()` macro to initialize an integer option named "repeat". The default value is 1 as in the standard greeting. The value is retrieved with `cfg_getint()`.

But, wait a moment, what if the user specified a negative value for "repeat"? Or a too large positive value? libConfuse can handle that with a so-called validating callback. We'll come back to this problem later on, but we will first take a look at lists.

## Introducing lists

That was easy. Now let's extend the program a bit so we can greet more than one "target". We'd like to be able to specify a list of targets to greet.

The list versions of the initialization macros are named `CFG_STR_LIST()`, `CFG_INT_LIST()`, `CFG_BOOL_LIST()` and `CFG_FLOAT_LIST()`. They take the same parameters as the non-list versions, except the default value must be a string surrounded by curly braces.

The modified program is shown below:

    01   #include <stdio.h>
    02   #include <confuse.h>
    03
    04   int main(void)
    05   {
    06       cfg_opt_t opts[] =
    07       {
    08           CFG_STR_LIST("targets", "{World}", CFGF_NONE),
    09           CFG_INT("repeat", 1, CFGF_NONE),
    10           CFG_END()
    11       };
    12       cfg_t *cfg;
    13       int repeat;
    14       int i;
    15
    16       cfg = cfg_init(opts, CFGF_NONE);
    17       cfg_parse(cfg, "hello.conf");
    18
    19       repeat = cfg_getint(cfg, "repeat");
    20       while(repeat--)
    21       {
    22           printf("Hello");
    23           for(i = 0; i < cfg_size(cfg, "targets"); i++)
    24               printf(", %s", cfg_getnstr(cfg, "targets", i));
    25           printf("!\n");
    26       }
    27
    28       cfg_free(cfg);
    29       return 0;
    30   }
    31

Three things are a bit different here. First, the macro to initialize the "targets" option is `CFG_STR_LIST()`. This tells libConfuse that "targets" is a list of strings. Second, the default value in the second parameter is surrounded by curly braces. This is needed to indicate to libConfuse where the list of values ends.

The third change is in the printing of the greeting. First we print the "Hello" string. Then we loop through all values found for the "targets" option. The number of values is retrieved with the `cfg_size()` function. The string values are then retrieved with `cfg_getnstr()`, which is an indexed version of `cfg_getstr()`. In fact, `cfg_getstr()` is equivalent to `cfg_getnstr()` with an index of zero.

In the configuration file hello.conf, we can now specify a list of targets to greet:

    # this is the configuration file for the hello program

    targets = {"Life", "Universe", "Everything"}
    repeat = 1

The output of the hello program, run with the above configuration file, is: "Hello, Life, Universe, Everything!"

Again, if no targets were configured, the greeting would have been the standard "Hello, World!".

## Using sections

So far, we have only use a flat configuration file. libConfuse can also handle sections to build a hierarchy of options. Sections can be used to group options in logical blocks, and those blocks can (optionally) be specified multiple times.

Sections are initialized with the `CFG_SEC()` macro. It also takes three parameters: the name of the option, an array of options allowed in the section and flags.

We'll extend the, now rather complex, hello program so we can do other kinds of greetings, not just "Hello". Each greeting will have its own settings for targets and repeat.

    01   #include <stdio.h>
    02   #include <confuse.h>
    03
    04   int validate_unsigned_int(cfg_t *cfg, cfg_opt_t *opt)
    05   {
    06       int value = cfg_opt_getnint(opt, cfg_opt_size(opt) - 1);
    07       if(value < 0)
    08       {
    09           cfg_error(cfg, "integer option '%s' must be positive in section '%s'",
    10                   opt->name, cfg->name);
    11           return -1;
    12       }
    13       return 0;
    14   }
    15
    16   int main(void)
    17   {
    18       cfg_opt_t greet_opts[] =
    19       {
    20           CFG_STR_LIST("targets", "{World}", CFGF_NONE),
    21           CFG_INT("repeat", 1, CFGF_NONE),
    22           CFG_END()
    23       };
    24       cfg_opt_t opts[] =
    25       {
    26           CFG_SEC("greeting", greet_opts, CFGF_TITLE | CFGF_MULTI),
    27           CFG_END()
    28       };
    29       cfg_t *cfg, *cfg_greet;
    30       int repeat;
    31       int i, j;
    32
    33       cfg = cfg_init(opts, CFGF_NONE);
    34       cfg_set_validate_func(cfg, "greeting|repeat", validate_unsigned_int);
    35       cfg_parse(cfg, "hello.conf");
    36
    37       for(j = 0; j < cfg_size(cfg, "greeting"); j++)
    38       {
    39           cfg_greet = cfg_getnsec(cfg, "greeting", j);
    40
    41           repeat = cfg_getint(cfg_greet, "repeat");
    42           while(repeat--)
    43           {
    44               printf("%s", cfg_title(cfg_greet));
    45               for(i = 0; i < cfg_size(cfg_greet, "targets"); i++)
    46                   printf(", %s", cfg_getnstr(cfg_greet, "targets", i));
    47               printf("!\n");
    48           }
    49       }
    50
    51       cfg_free(cfg);
    52       return 0;
    53   }
    54

We have renamed the option array from "opts" to "greet_opts", and introduced a new "opts" array that only has one option: a "greeting" section. The second parameter of the `CFG_SEC()` macro points to the old greeting options "targets" and "repeat".

We have also used a couple of flags to alter the behaviour of the section: CFGF_TITLE means that a greeting section should have a title and the CFGF_MULTI flag tells libConfuse that this section may be specified multiple times in the configuration file. The title of a section is retrieved with the `cfg_title()` function.

The outmost loop (with index j) now loops through all given sections in the configuration file. We retrieve a section with a `cfg_getnsec()` call. The value returned is a pointer to a cfg_t struct, the same type as returned by `cfg_init()`. Thus we can use the ordinary value retrieval functions `cfg_getstr()`, `cfg_getint()` and so on to retrieve values of options inside the section.

Ok, so how does the configuration file look like for this setup?

    # this is the configuration file for the hello program

    greeting Hello
    {
        targets = {"Life", "Universe", "Everything"}
        repeat = 1
    }

    greeting Bye
    {
        targets = {Adams}
        repeat = 1
    }

The program will loop through the sections in the order specified in the configuration file. First it will find the "Hello" section. It prints the title of the section, "Hello", retrieved with `cfg_title()`. Then the targets are printed just as in the previous exemples, but this time the values are retrieved from the cfg_greet section. Next, the section titled "Bye" is found, and the values are retrieved from that section.

## Parsing from internal buffers

So far, we have only parsed configuration data from files. libConfuse can also parse buffers, or in-memory character strings. We will use this to fix a problem in the previous code.

The problem is that without a configuration file, the hello program will not print anything. We want it to at least print the standard greeting "Hello, World!" if no configuration file is available.

We can't have a default value for a section that can be specified multiple times (ie, a section with the CFGF_MULTI flag set). Instead we will parse a default configuration string if no section has been parsed:

    01   #include <stdio.h>
    02   #include <confuse.h>
    03
    04   int main(void)
    05   {
    06       /* ... setup options ... */
    07
    08       cfg = cfg_init(opts, CFGF_NONE);
    09       cfg_parse(cfg, "hello.conf");
    10
    11       if(cfg_size(cfg, "greeting") == 0)
    12       {
    13           cfg_parse_buf(cfg, "greeting Hello {}");
    14       }
    15
    16       /* ... print the greetings ... */
    17   }

Only the changes from the previous code is shown here. We check if the size of the "greeting" section is zero (ie, no section has been defined). In that case we call `cfg_parse_buf()` to parse a default in-memory string "greeting Hello {}". This string defines a greeting section with title Hello, but without any sub-options. This way we rely on the default values of the (sub-)options "targets" and "repeat".

When this program is run, it issues the well-known standard greeting "Hello, World!" if no configuration file is present.

## Validating callback functions

Remember the problem about a negative or too large "repeat" value in the section called “Other types of options”? The code that prints the greeting has those lines:

    ...
    repeat = cfg_getint(cfg_greet, "repeat");
    while(repeat--)
    ...

The repeat variable is defined as an int, a signed integer. If the user specified a negative repeat value in the configuration file, this code would continue to decrease the repeat variable until it eventually underflowed.

We'll fix this by not allowing a negative value in the configuration file. Of course we could first just check if the value is negative and the abort, using `cfg_getint()` and a test. But we will use a validating callback function instead. This way `cfg_parse()` will return an error directly when parsing the file, additionally indicating on which line the error is.

A validating callback function is defined as:

    typedef int (*cfg_validate_callback_t)(cfg_t *cfg, cfg_opt_t *opt);

This function takes two arguments: the section and the option. It should return 0 on success (ie, the value validated ok). All other values indicates an error, and the parsing is aborted. The callback function should notify the error itself, for example by calling `cfg_error()`.

Here is the code for the callback function:

    01   int validate_unsigned_int(cfg_t *cfg, cfg_opt_t *opt)
    02   {
    03       int value = cfg_opt_getnint(opt, cfg_opt_size(opt) - 1);
    04       if(value < 0)
    05       {
    06           cfg_error(cfg, "integer option '%s' must be positive in section '%s'",
    07                   opt->name, cfg->name);
    08           return -1;
    09       }
    10       return 0;
    11   }
    12

Only the last value is validated, because libConfuse will call this function once for every value corresponding to the option. Since the "repeat" option is not a list, we could instead have used `cfg_opt_getint(opt)` to retrieve the only value. However, if we later want to use this callback to validate an integer list, it is already lists-aware.

### Installing the callback

## Value parsing callback

## Functions

## Saving configuration files