00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
00036
00037
00038
00039
00040
00041
00042
00043
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
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087 struct xmms_config_St {
00088 xmms_object_t obj;
00089
00090 const gchar *filename;
00091 GTree *properties;
00092
00093
00094 GMutex *mutex;
00095
00096
00097 gboolean is_parsing;
00098 GQueue *states;
00099 GQueue *sections;
00100 gchar *value_name;
00101 guint version;
00102 };
00103
00104
00105
00106
00107 struct xmms_config_property_St {
00108 xmms_object_t obj;
00109
00110
00111 const gchar *name;
00112
00113 gchar *value;
00114 };
00115
00116
00117
00118
00119
00120
00121
00122 static xmms_config_t *global_config;
00123
00124
00125
00126
00127 #define XMMS_CONFIG_VERSION 2
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
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
00168
00169
00170
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
00187
00188
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
00200
00201
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
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
00234
00235
00236 xmms_config_save ();
00237 }
00238
00239
00240
00241
00242
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
00253
00254
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
00268
00269
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
00283
00284
00285
00286
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
00305
00306
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
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
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
00368
00369
00370
00371
00372
00373
00374
00375
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
00401
00402
00403
00404
00405
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
00424
00425
00426
00427
00428
00429
00430
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
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
00493
00494
00495
00496
00497
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
00527
00528
00529
00530
00531
00532
00533
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
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
00570
00571
00572
00573
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
00593
00594
00595
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;
00604 }
00605
00606
00607
00608
00609
00610
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
00631
00632
00633
00634
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
00645
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
00675
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
00691
00692
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
00758
00759
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
00802
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
00821
00822
00823
00824
00825 if (data->prev_key) {
00826 gchar *c = current_key, *o = data->prev_key;
00827 gsize dots = 0;
00828
00829
00830
00831
00832 while (*c && *o && *c == *o) {
00833 c++;
00834 o++;
00835
00836 if (*c == '.')
00837 start = c + 1;
00838 };
00839
00840
00841
00842
00843
00844 while (*o) {
00845 if (*o == '.')
00846 dots++;
00847
00848 o++;
00849 };
00850
00851
00852
00853
00854 if (dots)
00855 data->prev_key = NULL;
00856
00857 while (dots--) {
00858
00859 data->indent[--data->indent_level] = '\0';
00860
00861 fprintf (data->fp, "%s</section>\n", data->indent);
00862 }
00863 }
00864
00865
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
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;
00892 }
00893
00894
00895
00896
00897
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
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
00931
00932
00933
00934 while (data.indent_level > 1) {
00935
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
00949
00950
00951
00952
00953
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
00961
00962
00963 g_free (prop->value);
00964 }
00965
00966
00967
00968
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
00983
00984
00985
00986
00987
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