INTRODUCTION ===================
The code presented here is a general config file reader/writer. The parser itself was created using flex. The syntax of the config file itself might not be completely logical but it works. Remember that the idea is that the program itself is used to edit the config file.
1. Overview 2. How does it work ? 2.1 Groups 2.2 Entries 3. How to use it 3.1 General usage 3.2 Config and Plugins 4. What does it look like ? 5. Info 6. Copyright
1. Overview =============
The purpose of this config class is to remove the burden of parsing and maintaining a config file. It works as follows: The user creates an instance of the ConfigClass in their program and specify a config file.
2. How does it work ? ======================
Well it does isn't that enough ? :) The basic idea is that the ConfigClass holds several structures which contain pointers to variables you use. When you call the function write_config () the ConfigClass will write the contents of the variables you provided to a configuration file. So when you change a variable during the course of your program you also change the value that will be written to disk.
2.1 Groups ======================
A program usually has a lot of variables. These variables belong to classes and structures. To make a difference between groups of variables, the group structure is invented. You can find the groups in the config file in the form of: [main] This defines the group "main". All variables beneath this group belong to this group untile a new group is defined. One consequence of this method is that you can have 2 or more variables with the same name. Of course these variables have to belong to different groups. One advantage of using groups is the possibility of plugins. A plugin is a shared library ( or piece of compiled code ) that is loaded and used at runtime. You can find out more on using plugins with the config manager in the chapter "Config and Plugins"
2.2 Entries ======================
An entry is a definition of a variable with all it's data. A config variable is NOT the same as a variable in "C" or "C++". A variable in a config file is an indicator for one or more values. We'll use the term entry from now on to indicate such a variable. First an example. Suppose you've got some code wich operates on colors. You probably want to save certain color values when you exit your program. You might have something like: struct rgb_values { float red; float green; float blue; }; in your program.This could be translated into one entry into the config file which would then look like: <rgb_values> 0.50 0.50 0.50 The entries are internally structured as follows ( don't worry if you don't understand it ):
group_entry [main group] | +---conf_entry <"variable1"> (var1) (var2) (var3) (var4) ....... | +---conf_entry <"variable2"> (var1) (var2) ...... . .
Each line of variables is accompanied by a string which gives enough information to determen what type of variable is used, the string can contain the following variable types: i => integer (int) f => floating point (float) s => string (char *) So for example you've got a line in a config file which looks like: 0.50 0.35 0.60 This would translate into a variable-type string of: fff Meaning that the config parser will store and look for 3 floats. You add an entry to these variables as follows: temp_config_class->insert_entry ("rgb_values","fff",&red,&green,&blue); When you add an entry, you effectively add it to the current group. Now when you've added all the groups and entries, you read in the config file. The ConfigClass will then try to match the entries you specified with the values it finds in the file. You've got to remember the following things when using this class: 1) When you specify a variable which was not found in the file, the ConfigClass will reset that variable to default. You can specify the default values by calling the function set_defaults (); For example you could call this function like: set_defaults (50,0.10,"main"); 2) When you've specified a variable which was not found in the file, then don't be surprised if you DO find it the next time. This is because the ConfigClass will write a new config file from it's data which of course includes the new variable.
3. How to use it ? =======================
You can use the sources in two ways: a) The first way is to take the libconfig.a you will find when the compilation process is complete and copy it to a default library directory. You could copy it to /usr/local/lib for example. Do the same with the include files you find in the main config dir and copy them to /usr/local/include for example. This should set things up properly and you can now use the config library in any other program.
b) The second way is to copy the entire source directory to your own source directory. So for example, suppose you are working on a project called foo wich resides in a directory called: ~/foo You could create a directory called config in the ~/foo directory. This would give you the following dirs: ~/foo Main source dir ~/foo/config Location of config sources ~/foo/flex Location of the source file for the flex parser You would then have to edit the Makefile and change the point where it says: all: $(OBJECTS) $(AR) -rs $(TARGET) $(OBJECTS) # cp $(TARGET) ../ Just remove the "#" and make will then copy the libconfig.a to the foo directory. Now when you create your own Makefiles in the foo directory don't forget to add -I./config to the default include directories.
3.1 General Usage =======================
During the operation of a program, you will have use several steps to use the config class successfully. The global usage is as follows: a) Create an instance of the config class b) Add groups and entries to this instance c) Let the config instance parse the config file d) During the course of the program use the variables. e) Write a config file. It all begins with the creation of an instance of the ConfigClass, like so: ConfigClass *config_object=new ConfigClass (".configrc"); This creates a config object which will do all it's file operations on the file .configrc NOTE: when you use plugins in combination with the config lib ALL plugin configurations will end up in the main config file. It's also possible to add a program or package name to the config-file header. To do this call the constructor: ConfigClass *config_object=new ConfigClass (".configrc","MindsEye"); This will add a string to the config header which says: # This config file belongs to the (MindsEye) package If you do not specify a config name then the config filename will default to: .config You then add groups and entries to this config object. Variables can be grouped together allowing plugins for example to share one config file. Adding a group is done by calling: config_object->add_group ("main_group"); When you now add an entry, you effectively add it to the main_group. In other words you always add it to the current active group. Unless you specify a group name with the insert_entry_to_group command.
3.2 Config and Plugins =======================
Plugins form a special case when it comes to configurations. Well they are a special case anyway. When you write plugins using the ConfigClass you have to know how certain things are done by this class. First of all you have to know how the ConfigClass handles the sequence by which entries are made and how the class parses the config file. You have to realise that the ConfigClass can not rely on the fact that the entries are made first and then the config file is parsed. So one requirement is that it does not matter in which order things happen. See chapter 3.1 step b and c So for example suppose you've created the proper instance and you've added a bunch of groups and variables to the instance. Then you call the function which parses the config file. The ConfigClass now has to match entries it finds in the file with what the user already has inserted. It can also happen the other way around, where the file has already been read and the ConfigClass has to see if the entries that are added exist in the file ( if it can't match an entry it will add it to the current group) Why do you have to know all of this ??? Well that's easy. When you load a plugin you can safely assume that the config class has already loaded the proper entries. So when you load a plugin and the plugin inserts it's own entries, the config instance has to match the new entries with the ones it has already found. So one important thing when using plugins is to add a special group for the plugin. Otherwise the variables end up in the current group and the config instance cannot guaranty the uniqueness of the variable.
4. What does it look like ? =============================
The config parser uses the following reserved token syntax: [ ] = group identifier < > = name identifier " " = string identifier
Example: --SOURCE: main.cpp------------------------------------------------------------- #include <m_config.h> main (void) { float red =0.5, green=0.5, blue =0.5; int path_max=1024; char last_model [256]="/home/vvelsen/sgi_fonts.3ds"; ConfigClass *temp_config=new ConfigClass (".mindseyerc","MindsEye"); //>--------- group [kernel] temp_config->insert_group ("kernel"); temp_config->insert_entry ("path_max","i",&path_max); temp_config->insert_entry ("rgb_values","fff",&red,&green,&blue); //>--------- group [modeler] temp_config->insert_group ("modeler"); temp_config->insert_entry ("last_model","s",&last_model); //>--------- group [plugins] temp_config->insert_group ("plugins"); //>--------- modify some variables red=0.10; // you should see this value in the config file temp_config->write_config (); delete (temp_config); } --OUTPUT: /root/.mindseyerc---------------------------------------------------- # This file was automaticly generated, please do not edit # by hand unless you know what you're doing # # File created by ConfigManager v0.10 on Sun May 25 17:17:29 1997 # Copyright (C) 1997 Martin van Velsen, vvelsen@ronix.ptf.hro.nl # # This config file belongs to the (MindsEye) package # #-------------------------------------------------------- [kernel] <path_max> 1024 <rgb_values> 0.100000 0.500000 0.500000 #-------------------------------------------------------- [modeler] <last_model> "/home/vvelsen/sgi_fonts.3ds" #-------------------------------------------------------- [plugins] # end of (.mindseyerc)
5. Info =========
I'm not that great with autoconf and the likes. That's the reason you have got to manually install the library and header files. See the main.cpp file for an example of how to use the configmanager. Questions and comments about these sources, as well as updates and bug fixes can be sent to: Martin van Velsen <vvelsen@ronix.ptf.hro.nl>
6. Copyright =============
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.