Go to the first, previous, next, last section, table of contents.


Using the library

For using the library only a few changes to your program are needed. Your need to create a configuration table, which assigns the configuration variables to internal program variables, and to call the functions to read the configuration file.

A Simple Example

This example show a program using two configuration variables. One of those may be overridden through an environment variable. It uses a configuration file which is not splitted into stanzas.

#include "ownstd.h"

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include "conflib.h"
#include "tinterp.h"

char *action;
char *tmpdir;

cl_var_t tab[] =
{
  { "action",    NULL,       CL_STRING,  &action     ,0          ,NULL },
  { "tmpdir",    "TMP",      CL_DIRNAME, &tmpdir     ,CL_PREENV  ,NULL },
  { NULL,NULL,0,NULL,0 }
};

int main(void)
{
  int result;

  result = cl_getconf("simple.cnf",tab);
  if (result != 0)
  {
    fprintf(stderr,"cannot read simple.cnf");
    exit(1);
  }

  printf("tmpdir=%s\n",tmpdir ? tmpdir : "not set");
  printf("action=%s\n",action ? action : "none");
  exit 0;
}

No magic is involved. cl_getconf is the function which reads the configuration file. See section A High Level Interface.

The Configuration File Handle

There are two ways to read more then one stanza from a configuration file. The first is more simple, it just reads each stanza with a call to cl_getconfstanza, see section A High Level Interface. This is indeed possible, but slow: For every stanza the file has to be opened, searched and closed again.

A better way would be to open the file once, scan it once, read it often and close once. To do so a lot of information has to be preserved. This information is saved in the configuration file handle.

Data Type: cl_file_t
The cl_file_t data structure is used to hold information the configuration file. It has many members, but there are only a few a program should deal with:

int report_unknown
set this to one if you wish the reading function to print out a warning if it finds a variable not described in the configuration table.

Never use the file handle (i might switch to mmap one day), and never change anything except report_unknown.

Many programs don't need this.

The data structure that describes a configuration variable

Data Type: cl_var_t
The cl_var_t data structure is used to hold information about an configuration variable. At the time i write this down it has the following members:

const char *varname
The name of the variable. It is used case insensitive, and underscores and minus are treated as the same character.
const char *secdata
used for multiple purposes in dependency of flags and variable type.
enum cl_var_typ typ
the type of the variable
void *adr
the address to write the variables value to.
unsigned long flags
flags.
const char *rangeexp;
A wildcard or regular expression, used for rangechecks on the variables value.

flags is a bitfield. For the moment the following bits are defined:

CL_MALLOCED
The memory adr points to is dynamically allocated and should be freed (this is done by cl_clearconf (see section Cleaning up) and if this variable is overwritten by a library function).
CL_PREENV
If this bit is set the library will, before it actually reads a stanza or a whole configuration file, read the environment variable with the name given in secdata into the configuration variable. This can be done for the data types CL_STRING, CL_DIRNAME, CL_CHAR, CL_NUM, CL_NUMSHORT and CL_BOOLEAN. The bit is ignored fo any other type.
CL_POSTENV
Like CL_PREENV, but the environment is read after the reading of the configuration file.
CL_VAR_NOT_DONE
internal use only.
CL_MAY_SAVE
Set this bit if cl_putstanza shall save this variable. See section Reading and writing.
CL_RANGE_REGEX
If this bit is set then a range check will be done before a new value is assigned to the variable. If this fails the new value is not assign. The range check is done with a casesensitive regular expression match against the string in rangeexp. No regular expression rangecheck will be done if your systems library does not support POSIX regular expressions-
CL_RANGE_WILDCARD
Like CL_RANGE_REGEX, but the match is done as a casesensitive wildcard match. This functionality is available even if the c library has no fnmatch function.
CL_WARNING
Warn if this variable is used.

Data Type: enum cl_var_typ
This enumerated type describes what the kind of variable. Here are the possible values:

CL_STRING
a simple string. adr should be of type char **.
CL_NUM
a long integer. adr should be of type long *.
CL_NUMSHORT
a short integer. adr should be of type short *.
CL_BOOLEAN
a boolean. adr should be of type int *.
CL_ARRAY
an array of simple strings. adr should be of type char *xxx[CL_MAXARRAY].
CL_ENUM
an enumeration. adr should be of type long *. See section enumerations for more information.
CL_ALIAS
an alias for another variable, very often used for compatability with older versions. See section other names for variables. adr is ignored. secdata should hold the name of the variable this one is an alias for.
CL_DUMMY
this variable will be completely ignored.
CL_LIST
a list. adr should be of type cl_list_t **.
CL_CHAR
a single character. adr should be of the char *. Any character after the first is ignored.
CL_DIRNAME
a directory name. Basically the same as CL_STRING, but a slash (/) ist appended, if it does end with another character.

