XMMS2
config.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2009 XMMS2 Team
3  *
4  * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  */
17 
18 #include <glib.h>
19 
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 
28 #include "xmmsc/xmmsc_idnumbers.h"
29 #include "xmmspriv/xmms_config.h"
30 #include "xmmspriv/xmms_utils.h"
31 #include "xmms/xmms_ipc.h"
32 #include "xmms/xmms_log.h"
33 
34 /*
35 #include "xmms/util.h"
36 #include "xmms/xmms.h"
37 #include "xmms/object.h"
38 #include "xmms/signal_xmms.h"
39 #include "xmms/plugin.h"
40 #include "xmms/ipc.h"
41 */
42 
43 /** @internal */
44 typedef enum {
50 
51 typedef struct dump_tree_data_St {
52  FILE *fp;
54 
55  gchar indent[128];
56  guint indent_level;
57 
58  gchar *prev_key;
60 
61 static GTree *xmms_config_client_list_values (xmms_config_t *conf, xmms_error_t *err);
62 static xmms_config_property_t *xmms_config_property_new (const gchar *name);
63 static gchar *xmms_config_client_get_value (xmms_config_t *conf, const gchar *key, xmms_error_t *err);
64 static gchar *xmms_config_client_register_value (xmms_config_t *config, const gchar *name, const gchar *def_value, xmms_error_t *error);
65 static gint compare_key (gconstpointer a, gconstpointer b, gpointer user_data);
66 static void xmms_config_client_set_value (xmms_config_t *conf, const gchar *key, const gchar *value, xmms_error_t *err);
67 
68 XMMS_CMD_DEFINE (setvalue, xmms_config_client_set_value, xmms_config_t *, NONE, STRING, STRING);
69 XMMS_CMD_DEFINE (listvalues, xmms_config_client_list_values, xmms_config_t *, DICT, NONE, NONE);
70 XMMS_CMD_DEFINE (getvalue, xmms_config_client_get_value, xmms_config_t *, STRING, STRING, NONE);
71 XMMS_CMD_DEFINE (regvalue, xmms_config_client_register_value, xmms_config_t *, STRING, STRING, STRING);
72 
73 /**
74  * @defgroup Config Config
75  * @brief Controls configuration for the server.
76  *
77  * The configuration is saved to, and loaded from an XML file. It's split into
78  * plugin, client and core parts. This documents the configuration for parts
79  * inside the server. For plugin config see each server object's documentation.
80  *
81  * @ingroup XMMSServer
82  * @{
83  */
84 
85 /**
86  * Global parsed config
87  */
88 struct xmms_config_St {
89  xmms_object_t obj;
90 
91  const gchar *filename;
92  GTree *properties;
93 
94  /* Lock on globals are great! */
95  GMutex *mutex;
96 
97  /* parsing */
98  gboolean is_parsing;
99  GQueue *states;
100  GQueue *sections;
101  gchar *value_name;
102  guint version;
103 };
104 
105 /**
106  * A config property in the configuration file
107  */
108 struct xmms_config_property_St {
109  xmms_object_t obj;
110 
111  /** Name of the config directive */
112  const gchar *name;
113  /** The data */
114  gchar *value;
115 };
116 
117 /**
118  * Global config
119  * Since there can only be one configuration per server
120  * we can have the convenience of having it as a global variable.
121  */
122 
123 static xmms_config_t *global_config;
124 
125 /**
126  * Config file version
127  */
128 #define XMMS_CONFIG_VERSION 2
129 
130 /**
131  * @}
132  * @addtogroup Config
133  * @{
134  */
135 
136 /**
137  * Config functions
138  */
139 
140 /**
141  * Lookup config key and return its associated value as a string.
142  * This is a convenient function to make it easier to get a configuration value
143  * rather than having to call #xmms_config_property_get_string separately.
144  *
145  * @param conf Global config
146  * @param key Configuration property to lookup
147  * @param err if error occurs this will be filled in
148  *
149  * @return A string with the value. If the value is an int it will return NULL
150  */
151 const gchar *
153  xmms_error_t *err)
154 {
156 
157  prop = xmms_config_lookup (key);
158  if (!prop) {
159  xmms_error_set (err, XMMS_ERROR_NOENT,
160  "Trying to get non-existent property");
161  return NULL;
162  }
163 
164  return xmms_config_property_get_string (prop);
165 }
166 
167 /**
168  * Look up a config key from the global config
169  * @param path A configuration path. Could be core.myconfig or
170  * effect.foo.myconfig
171  * @return An #xmms_config_property_t
172  */
174 xmms_config_lookup (const gchar *path)
175 {
177  g_return_val_if_fail (global_config, NULL);
178 
179  g_mutex_lock (global_config->mutex);
180  prop = g_tree_lookup (global_config->properties, path);
181  g_mutex_unlock (global_config->mutex);
182 
183  return prop;
184 }
185 
186 /**
187  * Get the name of a config property.
188  * @param prop The config property
189  * @return Name of config property
190  */
191 const gchar *
193 {
194  g_return_val_if_fail (prop, NULL);
195 
196  return prop->name;
197 }
198 
199 /**
200  * Set the data of the config property to a new value
201  * @param prop The config property
202  * @param data The value to set
203  */
204 void
206 {
207  GTree *dict;
208 
209  g_return_if_fail (prop);
210  g_return_if_fail (data);
211 
212  /* check whether the value changed at all */
213  if (prop->value && !strcmp (prop->value, data))
214  return;
215 
216  g_free (prop->value);
217  prop->value = g_strdup (data);
220  (gpointer) data);
221 
222  dict = g_tree_new_full (compare_key, NULL,
223  NULL, (GDestroyNotify) xmmsv_unref);
224  g_tree_insert (dict, (gchar *) prop->name,
225  xmmsv_new_string (prop->value));
226 
227  xmms_object_emit_f (XMMS_OBJECT (global_config),
230  dict);
231 
232  g_tree_destroy (dict);
233 
234  /* save the database to disk, so we don't lose any data
235  * if the daemon crashes
236  */
237  xmms_config_save ();
238 }
239 
240 /**
241  * Return the value of a config property as a string
242  * @param prop The config property
243  * @return value as string
244  */
245 const gchar *
247 {
248  g_return_val_if_fail (prop, NULL);
249  return prop->value;
250 }
251 
252 /**
253  * Return the value of a config property as an int
254  * @param prop The config property
255  * @return value as int
256  */
257 gint
259 {
260  g_return_val_if_fail (prop, 0);
261  if (prop->value)
262  return atoi (prop->value);
263 
264  return 0;
265 }
266 
267 /**
268  * Return the value of a config property as a float
269  * @param prop The config property
270  * @return value as float
271  */
272 gfloat
274 {
275  g_return_val_if_fail (prop, 0.0);
276  if (prop->value)
277  return atof (prop->value);
278 
279  return 0.0;
280 }
281 
282 /**
283  * Set a callback function for a config property.
284  * This will be called each time the property's value changes.
285  * @param prop The config property
286  * @param cb The callback to set
287  * @param userdata Data to pass on to the callback
288  */
289 void
292  gpointer userdata)
293 {
294  g_return_if_fail (prop);
295 
296  if (!cb)
297  return;
298 
301  (xmms_object_handler_t) cb, userdata);
302 }
303 
304 /**
305  * Remove a callback from a config property
306  * @param prop The config property
307  * @param cb The callback to remove
308  */
309 void
312  gpointer userdata)
313 {
314  g_return_if_fail (prop);
315 
316  if (!cb)
317  return;
318 
321 }
322 
323 /**
324  * Register a new config property. This should be called from the init code
325  * as XMMS2 won't allow set/get on properties that haven't been registered.
326  *
327  * @param path The path in the config tree.
328  * @param default_value If the value was not found in the configfile, what
329  * should we use?
330  * @param cb A callback function that will be called if the value is changed by
331  * the client. Can be set to NULL.
332  * @param userdata Data to pass to the callback function.
333  * @return A newly allocated #xmms_config_property_t for the registered
334  * property.
335  */
337 xmms_config_property_register (const gchar *path,
338  const gchar *default_value,
340  gpointer userdata)
341 {
342 
344 
345  g_mutex_lock (global_config->mutex);
346 
347  prop = g_tree_lookup (global_config->properties, path);
348  if (!prop) {
349  prop = xmms_config_property_new (g_strdup (path));
350 
351  xmms_config_property_set_data (prop, (gchar *) default_value);
352  g_tree_replace (global_config->properties,
353  (gchar *) prop->name, prop);
354  }
355 
356  if (cb) {
357  xmms_config_property_callback_set (prop, cb, userdata);
358  }
359 
360  g_mutex_unlock (global_config->mutex);
361 
362  return prop;
363 }
364 
365 /**
366  * @}
367  *
368  * @if internal
369  * @addtogroup Config
370  * @{
371  */
372 
373 /**
374  * @internal Get the current parser state for the given element name
375  * @param[in] name Element name to match to a state
376  * @return Parser state matching element name
377  */
379 get_current_state (const gchar *name)
380 {
381  static struct {
382  const gchar *name;
384  } *ptr, lookup[] = {
385  {"xmms", XMMS_CONFIG_STATE_START},
386  {"section", XMMS_CONFIG_STATE_SECTION},
387  {"property", XMMS_CONFIG_STATE_PROPERTY},
389  };
390 
391  for (ptr = lookup; ptr && ptr->name; ptr++) {
392  if (!strcmp (ptr->name, name)) {
393  return ptr->state;
394  }
395  }
396 
398 }
399 
400 /**
401  * @internal Look for the value associated with an attribute name, given lists
402  * of attribute names and attribute values.
403  * @param[in] names List of attribute names
404  * @param[in] values List of attribute values matching up to names
405  * @param[in] needle Attribute name to look for
406  * @return The attribute value, or NULL if not found
407  */
408 static const gchar *
409 lookup_attribute (const gchar **names, const gchar **values,
410  const gchar *needle)
411 {
412  const gchar **n, **v;
413 
414  for (n = names, v = values; *n && *v; n++, v++) {
415  if (!strcmp ((gchar *) *n, needle)) {
416  return *v;
417  }
418  }
419 
420  return NULL;
421 }
422 
423 /**
424  * @internal Parse start tag in config file. This function is called whenever
425  * a start tag is encountered by the GMarkupParser from #xmms_config_init
426  * @param ctx The parser context.
427  * @param name The name of the element encountered
428  * @param attr_name List of attribute names in tag
429  * @param attr_data List of attribute data in tag
430  * @param userdata User data - In this case, the global config
431  * @param error GError to be filled in if an error is encountered
432  */
433 static void
434 xmms_config_parse_start (GMarkupParseContext *ctx,
435  const gchar *name,
436  const gchar **attr_name,
437  const gchar **attr_data,
438  gpointer userdata,
439  GError **error)
440 {
441  xmms_config_t *config = userdata;
443  const gchar *attr;
444 
445  state = get_current_state (name);
446  g_queue_push_head (config->states, GINT_TO_POINTER (state));
447 
448  switch (state) {
450  *error = g_error_new (G_MARKUP_ERROR,
451  G_MARKUP_ERROR_UNKNOWN_ELEMENT,
452  "Unknown element '%s'", name);
453  return;
455  /* check config version here */
456  attr = lookup_attribute (attr_name, attr_data, "version");
457  if (attr) {
458  if (strcmp (attr, "0.02") == 0) {
459  config->version = 2;
460  } else {
461  config->version = atoi (attr);
462  }
463  }
464  return;
465  default:
466  break;
467  }
468 
469  attr = lookup_attribute (attr_name, attr_data, "name");
470  if (!attr) {
471  *error = g_error_new (G_MARKUP_ERROR,
472  G_MARKUP_ERROR_INVALID_CONTENT,
473  "Attribute 'name' missing");
474  return;
475  }
476 
477  switch (state) {
479  g_queue_push_head (config->sections, g_strdup (attr));
480 
481  break;
483  g_free (config->value_name);
484  config->value_name = g_strdup (attr);
485 
486  break;
487  default:
488  break;
489  }
490 }
491 
492 /**
493  * @internal Parse end tag in config file. This function is called whenever
494  * an end tag is encountered by the GMarkupParser from #xmms_config_init
495  * @param ctx The parser context.
496  * @param name The name of the element encountered
497  * @param userdata User data - In this case, the global config
498  * @param error GError to be filled in if an error is encountered
499  */
500 static void
501 xmms_config_parse_end (GMarkupParseContext *ctx,
502  const gchar *name,
503  gpointer userdata,
504  GError **error)
505 {
506  xmms_config_t *config = userdata;
508 
509  state = GPOINTER_TO_INT (g_queue_pop_head (config->states));
510 
511  switch (state) {
513  g_free (g_queue_pop_head (config->sections));
514 
515  break;
517  g_free (config->value_name);
518  config->value_name = NULL;
519 
520  break;
521  default:
522  break;
523  }
524 }
525 
526 /**
527  * @internal Parse text in config file. This function is called whenever
528  * text (anything between start and end tags) is encountered by the
529  * GMarkupParser from #xmms_config_init
530  * @param ctx The parser context.
531  * @param text The text
532  * @param text_len Length of the text
533  * @param userdata User data - In this case, the global config
534  * @param error GError to be filled in if an error is encountered
535  */
536 static void
537 xmms_config_parse_text (GMarkupParseContext *ctx,
538  const gchar *text,
539  gsize text_len,
540  gpointer userdata,
541  GError **error)
542 {
543  xmms_config_t *config = userdata;
546  GList *l;
547  gchar key[256] = "";
548  gsize siz = sizeof (key);
549 
550  state = GPOINTER_TO_INT (g_queue_peek_head (config->states));
551 
552  if (state != XMMS_CONFIG_STATE_PROPERTY)
553  return;
554 
555  /* assemble the config key, based on the traversed sections */
556  for (l = config->sections->tail; l; l = l->prev) {
557  g_strlcat (key, l->data, siz);
558  g_strlcat (key, ".", siz);
559  }
560 
561  g_strlcat (key, config->value_name, siz);
562 
563  prop = xmms_config_property_new (g_strdup (key));
564  xmms_config_property_set_data (prop, (gchar *) text);
565 
566  g_tree_replace (config->properties, (gchar *) prop->name, prop);
567 }
568 
569 /**
570  * @internal Set a key to a new value
571  * @param conf The config
572  * @param key The key to look for
573  * @param value The value to set the key to
574  * @param err To be filled in if an error occurs
575  */
576 static void
577 xmms_config_client_set_value (xmms_config_t *conf,
578  const gchar *key, const gchar *value,
579  xmms_error_t *err)
580 {
582 
583  prop = xmms_config_lookup (key);
584  if (prop) {
585  xmms_config_property_set_data (prop, value);
586  } else {
587  xmms_error_set (err, XMMS_ERROR_NOENT,
588  "Trying to set non-existent config property");
589  }
590 
591 }
592 
593 /**
594  * @internal Convert global config properties dict to a normal dict
595  * @param key The dict key
596  * @param property An xmms_config_property_t
597  * @param udata The dict to store configvals
598  */
599 static gboolean
600 xmms_config_foreach_dict (gpointer key, xmms_config_property_t *prop,
601  GTree *dict)
602 {
603  g_tree_insert (dict, g_strdup (key), xmmsv_new_string (prop->value));
604 
605  return FALSE; /* keep going */
606 }
607 
608 /**
609  * @internal List all keys and values in the config.
610  * @param conf The config
611  * @param err To be filled in if an error occurs
612  * @return a dict with config properties and values
613  */
614 static GTree *
615 xmms_config_client_list_values (xmms_config_t *conf, xmms_error_t *err)
616 {
617  GTree *ret;
618 
619  ret = g_tree_new_full (compare_key, NULL,
620  g_free, (GDestroyNotify)xmmsv_unref);
621 
622  g_mutex_lock (conf->mutex);
623  g_tree_foreach (conf->properties,
624  (GTraverseFunc) xmms_config_foreach_dict,
625  (gpointer) ret);
626  g_mutex_unlock (conf->mutex);
627 
628  return ret;
629 }
630 
631 /**
632  * @internal Look for a key in the config and return its value as a string
633  * @param conf The config
634  * @param key The key to look for
635  * @param err To be filled in if an error occurs
636  * @return The value of the key, or NULL if not found
637  */
638 static gchar *
639 xmms_config_client_get_value (xmms_config_t *conf, const gchar *key,
640  xmms_error_t *err)
641 {
642  return g_strdup (xmms_config_property_lookup_get_string (conf, key, err));
643 }
644 
645 /**
646  * @internal Destroy a config object
647  * @param object The object to destroy
648  */
649 static void
650 xmms_config_destroy (xmms_object_t *object)
651 {
652  xmms_config_t *config = (xmms_config_t *)object;
653 
654  g_mutex_free (config->mutex);
655 
656  g_tree_destroy (config->properties);
657 
660 }
661 
662 static gint
663 compare_key (gconstpointer a, gconstpointer b, gpointer user_data)
664 {
665  return strcmp ((gchar *) a, (gchar *) b);
666 }
667 
668 static GTree *
669 create_tree (void)
670 {
671  return g_tree_new_full (compare_key, NULL, g_free,
672  (GDestroyNotify) __int_xmms_object_unref);
673 }
674 
675 /**
676  * @internal Clear data in a config object
677  * @param config The config object to clear
678  */
679 static void
680 clear_config (xmms_config_t *config)
681 {
682  g_tree_destroy (config->properties);
683  config->properties = create_tree ();
684 
685  config->version = XMMS_CONFIG_VERSION;
686 
687  g_free (config->value_name);
688  config->value_name = NULL;
689 }
690 
691 /**
692  * @internal Initialize and parse the config file. Resets to default config
693  * on parse error.
694  * @param[in] filename The absolute path to a config file as a string.
695  */
696 void
697 xmms_config_init (const gchar *filename)
698 {
699  GMarkupParser pars;
700  GMarkupParseContext *ctx;
701  xmms_config_t *config;
702  int ret, fd = -1;
703  gboolean parserr = FALSE, eof = FALSE;
704 
705  config = xmms_object_new (xmms_config_t, xmms_config_destroy);
706  config->mutex = g_mutex_new ();
707  config->filename = filename;
708 
709  config->properties = create_tree ();
710 
711  config->version = 0;
712  global_config = config;
713 
717 
718  memset (&pars, 0, sizeof (pars));
719 
720  pars.start_element = xmms_config_parse_start;
721  pars.end_element = xmms_config_parse_end;
722  pars.text = xmms_config_parse_text;
723 
724  if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
725  fd = open (filename, O_RDONLY);
726  }
727 
728  if (fd > -1) {
729  config->is_parsing = TRUE;
730  config->states = g_queue_new ();
731  config->sections = g_queue_new ();
732  ctx = g_markup_parse_context_new (&pars, 0, config, NULL);
733 
734  while ((!eof) && (!parserr)) {
735  GError *error = NULL;
736  gchar buffer[1024];
737 
738  ret = read (fd, buffer, 1024);
739  if (ret < 1) {
740  g_markup_parse_context_end_parse (ctx, &error);
741  if (error) {
742  xmms_log_error ("Cannot parse config file: %s",
743  error->message);
744  g_error_free (error);
745  error = NULL;
746  parserr = TRUE;
747  }
748  eof = TRUE;
749  }
750 
751  g_markup_parse_context_parse (ctx, buffer, ret, &error);
752  if (error) {
753  xmms_log_error ("Cannot parse config file: %s",
754  error->message);
755  g_error_free (error);
756  error = NULL;
757  parserr = TRUE;
758  }
759  /* check config file version, assumes that g_markup_context_parse
760  * above managed to parse the <xmms> element during the first
761  * iteration of this loop */
762  if (XMMS_CONFIG_VERSION > config->version) {
763  clear_config (config);
764  break;
765  }
766  }
767 
768  close (fd);
769  g_markup_parse_context_free (ctx);
770 
771  while (!g_queue_is_empty (config->sections)) {
772  g_free (g_queue_pop_head (config->sections));
773  }
774 
775  g_queue_free (config->states);
776  g_queue_free (config->sections);
777 
778  config->is_parsing = FALSE;
779  } else {
780  xmms_log_info ("No configfile specified, using default values.");
781  }
782 
783  if (parserr) {
784  xmms_log_info ("The config file could not be parsed, reverting to default configuration..");
785  clear_config (config);
786  }
787 
790  XMMS_CMD_FUNC (setvalue));
793  XMMS_CMD_FUNC (getvalue));
796  XMMS_CMD_FUNC (listvalues));
799  XMMS_CMD_FUNC (regvalue));
800 }
801 
802 /**
803  * @internal Shut down the config layer - free memory from the global
804  * configuration.
805  */
806 void
808 {
809  xmms_object_unref (global_config);
810 
811 }
812 
813 static gboolean
814 dump_tree (gchar *current_key, xmms_config_property_t *prop,
815  dump_tree_data_t *data)
816 {
817  gchar *prop_name, section[256];
818  gchar *dot = NULL, *current_last_dot, *start = current_key;
819 
820  prop_name = strrchr (current_key, '.');
821 
822  /* check whether we need to open a new section.
823  * this is always the case if data->prev_key == NULL.
824  * but if the sections of the last key and the current key differ,
825  * we also need to do that.
826  */
827  if (data->prev_key) {
828  gchar *c = current_key, *o = data->prev_key;
829  gsize dots = 0;
830 
831  /* position c and o at the respective ends of the common
832  * prefixes of the previous and the current key.
833  */
834  while (*c && *o && *c == *o) {
835  c++;
836  o++;
837 
838  if (*c == '.')
839  start = c + 1;
840  };
841 
842  /* from this position on, count the number of dots in the
843  * previous key (= number of dots that are present in the
844  * previous key, but no the current key).
845  */
846  while (*o) {
847  if (*o == '.')
848  dots++;
849 
850  o++;
851  };
852 
853  /* we'll close the previous key's sections now, so we don't
854  * have to worry about it next time this function is called.
855  */
856  if (dots)
857  data->prev_key = NULL;
858 
859  while (dots--) {
860  /* decrease indent level */
861  data->indent[--data->indent_level] = '\0';
862 
863  fprintf (data->fp, "%s</section>\n", data->indent);
864  }
865  }
866 
867  /* open section tags */
868  dot = strchr (start, '.');
869  current_last_dot = start - 1;
870 
871  while (dot) {
872  strncpy (section, current_last_dot + 1, dot - current_last_dot + 1);
873  section[dot - current_last_dot - 1] = 0;
874 
875  fprintf (data->fp, "%s<section name=\"%s\">\n",
876  data->indent, section);
877 
878  /* increase indent level */
879  g_assert (data->indent_level < 127);
880  data->indent[data->indent_level] = '\t';
881  data->indent[++data->indent_level] = '\0';
882 
883  current_last_dot = dot;
884  dot = strchr (dot + 1, '.');
885  };
886 
887  data->prev_key = current_key;
888 
889  fprintf (data->fp, "%s<property name=\"%s\">%s</property>\n",
890  data->indent, prop_name + 1,
892 
893  return FALSE; /* keep going */
894 }
895 
896 /**
897  * @internal Save the global configuration to disk.
898  * @param file Absolute path to configfile. This will be overwritten.
899  * @return TRUE on success.
900  */
901 gboolean
903 {
904  FILE *fp = NULL;
905  dump_tree_data_t data;
906 
907  g_return_val_if_fail (global_config, FALSE);
908 
909  /* don't try to save config while it's being read */
910  if (global_config->is_parsing)
911  return FALSE;
912 
913  if (!(fp = fopen (global_config->filename, "w"))) {
914  xmms_log_error ("Couldn't open %s for writing.",
915  global_config->filename);
916  return FALSE;
917  }
918 
919  fprintf (fp, "<?xml version=\"1.0\"?>\n<xmms version=\"%i\">\n",
921 
922  data.fp = fp;
923  data.state = XMMS_CONFIG_STATE_START;
924  data.prev_key = NULL;
925 
926  strcpy (data.indent, "\t");
927  data.indent_level = 1;
928 
929  g_tree_foreach (global_config->properties,
930  (GTraverseFunc) dump_tree, &data);
931 
932  /* close the remaining section tags. the final indent level
933  * was started with the opening xmms tag, so the loop condition
934  * is '> 1' here rather than '> 0'.
935  */
936  while (data.indent_level > 1) {
937  /* decrease indent level */
938  data.indent[--data.indent_level] = '\0';
939 
940  fprintf (fp, "%s</section>\n", data.indent);
941  }
942 
943  fprintf (fp, "</xmms>\n");
944  fclose (fp);
945 
946  return TRUE;
947 }
948 
949 /*
950  * Value manipulation
951  */
952 
953 /**
954  * @internal Destroy a config value
955  * @param object The object to destroy
956  */
957 static void
958 xmms_config_property_destroy (xmms_object_t *object)
959 {
961 
962  /* don't free val->name here, it's taken care of in
963  * xmms_config_destroy()
964  */
965  g_free (prop->value);
966 }
967 
968 /**
969  * @internal Create a new config value
970  * @param name The name of the new config value
971  */
972 static xmms_config_property_t *
973 xmms_config_property_new (const gchar *name)
974 {
976 
977  ret = xmms_object_new (xmms_config_property_t, xmms_config_property_destroy);
978  ret->name = name;
979 
980  return ret;
981 }
982 
983 /**
984  * @internal Register a client config value
985  * @param config The config
986  * @param name The name of the config value
987  * @param def_value The default value to use
988  * @param error To be filled in if an error occurs
989  * @return The full path to the config value registered
990  */
991 static gchar *
992 xmms_config_client_register_value (xmms_config_t *config,
993  const gchar *name,
994  const gchar *def_value,
995  xmms_error_t *error)
996 {
997  gchar *tmp;
998  tmp = g_strdup_printf ("clients.%s", name);
999  xmms_config_property_register (tmp, def_value, NULL, NULL);
1000  return tmp;
1001 }
1002 
1003 /** @} */
#define XMMS_CMD_FUNC(cmdid)
Definition: xmms_object.h:181
#define XMMS_OBJECT(p)
Definition: xmms_object.h:84
#define XMMS_CONFIG_VERSION
Config file version.
Definition: config.c:128
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed...
Definition: value.c:301
#define xmms_object_unref(obj)
Definition: xmms_object.h:193
gfloat xmms_config_property_get_float(const xmms_config_property_t *prop)
Return the value of a config property as a float.
Definition: config.c:273
void xmms_config_property_callback_remove(xmms_config_property_t *prop, xmms_object_handler_t cb, gpointer userdata)
Remove a callback from a config property.
Definition: config.c:310
void xmms_object_cmd_add(xmms_object_t *object, guint cmdid, const xmms_object_cmd_desc_t *desc)
Add a command that could be called from the client API to a object.
Definition: object.c:321
const gchar * xmms_config_property_get_name(const xmms_config_property_t *prop)
Get the name of a config property.
Definition: config.c:192
xmms_config_property_t * xmms_config_lookup(const gchar *path)
Look up a config key from the global config.
Definition: config.c:174
void xmms_ipc_broadcast_register(xmms_object_t *object, xmms_ipc_signals_t signalid)
Register a broadcast signal.
Definition: ipc.c:694
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:178
xmms_configparser_state_t
Definition: config.c:44
gboolean xmms_config_save(void)
Definition: config.c:902
void xmms_config_init(const gchar *filename)
Definition: config.c:697
gint xmms_config_property_get_int(const xmms_config_property_t *prop)
Return the value of a config property as an int.
Definition: config.c:258
struct dump_tree_data_St dump_tree_data_t
void xmms_config_property_set_data(xmms_config_property_t *prop, const gchar *data)
Set the data of the config property to a new value.
Definition: config.c:205
void xmms_object_emit(xmms_object_t *object, guint32 signalid, xmmsv_t *data)
Emit a signal and thus call all the handlers that are connected.
Definition: object.c:196
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
void xmms_config_property_callback_set(xmms_config_property_t *prop, xmms_object_handler_t cb, gpointer userdata)
Set a callback function for a config property.
Definition: config.c:290
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition: object.c:257
typedefG_BEGIN_DECLS struct xmms_config_St xmms_config_t
Definition: xmms_config.h:25
void __int_xmms_object_unref(xmms_object_t *object)
Definition: object.c:489
const gchar * xmms_config_property_get_string(const xmms_config_property_t *prop)
Return the value of a config property as a string.
Definition: config.c:246
#define XMMS_CMD_DEFINE(cmdid, realfunc, argtype0, _rettype, argtype1, argtype2)
Definition: xmms_object.h:176
void xmms_ipc_object_unregister(xmms_ipc_objects_t objectid)
Remove a object from the IPC core.
Definition: ipc.c:769
#define xmms_log_info(fmt,...)
Definition: xmms_log.h:34
void xmms_object_connect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Connect to a signal that is emitted by this object.
Definition: object.c:116
void xmms_ipc_broadcast_unregister(xmms_ipc_signals_t signalid)
Unregister a broadcast signal.
Definition: ipc.c:709
void xmms_object_disconnect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Disconnect from a signal.
Definition: object.c:147
const gchar * xmms_config_property_lookup_get_string(xmms_config_t *conf, const gchar *key, xmms_error_t *err)
Config functions.
Definition: config.c:152
void xmms_config_shutdown()
Definition: config.c:807
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:199
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
xmms_config_property_t * xmms_config_property_register(const gchar *path, const gchar *default_value, xmms_object_handler_t cb, gpointer userdata)
Register a new config property.
Definition: config.c:337
struct xmms_config_property_St xmms_config_property_t
Definition: xmms_config.h:26
void xmms_ipc_object_register(xmms_ipc_objects_t objectid, xmms_object_t *object)
Register a object to the IPC core.
Definition: ipc.c:758
void(* xmms_object_handler_t)(xmms_object_t *object, xmmsv_t *data, gpointer userdata)
Definition: xmms_object.h:67