qLibc
qaconf.c File Reference

Apache-style configuration file parser. More...

Go to the source code of this file.

Functions

qaconf_t * qaconf (void)
 Create a new configuration object.
 
static int addoptions (qaconf_t *qaconf, const qaconf_option_t *options)
 qaconf_t->addoptions(): Register option directives.
 
static void setdefhandler (qaconf_t *qaconf, qaconf_cb_t *callback)
 Set default callback function.
 
static void setuserdata (qaconf_t *qaconf, const void *userdata)
 qaconf_t->setuserdata(): Set userdata which will be provided on callback.
 
static int parse (qaconf_t *qaconf, const char *filepath, uint8_t flags)
 qaconf_t->parse(): Run parser.
 
static const char * errmsg (qaconf_t *qaconf)
 qaconf_t->errmsg(): Get last error message.
 
static void reseterror (qaconf_t *qaconf)
 qaconf_t->reseterror(): Clear error message.
 
static void free_ (qaconf_t *qaconf)
 qaconf_t->free(): Release resources.
 

Detailed Description

Apache-style configuration file parser.

Apache-style Configuration is a configuration file syntax and format originally introduced by Apache HTTPd project. This format is powerful, flexible and human friendly. Even though this code gets distributed as a part of qLibc project, the code is written not to have any external dependencies to make this single file stands alone for better portability. It is purely written from the ground up and dedicated to the public by Seungyoung Kim.

Sample Apache-style Configuration Syntax:

