XMMS2
|
00001 /* XMMS2 - X Music Multiplexer System 00002 * Copyright (C) 2003-2009 XMMS2 Team 00003 * 00004 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 */ 00017 00018 #include <glib.h> 00019 00020 #include <stdlib.h> 00021 #include <unistd.h> 00022 #include <stdio.h> 00023 #include <string.h> 00024 #include <fcntl.h> 00025 #include <sys/types.h> 00026 #include <sys/stat.h> 00027 00028 #include "xmmsc/xmmsc_idnumbers.h" 00029 #include "xmmspriv/xmms_config.h" 00030 #include "xmmspriv/xmms_utils.h" 00031 #include "xmms/xmms_ipc.h" 00032 #include "xmms/xmms_log.h" 00033 00034 /* 00035 #include "xmms/util.h" 00036 #include "xmms/xmms.h" 00037 #include "xmms/object.h" 00038 #include "xmms/signal_xmms.h" 00039 #include "xmms/plugin.h" 00040 #include "xmms/ipc.h" 00041 */ 00042 00043 /** @internal */ 00044 typedef enum { 00045 XMMS_CONFIG_STATE_INVALID, 00046 XMMS_CONFIG_STATE_START, 00047 XMMS_CONFIG_STATE_SECTION, 00048 XMMS_CONFIG_STATE_PROPERTY 00049 } xmms_configparser_state_t; 00050 00051 typedef struct dump_tree_data_St { 00052 FILE *fp; 00053 xmms_configparser_state_t state; 00054 00055 gchar indent[128]; 00056 guint indent_level; 00057 00058 gchar *prev_key; 00059 } dump_tree_data_t; 00060 00061 static GTree *xmms_config_client_list_values (xmms_config_t *conf, xmms_error_t *err); 00062 static xmms_config_property_t *xmms_config_property_new (const gchar *name); 00063 static gchar *xmms_config_client_get_value (xmms_config_t *conf, const gchar *key, xmms_error_t *err); 00064 static gchar *xmms_config_client_register_value (xmms_config_t *config, const gchar *name, const gchar *def_value, xmms_error_t *error); 00065 static gint compare_key (gconstpointer a, gconstpointer b, gpointer user_data); 00066 static void xmms_config_client_set_value (xmms_config_t *conf, const gchar *key, const gchar *value, xmms_error_t *err); 00067 00068 XMMS_CMD_DEFINE (setvalue, xmms_config_client_set_value, xmms_config_t *, NONE, STRING, STRING); 00069 XMMS_CMD_DEFINE (listvalues, xmms_config_client_list_values, xmms_config_t *, DICT, NONE, NONE); 00070 XMMS_CMD_DEFINE (getvalue, xmms_config_client_get_value, xmms_config_t *, STRING, STRING, NONE); 00071 XMMS_CMD_DEFINE (regvalue, xmms_config_client_register_value, xmms_config_t *, STRING, STRING, STRING); 00072 00073 /** 00074 * @defgroup Config Config 00075 * @brief Controls configuration for the server. 00076 * 00077 * The configuration is saved to, and loaded from an XML file. It's split into 00078 * plugin, client and core parts. This documents the configuration for parts 00079 * inside the server. For plugin config see each server object's documentation. 00080 * 00081 * @ingroup XMMSServer 00082 * @{ 00083 */ 00084 00085 /** 00086 * Global parsed config 00087 */ 00088 struct xmms_config_St { 00089 xmms_object_t obj; 00090 00091 const gchar *filename; 00092 GTree *properties; 00093 00094 /* Lock on globals are great! */ 00095 GMutex *mutex; 00096 00097 /* parsing */ 00098 gboolean is_parsing; 00099 GQueue *states; 00100 GQueue *sections; 00101 gchar *value_name; 00102 guint version; 00103 }; 00104 00105 /** 00106 * A config property in the configuration file 00107 */ 00108 struct xmms_config_property_St { 00109 xmms_object_t obj; 00110 00111 /** Name of the config directive */ 00112 const gchar *name; 00113 /** The data */ 00114 gchar *value; 00115 }; 00116 00117 /** 00118 * Global config 00119 * Since there can only be one configuration per server 00120 * we can have the convenience of having it as a global variable. 00121 */ 00122 00123 static xmms_config_t *global_config; 00124 00125 /** 00126 * Config file version 00127 */ 00128 #define XMMS_CONFIG_VERSION 2 00129 00130 /** 00131 * @} 00132 * @addtogroup Config 00133 * @{ 00134 */ 00135 00136 /** 00137 * Config functions 00138 */ 00139 00140 /** 00141 * Lookup config key and return its associated value as a string. 00142 * This is a convenient function to make it easier to get a configuration value 00143 * rather than having to call #xmms_config_property_get_string separately. 00144 * 00145 * @param conf Global config 00146 * @param key Configuration property to lookup 00147 * @param err if error occurs this will be filled in 00148 * 00149 * @return A string with the value. If the value is an int it will return NULL 00150 */ 00151 const gchar * 00152 xmms_config_property_lookup_get_string (xmms_config_t *conf, const gchar *key, 00153 xmms_error_t *err) 00154 { 00155 xmms_config_property_t *prop; 00156 00157 prop = xmms_config_lookup (key); 00158 if (!prop) { 00159 xmms_error_set (err, XMMS_ERROR_NOENT, 00160 "Trying to get non-existent property"); 00161 return NULL; 00162 } 00163 00164 return xmms_config_property_get_string (prop); 00165 } 00166 00167 /** 00168 * Look up a config key from the global config 00169 * @param path A configuration path. Could be core.myconfig or 00170 * effect.foo.myconfig 00171 * @return An #xmms_config_property_t 00172 */ 00173 xmms_config_property_t * 00174 xmms_config_lookup (const gchar *path) 00175 { 00176 xmms_config_property_t *prop; 00177 g_return_val_if_fail (global_config, NULL); 00178 00179 g_mutex_lock (global_config->mutex); 00180 prop = g_tree_lookup (global_config->properties, path); 00181 g_mutex_unlock (global_config->mutex); 00182 00183 return prop; 00184 } 00185 00186 /** 00187 * Get the name of a config property. 00188 * @param prop The config property 00189 * @return Name of config property 00190 */ 00191 const gchar * 00192 xmms_config_property_get_name (const xmms_config_property_t *prop) 00193 { 00194 g_return_val_if_fail (prop, NULL); 00195 00196 return prop->name; 00197 } 00198 00199 /** 00200 * Set the data of the config property to a new value 00201 * @param prop The config property 00202 * @param data The value to set 00203 */ 00204 void 00205 xmms_config_property_set_data (xmms_config_property_t *prop, const gchar *data) 00206 { 00207 GTree *dict; 00208 00209 g_return_if_fail (prop); 00210 g_return_if_fail (data); 00211 00212 /* check whether the value changed at all */ 00213 if (prop->value && !strcmp (prop->value, data)) 00214 return; 00215 00216 g_free (prop->value); 00217 prop->value = g_strdup (data); 00218 xmms_object_emit (XMMS_OBJECT (prop), 00219 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED, 00220 (gpointer) data); 00221 00222 dict = g_tree_new_full (compare_key, NULL, 00223 NULL, (GDestroyNotify) xmmsv_unref); 00224 g_tree_insert (dict, (gchar *) prop->name, 00225 xmmsv_new_string (prop->value)); 00226 00227 xmms_object_emit_f (XMMS_OBJECT (global_config), 00228 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED, 00229 XMMSV_TYPE_DICT, 00230 dict); 00231 00232 g_tree_destroy (dict); 00233 00234 /* save the database to disk, so we don't lose any data 00235 * if the daemon crashes 00236 */ 00237 xmms_config_save (); 00238 } 00239 00240 /** 00241 * Return the value of a config property as a string 00242 * @param prop The config property 00243 * @return value as string 00244 */ 00245 const gchar * 00246 xmms_config_property_get_string (const xmms_config_property_t *prop) 00247 { 00248 g_return_val_if_fail (prop, NULL); 00249 return prop->value; 00250 } 00251 00252 /** 00253 * Return the value of a config property as an int 00254 * @param prop The config property 00255 * @return value as int 00256 */ 00257 gint 00258 xmms_config_property_get_int (const xmms_config_property_t *prop) 00259 { 00260 g_return_val_if_fail (prop, 0); 00261 if (prop->value) 00262 return atoi (prop->value); 00263 00264 return 0; 00265 } 00266 00267 /** 00268 * Return the value of a config property as a float 00269 * @param prop The config property 00270 * @return value as float 00271 */ 00272 gfloat 00273 xmms_config_property_get_float (const xmms_config_property_t *prop) 00274 { 00275 g_return_val_if_fail (prop, 0.0); 00276 if (prop->value) 00277 return atof (prop->value); 00278 00279 return 0.0; 00280 } 00281 00282 /** 00283 * Set a callback function for a config property. 00284 * This will be called each time the property's value changes. 00285 * @param prop The config property 00286 * @param cb The callback to set 00287 * @param userdata Data to pass on to the callback 00288 */ 00289 void 00290 xmms_config_property_callback_set (xmms_config_property_t *prop, 00291 xmms_object_handler_t cb, 00292 gpointer userdata) 00293 { 00294 g_return_if_fail (prop); 00295 00296 if (!cb) 00297 return; 00298 00299 xmms_object_connect (XMMS_OBJECT (prop), 00300 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED, 00301 (xmms_object_handler_t) cb, userdata); 00302 } 00303 00304 /** 00305 * Remove a callback from a config property 00306 * @param prop The config property 00307 * @param cb The callback to remove 00308 */ 00309 void 00310 xmms_config_property_callback_remove (xmms_config_property_t *prop, 00311 xmms_object_handler_t cb, 00312 gpointer userdata) 00313 { 00314 g_return_if_fail (prop); 00315 00316 if (!cb) 00317 return; 00318 00319 xmms_object_disconnect (XMMS_OBJECT (prop), 00320 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED, cb, userdata); 00321 } 00322 00323 /** 00324 * Register a new config property. This should be called from the init code 00325 * as XMMS2 won't allow set/get on properties that haven't been registered. 00326 * 00327 * @param path The path in the config tree. 00328 * @param default_value If the value was not found in the configfile, what 00329 * should we use? 00330 * @param cb A callback function that will be called if the value is changed by 00331 * the client. Can be set to NULL. 00332 * @param userdata Data to pass to the callback function. 00333 * @return A newly allocated #xmms_config_property_t for the registered 00334 * property. 00335 */ 00336 xmms_config_property_t * 00337 xmms_config_property_register (const gchar *path, 00338 const gchar *default_value, 00339 xmms_object_handler_t cb, 00340 gpointer userdata) 00341 { 00342 00343 xmms_config_property_t *prop; 00344 00345 g_mutex_lock (global_config->mutex); 00346 00347 prop = g_tree_lookup (global_config->properties, path); 00348 if (!prop) { 00349 prop = xmms_config_property_new (g_strdup (path)); 00350 00351 xmms_config_property_set_data (prop, (gchar *) default_value); 00352 g_tree_replace (global_config->properties, 00353 (gchar *) prop->name, prop); 00354 } 00355 00356 if (cb) { 00357 xmms_config_property_callback_set (prop, cb, userdata); 00358 } 00359 00360 g_mutex_unlock (global_config->mutex); 00361 00362 return prop; 00363 } 00364 00365 /** 00366 * @} 00367 * 00368 * @if internal 00369 * @addtogroup Config 00370 * @{ 00371 */ 00372 00373 /** 00374 * @internal Get the current parser state for the given element name 00375 * @param[in] name Element name to match to a state 00376 * @return Parser state matching element name 00377 */ 00378 static xmms_configparser_state_t 00379 get_current_state (const gchar *name) 00380 { 00381 static struct { 00382 const gchar *name; 00383 xmms_configparser_state_t state; 00384 } *ptr, lookup[] = { 00385 {"xmms", XMMS_CONFIG_STATE_START}, 00386 {"section", XMMS_CONFIG_STATE_SECTION}, 00387 {"property", XMMS_CONFIG_STATE_PROPERTY}, 00388 {NULL, XMMS_CONFIG_STATE_INVALID} 00389 }; 00390 00391 for (ptr = lookup; ptr && ptr->name; ptr++) { 00392 if (!strcmp (ptr->name, name)) { 00393 return ptr->state; 00394 } 00395 } 00396 00397 return XMMS_CONFIG_STATE_INVALID; 00398 } 00399 00400 /** 00401 * @internal Look for the value associated with an attribute name, given lists 00402 * of attribute names and attribute values. 00403 * @param[in] names List of attribute names 00404 * @param[in] values List of attribute values matching up to names 00405 * @param[in] needle Attribute name to look for 00406 * @return The attribute value, or NULL if not found 00407 */ 00408 static const gchar * 00409 lookup_attribute (const gchar **names, const gchar **values, 00410 const gchar *needle) 00411 { 00412 const gchar **n, **v; 00413 00414 for (n = names, v = values; *n && *v; n++, v++) { 00415 if (!strcmp ((gchar *) *n, needle)) { 00416 return *v; 00417 } 00418 } 00419 00420 return NULL; 00421 } 00422 00423 /** 00424 * @internal Parse start tag in config file. This function is called whenever 00425 * a start tag is encountered by the GMarkupParser from #xmms_config_init 00426 * @param ctx The parser context. 00427 * @param name The name of the element encountered 00428 * @param attr_name List of attribute names in tag 00429 * @param attr_data List of attribute data in tag 00430 * @param userdata User data - In this case, the global config 00431 * @param error GError to be filled in if an error is encountered 00432 */ 00433 static void 00434 xmms_config_parse_start (GMarkupParseContext *ctx, 00435 const gchar *name, 00436 const gchar **attr_name, 00437 const gchar **attr_data, 00438 gpointer userdata, 00439 GError **error) 00440 { 00441 xmms_config_t *config = userdata; 00442 xmms_configparser_state_t state; 00443 const gchar *attr; 00444 00445 state = get_current_state (name); 00446 g_queue_push_head (config->states, GINT_TO_POINTER (state)); 00447 00448 switch (state) { 00449 case XMMS_CONFIG_STATE_INVALID: 00450 *error = g_error_new (G_MARKUP_ERROR, 00451 G_MARKUP_ERROR_UNKNOWN_ELEMENT, 00452 "Unknown element '%s'", name); 00453 return; 00454 case XMMS_CONFIG_STATE_START: 00455 /* check config version here */ 00456 attr = lookup_attribute (attr_name, attr_data, "version"); 00457 if (attr) { 00458 if (strcmp (attr, "0.02") == 0) { 00459 config->version = 2; 00460 } else { 00461 config->version = atoi (attr); 00462 } 00463 } 00464 return; 00465 default: 00466 break; 00467 } 00468 00469 attr = lookup_attribute (attr_name, attr_data, "name"); 00470 if (!attr) { 00471 *error = g_error_new (G_MARKUP_ERROR, 00472 G_MARKUP_ERROR_INVALID_CONTENT, 00473 "Attribute 'name' missing"); 00474 return; 00475 } 00476 00477 switch (state) { 00478 case XMMS_CONFIG_STATE_SECTION: 00479 g_queue_push_head (config->sections, g_strdup (attr)); 00480 00481 break; 00482 case XMMS_CONFIG_STATE_PROPERTY: 00483 g_free (config->value_name); 00484 config->value_name = g_strdup (attr); 00485 00486 break; 00487 default: 00488 break; 00489 } 00490 } 00491 00492 /** 00493 * @internal Parse end tag in config file. This function is called whenever 00494 * an end tag is encountered by the GMarkupParser from #xmms_config_init 00495 * @param ctx The parser context. 00496 * @param name The name of the element encountered 00497 * @param userdata User data - In this case, the global config 00498 * @param error GError to be filled in if an error is encountered 00499 */ 00500 static void 00501 xmms_config_parse_end (GMarkupParseContext *ctx, 00502 const gchar *name, 00503 gpointer userdata, 00504 GError **error) 00505 { 00506 xmms_config_t *config = userdata; 00507 xmms_configparser_state_t state; 00508 00509 state = GPOINTER_TO_INT (g_queue_pop_head (config->states)); 00510 00511 switch (state) { 00512 case XMMS_CONFIG_STATE_SECTION: 00513 g_free (g_queue_pop_head (config->sections)); 00514 00515 break; 00516 case XMMS_CONFIG_STATE_PROPERTY: 00517 g_free (config->value_name); 00518 config->value_name = NULL; 00519 00520 break; 00521 default: 00522 break; 00523 } 00524 } 00525 00526 /** 00527 * @internal Parse text in config file. This function is called whenever 00528 * text (anything between start and end tags) is encountered by the 00529 * GMarkupParser from #xmms_config_init 00530 * @param ctx The parser context. 00531 * @param text The text 00532 * @param text_len Length of the text 00533 * @param userdata User data - In this case, the global config 00534 * @param error GError to be filled in if an error is encountered 00535 */ 00536 static void 00537 xmms_config_parse_text (GMarkupParseContext *ctx, 00538 const gchar *text, 00539 gsize text_len, 00540 gpointer userdata, 00541 GError **error) 00542 { 00543 xmms_config_t *config = userdata; 00544 xmms_configparser_state_t state; 00545 xmms_config_property_t *prop; 00546 GList *l; 00547 gchar key[256] = ""; 00548 gsize siz = sizeof (key); 00549 00550 state = GPOINTER_TO_INT (g_queue_peek_head (config->states)); 00551 00552 if (state != XMMS_CONFIG_STATE_PROPERTY) 00553 return; 00554 00555 /* assemble the config key, based on the traversed sections */ 00556 for (l = config->sections->tail; l; l = l->prev) { 00557 g_strlcat (key, l->data, siz); 00558 g_strlcat (key, ".", siz); 00559 } 00560 00561 g_strlcat (key, config->value_name, siz); 00562 00563 prop = xmms_config_property_new (g_strdup (key)); 00564 xmms_config_property_set_data (prop, (gchar *) text); 00565 00566 g_tree_replace (config->properties, (gchar *) prop->name, prop); 00567 } 00568 00569 /** 00570 * @internal Set a key to a new value 00571 * @param conf The config 00572 * @param key The key to look for 00573 * @param value The value to set the key to 00574 * @param err To be filled in if an error occurs 00575 */ 00576 static void 00577 xmms_config_client_set_value (xmms_config_t *conf, 00578 const gchar *key, const gchar *value, 00579 xmms_error_t *err) 00580 { 00581 xmms_config_property_t *prop; 00582 00583 prop = xmms_config_lookup (key); 00584 if (prop) { 00585 xmms_config_property_set_data (prop, value); 00586 } else { 00587 xmms_error_set (err, XMMS_ERROR_NOENT, 00588 "Trying to set non-existent config property"); 00589 } 00590 00591 } 00592 00593 /** 00594 * @internal Convert global config properties dict to a normal dict 00595 * @param key The dict key 00596 * @param property An xmms_config_property_t 00597 * @param udata The dict to store configvals 00598 */ 00599 static gboolean 00600 xmms_config_foreach_dict (gpointer key, xmms_config_property_t *prop, 00601 GTree *dict) 00602 { 00603 g_tree_insert (dict, g_strdup (key), xmmsv_new_string (prop->value)); 00604 00605 return FALSE; /* keep going */ 00606 } 00607 00608 /** 00609 * @internal List all keys and values in the config. 00610 * @param conf The config 00611 * @param err To be filled in if an error occurs 00612 * @return a dict with config properties and values 00613 */ 00614 static GTree * 00615 xmms_config_client_list_values (xmms_config_t *conf, xmms_error_t *err) 00616 { 00617 GTree *ret; 00618 00619 ret = g_tree_new_full (compare_key, NULL, 00620 g_free, (GDestroyNotify)xmmsv_unref); 00621 00622 g_mutex_lock (conf->mutex); 00623 g_tree_foreach (conf->properties, 00624 (GTraverseFunc) xmms_config_foreach_dict, 00625 (gpointer) ret); 00626 g_mutex_unlock (conf->mutex); 00627 00628 return ret; 00629 } 00630 00631 /** 00632 * @internal Look for a key in the config and return its value as a string 00633 * @param conf The config 00634 * @param key The key to look for 00635 * @param err To be filled in if an error occurs 00636 * @return The value of the key, or NULL if not found 00637 */ 00638 static gchar * 00639 xmms_config_client_get_value (xmms_config_t *conf, const gchar *key, 00640 xmms_error_t *err) 00641 { 00642 return g_strdup (xmms_config_property_lookup_get_string (conf, key, err)); 00643 } 00644 00645 /** 00646 * @internal Destroy a config object 00647 * @param object The object to destroy 00648 */ 00649 static void 00650 xmms_config_destroy (xmms_object_t *object) 00651 { 00652 xmms_config_t *config = (xmms_config_t *)object; 00653 00654 g_mutex_free (config->mutex); 00655 00656 g_tree_destroy (config->properties); 00657 00658 xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED); 00659 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_CONFIG); 00660 } 00661 00662 static gint 00663 compare_key (gconstpointer a, gconstpointer b, gpointer user_data) 00664 { 00665 return strcmp ((gchar *) a, (gchar *) b); 00666 } 00667 00668 static GTree * 00669 create_tree (void) 00670 { 00671 return g_tree_new_full (compare_key, NULL, g_free, 00672 (GDestroyNotify) __int_xmms_object_unref); 00673 } 00674 00675 /** 00676 * @internal Clear data in a config object 00677 * @param config The config object to clear 00678 */ 00679 static void 00680 clear_config (xmms_config_t *config) 00681 { 00682 g_tree_destroy (config->properties); 00683 config->properties = create_tree (); 00684 00685 config->version = XMMS_CONFIG_VERSION; 00686 00687 g_free (config->value_name); 00688 config->value_name = NULL; 00689 } 00690 00691 /** 00692 * @internal Initialize and parse the config file. Resets to default config 00693 * on parse error. 00694 * @param[in] filename The absolute path to a config file as a string. 00695 */ 00696 void 00697 xmms_config_init (const gchar *filename) 00698 { 00699 GMarkupParser pars; 00700 GMarkupParseContext *ctx; 00701 xmms_config_t *config; 00702 int ret, fd = -1; 00703 gboolean parserr = FALSE, eof = FALSE; 00704 00705 config = xmms_object_new (xmms_config_t, xmms_config_destroy); 00706 config->mutex = g_mutex_new (); 00707 config->filename = filename; 00708 00709 config->properties = create_tree (); 00710 00711 config->version = 0; 00712 global_config = config; 00713 00714 xmms_ipc_object_register (XMMS_IPC_OBJECT_CONFIG, XMMS_OBJECT (config)); 00715 xmms_ipc_broadcast_register (XMMS_OBJECT (config), 00716 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED); 00717 00718 memset (&pars, 0, sizeof (pars)); 00719 00720 pars.start_element = xmms_config_parse_start; 00721 pars.end_element = xmms_config_parse_end; 00722 pars.text = xmms_config_parse_text; 00723 00724 if (g_file_test (filename, G_FILE_TEST_EXISTS)) { 00725 fd = open (filename, O_RDONLY); 00726 } 00727 00728 if (fd > -1) { 00729 config->is_parsing = TRUE; 00730 config->states = g_queue_new (); 00731 config->sections = g_queue_new (); 00732 ctx = g_markup_parse_context_new (&pars, 0, config, NULL); 00733 00734 while ((!eof) && (!parserr)) { 00735 GError *error = NULL; 00736 gchar buffer[1024]; 00737 00738 ret = read (fd, buffer, 1024); 00739 if (ret < 1) { 00740 g_markup_parse_context_end_parse (ctx, &error); 00741 if (error) { 00742 xmms_log_error ("Cannot parse config file: %s", 00743 error->message); 00744 g_error_free (error); 00745 error = NULL; 00746 parserr = TRUE; 00747 } 00748 eof = TRUE; 00749 } 00750 00751 g_markup_parse_context_parse (ctx, buffer, ret, &error); 00752 if (error) { 00753 xmms_log_error ("Cannot parse config file: %s", 00754 error->message); 00755 g_error_free (error); 00756 error = NULL; 00757 parserr = TRUE; 00758 } 00759 /* check config file version, assumes that g_markup_context_parse 00760 * above managed to parse the <xmms> element during the first 00761 * iteration of this loop */ 00762 if (XMMS_CONFIG_VERSION > config->version) { 00763 clear_config (config); 00764 break; 00765 } 00766 } 00767 00768 close (fd); 00769 g_markup_parse_context_free (ctx); 00770 00771 while (!g_queue_is_empty (config->sections)) { 00772 g_free (g_queue_pop_head (config->sections)); 00773 } 00774 00775 g_queue_free (config->states); 00776 g_queue_free (config->sections); 00777 00778 config->is_parsing = FALSE; 00779 } else { 00780 xmms_log_info ("No configfile specified, using default values."); 00781 } 00782 00783 if (parserr) { 00784 xmms_log_info ("The config file could not be parsed, reverting to default configuration.."); 00785 clear_config (config); 00786 } 00787 00788 xmms_object_cmd_add (XMMS_OBJECT (config), 00789 XMMS_IPC_CMD_SETVALUE, 00790 XMMS_CMD_FUNC (setvalue)); 00791 xmms_object_cmd_add (XMMS_OBJECT (config), 00792 XMMS_IPC_CMD_GETVALUE, 00793 XMMS_CMD_FUNC (getvalue)); 00794 xmms_object_cmd_add (XMMS_OBJECT (config), 00795 XMMS_IPC_CMD_LISTVALUES, 00796 XMMS_CMD_FUNC (listvalues)); 00797 xmms_object_cmd_add (XMMS_OBJECT (config), 00798 XMMS_IPC_CMD_REGVALUE, 00799 XMMS_CMD_FUNC (regvalue)); 00800 } 00801 00802 /** 00803 * @internal Shut down the config layer - free memory from the global 00804 * configuration. 00805 */ 00806 void 00807 xmms_config_shutdown () 00808 { 00809 xmms_object_unref (global_config); 00810 00811 } 00812 00813 static gboolean 00814 dump_tree (gchar *current_key, xmms_config_property_t *prop, 00815 dump_tree_data_t *data) 00816 { 00817 gchar *prop_name, section[256]; 00818 gchar *dot = NULL, *current_last_dot, *start = current_key; 00819 00820 prop_name = strrchr (current_key, '.'); 00821 00822 /* check whether we need to open a new section. 00823 * this is always the case if data->prev_key == NULL. 00824 * but if the sections of the last key and the current key differ, 00825 * we also need to do that. 00826 */ 00827 if (data->prev_key) { 00828 gchar *c = current_key, *o = data->prev_key; 00829 gsize dots = 0; 00830 00831 /* position c and o at the respective ends of the common 00832 * prefixes of the previous and the current key. 00833 */ 00834 while (*c && *o && *c == *o) { 00835 c++; 00836 o++; 00837 00838 if (*c == '.') 00839 start = c + 1; 00840 }; 00841 00842 /* from this position on, count the number of dots in the 00843 * previous key (= number of dots that are present in the 00844 * previous key, but no the current key). 00845 */ 00846 while (*o) { 00847 if (*o == '.') 00848 dots++; 00849 00850 o++; 00851 }; 00852 00853 /* we'll close the previous key's sections now, so we don't 00854 * have to worry about it next time this function is called. 00855 */ 00856 if (dots) 00857 data->prev_key = NULL; 00858 00859 while (dots--) { 00860 /* decrease indent level */ 00861 data->indent[--data->indent_level] = '\0'; 00862 00863 fprintf (data->fp, "%s</section>\n", data->indent); 00864 } 00865 } 00866 00867 /* open section tags */ 00868 dot = strchr (start, '.'); 00869 current_last_dot = start - 1; 00870 00871 while (dot) { 00872 strncpy (section, current_last_dot + 1, dot - current_last_dot + 1); 00873 section[dot - current_last_dot - 1] = 0; 00874 00875 fprintf (data->fp, "%s<section name=\"%s\">\n", 00876 data->indent, section); 00877 00878 /* increase indent level */ 00879 g_assert (data->indent_level < 127); 00880 data->indent[data->indent_level] = '\t'; 00881 data->indent[++data->indent_level] = '\0'; 00882 00883 current_last_dot = dot; 00884 dot = strchr (dot + 1, '.'); 00885 }; 00886 00887 data->prev_key = current_key; 00888 00889 fprintf (data->fp, "%s<property name=\"%s\">%s</property>\n", 00890 data->indent, prop_name + 1, 00891 xmms_config_property_get_string (prop)); 00892 00893 return FALSE; /* keep going */ 00894 } 00895 00896 /** 00897 * @internal Save the global configuration to disk. 00898 * @param file Absolute path to configfile. This will be overwritten. 00899 * @return TRUE on success. 00900 */ 00901 gboolean 00902 xmms_config_save (void) 00903 { 00904 FILE *fp = NULL; 00905 dump_tree_data_t data; 00906 00907 g_return_val_if_fail (global_config, FALSE); 00908 00909 /* don't try to save config while it's being read */ 00910 if (global_config->is_parsing) 00911 return FALSE; 00912 00913 if (!(fp = fopen (global_config->filename, "w"))) { 00914 xmms_log_error ("Couldn't open %s for writing.", 00915 global_config->filename); 00916 return FALSE; 00917 } 00918 00919 fprintf (fp, "<?xml version=\"1.0\"?>\n<xmms version=\"%i\">\n", 00920 XMMS_CONFIG_VERSION); 00921 00922 data.fp = fp; 00923 data.state = XMMS_CONFIG_STATE_START; 00924 data.prev_key = NULL; 00925 00926 strcpy (data.indent, "\t"); 00927 data.indent_level = 1; 00928 00929 g_tree_foreach (global_config->properties, 00930 (GTraverseFunc) dump_tree, &data); 00931 00932 /* close the remaining section tags. the final indent level 00933 * was started with the opening xmms tag, so the loop condition 00934 * is '> 1' here rather than '> 0'. 00935 */ 00936 while (data.indent_level > 1) { 00937 /* decrease indent level */ 00938 data.indent[--data.indent_level] = '\0'; 00939 00940 fprintf (fp, "%s</section>\n", data.indent); 00941 } 00942 00943 fprintf (fp, "</xmms>\n"); 00944 fclose (fp); 00945 00946 return TRUE; 00947 } 00948 00949 /* 00950 * Value manipulation 00951 */ 00952 00953 /** 00954 * @internal Destroy a config value 00955 * @param object The object to destroy 00956 */ 00957 static void 00958 xmms_config_property_destroy (xmms_object_t *object) 00959 { 00960 xmms_config_property_t *prop = (xmms_config_property_t *) object; 00961 00962 /* don't free val->name here, it's taken care of in 00963 * xmms_config_destroy() 00964 */ 00965 g_free (prop->value); 00966 } 00967 00968 /** 00969 * @internal Create a new config value 00970 * @param name The name of the new config value 00971 */ 00972 static xmms_config_property_t * 00973 xmms_config_property_new (const gchar *name) 00974 { 00975 xmms_config_property_t *ret; 00976 00977 ret = xmms_object_new (xmms_config_property_t, xmms_config_property_destroy); 00978 ret->name = name; 00979 00980 return ret; 00981 } 00982 00983 /** 00984 * @internal Register a client config value 00985 * @param config The config 00986 * @param name The name of the config value 00987 * @param def_value The default value to use 00988 * @param error To be filled in if an error occurs 00989 * @return The full path to the config value registered 00990 */ 00991 static gchar * 00992 xmms_config_client_register_value (xmms_config_t *config, 00993 const gchar *name, 00994 const gchar *def_value, 00995 xmms_error_t *error) 00996 { 00997 gchar *tmp; 00998 tmp = g_strdup_printf ("clients.%s", name); 00999 xmms_config_property_register (tmp, def_value, NULL, NULL); 01000 return tmp; 01001 } 01002 01003 /** @} */