One of the better know time-consuming activities for any developer is to provide the users of his programs a way to set up the configuration. Likewise, users can tell you that one of their biggest problems often is to figure out how to configure a given program. Finally, system administrators are often driven to distraction by the fact that a program they'd like to use just cannot be [easily] configured due to security or manageability issues.
As I'm one of the above developers that has to cope with both other groups, I decided to end this issues for once and for all.
The result is a library called AnyConfig. In short what it does is look for the configuration in all the likely places until it finds one that satisfies it's requirements.
If all goes well, autoconf should detect which libraries are available on your system and the various modules should comment themselves out if the required support is not available...
Unfortunately, there is no autoconf support for MSDEV/MFC so here you'll probably have to do some manual configuring. Also, including both MFC (for registry access) and Orbacus (for CORBA support) does not work.
If all else fails you should remove the offending modules from either Makefile.am or delete the sub-projects from the MSDEV workspace.
Do let me (Author) know if you're having difficulties and/or ideas for better solutions so I can try to fix it in future versions.
See MasterConfig for a programming example.
Also see the file 'testMe.cpp' for an example of using AnyConfig::AcConfiguration::loadRedundantConfig () which supports fall-back and fail-over type configurations.
Basically all you need to do is to include "AnyConfig.h" in your program (or "anycfg.h" if you are using C instead of C++) and call AcConfiguration::findAnyConfig() (or acfOpen() for plain C).
If there is any kind of configuration lurking around on your system that looks reasonable to AnyConfig, you will obtain an initialized "parent" configuration or even a bootstrapped larger configuration.
Usually you would select the section in this configuration that corresponds to your program and you can obtain (and usually even write back) any configuration items you want.
Lets look at an example. We will assume that you have many programs that use AnyConfig so you have put all of their configurations in a parent configuration file. For each group of programs you have a separate configuration source as well:
~/anycfg/parent.ini or %SYSTEMROOT%\\anycfg.ini.
[PROGRAM-GROUP-1] ; simple file based configuration CFGFILENAME=/home/username/.anyconfig/group1.ini [PROGRAM-GROUP-2] ; as above for an overpriced OS CFGFILENAME=C:\\somedir\\group2.ini [PROGRAM-GROUP-3] USERNAME=username PASSWORD=password HOSTNAME=localhost DATABASE=config CFGTABLE=config
Now, to load the configuration for your program:
// if we don't have/want a command line: int argn = 0; char *argv [] = { NULL }: AnyConfig::AcConfiguration *pParent = AnyConfig::AcConfiguration::findAnyConfig (argn, argv); AnyConfig::AcConfiguration *pConfig = NULL; if (NULL != pParent) { pParent->SetCurrentSection ("PROGRAM-GROUP-3"); pConfig = new MySQLConfig; if (!pConfig->initialize (*pParent)) { delete pConfig; pConfig = NULL; } else { pConfig->SetCurrentSection ("MYPROGRAM"); } }
This program will convert a stringified configuration to a readable one.
You can redirect the output to file or use "copy and paste".
Usage:
This program will convert a readable configuration to a stringified representation.
You can redirect the output to file or use "copy and paste".
Usage:
It is possible in some cases to indicate a specific configuration source. See AnyConfig::AcConfiguration::loadFrom() for more details.
This is done by supplying a single string in the following format:
source:bootstrap:source section:source location
Where:
Note that there exists an interaction between bootstrap and source section:
Examples:
FILE:0:MYPROGRAM:CFGFILENAME=/home/soap2corba/myprograms.ini@ MYSQL:0:MYPROGRAM:USERNAME=soap2corba@PASSWORD=soap2corba@
Some usage notes and common pitfalls.
Ordering of keys and/or iterating a configuration.
As the underlaying configuration sources have different characteristics, keys might be ordered differently. Therefore there are no intrinsic Next/Previous key methods or indeed any other method to iterate through a configuration.
One possible but not recommended work-around is to copy an AnyConfig to an AnyConfig::AcMemConfig, which does support various forms of iteration, using the AnyConfig::AcConfiguration::copyTo() or AnyConfig::AcConfiguration::copyFrom() methods.
The reason this practice is not recommended is that not all configuration sources support the copy methods, for the same reason there is no global iteration facility. Also, this practice might hide changes that were made to the original configuration after the program started, which might not have been your intent.
If you need a sequence in your configuration, like the supplied MasterConfig program for instance, you will have to define your own. Using the CString implementation you will find in the AnyConfig library as well, this can be easily done as follows:
CString key; int iter; AnyConfig::AcConfiguration *pCfg = AnyConfig::AcConfiguration::findAnyConfig (argn, argv); for (iter = 0; iter < 100; iter ++) { key.Format ("CMD_%02d", iter); if (!pCfg->keyExists (key)) break; // use the key here }
Manually entering stringified representations of a configuration.
It is very easy to make errors when typing the stringified representation of a configuration by hand.
You should use the supplied utility programs (cfg2str and str2cfg) instead.
You might wonder how AnyConfig determines the type (Long, Number, Boolean, String) of a configuration item. The answer is simple, it doesn't. Nor is this capability specifically useful.
After all, the program obtaining the configuration defines the type of the configuration item, not the person entering the configuration. If the program expects a Boolean, entering the string "I have no idea" in the configuration does not grant it sudden AI capabilities (AnyConfig would return true for this, by the way, as it is not a number and does not start with either a N or a F).
Nor is it the task of the configuration reading functions to do type checking. Even though the configuration yields a number as expected, the program must still contain code to reject 1E300 or -20000 as being valid percentages...
Limits of section names, key names and configuration values.
There are no fixed limits for any of these as again they are mostly defined by the underlaying configuration source.
The default table as created by the AcMySQLConfig class, for instance, limits the names to 40 characters and the value to 255. Copying strings that long to an environment based configuration, however, has a good chance of not working as expected.
You might have noticed that the AnyConfig::AcRegConfig in particular is not very concerned about the length of the section names... About the only target that would accept such long names is the file based configuration class. That one as a limit of 4095 bytes per line, by the way.
AnyConfig is based on the philosophy of allowing as much as possible while doing things as strict as possible itself. In short, any reasonable inputs (and even some debatable ones) are accepted while obvious errors are silently ignored.
A few rudimentary debugging options exist, you can set AnyConfig::acfThrowExceptions which will cause AnyConfig to throw an AnyConfig::AcException for some glaring errors.
Another way of finding out what went wrong is by invoking the AnyConfig::AcConfiguration::display() method.
But mostly, the task of validating a configuration falls to the program itself. Again, generic routines have no way or knowning what is acceptable input for your program.
This program allows starting other, non AnyConfig aware, programs by first creating their native configuration before starting them.
See also: AnyConfig::MasterConfig() Building a command line.
A MasterConfig AnyConfig section can contain either a single command line or a compound one with keyword substitution.
Keywords are only substituted if they start with an At sign (@). The whole string is considered to be the keyword to substitute.
This works as follows:
USERNAME=me PASSWORD=mine PROGRAM=/bin/mysql CMD_00=-u CMD_01=@+@username CMD_02=-p CMD_03=@+@password
This would build the following command line:
mysql -ume -pmine
The command line normally has spaces inserted between each CMD_xx part, to avoid this start the argument with @+. You can use @@ if you need an At sign at the beginning of a command line part.
Lines that do not start with an At sign are not parsed so E-mail addresses can be inserted as usual.
Remember that everything after the At sign is considered the keyword.
This will not work:
GIVENNAME=Second SURNAME=Lady PROGRAM=mail CMD_00=-s"To the attention of Mr. CMD_01=@surname, CMD_02=@givenname" office@company.domain
The trailing text will cause the lookup to fail. Use this instead:
GIVENNAME=Second SURNAME=Lady PROGRAM=mail CMD_00=-s"To the attention of Mr. CMD_01=@surname CMD_02=@+, CMD_03=@givenname CMD_04=" office@company.domain
The result, will be:
mail -s"To the attention of Mr. Lady, Second" office@company.domain
Which is still nonsense but no longer due to a bad configuration.
Entering values in the environment.
Building environment strings for a program is rather simular to building a command line.
The main difference is that there is only one commandline but there can be multiple environment strings. Therefore you must start an environment string definition by specifiying the environment key, prefixed with an At sign (@) followed by an Equals sign (=). The following example should clarify this.
PROTOCOL=1 ENV_00=@=CVS_RSH ENV_01=ssh - ENV_02=@+@protocol ENV_03=@=CVSROOT ENV_04=:ext:soap2corba@cvs.anyconfig.sourceforge.net:/cvsroot/anyconfig checkout anyconfig
Example of using MasterConfig.
On a UN*X system, there is a file in the home directory of the user called '.anycfg.ini'. The MasterConfig will find and load this file automatically.
For this example, we will suppose it has the following content:
[CVS-ANYCONFIG] PROGRAM=/bin/cvs ENV_00=@=CVS_RSH ENV_01=ssh CMDLINE=-d:ext:soap2corba@cvs.anyconfig.sourceforge.net:/cvsroot/anyconfig checkout anyconfig STARTDIR=//e/sf
Or, alternatively and yielding the same result:
PROGRAM=/bin/cvs ENVIRON=CVS_RSH=ssh CMD_00=-d:ext: CMD_01=@+@username CMD_02=@+@@cvs. CMD_03=@+@project CMD_04=@+.sourceforge.net:/cvsroot/ CMD_05=@+@project CMD_06=checkout CMD_07=@project USERNAME=soap2corba PROJECT=anyconfig STARTDIR=//e/sf
We call MasterConfig as follows:
MasterConfig cvs-anyconfig
And, after entering CVS_RSH=ssh in the environment, it will execute in the "//e/sf" directory the following command:
/bin/cvs -d:ext:soap2corba@cvs.anyconfig.sourceforge.net:/cvsroot/anyconfig checkout anyconfig
Walter Stroebel. LifeLine Networks bv. soap2corba@lifeline.nl More about me.