# Lines that begin with the hash character "#" are considered comments.
Listen 53
Protocols UDP TCP
IPSEC On
<Domain "qdecoder.org">
TTL 86400
MX 10 mail.qdecoder.org
<Host mail>
IPv4 192.168.10.1
TXT "US Rack-13D-18 \"San Jose's\""
</Host>
<Host www>
IPv4 192.168.10.2
TXT 'KR Rack-48H-31 "Seoul\'s"'
TTL 3600
</Host>
</Domain>
<Domain "ringfs.org">
<Host www>
CNAME www.qdecoder.org
</Host>
</Domain>
// THIS EXAMPLE CODE CAN BE FOUND IN EXAMPLES DIRECTORY.
// Define scope.
// QAC_SCOPE_ALL and QAC_SCOPE_ROOT are predefined.
// Custum scope should be defined from 2(1 << 1).
// Note) These values are ORed(bit operation), so the number should be
// 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...
enum {
OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
OPT_SECTION_HOST = (1 << 2), // user-defined section
};
// Define callback proto-types.
static QAC_CB(confcb_debug);
// Define options and callbacks.
static qaconf_option_t options[] = {
{"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
{"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
{"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
{"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
{ "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
{ "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
{ "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
{ "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
{ "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
{ "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
QAC_OPTION_END
};
int user_main(void)
{
// Create a userdata structure.
struct MyConf myconf;
// Initialize and create a qaconf object.
qaconf_t *conf = qaconf();
if (conf == NULL) {
printf("Failed to open '" CONF_PATH "'.\n");
return -1;
}
// Register options.
conf->addoptions(conf, options);
// Set callback userdata
// This is a userdata which will be provided on callback
conf->setuserdata(conf, &myconf);
// Run parser.
int count = conf->parse(conf, CONF_PATH, QAC_CASEINSENSITIVE);
if (count < 0) {
printf("Error: %s\n", conf->errmsg(conf));
} else {
printf("Successfully loaded.\n");
}
// Verify userdata structure.
if (conf->errmsg(conf) == NULL) { // another way to check parsing error.
// codes here
}
// Release resources.
conf->free(conf);
}
static QAC_CB(confcb_debug)
{
int i;
for (i = 0; i < data->level; i++) {
printf (" ");
}
// Print option name
if (data->otype == QAC_OTYPE_SECTIONOPEN) {
printf("<%s>", data->argv[0]);
} else if (data->otype == QAC_OTYPE_SECTIONCLOSE) {
printf("</%s>", data->argv[0]);
} else { // This is QAC_OTYPE_OPTION type.
printf("%s", data->argv[0]);
}
// Print parent names
qaconf_cbdata_t *parent;
for (parent = data->parent; parent != NULL; parent = parent->parent) {
printf(" ::%s(%s)", parent->argv[0], parent->argv[1]);
}
// Print option arguments
for (i = 1; i < data->argc; i++) {
printf(" [%d:%s]", i, data->argv[i]);
}
printf("\n");
// Return OK
return NULL;
}
qaconf_t * qaconf(void)
Create a new configuration object.
Definition qaconf.c:245
[Output]
Listen [1:53]
Protocols [1:UDP] [2:TCP]
IPSEC [1:1]
<Domain> [1:qdecoder.org]
TTL ::Domain(qdecoder.org) [1:86400]
MX ::Domain(qdecoder.org) [1:10] [2:mail.qdecoder.org]
<Host> ::Domain(qdecoder.org) [1:mail]
IPv4 ::Host(mail) ::Domain(qdecoder.org) [1:192.168.10.1]
TXT ::Host(mail) ::Domain(qdecoder.org) [1:US Rack-13D-18 "San Jose's"]
</Host> ::Domain(qdecoder.org) [1:mail]
<Host> ::Domain(qdecoder.org) [1:www]
IPv4 ::Host(www) ::Domain(qdecoder.org) [1:192.168.10.2]
TXT ::Host(www) ::Domain(qdecoder.org) [1:KR Rack-48H-31 "Seoul's"]
TTL ::Host(www) ::Domain(qdecoder.org) [1:3600]
</Host> ::Domain(qdecoder.org) [1:www]
</Domain> [1:qdecoder.org]
<Domain> [1:ringfs.org]
<Host> ::Domain(ringfs.org) [1:www]
CNAME ::Host(www) ::Domain(ringfs.org) [1:www.qdecoder.org]
</Host> ::Domain(ringfs.org) [1:www]
</Domain> [1:ringfs.org]
Successfully loaded.

Definition in file qaconf.c.

Function Documentation

◆ qaconf()

qaconf_t * qaconf ( void  )

Create a new configuration object.

Returns
a pointer of new qaconf_t object.
qaconf_t *conf = qaconf();
if (conf == NULL) {
// Insufficient memory.
}

Definition at line 245 of file qaconf.c.

◆ addoptions()

static int addoptions ( qaconf_t *  qaconf,
const qaconf_option_t *  options 
)
static

qaconf_t->addoptions(): Register option directives.

Parameters
qaconfqaconf_t object.
optionsarray pointer of qaconf_option_t.
Returns
a number of options registered(added).
qaconf_option_t options[] = {
{"Listen", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_ALL},
{"Protocols", QAC_TAKEALL, confcb_debug, 0, OPT_SECTION_ROOT},
{"IPSEC", QAC_TAKE_BOOL, confcb_debug, 0, OPT_SECTION_ROOT},
{"Domain", QAC_TAKE_STR, confcb_debug, OPT_SECTION_DOMAIN, OPT_SECTION_ROOT},
{ "TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST},
{ "MX", QAC_TAKE2 | QAC_A1_INT, confcb_debug, 0, OPT_SECTION_DOMAIN},
{ "Host", QAC_TAKE_STR, confcb_debug, OPT_SECTION_HOST, OPT_SECTION_DOMAIN},
{ "IPv4", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
{ "TXT", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
{ "CNAME", QAC_TAKE_STR, confcb_debug, 0, OPT_SECTION_HOST},
QAC_OPTION_END
};
// Register options.
qaconf_t *conf = qaconf();
conf->addoptions(conf, options);
(...codes goes...)

It takes an array of options as provided in the sample codes. Each option consists of 5 parameters as below

1st) Option Name : A option directive name.
2nd) Arguments : A number of arguments this option takes and their types.
3rd) Callback : A function pointer for callback.
4th) Section ID : Section ID if this option is a section like <Option>.
Otherwise 0 for regular option.
5th) Sections : ORed section IDs where this option can be specified.

Example:

{"TTL", QAC_TAKE_INT, confcb_debug, 0, OPT_SECTION_DOMAIN | OPT_SECTION_HOST}
1st) Option name is "TTL"
2nd) It takes 1 argument and its type must be integer.
3rd) Callback function, confcb_debug, will be called.
4th) This is a regular option and does not have section id.
5th) This option can be specified in OPT_SECTION_DOMAIN and OPT_SECTION_HOST.

OPTION NAME field:

Option name is a unique string. Even an option is section type like <option> only name part without bracket needs to be specifed.

ARGUMENT field:

This field is for providing argument checking in parser level. So in user's callback routine can go simple. This provides checking of number of arguments this option can take and those argument type.

In terms of argument types. There are 4 argument types as below. And first 5 arguments can be checked individually with different types.

STR type : any type
INT type : integer type. ex) 23, -12, 0
FLOAT type : integer + floating point type. ex) 1.32, -32.5, 23, -12, 0
BOOL type : bool type ex) 1/0, true/false, on/off, yes/no

