1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <glib/gstdio.h>
26 #include <sys/types.h>
31 #define LOG_DOMAIN "Config"
33 typedef enum _ConfigNodeType
43 typedef struct _config_node
45 struct _config_node
*next
;
46 struct _config_node
*prev
;
47 struct _config_node
*parent
;
50 /* Save some extra memory by using a union
51 * It is actually effective because
52 * we build a resonable large tree using this
57 struct _config_node
*children
;
58 gchar
*value
; /* TYPE_ITEM* */
62 typedef struct _config_obj
70 static void __int_cfg_set_single_value_as_string(config_obj
* cfg
,
75 static gboolean
cfg_save_real(config_obj
*);
76 static void __int_cfg_remove_node(config_obj
*, config_node
*);
77 static config_node
*cfg_get_class_multiple(config_obj
* cfg
,
80 static config_node
*cfg_add_class(config_obj
*, config_node
*, const char *);
81 static config_node
*cfg_new_node(void);
82 static void cfg_add_child(config_node
*, config_node
*);
83 static void cfg_save_delayed(config_obj
* cfg
);
85 void cfg_del_single_value_mm(config_obj
* cfg
,
86 const char *class, const char *key
);
88 static void cfg_open_parse_file(config_obj
* cfgo
, FILE * fp
)
93 config_node
*cur
= NULL
;
94 config_node
*multiple
= NULL
;
95 while ((c
= fgetc(fp
)) != EOF
)
101 while (c
!= ']' && c
!= EOF
&& len
< 1023)
108 if (len
> 0 && len
< 256)
110 cur
= cfg_add_class(cfgo
, cur
, buffer
);
115 /* seek end of line */
116 while (c
!= EOF
&& c
!= '\n')
123 while (c
!= '}' && c
!= EOF
&& len
< 1023)
130 if (len
> 0 && len
< 256)
132 config_node
*child
= cfg_new_node();
133 child
->type
= TYPE_ITEM_MULTIPLE
;
134 child
->name
= g_strndup(buffer
, len
);
136 child
->children
= NULL
;
137 cfg_add_child(cur
, child
);
139 cfgo
->total_size
+= len
+ sizeof(config_node
);
146 /* seek end of line */
147 while (c
!= EOF
&& c
!= '\n')
151 /* next, ignore commants and there must be a category */
152 else if (cur
&& (c
== '#' || c
== '/' || c
== '\n' || c
== ';'))
154 while (c
!= EOF
&& c
!= '\n')
158 config_node
*new = NULL
;
160 while (c
!= '=' && c
!= EOF
)
166 if (len
< 256 && len
> 0)
171 new = cfg_new_node();
173 new->type
= TYPE_ITEM
;
174 new->name
= g_strndup(buffer
, len
);
175 cfgo
->total_size
+= len
+ sizeof(config_node
);
179 while ((c
= fgetc(fp
)) == ' ') ;
180 /* we got a quoted string */
188 /* add escaped char */
202 /* We have a quoted string, and the closing quote comes */
203 else if (c
== '"' && quote
)
211 } while ((c
!= '\n' || quote
)
212 && c
!= EOF
&& quote
>= 0 && len
< 1023);
213 new->value
= g_strndup(buffer
, len
);
214 cfgo
->total_size
+= len
;
217 cfg_add_child(multiple
, new);
220 cfg_add_child(cur
, new);
223 /* seek end of line */
224 while (c
!= EOF
&& c
!= '\n')
227 while (c
!= EOF
&& c
!= '\n')
232 config_obj
*cfg_open(const gchar
* url
)
234 config_obj
*cfgo
= NULL
;
235 /* check if there is an url passed */
241 cfgo
= g_malloc(sizeof(config_obj
));
242 /* check if malloc went ok */
247 cfgo
->url
= g_strdup(url
);
249 cfgo
->total_size
= sizeof(config_obj
) + strlen(cfgo
->url
);
250 cfgo
->save_timeout
= 0;
252 if (g_file_test(cfgo
->url
, G_FILE_TEST_EXISTS
))
254 FILE *fp
= g_fopen(cfgo
->url
, "r");
257 cfg_open_parse_file(cfgo
, fp
);
261 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
262 "Config %s: allocated: %i\n", cfgo
->url
, cfgo
->total_size
);
266 void cfg_close(config_obj
* cfgo
)
273 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
274 "Closing config '%s' with %i bytes allocated\n",
275 cfgo
->url
, cfgo
->total_size
);
276 if (cfgo
->url
!= NULL
)
278 cfgo
->total_size
-= strlen(cfgo
->url
);
279 cfg_free_string(cfgo
->url
);
282 __int_cfg_remove_node(cfgo
, cfgo
->root
);
283 cfgo
->total_size
-= sizeof(config_obj
);
284 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
285 "Memory remaining: %i\n", cfgo
->total_size
);
290 static config_node
*cfg_new_node(void)
292 config_node
*newnode
= g_slice_new(config_node
);
293 newnode
->type
= TYPE_CATEGORY
;
294 newnode
->name
= NULL
;
295 newnode
->next
= NULL
;
296 newnode
->prev
= NULL
;
297 newnode
->parent
= NULL
;
298 newnode
->value
= NULL
;
299 newnode
->children
= NULL
;
303 static config_node
*cfg_add_class(config_obj
* cfg
,
304 config_node
* parent
, const char *class)
306 config_node
*newnode
= cfg_new_node();
307 newnode
->type
= TYPE_CATEGORY
;
308 newnode
->name
= g_strdup(class);
309 newnode
->parent
= parent
;
310 cfg
->total_size
+= sizeof(config_node
) + strlen(class);
313 if (cfg
->root
== NULL
)
318 config_node
*temp
= cfg
->root
;
319 while (temp
->next
!= NULL
)
321 temp
->next
= newnode
;
322 newnode
->prev
= temp
;
326 if (parent
->children
== NULL
)
328 parent
->children
= newnode
;
331 config_node
*temp
= parent
->children
;
333 while (temp
->next
!= NULL
)
335 temp
->next
= newnode
;
336 newnode
->prev
= temp
;
343 void cfg_add_child(config_node
* parent
, config_node
* child
)
345 if (parent
== NULL
|| child
== NULL
)
347 if (parent
->type
== TYPE_ITEM
)
349 if (parent
->children
== NULL
)
351 parent
->children
= child
;
352 child
->parent
= parent
;
355 config_node
*temp
= parent
->children
;
358 while (temp
->next
!= NULL
)
362 child
->parent
= parent
;
366 static void cfg_save_category(config_obj
* cfg
, config_node
* node
, FILE * fp
)
368 config_node
*temp
= NULL
;
373 while (node
->prev
!= NULL
)
375 /* save some stuff */
376 for (temp
= node
; temp
!= NULL
; temp
= temp
->next
)
378 if (temp
->type
== TYPE_CATEGORY
)
381 fputs(temp
->name
, fp
);
383 cfg_save_category(cfg
, temp
->children
, fp
);
386 if (temp
->type
== TYPE_ITEM_MULTIPLE
)
389 fputs(temp
->name
, fp
);
391 cfg_save_category(cfg
, temp
->children
, fp
);
393 } else if (temp
->type
== TYPE_ITEM
)
396 int length
= (temp
->value
) ? strlen(temp
->value
) : 0;
397 fputs(temp
->name
, fp
);
399 for (i
= 0; i
< length
; i
++)
401 if (temp
->value
[i
] == '"')
404 } else if (temp
->value
[i
] == '\\')
407 } else if (temp
->value
[i
] == '\n')
412 putc(temp
->value
[i
], fp
);
420 static gboolean
cfg_save_real(config_obj
* cfgo
)
427 if (cfgo
->save_timeout
)
429 g_source_remove(cfgo
->save_timeout
);
430 cfgo
->save_timeout
= 0;
435 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Save triggered:%s", cfgo
->url
);
436 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
437 "Saving config file: %s (%i bytes)", cfgo
->url
, cfgo
->total_size
);
438 if (cfgo
->root
!= NULL
)
440 FILE *fp
= g_fopen(cfgo
->url
, "w");
443 fputs("# Do not edit this file!!!\n", fp
);
444 cfg_save_category(cfgo
, cfgo
->root
, fp
);
447 chmod(cfgo
->url
, 0600);
454 static config_node
*cfg_get_class(config_obj
* cfg
, const char *class)
456 return cfg_get_class_multiple(cfg
, NULL
, class);
459 static config_node
*cfg_get_class_multiple(config_obj
* cfg
,
463 config_node
*node
= NULL
;
466 node
= root
->children
;
475 while (node
->prev
!= NULL
)
478 for (; node
!= NULL
; node
= node
->next
)
480 if (node
->type
== TYPE_CATEGORY
&& !strcmp(node
->name
, class))
489 static config_node
*cfg_get_single_value(config_obj
* cfg
, const char *class,
493 config_node
*cur
= NULL
;
494 cur
= cfg_get_class(cfg
, class);
495 if (cur
== NULL
|| cur
->children
== NULL
)
500 for (; cur
!= NULL
; cur
= cur
->next
)
502 if (!strcmp(cur
->name
, key
))
510 static char *__int_cfg_get_single_value_as_string(config_obj
* cfg
,
514 config_node
*cur
= cfg_get_single_value(cfg
, class, key
);
517 if (cur
->type
== TYPE_ITEM
)
519 return g_strdup((char *)cur
->value
);
525 char *cfg_get_single_value_as_string(config_obj
* cfg
, const char *class,
528 return __int_cfg_get_single_value_as_string(cfg
, class, key
);
531 char *cfg_get_single_value_as_string_with_default(config_obj
* cfg
,
537 retv
= __int_cfg_get_single_value_as_string(cfg
, class, key
);
540 __int_cfg_set_single_value_as_string(cfg
, class, key
, def
);
541 retv
= __int_cfg_get_single_value_as_string(cfg
, class, key
);
546 static int __int_cfg_get_single_value_as_int_mm(config_obj
* cfg
,
550 config_node
*cur
= NULL
;
551 int retv
= CFG_INT_NOT_DEFINED
;
553 cur
= cfg_get_single_value(cfg
, class, key
);
556 if (cur
->type
== TYPE_ITEM
)
558 retv
= (int)g_ascii_strtoull(cur
->value
, NULL
, 0);
565 int cfg_get_single_value_as_int(config_obj
* cfg
, const char *class,
569 retv
= __int_cfg_get_single_value_as_int_mm(cfg
, class, key
);
573 void cfg_set_single_value_as_int(config_obj
* cfg
, const char *class,
574 const char *key
, int value
)
577 temp
= g_strdup_printf("%i", value
);
578 __int_cfg_set_single_value_as_string(cfg
, class, key
, temp
);
579 cfg_free_string(temp
);
582 static void __int_cfg_set_single_value_as_int(config_obj
* cfg
,
584 const char *key
, int value
)
587 temp
= g_strdup_printf("%i", value
);
588 __int_cfg_set_single_value_as_string(cfg
, class, key
, temp
);
589 cfg_free_string(temp
);
592 int cfg_get_single_value_as_int_with_default(config_obj
* cfg
,
593 const char *class, const char *key
,
596 int retv
= CFG_INT_NOT_DEFINED
;
597 retv
= __int_cfg_get_single_value_as_int_mm(cfg
, class, key
);
598 if (retv
== CFG_INT_NOT_DEFINED
)
600 __int_cfg_set_single_value_as_int(cfg
, class, key
, def
);
601 retv
= __int_cfg_get_single_value_as_int_mm(cfg
, class, key
);
607 static float __int_cfg_get_single_value_as_float(config_obj
* cfg
,
612 config_node
*cur
= NULL
;
613 cur
= cfg_get_single_value(cfg
, class, key
);
617 return CFG_INT_NOT_DEFINED
;
619 /* make it return an error */
620 result
= g_ascii_strtod(cur
->value
, NULL
);
624 float cfg_get_single_value_as_float(config_obj
* cfg
, const char *class,
628 retv
= __int_cfg_get_single_value_as_float(cfg
, class, key
);
632 void cfg_set_single_value_as_float(config_obj
* cfg
, const char *class,
633 const char *key
, float value
)
636 value1
= g_strdup_printf("%f", value
);
637 __int_cfg_set_single_value_as_string(cfg
, class, key
, value1
);
638 cfg_free_string(value1
);
641 float cfg_get_single_value_as_float_with_default(config_obj
* cfg
,
643 const char *key
, float def
)
646 retv
= __int_cfg_get_single_value_as_float(cfg
, class, key
);
647 if (retv
== CFG_INT_NOT_DEFINED
)
649 char *value1
= g_strdup_printf("%f", def
);
650 __int_cfg_set_single_value_as_string(cfg
, class, key
, value1
);
651 cfg_free_string(value1
);
652 retv
= __int_cfg_get_single_value_as_float(cfg
, class, key
);
654 /* make it return an error */
658 static void __int_cfg_remove_node(config_obj
* cfg
, config_node
* node
)
660 if (node
->type
!= TYPE_ITEM
)
662 while (node
->children
)
664 __int_cfg_remove_node(cfg
, node
->children
);
667 /* only child, and I have a parent */
668 if (node
->next
== NULL
&& node
->prev
== NULL
&& node
->parent
)
670 /* remove from list */
671 if (node
->parent
->type
!= TYPE_ITEM
)
673 node
->parent
->children
= NULL
;
676 /* remove node from linked list */
677 if (node
->prev
!= NULL
)
679 if (node
->parent
&& node
->parent
->children
== node
)
681 node
->parent
->children
= node
->prev
;
683 node
->prev
->next
= node
->next
;
685 if (node
->next
!= NULL
)
687 if (node
->parent
&& node
->parent
->children
== node
)
689 node
->parent
->children
= node
->next
;
691 node
->next
->prev
= node
->prev
;
693 if (node
== cfg
->root
)
697 cfg
->root
= node
->prev
;
698 } else if (node
->next
)
700 cfg
->root
= node
->next
;
706 cfg
->total_size
-= sizeof(config_node
);
709 cfg
->total_size
-= strlen(node
->name
);
710 cfg_free_string(node
->name
);
714 cfg
->total_size
-= strlen(node
->value
);
715 cfg_free_string(node
->value
);
717 g_slice_free(config_node
, node
);
720 void cfg_del_single_value_mm(config_obj
* cfg
, const char *class,
723 config_node
*node
= NULL
;
724 node
= cfg_get_single_value(cfg
, class, key
);
727 __int_cfg_remove_node(cfg
, node
);
728 cfg_save_delayed(cfg
);
729 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
730 "triggered save delay: del: %s, %s", class, key
);
734 void cfg_del_single_value(config_obj
* cfg
, const char *class, const char *key
)
736 cfg_del_single_value_mm(cfg
, class, key
);
739 void cfg_remove_class(config_obj
* cfg
, const char *class)
741 config_node
*node
= NULL
;
742 if (cfg
== NULL
|| class == NULL
)
745 node
= cfg_get_class(cfg
, class);
748 __int_cfg_remove_node(cfg
, node
);
750 cfg_save_delayed(cfg
);
751 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "triggered save delay: del: %s",
755 static void __int_cfg_set_single_value_as_string(config_obj
* cfg
,
760 config_node
*newnode
= cfg_get_single_value(cfg
, class, key
);
763 config_node
*node
= cfg_get_class(cfg
, class);
766 node
= cfg_add_class(cfg
, NULL
, class);
771 newnode
= cfg_new_node();
772 newnode
->name
= g_strdup(key
);
773 cfg
->total_size
+= sizeof(config_node
) + strlen(key
);
774 cfg_add_child(node
, newnode
);
776 } else if ((value
== NULL
&& newnode
->value
== NULL
) ||
777 (value
!= NULL
&& newnode
->value
!= NULL
778 && strlen(newnode
->value
) == strlen(value
)
779 && !memcmp(newnode
->value
, value
, strlen(newnode
->value
))))
781 /* Check if the content is the same, if it is, do nothing */
784 newnode
->type
= TYPE_ITEM
;
787 cfg
->total_size
-= strlen(newnode
->value
);
788 cfg_free_string(newnode
->value
);
792 newnode
->value
= g_strdup(value
);
793 cfg
->total_size
+= strlen(value
);
796 newnode
->value
= NULL
;
798 cfg_save_delayed(cfg
);
799 g_log(LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
800 "triggered save delay: set: %s,%s -> %s", class, key
, value
);
803 void cfg_set_single_value_as_string(config_obj
* cfg
, const char *class,
804 const char *key
, const char *value
)
806 __int_cfg_set_single_value_as_string(cfg
, class, key
, value
);
809 /* multiple values */
810 void cfg_free_multiple(conf_mult_obj
* data
)
812 conf_mult_obj
*list
= data
;
816 while (list
->prev
!= NULL
)
823 cfg_free_string(list
->value
);
825 cfg_free_string(list
->key
);
826 if (list
->next
!= NULL
)
829 g_slice_free(conf_mult_obj
, list
->prev
);
833 g_slice_free(conf_mult_obj
, list
->prev
);
834 g_slice_free(conf_mult_obj
, list
);
840 conf_mult_obj
*cfg_get_class_list(config_obj
* data
)
842 conf_mult_obj
*list
= NULL
;
843 config_node
*root
= NULL
;
849 while (root
->prev
!= NULL
)
853 if (root
->type
== TYPE_CATEGORY
)
855 conf_mult_obj
*temp
= g_slice_new0(conf_mult_obj
);
857 temp
->key
= g_strdup(root
->name
);
860 temp
->next
->prev
= temp
;
864 } while (root
!= NULL
);
865 while (list
->prev
!= NULL
)
871 conf_mult_obj
*cfg_get_key_list(config_obj
* data
, const char *class)
873 conf_mult_obj
*list
= NULL
;
874 config_node
*root
= NULL
;
877 if (data
->root
== NULL
)
879 root
= cfg_get_class(data
, class);
880 if (!root
|| !root
->children
)
884 root
= root
->children
;
885 while (root
->prev
!= NULL
)
889 if (root
->type
== TYPE_ITEM
)
891 conf_mult_obj
*temp
= g_slice_new0(conf_mult_obj
);
892 temp
->value
= g_strdup(root
->value
);
893 temp
->key
= g_strdup(root
->name
);
896 temp
->next
->prev
= temp
;
900 } while (root
!= NULL
);
901 while (list
&& list
->prev
!= NULL
)
906 static void cfg_save_delayed(config_obj
* cfg
)
908 if (cfg
->save_timeout
)
910 g_source_remove(cfg
->save_timeout
);
911 cfg
->save_timeout
= 0;
913 cfg
->save_timeout
= g_timeout_add_seconds(5,
914 (GSourceFunc
) cfg_save_real
, cfg
);