SYNOPSIS
conf2struct <file.cfg>
DESCRIPTION
conf2struct
takes a configuration file that describes a
configuration file in the libconfig format, and generates a C parser that will read a configuration file directly into a C structure. The goal is to accept any file that is valid for libconfig
, which in particular means using conf2struct
does not introduce restrictions to what the configuration file should look like.
conf2struct
manageѕ optional settings and default values.
(A bit of history:
sslh uses
libconfig
, which provides a C API. There are two downsides
to that: with the increase of the number of settings, the
code to read the configuration file grew and became harder
to read, while being all mostly boilerplate code; also,
every time it reads a setting libconfig
traverses a tree
doing string compares on the setting names, which is very
inefficient for settings that get used very often: those
setting need to be copied to a structure. Put both problems
together, and you get this program as a solution by taking a
step up.)
conf2struct
is published on github.
Configuration file
The format of the target configuration file is itself described as a configuration file which is somewhat similar to a schema file.
Global declarations
The following settings are located at the root of the configuration file:
header
: output file name for the struct declarationsparser
: output file for the C codeconfig
: description of the target configuration file. This contains aname
entry which will prefix all symbols, and aitems
declaration equivalent to that of a group (see below).
Settings
The following entries are mandatory for each setting:
name
specifies the name of the settingtype
specifies the type of the setting. It is one of the libconfig types:boolean
,int
,int64
,float
,group
,list
,array
. Type can also be specified asruntime
if you want to add runtime data to the struct. The C type will then be specified by the additionnalc_type
setting.c2s
will do nothing with these types (no init, no alloc, no printing).
Scalars
Scalar types take the additional following entries:
default
specifies its default value. If the setting has no default, then it is mandatory.optional
specifies that a setting can be undefined. Undefined strings will point to NULL, and optional settings receive an additional<id>_is_present
boolean in the final struct.
Groups
Group entries must contain an items
setting which contains
a list of named settings. Groups can contain items of any type.
Lists
List entries must contain an items
setting which contains
the list of setting expected in each item of the list.
Lists can contain anything, including other lists.
Note this does not support items of varying types, as is possible in libconfig. This libconfig feature feels counter-intuitive when targeting a struct for storage.
Arrays
Settings of type array
must contain an item_type
setting which specifies the type of the scalars contained in
the array.
Lists and arrays both get converted to a static array. A
field <name>_len
is added containing the number of
elements in the array.
Parser API
Assuming the prefix for our configuration is foo
, the prototype for the parser produced is:
int foo_parse_file(
const char* filename,
struct foo_items* config,
const char** errmsg);
-
filename
is the name of the file, passed directly to libconfig. -
config
is the root configuration struct as output byconf2struct
. The parser will fill that structure and allocate memory for groups, lists and arrays as required. -
errmsg
is a text string that explains what went wrong if parsing failed (e.g. a mandatory option is missing).
config_parser()
returns 0 on failure, 1 on success.
Printer API
conf2struct
also builds a pretty-printer that takes a
struct
as input.
Example
The file eg_conf.cfg
documents the conf2struct
configuration to build a parser for the libconfig
example.cfg
. parser.c
shows a very simple parser that
also reports errors, using this example. A simple:
make
./example
will produce the parser in example.c
and example.h
(as
specified in eg_conf.cfg
), which parses example.cfg
and
prints the result directly from the in-memory struct.
confcheck
conf2struct
is written in Perl, and the Perl
implementation for libconfig
appears to be very bad at
reporting parsing errors (that, or the documentation is very
bad). The small confcheck
program can be used to validate configuration files: it will
provide the exact parse error location, if any.
TODO
-
validation parameters, e.g.
min_length
/max_length
for arrays and lists, and strings, which can be verified to check the validity of the configuration. -
generator of a command-line parser to override the configuration file.
Similar project
x2struct does a similar job, but for C++, added constraints to the target configuration file (while I did not want users to have to change existing configuration files), and I didn’t see how to extend it to get useful error messages, default values, and setting validation.