When a BOOL type is specified, the argument passed to callback will be replaced to "1" or "0" for convenience use. For example, if "On" is specified as a argument and if BOOL type checking is specified, then actual argument which will be passed to callback will have "1". So we can simply determine it like "bool enabled = atoi(data->argv[1])".

If original input argument needs to be passed to callback, specify STR type.

Here is some examples of how to specify "Arguments" field.

An option takes 1 argument.
QAC_TAKE_STR <= String(any) type
QAC_TAKE_INT <= Integer type
QAC_TAKE_FLOAT <= Float type
QAC_TAKE_BOOL <= Bool type
QAC_TAKE1 <= Equavalent to QAC_TAKE_STR
QAC_TAKE1 | QAC_A1_BOOL <= Equavalent to QAC_TAKE_BOOL
An option takes 2 arguments, bool and float.
QAC_TAKE2 | QAC_A1_BOOL | QAC_A2_FLOAT
An option takes any number of arguments in any type.
QAC_TAKEALL
An option takes any number of arguments but 1st one must be bool and
2nd one must be integer and rest of them must be float.
QAC_TAKEALL | QAC_A1_BOOL | QAC_A2_INT | QAC_AA_FLOAT

CALLBACK field:

User defined callback function. We provide a macro, QAC_CB, for function proto type. Always use QAC_CB macro.

QAC_CB(sample_cb) {
(...codes...)
}
is equavalent to
char *sample_cb(qaconf_cbdata_t *data, void *userdata) {
(...codes...)
}

Callback function will be called with 2 arguments. One is callback data and the other one is userdata. Userdata is the data pointer set by setuserdata().

Here is data structure. Arguments belong to the option can be accessed via argv variables like data->argv[1]. argv[0] is for the option name.

struct qaconf_cbdata_s {
enum qaconf_otype otype; // option type
uint64_t section; // current section where this option is located
uint64_t sections; // ORed all parent's sectionid(s) including current sections
uint8_t level; // number of parents(level), root level is 0
qaconf_cbdata_t *parent; // upper parent link
int argc; // number arguments. always equal or greater than 1.
char **argv; // argument pointers. argv[0] is option name.
}

SECTION ID field:

If an option is an section like <Option>, section id can be assigned. This section id can be used to limit some other option directives to be located only inside of that section. So this is your choice. If it doesn't require to check directory scope, we can just specify 0 here.

There are 2 pre-defined section id, QAC_SECTION_ALL and QAC_SECTION_ROOT. When we define user section, it has to be defined from 2(1 << 1)as below.

