#ifndef OSRF_CONFIG_H
#define OSRF_CONFIG_H
+/**
+ @file osrfConfig.h
+ @brief Routines for loading, storing, and searching configurations.
+
+ A configuration file, encoded as XML, is loaded and translated into a jsonObject. This
+ object is stored in an osrfConfig, along with an optional context string which, if
+ present, restricts subsequent searches to a subset of the jsonObject.
+
+ In theory the context string could identify multiple subtrees of the total configuration.
+ In practice it is used to partition a configuration file into different pieces, each piece
+ to be used by a different application.
+
+ Normally an application loads a default configuration, accessible from every linked
+ module. It is also possible, although seldom useful, to create and search a configuration
+ distinct from the default configuration.
+*/
+
#include <opensrf/xml_utils.h>
#include <opensrf/utils.h>
#include <opensrf/string_array.h>
extern "C" {
#endif
+/**
+ @brief Represents a configuration; normally loaded from an XML configuration file.
+*/
typedef struct {
- jsonObject* config;
- char* configContext;
+ jsonObject* config; /**< Represents the contents of the XML configuration file. */
+ char* configContext; /**< Context string (optional). */
} osrfConfig;
-
-/**
- Parses a new config file. Caller is responsible for freeing the returned
- config object when finished.
- @param configFile The XML config file to parse.
- @param configContext Optional root of the subtree in the config file where
- we will look for values. If it's not provided, searches will be
- performed from the root of the config file
- @return The config object if the file parses successfully. Otherwise
- it returns NULL;
-*/
osrfConfig* osrfConfigInit(const char* configFile, const char* configContext);
-/**
- @return True if we have a default config defined
-*/
-int osrfConfigHasDefaultConfig();
+int osrfConfigHasDefaultConfig( void );
-/**
- Replaces the config object's json object. This is useful
- if you have a json object already and not an XML config
- file to parse.
- @param cfg The config object to alter
- @param obj The json object to use when searching values
-*/
void osrfConfigReplaceConfig(osrfConfig* cfg, const jsonObject* obj);
-/** Deallocates a config object
- @param cfg The config object to free
-*/
void osrfConfigFree(osrfConfig* cfg);
-
-/* Assigns the default config file. This file will be used whenever
- NULL is passed to config retrieval functions
- @param cfg The config object to use as the default config
-*/
void osrfConfigSetDefaultConfig(osrfConfig* cfg);
-/* frees the default config if one exists */
-void osrfConfigCleanup();
+void osrfConfigCleanup( void );
-
-/**
- Returns the value in the config found at 'path'.
- If the value found at 'path' is a long or a double,
- the value is stringified and then returned.
- The caller must free the returned char*
-
- if there is a configContext, then it will be appended to
- the front of the path like so: //<configContext>/<path>
- if no configContext was provided to osfConfigSetFile, then
- the path is interpreted literally.
- @param cfg The config file to search or NULL if the default
- config should be used
- @param path The search path
-*/
char* osrfConfigGetValue(const osrfConfig* cfg, const char* path, ...);
-
-/**
- * @see osrfConfigGetValue
- * @return jsonObject found at path
- */
-jsonObject* osrfConfigGetValueObject(osrfConfig* cfg, char* path, ...);
-
-
-/**
- Puts the list of values found at 'path' into the pre-allocated
- string array.
- Note that the config node found at 'path' must be an array.
- @param cfg The config file to search or NULL if the default
- config should be used
- @param arr An allocated string_array where the values will
- be stored
- @param path The search path
- @return the number of values added to the string array;
-*/
+jsonObject* osrfConfigGetValueObject(osrfConfig* cfg, const char* path, ...);
int osrfConfigGetValueList(const osrfConfig* cfg, osrfStringArray* arr,
const char* path, ...);
/* defines the currently used bootstrap config file */
#include <opensrf/osrfConfig.h>
+/**
+ @file osrfConfig.c
+ @brief Routines for managing osrfConfigs to represent configuration information.
+*/
+
+/**
+ @brief Points to the default configuration.
+*/
static osrfConfig* osrfConfigDefault = NULL;
+/**
+ @brief Install a specified osrfConfig as the default configuration.
+ @param cfg Pointer to the configuration to be stored.
+ Store the passed pointer for future reference. The calling code yields ownership of the
+ associated osrfConfig.
+*/
void osrfConfigSetDefaultConfig(osrfConfig* cfg) {
if(cfg) {
if( osrfConfigDefault )
}
}
+/**
+ @brief Free an osrfConfig.
+ @param cfg Pointer to the osrfConfig to be freed.
+*/
void osrfConfigFree(osrfConfig* cfg) {
if(cfg) {
jsonObjectFree(cfg->config);
free(cfg->configContext);
free(cfg);
- }
+ }
}
-
-int osrfConfigHasDefaultConfig() {
+/**
+ @brief Report whether a default configuration has been installed.
+ @return Boolean: true if a default configuration is available, or false if not.
+*/
+int osrfConfigHasDefaultConfig( void ) {
return ( osrfConfigDefault != NULL );
}
-
-void osrfConfigCleanup() {
+/**
+ @brief Free the default configuration, if it exists.
+*/
+void osrfConfigCleanup( void ) {
osrfConfigFree(osrfConfigDefault);
osrfConfigDefault = NULL;
}
+/**
+ @brief Replace the jsonObject of an osrfConfig.
+ @param cfg The osrfConfig to alter.
+ @param obj The jsonObject to install in the osrfConfig.
+ This is useful if you have a json object already, rather than an XML configuration file
+ to parse.
+*/
void osrfConfigReplaceConfig(osrfConfig* cfg, const jsonObject* obj) {
if(!cfg || !obj) return;
jsonObjectFree(cfg->config);
- cfg->config = jsonObjectClone(obj);
+ cfg->config = jsonObjectClone(obj);
}
+/**
+ @brief Load an XML configuration file into a jsonObject within an osrfConfig.
+ @param configFile Name of the XML configuration file.
+ @param configContext (Optional) Root of a subtree in the configuration file,
+ @return If successful, a pointer to the resulting osrfConfig; otherwise NULL.
+
+ If @a configContext is not NULL, save a copy of the string to which it points. It is a
+ tag identifying one or more subtrees within the XML; subsequent searches will examine
+ only matching subtrees. Otherwise they will search the tree from the root.
+
+ (In practice a configContext is always supplied, so that different programs can share
+ the same configuration file, partitioned by context tags.)
+
+ The calling code is responsible for freeing the returned config object by calling
+ osrfConfigFree().
+*/
osrfConfig* osrfConfigInit(const char* configFile, const char* configContext) {
if(!configFile) return NULL;
// Load XML from the configuration file
-
+
xmlDocPtr doc = xmlParseFile(configFile);
if(!doc) {
osrfLogWarning( OSRF_LOG_MARK, "Unable to parse XML config file %s", configFile);
}
// Translate it into a jsonObject
-
jsonObject* json_config = xmlDocToJSON(doc);
xmlFreeDoc(doc);
if(!json_config ) {
osrfLogWarning( OSRF_LOG_MARK, "xmlDocToJSON failed for config %s", configFile);
return NULL;
- }
+ }
// Build an osrfConfig and return it by pointer
-
osrfConfig* cfg = safe_malloc(sizeof(osrfConfig));
- if(configContext) cfg->configContext = strdup(configContext);
- else cfg->configContext = NULL;
+ if( configContext )
+ cfg->configContext = strdup(configContext);
+ else
+ cfg->configContext = NULL;
cfg->config = json_config;
-
+
return cfg;
}
+/**
+ @brief Search a configuration for a specified value.
+ @param cfg (Optional) The configuration to search, or NULL for the default configuration.
+ @param path A printf-style format string representing the search path. Subsequent
+ parameters, if any, are inserted into the format string to form the search path.
+ @return A pointer to a newly allocated string containing the value, if found; otherwise
+ NULL.
+
+ Search for a value in the configuration, as specified by the search path.
+
+ If cfg is NULL, search the default configuration; otherwise search the configuration
+ specified.
+
+ If the configuration includes a configContext, then prepend it to the path, like so:
+ "//<configContext><path>". Hence the path should begin with a slash to separate it from
+ the context. Search for the resulting effective path at any level within the
+ configuration.
+
+ If the configuration does @em not include a configContext, then start the search at the
+ root of the configuration. In this case the path should @em not begin with a slash.
+
+ If the configuration contains more than one entry at locations matching the effective
+ search path, return NULL.
+ Return numeric values as numeric strings.
+
+ The calling code is responsible for freeing the returned string by calling free().
+*/
char* osrfConfigGetValue(const osrfConfig* cfg, const char* path, ...) {
if(!path) return NULL;
- if(!cfg) cfg = osrfConfigDefault;
- if(!cfg) {
- osrfLogWarning( OSRF_LOG_MARK, "No Config object in osrfConfigGetValue()");
- return NULL;
+ if(!cfg)
+ cfg = osrfConfigDefault;
+ if(!cfg) {
+ osrfLogWarning( OSRF_LOG_MARK, "No Config object in osrfConfigGetValue()");
+ return NULL;
}
VA_LIST_TO_STRING(path);
obj = jsonObjectExtractIndex( outer_obj, 0 );
jsonObjectFree( outer_obj );
} else
- obj = jsonObjectFindPath( cfg->config, VA_BUF);
+ obj = jsonObjectFindPath( cfg->config, VA_BUF );
char* val = jsonObjectToSimpleString(obj);
jsonObjectFree(obj);
return val;
}
-jsonObject* osrfConfigGetValueObject(osrfConfig* cfg, char* path, ...) {
+/**
+ @brief Search for one or more subtrees of a configuration.
+ @param cfg (Optional) The configuration to search, or NULL for the default configuration.
+ @param path A printf-style format string representing the search path. Subsequent
+ parameters, if any, are inserted into the format string to form the search path.
+ @return A pointer to a jsonObject representing a subset of the specified configuration,
+ if found; otherwise NULL.
+
+ Search for subtrees of the configuration, as specified by the search path.
+
+ If the configuration includes a configContext, then prepend it to the path, like so:
+ "//<configContext><path>". Hence the path should begin with a slash to separate it from
+ the context. Search for the resulting effective path at any level within the
+ configuration.
+
+ If the configuration does @em not include a configContext, then start the search at the
+ root of the configuration. In this case the path should @em not begin with a slash.
+
+ If any entries match the effective path, return copies of them all as elements of a
+ jsonObject of type JSON_ARRAY.
+
+ If no entries match the effective path, return a jsonObject of type JSON_NULL.
+
+ The calling code is responsible for freeing the returned jsonObject by calling
+ jsonObjectFree().
+*/
+jsonObject* osrfConfigGetValueObject(osrfConfig* cfg, const char* path, ...) {
if(!path) return NULL;
- if(!cfg) cfg = osrfConfigDefault;
+ if(!cfg)
+ cfg = osrfConfigDefault;
+ if(!cfg) {
+ osrfLogWarning( OSRF_LOG_MARK, "No Config object in osrfConfigGetValueObject()");
+ return NULL;
+ }
+
VA_LIST_TO_STRING(path);
- if(cfg->configContext)
- return jsonObjectFindPath(cfg->config, "//%s%s", cfg->configContext, VA_BUF);
+ if(cfg->configContext)
+ return jsonObjectFindPath(cfg->config, "//%s%s", cfg->configContext, VA_BUF);
else
return jsonObjectFindPath(cfg->config, VA_BUF);
}
+/**
+ @brief Search for one or more values in a configuration, specified by a path.
+ @param cfg (Optional) The configuration to search, or NULL for the default configuration.
+ @param arr Pointer to an osrfStringArray to be populated with values.
+ @param path A printf-style format string representing the search path. Subsequent
+ parameters, if any, are inserted into the format string to form the search path.
+ @return The number of values loaded into the osrfStringArray.
+
+ Search the configuration for values specified by the search path. Load any values
+ found (either strings or numbers) into an existing osrfStringArray supplied by the
+ calling code.
+
+ Make no effort to delete any strings that may already be in the osrfStringArray.
+ Ordinarily the calling code should ensure that the osrfStringArray is empty when passed.
+
+ If the configuration includes a configContext, then prepend it to the path, like so:
+ "//<configContext><path>". Hence the path should begin with a slash to separate it from
+ the context. Search for the resulting effective path at any level within the
+ configuration.
+
+ If the configuration does @em not include a configContext, then start the search at the
+ root of the configuration. In this case the path should @em not begin with a slash.
+*/
int osrfConfigGetValueList(const osrfConfig* cfg, osrfStringArray* arr,
const char* path, ...) {
if(!arr || !path) return 0;
- if(!cfg) cfg = osrfConfigDefault;
- if(!cfg) { osrfLogWarning( OSRF_LOG_MARK, "No Config object!"); return -1;}
+ if(!cfg)
+ cfg = osrfConfigDefault;
+ if(!cfg) {
+ osrfLogWarning( OSRF_LOG_MARK, "No Config object!");
+ return -1;
+ }
VA_LIST_TO_STRING(path);
int i;
for( i = 0; i < obj->size; i++ ) {
- char* val = jsonObjectToSimpleString(jsonObjectGetIndex(obj, i));
+ const char* val = jsonObjectGetString( jsonObjectGetIndex(obj, i) );
if(val) {
count++;
osrfStringArrayAdd(arr, val);
- free(val);
}
}
}
jsonObjectFree(obj);
return count;
}
-