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.
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.
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.
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:
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.
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:
flags is a bitfield. For the moment the following bits are defined:
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_STRING
, CL_DIRNAME
,
CL_CHAR
, CL_NUM
, CL_NUMSHORT
and CL_BOOLEAN
.
The bit is ignored fo any other type.
cl_putstanza
shall save this variable.
See section Reading and writing.
char **
.
long *
.
short *
.
int *
.
char *xxx[CL_MAXARRAY]
.
long *
.
See section enumerations for more information.
CL_STRING
, but
a slash (/
) ist appended, if it does end with another
character.
enumerations are described with this data type:
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 } };
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 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).
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 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 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.
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.
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.
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:
r
or w
.
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).
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:
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.
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:
include
- and includestanza
-statements are
ignored (it would be hard to do that right, and i don't see a really
good reason for it).
CL_MAY_SAVE
flag
set will not be saved (this means: if you want to save all variables
all variables need that flag). If the variable is already found
in the stanza stanza it will be preserved (this means: it's
not enough to leave out the CL_MA_SAVE
flag to delete a
variable. In fact it's impossible to delete a variable at this
time).
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.
This function may be used to dump a programs configuration to file file. It should not be used to overwrite a configuration 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.
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);
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.
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.
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.
cl_clearconf walks through tab and
CL_MALLOCED
flag set).
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.
cl_getversion returns an integer representing the version number of the library. It's @math{10000 * major + 100 * minor + patchlevel}.
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.
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:
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.
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:
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:
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.
the library has some limits:
Go to the first, previous, next, last section, table of contents.