1. Added doxygen-style comments to document all functions.
[OpenSRF.git] / src / libopensrf / osrfConfig.c
1 /* defines the currently used bootstrap config file */
2 #include <opensrf/osrfConfig.h>
3
4 /**
5         @file osrfConfig.c
6         @brief Routines for managing osrfConfigs to represent configuration information.
7 */
8
9 /**
10         @brief Points to the default configuration.
11 */
12 static osrfConfig* osrfConfigDefault = NULL;
13
14 /**
15         @brief Install a specified osrfConfig as the default configuration.
16         @param cfg Pointer to the configuration to be stored.
17
18         Store the passed pointer for future reference.  The calling code yields ownership of the
19         associated osrfConfig.
20 */
21 void osrfConfigSetDefaultConfig(osrfConfig* cfg) {
22         if(cfg) {
23                 if( osrfConfigDefault )
24                         osrfConfigFree( osrfConfigDefault );
25                 osrfConfigDefault = cfg;
26         }
27 }
28
29 /**
30         @brief Free an osrfConfig.
31         @param cfg Pointer to the osrfConfig to be freed.
32 */
33 void osrfConfigFree(osrfConfig* cfg) {
34         if(cfg) {
35                 jsonObjectFree(cfg->config);
36                 free(cfg->configContext);
37                 free(cfg);
38         }
39 }
40
41 /**
42         @brief Report whether a default configuration has been installed.
43         @return Boolean: true if a default configuration is available, or false if not.
44 */
45 int osrfConfigHasDefaultConfig( void ) {
46         return ( osrfConfigDefault != NULL );
47 }
48
49 /**
50         @brief Free the default configuration, if it exists.
51 */
52 void osrfConfigCleanup( void ) {
53         osrfConfigFree(osrfConfigDefault);
54         osrfConfigDefault = NULL;
55 }
56
57 /**
58         @brief Replace the jsonObject of an osrfConfig.
59         @param cfg The osrfConfig to alter.
60         @param obj The jsonObject to install in the osrfConfig.
61
62         This is useful if you have a json object already, rather than an XML configuration file
63         to parse.
64 */
65 void osrfConfigReplaceConfig(osrfConfig* cfg, const jsonObject* obj) {
66         if(!cfg || !obj) return;
67         jsonObjectFree(cfg->config);
68         cfg->config = jsonObjectClone(obj);
69 }
70
71 /**
72         @brief Load an XML configuration file into a jsonObject within an osrfConfig.
73         @param configFile Name of the XML configuration file.
74         @param configContext (Optional) Root of a subtree in the configuration file,
75         @return If successful, a pointer to the resulting osrfConfig; otherwise NULL.
76
77         If @a configContext is not NULL, save a copy of the string to which it points.  It is a
78         tag identifying one or more subtrees within the XML; subsequent searches will examine
79         only matching subtrees.  Otherwise they will search the tree from the root.
80
81         (In practice a configContext is always supplied, so that different programs can share
82         the same configuration file, partitioned by context tags.)
83
84         The calling code is responsible for freeing the returned config object by calling
85         osrfConfigFree().
86 */
87 osrfConfig* osrfConfigInit(const char* configFile, const char* configContext) {
88         if(!configFile) return NULL;
89
90         // Load XML from the configuration file
91
92         xmlDocPtr doc = xmlParseFile(configFile);
93         if(!doc) {
94                 osrfLogWarning( OSRF_LOG_MARK, "Unable to parse XML config file %s", configFile);
95                 return NULL;
96         }
97
98         // Translate it into a jsonObject
99         jsonObject* json_config = xmlDocToJSON(doc);
100         xmlFreeDoc(doc);
101
102         if(!json_config ) {
103                 osrfLogWarning( OSRF_LOG_MARK, "xmlDocToJSON failed for config %s", configFile);
104                 return NULL;
105         }
106
107         // Build an osrfConfig and return it by pointer
108         osrfConfig* cfg = safe_malloc(sizeof(osrfConfig));
109
110         if( configContext )
111                 cfg->configContext = strdup(configContext);
112         else
113                 cfg->configContext = NULL;
114
115         cfg->config = json_config;
116
117         return cfg;
118 }
119
120 /**
121         @brief Search a configuration for a specified value.
122         @param cfg (Optional) The configuration to search, or NULL for the default configuration.
123         @param path A printf-style format string representing the search path.  Subsequent
124         parameters, if any, are inserted into the format string to form the search path.
125         @return A pointer to a newly allocated string containing the value, if found; otherwise
126         NULL.
127
128         Search for a value in the configuration, as specified by the search path.
129
130         If cfg is NULL, search the default configuration; otherwise search the configuration
131         specified.
132
133         If the configuration includes a configContext, then prepend it to the path, like so:
134         "//<configContext><path>".  Hence the path should begin with a slash to separate it from
135         the context.  Search for the resulting effective path at any level within the
136         configuration.
137
138         If the configuration does @em not include a configContext, then start the search at the
139         root of the configuration.  In this case the path should @em not begin with a slash.
140
141         If the configuration contains more than one entry at locations matching the effective
142         search path, return NULL.
143
144         Return numeric values as numeric strings.
145
146         The calling code is responsible for freeing the returned string by calling free().
147 */
148 char* osrfConfigGetValue(const osrfConfig* cfg, const char* path, ...) {
149         if(!path) return NULL;
150         if(!cfg)
151                 cfg = osrfConfigDefault;
152         if(!cfg) {
153                 osrfLogWarning( OSRF_LOG_MARK, "No Config object in osrfConfigGetValue()");
154                 return NULL;
155         }
156
157         VA_LIST_TO_STRING(path);
158         jsonObject* obj;
159
160         if(cfg->configContext) {
161                 jsonObject* outer_obj =
162                         jsonObjectFindPath(cfg->config, "//%s%s", cfg->configContext, VA_BUF);
163                 obj = jsonObjectExtractIndex( outer_obj, 0 );
164                 jsonObjectFree( outer_obj );
165         } else
166                 obj = jsonObjectFindPath( cfg->config, VA_BUF );
167
168         char* val = jsonObjectToSimpleString(obj);
169         jsonObjectFree(obj);
170         return val;
171 }
172
173 /**
174         @brief Search for one or more subtrees of a configuration.
175         @param cfg (Optional) The configuration to search, or NULL for the default configuration.
176         @param path A printf-style format string representing the search path.  Subsequent
177         parameters, if any, are inserted into the format string to form the search path.
178         @return A pointer to a jsonObject representing a subset of the specified configuration,
179         if found; otherwise NULL.
180
181         Search for subtrees of the configuration, as specified by the search path.
182
183         If the configuration includes a configContext, then prepend it to the path, like so:
184         "//<configContext><path>".  Hence the path should begin with a slash to separate it from
185         the context.  Search for the resulting effective path at any level within the
186         configuration.
187
188         If the configuration does @em not include a configContext, then start the search at the
189         root of the configuration.  In this case the path should @em not begin with a slash.
190
191         If any entries match the effective path, return copies of them all as elements of a
192         jsonObject of type JSON_ARRAY.
193
194         If no entries match the effective path, return a jsonObject of type JSON_NULL.
195
196         The calling code is responsible for freeing the returned jsonObject by calling
197         jsonObjectFree().
198 */
199 jsonObject* osrfConfigGetValueObject(osrfConfig* cfg, const char* path, ...) {
200         if(!path) return NULL;
201         if(!cfg)
202                 cfg = osrfConfigDefault;
203         if(!cfg) {
204                 osrfLogWarning( OSRF_LOG_MARK, "No Config object in osrfConfigGetValueObject()");
205                 return NULL;
206         }
207
208         VA_LIST_TO_STRING(path);
209         if(cfg->configContext)
210                 return jsonObjectFindPath(cfg->config, "//%s%s", cfg->configContext, VA_BUF);
211         else
212                 return jsonObjectFindPath(cfg->config, VA_BUF);
213 }
214
215 /**
216         @brief Search for one or more values in a configuration, specified by a path.
217         @param cfg (Optional) The configuration to search, or NULL for the default configuration.
218         @param arr Pointer to an osrfStringArray to be populated with values.
219         @param path A printf-style format string representing the search path.  Subsequent
220         parameters, if any, are inserted into the format string to form the search path.
221         @return The number of values loaded into the osrfStringArray.
222
223         Search the configuration for values specified by the search path.  Load any values
224         found (either strings or numbers) into an existing osrfStringArray supplied by the
225         calling code.
226
227         Make no effort to delete any strings that may already be in the osrfStringArray.
228         Ordinarily the calling code should ensure that the osrfStringArray is empty when passed.
229
230         If the configuration includes a configContext, then prepend it to the path, like so:
231         "//<configContext><path>".  Hence the path should begin with a slash to separate it from
232         the context.  Search for the resulting effective path at any level within the
233         configuration.
234
235         If the configuration does @em not include a configContext, then start the search at the
236         root of the configuration.  In this case the path should @em not begin with a slash.
237 */
238 int osrfConfigGetValueList(const osrfConfig* cfg, osrfStringArray* arr,
239                 const char* path, ...) {
240
241         if(!arr || !path) return 0;
242         if(!cfg)
243                 cfg = osrfConfigDefault;
244         if(!cfg) {
245                 osrfLogWarning( OSRF_LOG_MARK, "No Config object!");
246                 return -1;
247         }
248
249         VA_LIST_TO_STRING(path);
250
251         jsonObject* obj;
252         if(cfg->configContext) {
253                 obj = jsonObjectFindPath( cfg->config, "//%s%s", cfg->configContext, VA_BUF);
254         } else {
255                 obj = jsonObjectFindPath( cfg->config, VA_BUF);
256         }
257
258         int count = 0;
259
260         if(obj && obj->type == JSON_ARRAY ) {
261
262                 int i;
263                 for( i = 0; i < obj->size; i++ ) {
264
265                         const char* val = jsonObjectGetString( jsonObjectGetIndex(obj, i) );
266                         if(val) {
267                                 count++;
268                                 osrfStringArrayAdd(arr, val);
269                         }
270                 }
271         }
272
273         jsonObjectFree(obj);
274         return count;
275 }