enum {
OPT_SECTION_ALL = QAC_SECTION_ALL, // pre-defined
OPT_SECTION_ROOT = QAC_SECTION_ROOT, // pre-defined
OPT_SECTION_DOMAIN = (1 << 1), // user-defined section
OPT_SECTION_HOST = (1 << 2), // user-defined section
};

Please note that this section IDs are ORed. So the section id should be assigned in bit operation manner as 2(1<<1), 4(1<<2), 6(1<<3), 8(1<<4), ...

SECTION IDS field:

This field is to limit the scope where an option is allowed to be specified. Multiple section IDs can be ORed.

QAC_SECTION_ALL means an option can be appeared in anywhere.

QAC_SECTION_ROOT means an option can be appeared only in top level and not inside of any sections.

Definition at line 438 of file qaconf.c.

◆ setdefhandler()

static void setdefhandler ( qaconf_t *  qaconf,
qaconf_cb_t *  callback 
)
static

Set default callback function.

Default callback function will be called for unregistered option directives. QAC_IGNOREUNKNOWN flag will be ignored when default callback has set.

Parameters
qaconfqaconf_t object.
callbackcallback function pointer

Definition at line 470 of file qaconf.c.

◆ setuserdata()

static void setuserdata ( qaconf_t *  qaconf,
const void *  userdata 
)
static

qaconf_t->setuserdata(): Set userdata which will be provided on callback.

Parameters
qaconfqaconf_t object.
userdataa pointer of userdata.
// Define an example userdata
struct MyConf {
int sample;
};
int user_main(void) {
struct MyConf myconf;
(...codes...)
// Set callback userdata.
conf->setuserdata(conf, &myconf);
(...codes...)
}
QAC_CB(confcb_callback_func) {
(...codes...)
// Type casting userdata for convenient use.
struct MyConf *myconf = (struct MyConf *)userdata;
myconf->sample++;
(...codes...)
return NULL;
}
static void setuserdata(qaconf_t *qaconf, const void *userdata)
qaconf_t->setuserdata(): Set userdata which will be provided on callback.
Definition qaconf.c:506

Definition at line 506 of file qaconf.c.

◆ parse()

static int parse ( qaconf_t *  qaconf,
const char *  filepath,
uint8_t  flags 
)
static

qaconf_t->parse(): Run parser.

Parameters
qaconfqaconf_t object.
filepathconfiguration file path.
flagsparser options. (0 for default)
Returns
A number of option directives parsed. -1 will be returned in case of error.

Here is a list of flags. Multiple flags can be ORed.

QAC_CASEINSENSITIVE: Option name is case-insensitive.

QAC_IGNOREUNKNOWN : Ignore unknown option directives. This flag will be ignored if setdefhandler() has set.

int c;
c = conf->parse(conf, "sm1.conf", 0);
c = conf->parse(conf, "sm2.conf", QAC_CASEINSENSITIVE);
c = conf->parse(conf, "sm3.conf", QAC_CASEINSENSITIVE | QAC_IGNOREUNKNOWN);

Definition at line 534 of file qaconf.c.

◆ errmsg()

static const char * errmsg ( qaconf_t *  qaconf)
static

qaconf_t->errmsg(): Get last error message.

Parameters
qaconfqaconf_t object.
Returns
A const pointer of error message string.
int c = conf->parse(conf, "sample.conf", 0);
if (c < 0) {
// ERROR
printf("%s\n", conf->errmsg(conf));
}

Definition at line 572 of file qaconf.c.

◆ reseterror()

static void reseterror ( qaconf_t *  qaconf)
static

qaconf_t->reseterror(): Clear error message.

Parameters
qaconfqaconf_t object.
conf->reseterror(conf);
conf->parse(conf, "sample.conf", 0);
if (conf->errmsg(conf) != NULL) {
// ERROR
}

Definition at line 589 of file qaconf.c.

◆ free_()

static void free_ ( qaconf_t *  qaconf)
static

qaconf_t->free(): Release resources.

Parameters
qaconfqaconf_t object.
conf->free(conf);

Definition at line 605 of file qaconf.c.