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