enumerations

enumerations are described with this data type:

Data Type: cl_enum_t
char *magic
This string has to be found in the configuration file.
long value
The value to be assigned to the configuration variable.

The secdata element of an enumeration variable (See section The data structure that describes a configuration variable) points to an array of cl_enum_t. This array is terminated with an element with magic NULL.

An example:

cl_enum_t zahlen[]=
{
{   "Null",   0 }, {    "Zero",   0 },
{   "Eins",   1 }, {    "One",    1 },
{   "Zwei",   2 }, {    "Two",    2 },
{   "Drei",   3 }, {    "Three",  3 },
{   "Vier",   4 }, {    "Four",   4 },
{   "Fuenf",  5 }, {    "Five",   5 },
{   "Sechs",  6 }, {    "Six",    6 },
{   "Sieben", 7 }, {    "Seven",  7 },
{   "Acht",   8 }, {    "Eight",  8 },
{   "Neun",   9 }, {    "Nine",   9 },
{   NULL,     0 }
};
long value;
cl_var_t Tab[] =
{
    { "value", zahlen, CL_ENUM, &value, 0,NULL},
    { NULL,NULL,0,NULL,0 }
};

other names for variables

An alias makes it possible to use two or more names for the same configuration variable. This is useful to be able to read old configuration files even if some variables have changed their names.

The alias gets an element in the configuration table, with an own and uniq name in the varname element, and the name of the real variable in secdata.

If the variable in secdata is not found the functions will return an error (i still think it should simply call abort()). Fix your software.

An alias may point to another alias.

Lists

Lists are lines in the configuration file, which are taken apart by cl_getstanza (see section Reading and writing). The elements are separated with special characters.

The default separators are horizontal tabulator, space and colon, but you can change that with the secdata element of the variable description (see section The data structure that describes a configuration variable).

Data Type: cl_list_t
This structure describes a list element. It contains the following members:

char *inhalt
the value of the list element
struct cl_list_t *next
points to the next list element.

A list may be splitted into many lines. An example:

list=Hello
list=world

The next example gives an identical result:

list=Hello world

Separators are ignored if they follow another separator. The following examples results a list containing Hello and world:

list=Hello: world

Example:

cl_list_t *list;
cl_list_t *ptr;

cont_f tab[]=
{
	{ "list", ": ,\t",      CL_LIST,      &list     ,0 },
	{ NULL,NULL,0,NULL,0 }
}
cl_getconf("config.cnf",tab);
for (ptr=list; ptr != NULL; ptr=prt->next)
    puts(ptr->inhalt);

Bitfields

Bitfields are simple but powerful constructs which can often be used to put many related boolean variables in a single variable.

