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, 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, 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)xmms_object_cmd_value_unref);
00223 g_tree_insert (dict, (gchar *) prop->name,
00224 xmms_object_cmd_value_str_new (prop->value));
00225
00226 xmms_object_emit_f (XMMS_OBJECT (global_config),
00227 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED,
00228 XMMS_OBJECT_CMD_ARG_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),
00602 xmms_object_cmd_value_str_new (prop->value));
00603
00604 return FALSE;
00605 }
00606
00607
00608
00609
00610
00611
00612
00613 static GTree *
00614 xmms_config_listvalues (xmms_config_t *conf, xmms_error_t *err)
00615 {
00616 GTree *ret;
00617
00618 ret = g_tree_new_full (compare_key, NULL,
00619 g_free, (GDestroyNotify)xmms_object_cmd_value_unref);
00620
00621 g_mutex_lock (conf->mutex);
00622 g_tree_foreach (conf->properties,
00623 (GTraverseFunc) xmms_config_foreach_dict,
00624 (gpointer) ret);
00625 g_mutex_unlock (conf->mutex);
00626
00627 return ret;
00628 }
00629
00630
00631
00632
00633
00634
00635
00636
00637 static gchar *
00638 xmms_config_property_client_lookup (xmms_config_t *conf, gchar *key,
00639 xmms_error_t *err)
00640 {
00641 return g_strdup (xmms_config_property_lookup_get_string (conf, key, err));
00642 }
00643
00644
00645
00646
00647
00648 static void
00649 xmms_config_destroy (xmms_object_t *object)
00650 {
00651 xmms_config_t *config = (xmms_config_t *)object;
00652
00653 g_mutex_free (config->mutex);
00654
00655 g_tree_destroy (config->properties);
00656
00657 xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED);
00658 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_CONFIG);
00659 }
00660
00661 static gint
00662 compare_key (gconstpointer a, gconstpointer b, gpointer user_data)
00663 {
00664 return strcmp ((gchar *) a, (gchar *) b);
00665 }
00666
00667 static GTree *
00668 create_tree ()
00669 {
00670 return g_tree_new_full (compare_key, NULL, g_free,
00671 (GDestroyNotify) __int_xmms_object_unref);
00672 }
00673
00674
00675
00676
00677
00678 static void
00679 clear_config (xmms_config_t *config)
00680 {
00681 g_tree_destroy (config->properties);
00682 config->properties = create_tree ();
00683
00684 config->version = XMMS_CONFIG_VERSION;
00685
00686 g_free (config->value_name);
00687 config->value_name = NULL;
00688 }
00689
00690
00691
00692
00693
00694
00695 void
00696 xmms_config_init (const gchar *filename)
00697 {
00698 GMarkupParser pars;
00699 GMarkupParseContext *ctx;
00700 xmms_config_t *config;
00701 int ret, fd = -1;
00702 gboolean parserr = FALSE, eof = FALSE;
00703
00704 config = xmms_object_new (xmms_config_t, xmms_config_destroy);
00705 config->mutex = g_mutex_new ();
00706 config->filename = filename;
00707
00708 config->properties = create_tree ();
00709
00710 config->version = 0;
00711 global_config = config;
00712
00713 xmms_ipc_object_register (XMMS_IPC_OBJECT_CONFIG, XMMS_OBJECT (config));
00714 xmms_ipc_broadcast_register (XMMS_OBJECT (config),
00715 XMMS_IPC_SIGNAL_CONFIGVALUE_CHANGED);
00716
00717 memset (&pars, 0, sizeof (pars));
00718
00719 pars.start_element = xmms_config_parse_start;
00720 pars.end_element = xmms_config_parse_end;
00721 pars.text = xmms_config_parse_text;
00722
00723 if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
00724 fd = open (filename, O_RDONLY);
00725 }
00726
00727 if (fd > -1) {
00728 config->is_parsing = TRUE;
00729 config->states = g_queue_new ();
00730 config->sections = g_queue_new ();
00731 ctx = g_markup_parse_context_new (&pars, 0, config, NULL);
00732
00733 while ((!eof) && (!parserr)) {
00734 GError *error = NULL;
00735 gchar buffer[1024];
00736
00737 ret = read (fd, buffer, 1024);
00738 if (ret < 1) {
00739 g_markup_parse_context_end_parse (ctx, &error);
00740 if (error) {
00741 xmms_log_error ("Cannot parse config file: %s",
00742 error->message);
00743 g_error_free (error);
00744 error = NULL;
00745 parserr = TRUE;
00746 }
00747 eof = TRUE;
00748 }
00749
00750 g_markup_parse_context_parse (ctx, buffer, ret, &error);
00751 if (error) {
00752 xmms_log_error ("Cannot parse config file: %s",
00753 error->message);
00754 g_error_free (error);
00755 error = NULL;
00756 parserr = TRUE;
00757 }
00758
00759
00760
00761 if (XMMS_CONFIG_VERSION > config->version) {
00762 clear_config (config);
00763 break;
00764 }
00765 }
00766
00767 close (fd);
00768 g_markup_parse_context_free (ctx);
00769
00770 while (!g_queue_is_empty (config->sections)) {
00771 g_free (g_queue_pop_head (config->sections));
00772 }
00773
00774 g_queue_free (config->states);
00775 g_queue_free (config->sections);
00776
00777 config->is_parsing = FALSE;
00778 } else {
00779 xmms_log_info ("No configfile specified, using default values.");
00780 }
00781
00782 if (parserr) {
00783 xmms_log_info ("The config file could not be parsed, reverting to default configuration..");
00784 clear_config (config);
00785 }
00786
00787 xmms_object_cmd_add (XMMS_OBJECT (config),
00788 XMMS_IPC_CMD_SETVALUE,
00789 XMMS_CMD_FUNC (setvalue));
00790 xmms_object_cmd_add (XMMS_OBJECT (config),
00791 XMMS_IPC_CMD_GETVALUE,
00792 XMMS_CMD_FUNC (getvalue));
00793 xmms_object_cmd_add (XMMS_OBJECT (config),
00794 XMMS_IPC_CMD_LISTVALUES,
00795 XMMS_CMD_FUNC (listvalues));
00796 xmms_object_cmd_add (XMMS_OBJECT (config),
00797 XMMS_IPC_CMD_REGVALUE,
00798 XMMS_CMD_FUNC (regvalue));
00799 }
00800
00801
00802
00803
00804
00805 void
00806 xmms_config_shutdown ()
00807 {
00808 xmms_object_unref (global_config);
00809
00810 }
00811
00812 static gboolean
00813 dump_tree (gchar *current_key, xmms_config_property_t *prop,
00814 dump_tree_data_t *data)
00815 {
00816 gchar *prop_name, section[256];
00817 gchar *dot = NULL, *current_last_dot, *start = current_key;
00818
00819 prop_name = strrchr (current_key, '.');
00820
00821
00822
00823
00824
00825
00826 if (data->prev_key) {
00827 gchar *c = current_key, *o = data->prev_key;
00828 gsize dots = 0;
00829
00830
00831
00832
00833 while (*c && *o && *c == *o) {
00834 c++;
00835 o++;
00836
00837 if (*c == '.')
00838 start = c + 1;
00839 };
00840
00841
00842
00843
00844
00845 while (*o) {
00846 if (*o == '.')
00847 dots++;
00848
00849 o++;
00850 };
00851
00852
00853
00854
00855 if (dots)
00856 data->prev_key = NULL;
00857
00858 while (dots--) {
00859
00860 data->indent[--data->indent_level] = '\0';
00861
00862 fprintf (data->fp, "%s</section>\n", data->indent);
00863 }
00864 }
00865
00866
00867 dot = strchr (start, '.');
00868 current_last_dot = start - 1;
00869
00870 while (dot) {
00871 strncpy (section, current_last_dot + 1, dot - current_last_dot + 1);
00872 section[dot - current_last_dot - 1] = 0;
00873
00874 fprintf (data->fp, "%s<section name=\"%s\">\n",
00875 data->indent, section);
00876
00877
00878 g_assert (data->indent_level < 127);
00879 data->indent[data->indent_level] = '\t';
00880 data->indent[++data->indent_level] = '\0';
00881
00882 current_last_dot = dot;
00883 dot = strchr (dot + 1, '.');
00884 };
00885
00886 data->prev_key = current_key;
00887
00888 fprintf (data->fp, "%s<property name=\"%s\">%s</property>\n",
00889 data->indent, prop_name + 1,
00890 xmms_config_property_get_string (prop));
00891
00892 return FALSE;
00893 }
00894
00895
00896
00897
00898
00899
00900 gboolean
00901 xmms_config_save (void)
00902 {
00903 FILE *fp = NULL;
00904 dump_tree_data_t data;
00905
00906 g_return_val_if_fail (global_config, FALSE);
00907
00908
00909 if (global_config->is_parsing)
00910 return FALSE;
00911
00912 if (!(fp = fopen (global_config->filename, "w"))) {
00913 xmms_log_error ("Couldn't open %s for writing.",
00914 global_config->filename);
00915 return FALSE;
00916 }
00917
00918 fprintf (fp, "<?xml version=\"1.0\"?>\n<xmms version=\"%i\">\n",
00919 XMMS_CONFIG_VERSION);
00920
00921 data.fp = fp;
00922 data.state = XMMS_CONFIG_STATE_START;
00923 data.prev_key = NULL;
00924
00925 strcpy (data.indent, "\t");
00926 data.indent_level = 1;
00927
00928 g_tree_foreach (global_config->properties,
00929 (GTraverseFunc) dump_tree, &data);
00930
00931
00932
00933
00934
00935 while (data.indent_level > 1) {
00936
00937 data.indent[--data.indent_level] = '\0';
00938
00939 fprintf (fp, "%s</section>\n", data.indent);
00940 }
00941
00942 fprintf (fp, "</xmms>\n");
00943 fclose (fp);
00944
00945 return TRUE;
00946 }
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956 static void
00957 xmms_config_property_destroy (xmms_object_t *object)
00958 {
00959 xmms_config_property_t *prop = (xmms_config_property_t *) object;
00960
00961
00962
00963
00964 g_free (prop->value);
00965 }
00966
00967
00968
00969
00970
00971 static xmms_config_property_t *
00972 xmms_config_property_new (const gchar *name)
00973 {
00974 xmms_config_property_t *ret;
00975
00976 ret = xmms_object_new (xmms_config_property_t, xmms_config_property_destroy);
00977 ret->name = name;
00978
00979 return ret;
00980 }
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990 static gchar *
00991 xmms_config_property_client_register (xmms_config_t *config,
00992 const gchar *name,
00993 const gchar *def_value,
00994 xmms_error_t *error)
00995 {
00996 gchar *tmp;
00997 tmp = g_strdup_printf ("clients.%s", name);
00998 xmms_config_property_register (tmp, def_value, NULL, NULL);
00999 return tmp;
01000 }
01001
01002