• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/config.c

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

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1