Their implementation is much like that of the enumerations, see section enumerations, especially they are described by the same data type cl_enum_t (see section enumerations. A significant difference is that enumerations are saved as signed longs, bitfields as unsigned longs.

Words can have a ! as prefix, meaning negate this bit.

Here is an example of their usage in a program:

cl_enum_t bitfield_enum[]=
{
{   "bit0",   1 },
{   "bit1",   2 },
{   "bit2",   4 },
{   "bit3",   8 },
{   "bit4",   16 },
{   "bit5",   32 },
{   "bit6",   64 },
{   "bit7",   128 },
{   NULL,     0 }
};
unsigned long bitfield;
cl_var_t Tab[] = {
    { "bitfield",  bitfield_enum, CL_BITFIELD,&bitfield,0          ,NULL },
[etc]

and an example of a configuration file:

  bitfield=bit2|bit7
  bitfield=bit2|bit7|!bit6|!!bit5

The Configuration Table

The configuration table is the interface between program and library. It's an array of cl_var_t (see section The data structure that describes a configuration variable), terminated with an NULL element. Each array element describes a variable.

Example:

short numshort;
long num;
int  bool_true;
int  bool_false;
char *stanza;
char *array[CL_MAXARRAY]={NULL};

cl_var_t Tab[] =
{
    { "numshort",   "Enumshort",   CL_NUMSHORT,  &numshort ,CL_POSTENV},
    { "num",        "Enum",        CL_NUM,       &num        ,0},
    { "bool_false", "Ebool_false", CL_BOOLEAN,   &bool_false ,0},
    { "bool_true",  "Ebool_true",  CL_BOOLEAN,   &bool_true  ,0},
    { "string",     "Estring",     CL_STRING,    &string     ,0},
    { "array",      "Earray",      CL_ARRAY,     array       ,0},
    { NULL,NULL,0,NULL ,0}
};

The line

    { "numshort",   "Enumshort",   CL_NUMSHORT,  &numshort ,CL_POSTENV},

describes a variable with the name numshort. It's a short number (CL_NUMSHORT), and the value is written into the C variable numshort. It the environment variable Enumshort is set it overrides the value in the configuration file.

A High Level Interface

Function: int cl_getconf (const char *fname, cl_var_t *conftab)

Reads the whole configuration file fname into conftab.

This function is identical with calling cl_getconfstanza(fname,conftab,0).

Returns 0 if successful, and other values if not.

Function: int cl_getconfstanza (const char *fname, cl_var_t *conftab, const char *stanza)

cl_getconfstanza reads the stanza stanza from the configuration file fname into conftab. If stanza is NULL then the whole configuration file is read.

Returns 0 if successful and other values if not.

Opening and closing a configuration file

Function: cl_file_t cl_openconf (const char *fname, const char *mode)

Open the configuration file fname for reading (mode == r) or writing (mode == r). The normal return value is the handle needed to access fname with other conflib-functions.

n the case of an error, a value of NULL is returned instead. In addition to the usual file open errors (see section `Opening and Closing Files' in The GNU C Library Reference Manual), the following errno error conditions are defined for this function:

ENOMEM
no more memory available.
EINVAL
mode is not equal to r or w.

Function: void cl_openconf (cl_file_t *hadle)

Closes the configurations file with the handle handle and frees all resources of handle (this means that you really should not access handle and its data any longer).

Reading and writing

Function: int cl_getstanza (cl_file_t *handle, cl_var_t *tab, const char *var{stanza},
long flags, cl_ulist_t ** unknowns)

cl_getstanza reads the stanza stanza (or all stanzas, if stanza is NULL) from the already opened configuration file pointed to by handle into tab.

flags is a bitfield consisting of the following bits:

* CL_GET_NOINCLUDE
If this bit is set include- and includestanza-statements in handle will be ignored.

If unknowns is not NULL then the pointer it points to will be the start of a list of unknown variables (basically this are variables set in the stanza which are not found in tab). The list is dynamically allocated, use cl_free_unknowns to deallocate the memory. See section Cleaning up.

This function returns 0 if successful and some other value if not.

Function: int cl_file_t (cl_file_t *handle, cl_var_t *tab, const char *stanza,
long flags, cl_ulist_t *unknowns)

cl_putstanza writes tab to the configuration file handle into the stanza stanza. It tries hard to do the right thing (especially it preserves any variables found in the it doesn't know about).

But there a things cl_putstanza can or will not handle:

All variables in unknowns will be saved.

To state it clearly: At the moment there is no way to remove a variable from the configuration file.

Returns 0 if successful, other values is unsuccessful.

Function: void cl_writeconf (FILE *file,const cl_var_t *tab)

This function may be used to dump a programs configuration to file file. It should not be used to overwrite a configuration file.

Analyzing a configuration file

Function: char ** cl_build_stanza_array (const char *pattern, cl_file_t *var{file})

Generates an array containing the names of all stanzas matching pattern in the configuration file file. The last element is a NULL pointer.

pattern must not be a NULL pointer. Use * instead.

This function returns the array or NULL if an error occured.

If no stanza name matches pattern the function returns an empty array with the first element beeing the NULL pointer.

Function: void cl_delete_stanza_array (char **array)

Frees all memory used by array and its elements. Sets *array to NULL.

An example, with error handling left out:

cl_file_t *f;
char **ar;
f=cl_openconf("something.cnf","r");
ar=cl_build_stanza_array("*",f);
if (*ar) {
    char **p;
	for (p=ar;*p;p++)
	    puts(*p);
}
cl_closeconf(f);
cl_delete_stanza_array(ar);

Searching functions

Function: cl_var_t * cl_find_confvar (cl_var_t *tab, const char *name)

This functions searches the array tab for the variable called name and returnes a pointer to the array element containing name, or NULL if name is not found.

The following two functions search conflib array. They are here for compatability only, i'm thinking about a better interface.

Function: int cl_isinarray (const char **Array, const char *St, int insensitive)

cl_isinarray searches the elements of array Array for a string equal to st. If insensitive is not zero then the string comparision is caseinsensitive.

The function returns 0 if st found and one otherwise.

Function: int cl_isinwarray (const char **Array, const char *St)

cl_isinarray searches the elements of array Array for a string matching st. This function uses wildcard comparision (with st being the pattern) and works casesensitive).

The function returns 0 if st found and one otherwise.

Cleaning up

Function: void cl_clearconf cl_var_t *tab

cl_clearconf walks through tab and

Function: void cl_free_unknowns cl_ulist_t *list

cl_free_unknowns frees all memory allocated to the list list, which should be a list of unknown variables created from cl_getstanza. See section Reading and writing.

Miscellaneous functions

Function: int cl_getversion (void)

cl_getversion returns an integer representing the version number of the library. It's @math{10000 * major + 100 * minor + patchlevel}.

Function: cl_var_t * cl_unalias (cl_file_f *file, cl_var_t *tab, cl_var_t *entry)

This function follows an alias chain until it finds an entry in tab which is not an alias, and returns it's address.

FILE is needed for error messages.

ENTRY is the initial variable. If it is not an alias this function returns ENTRY.

cl_unalias returns NULL in case of any error.

Function: int cl_setvar (cl_var_t *tab, char *zeile, long flags,
cl_ulist_t ** unknowns)

cl_setvar works as if zeile was a line in a configuration file. It looks in tab for the variable name.

flags is a bitfield consisting of the following bits:

* CL_GET_NOINCLUDE
If this bit is set include- and includestanza-statements in handle will be ignored.

If unknowns is not NULL then the pointer it points to will be the start of a list of unknown variables (basically this are variables set in the stanza which are not found in tab). The list is dynamically allocated, use cl_free_unknowns to deallocate the memory. See section Cleaning up.

This function returns 0 if successful and some other value if not.

Handling Of Unknown Variables

There may be unknown variables used in the configuration file your program doesn't know about. In many cases the best solution is to ignore them - your program doesn't know what to do with them, and other program might need them (this might happen if configuration files or stanzas are shared between applications). So the default behaviour if to ignore them - cl_getstanza will not read them, and cl_putstanza will leave them in.

But there are situations you want to know them. Think of a generic configuration file editor - it has to handle them.

First the good news: cl_getstanza and cl_putstanza can handle unknown variables. The bad news: The solution is not beautiful (but i haven't found a better one).

This data type describes an unknown variable:

Data Type: cl_ulist_t
The cl_ulist_t data structure is used to hold information about an unknown variable variable. At the time i write this down it has the following members:

* char *varname
the name of the variable
* char *inhalt
the value
* struct cl_ulist_t *next
a pointer to the next unknown variable.

You may use this data type as parameter for cl_getstanza and cl_putstanza like this (error handling left out):

cl_ulist_t *unknowns=NULL;
cl_ulist_t *ptr=NULL;
extern cl_var_t *tab;
extern cl_file_t *handle;
cl_getstanza(handle,tab,"stanza25,0,&unknowns);
for (ptr=unknowns;ptr!=NULL;ptr=ptr->next) {
    printf("%s=%s\n",ptr->varname,ptr->inhalt);
    /* do something else */
}
cl_putstanza(handle,tab,"stanza25",0,&unknowns);
cl_free_unknowns(unknowns);

For more information see section Reading and writing, section Reading and writing, and section Cleaning up.

Limitations

the library has some limits:

KT_MAXARRAY
limits the maximal number of array elements. It's currently 256 and can be changed in conflib.h (but think again before doing so - a better solution is a new data type which allocates the array dynamically). The last element, the NULL element, counts as used, so an array has 255 usable elements maximal (but that should be enough for the very most purposes).
1023
is the maximal length of a line in a configuration file. Splitted lines (the ones with a backslash at the end) count as one line. It's not too hard to make that dynamic, have a look into getgcstanza.c.
CL_PREENV
CL_POSTENV
are only used for simple data types. This are:
  1. CL_NUM
  2. CL_NUMSHORT
  3. CL_BOOLEAN
  4. CL_STRING


Go to the first, previous, next, last section, table of contents.