Configuration helpers
Name
ggLoadConfig, ggFreeConfig, ggConfigIterTarget, ggConfigIterLocation : Configuration helpers
Synopsis
int ggLoadConfig(const char *file, struct gg_config **config); void ggFreeConfig(struct gg_config *config); struct gg_location_iter { struct gg_iter iter; struct gg_config * config; const char * name; char * location; const char * symbol; void * _state; }; int ggConfigIterLocation(struct gg_location_iter * iter); struct gg_target_iter { struct gg_iter iter; struct gg_config * config; const char * input; char * target; char * options; void * nested; }; int ggConfigIterTarget(struct gg_target_iter *iter);
Description
These functions provide a simple way of handling configuration files.
ggLoadConfig tries to load the configuration file given as parameter. It adds the content to the config handle found at *config. If the handle is NULL, a new one is created. If file matches one of the following format string, 'array@%p' or 'string@%p', ggLoadConfig will look for the configuration file contents at the address following the '@' sign. In the first case, the content must be a NULL-terminated array of strings representing indivudual lines. In the second case the content is simply read from a string with classical line separator (LF or CR+LF).
ggFreeConfig deletes the internal structure for the configuration handle config. This handle becomes invalid and should not be used anymore.
ggConfigIterLocation allows to retrieve the location and symbol associated to canonical names. This function is mainly used together with the scope abstraction to query application modules at runtime. This function will prepare the iter structure to be used as an iterator that will generate correct matches. iter is a pointer to a gg_location_iter structure owned by the caller. This user part of the structure must be set by the caller before calling ggConfigIterLocation. The config field must be filled with the config handle which contains target information; the name field must be filled with the canonical name that is being looked for. location and symbol are placeholders in which the results will be found along the iteration. The resulting strings do belong to libgg and must not be freed or altered, and they may be invalidated if not used during the iteration process. They must be copied if needed later. _state is an internal opaque pointer that keeps track of the iterator state. Its value must never be touched by the caller.
ggConfigIterTarget allows to iterate over canonical target names and options strings found in a target spec string. This function also work as a generator. iter is a pointer to a gg_target_iter structure owned by the caller. This user part of the structure must be set by the caller before calling ggConfigIterTarget. The config field must be filled with the config handle which contains target information; the input field must be filled with the initial input spec string. target and options are placeholders in which the results will be found along the iteration. Here again, the resulting strings do belong to libgg and must not be freed or altered, and they may be invalidated if not used during the iteration process. They must be copied if needed later. nested is an internal opaque pointer that keeps track of the iterator state. Its value must never be touched by the caller.
Return value
ggLoadConfig returns GGI_OK on success, or:
- GGI_ENOMEM if it can not allocate a new configuration handle. Note that if subsequent memory allocations fail (when feeding the handle), those will not be reported as an error. The handle will be incomplete with regard to the config file contents;
- GGI_ENOTFOUND if the file does not exists. Note that missing files in include directives will not be reported an error. Also, other parse error in the file will simply cause the incriminated lines to be ignored.
ggConfigIterLocation returns GGI_OK on success or GGI_ENOMEM.
ggConfigIterTarget always return GGI_OK and will never fail.
Config file format
The configuration file is line oriented. Each line may define a mapping between a canonical name and a location. The generic format is:
<pattern> <location>[:<symbol>] alias <name> <expansion>
On the first line 'pattern' is a canonical name that may contain one '*' as a wildcard, 'location' is a string that corresponds to a scope location, and 'symbol' is an optional string that gives the name of the symbol to retrieve from the scope. If not given, NULL will be reported. The second line defines an alias. When a target called 'name' is found while iterating over target spec strings, the iterator parse 'expansion' as a sub-input spec, and aggregate all option strings that where found on upstream aliases.
In addition, the configuration file loader knowns the following directives:
.root <path> .include <file>
When locations following the '.root' directive are given as relative path, 'path' will be prepended. '.include' will parse the given file recursively, before continuing with the current one.
Example
This code demonstrates how to get the first module init function accessible from a scope for a given name. It also shows how to use the libgg iterator scheme:
int openModule(struct gg_config *cfg, const char * moduleName, const char * defaultSymbol) { struct gg_location_iter match; struct gg_scope *scope; module_init_func *init; /* prepare the iterator */ match.name = name; match.config = cfg; ggConfigMatchIter(&match); /* iterate over the matches */ GG_ITER_FOREACH(&match) { /* try to retrieve the collection at suggested location */ if((scope = ggGetScope(match.location)) == NULL) continue; /* use default symbol if none suggested */ if (match.symbol == NULL) match.symbol = defaultSymbol; /* try to retrieve the symbol from that scope */ if((init = ggFromScope(scope, match.symbol)) == NULL) { ggDelScope(scope); continue; } /* try to initialize the module */ if(init() != GGI_OK) { ggDelScope(scope); continue; } /* the module is up, abort iteration and return */ GG_ITER_DONE(&match); return GGI_OK; } /* module not found */ GG_ITER_DONE(&match); return GGI_ENOTFOUND;
Note that this code is not completely correct, because the scope cannot be deleted later if it is not remembered somewhere. This next example shows how to list all targets and options specified by a string:
static void showAllTargets(struct gg_config *cfg, const char * input) { struct gg_target_iter match; match.config = cfg; match.input = input; ggConfigIterTarget(&match); GG_ITER_FOREACH(&match) { printf("Target \"%s\" with options \"%s\".\n", match.target, match.options); } GG_ITER_DONE(&match); }
This code shows how to use a builtin configuration:
static char *_conf[] = { "input-foo /usr/lib/ggi/foo.so", "alias foo input-foo", NULL } struct gg_config *cfg = NULL; int loadInternalConfig() { static char s[32]; snprintf(s, sizeof(s), "array@%p", (void*)_conf); return ggLoadConfig(s, &cfg); }