Cleanups
[gmpc.git] / src / config1.c
blob5041c31b1b4043471be33ec07bba5821daf93036
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.
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdio.h>
23 #include <glib.h>
24 #include <glib/gstdio.h>
25 #ifndef WIN32
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #endif
29 #include "config1.h"
31 #define LOG_DOMAIN "Config"
33 typedef enum _ConfigNodeType
35 TYPE_CATEGORY,
36 TYPE_ITEM,
37 TYPE_ITEM_MULTIPLE
38 } ConfigNodeType;
39 /**
40 * A config node
41 * 24byte large
43 typedef struct _config_node
45 struct _config_node *next;
46 struct _config_node *prev;
47 struct _config_node *parent;
48 gchar *name;
49 ConfigNodeType type;
50 /* Save some extra memory by using a union
51 * It is actually effective because
52 * we build a resonable large tree using this
54 union
56 /* TYPE_CATEGORY */
57 struct _config_node *children;
58 gchar *value; /* TYPE_ITEM* */
60 } config_node;
62 typedef struct _config_obj
64 gchar *url;
65 config_node *root;
66 int total_size;
67 guint save_timeout;
68 } _config_obj;
70 static void __int_cfg_set_single_value_as_string(config_obj * cfg,
71 const char *class,
72 const char *key,
73 const char *value);
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,
78 config_node * root,
79 const char *class);
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)
90 char buffer[1024];
91 int len = 0;
92 int c;
93 config_node *cur = NULL;
94 config_node *multiple = NULL;
95 while ((c = fgetc(fp)) != EOF)
97 if (c == '[')
99 len = 0;
100 c = fgetc(fp);
101 while (c != ']' && c != EOF && len < 1023)
103 buffer[len] = c;
104 len++;
105 c = fgetc(fp);
107 buffer[len] = '\0';
108 if (len > 0 && len < 256)
110 cur = cfg_add_class(cfgo, cur, buffer);
111 } else if (len == 0)
113 cur = cur->parent;
115 /* seek end of line */
116 while (c != EOF && c != '\n')
117 c = fgetc(fp);
119 if (cur && c == '{')
121 len = 0;
122 c = fgetc(fp);
123 while (c != '}' && c != EOF && len < 1023)
125 buffer[len] = c;
126 len++;
127 c = fgetc(fp);
129 buffer[len] = '\0';
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);
135 child->parent = cur;
136 child->children = NULL;
137 cfg_add_child(cur, child);
138 multiple = child;
139 cfgo->total_size += len + sizeof(config_node);
142 if (len == 0)
144 multiple = NULL;
146 /* seek end of line */
147 while (c != EOF && c != '\n')
148 c = fgetc(fp);
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')
155 c = fgetc(fp);
156 } else if (cur)
158 config_node *new = NULL;
159 len = 0;
160 while (c != '=' && c != EOF)
162 buffer[len] = c;
163 len++;
164 c = fgetc(fp);
166 if (len < 256 && len > 0)
168 int quote = 0;
170 /* write key name */
171 new = cfg_new_node();
172 new->parent = cur;
173 new->type = TYPE_ITEM;
174 new->name = g_strndup(buffer, len);
175 cfgo->total_size += len + sizeof(config_node);
176 /* Get value */
177 len = 0;
178 /* skip spaces */
179 while ((c = fgetc(fp)) == ' ') ;
180 /* we got a quoted string */
181 if (c == '"')
183 quote = 1;
184 c = fgetc(fp);
188 /* add escaped char */
189 if (c == '\\')
191 c = fgetc(fp);
192 if (c == 'n')
194 buffer[len] = '\n';
195 len++;
196 } else
198 buffer[len] = c;
199 len++;
202 /* We have a quoted string, and the closing quote comes */
203 else if (c == '"' && quote)
204 quote = -1;
205 else
207 buffer[len] = c;
208 len++;
210 c = fgetc(fp);
211 } while ((c != '\n' || quote)
212 && c != EOF && quote >= 0 && len < 1023);
213 new->value = g_strndup(buffer, len);
214 cfgo->total_size += len;
215 if (multiple)
217 cfg_add_child(multiple, new);
218 } else
220 cfg_add_child(cur, new);
223 /* seek end of line */
224 while (c != EOF && c != '\n')
225 c = fgetc(fp);
226 } else
227 while (c != EOF && c != '\n')
228 c = fgetc(fp);
232 config_obj *cfg_open(const gchar * url)
234 config_obj *cfgo = NULL;
235 /* check if there is an url passed */
236 if (url == NULL)
238 return NULL;
241 cfgo = g_malloc(sizeof(config_obj));
242 /* check if malloc went ok */
243 if (cfgo == NULL)
245 return NULL;
247 cfgo->url = g_strdup(url);
248 cfgo->root = NULL;
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");
255 if (fp)
257 cfg_open_parse_file(cfgo, fp);
258 fclose(fp);
261 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
262 "Config %s: allocated: %i\n", cfgo->url, cfgo->total_size);
263 return cfgo;
266 void cfg_close(config_obj * cfgo)
268 if (cfgo == NULL)
270 return;
272 cfg_save_real(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);
281 while (cfgo->root)
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);
286 g_free(cfgo);
287 cfgo = NULL;
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;
300 return newnode;
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);
311 if (!parent)
313 if (cfg->root == NULL)
315 cfg->root = newnode;
316 } else
318 config_node *temp = cfg->root;
319 while (temp->next != NULL)
320 temp = temp->next;
321 temp->next = newnode;
322 newnode->prev = temp;
324 } else
326 if (parent->children == NULL)
328 parent->children = newnode;
329 } else
331 config_node *temp = parent->children;
332 /* get last node */
333 while (temp->next != NULL)
334 temp = temp->next;
335 temp->next = newnode;
336 newnode->prev = temp;
340 return newnode;
343 void cfg_add_child(config_node * parent, config_node * child)
345 if (parent == NULL || child == NULL)
346 return;
347 if (parent->type == TYPE_ITEM)
348 return;
349 if (parent->children == NULL)
351 parent->children = child;
352 child->parent = parent;
353 } else
355 config_node *temp = parent->children;
357 /* get last node */
358 while (temp->next != NULL)
359 temp = temp->next;
360 temp->next = child;
361 child->prev = temp;
362 child->parent = parent;
366 static void cfg_save_category(config_obj * cfg, config_node * node, FILE * fp)
368 config_node *temp = NULL;
370 if (node == NULL)
371 return;
372 /* find the first */
373 while (node->prev != NULL)
374 node = node->prev;
375 /* save some stuff */
376 for (temp = node; temp != NULL; temp = temp->next)
378 if (temp->type == TYPE_CATEGORY)
380 fputs("\n[", fp);
381 fputs(temp->name, fp);
382 fputs("]\n", fp);
383 cfg_save_category(cfg, temp->children, fp);
384 fputs("[]\n", fp);
386 if (temp->type == TYPE_ITEM_MULTIPLE)
388 fputs("\n{", fp);
389 fputs(temp->name, fp);
390 fputs("}\n", fp);
391 cfg_save_category(cfg, temp->children, fp);
392 fputs("{}\n\n", fp);
393 } else if (temp->type == TYPE_ITEM)
395 int i = 0;
396 int length = (temp->value) ? strlen(temp->value) : 0;
397 fputs(temp->name, fp);
398 fputs("=\"", fp);
399 for (i = 0; i < length; i++)
401 if (temp->value[i] == '"')
403 fputs("\\\"", fp);
404 } else if (temp->value[i] == '\\')
406 fputs("\\\\", fp);
407 } else if (temp->value[i] == '\n')
409 fputs("\\n", fp);
410 } else
412 putc(temp->value[i], fp);
415 fputs("\"\n", fp);
420 static gboolean cfg_save_real(config_obj * cfgo)
422 if (cfgo == NULL)
424 return FALSE;
427 if (cfgo->save_timeout)
429 g_source_remove(cfgo->save_timeout);
430 cfgo->save_timeout = 0;
431 } else
433 return FALSE;
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");
441 if (!fp)
442 return FALSE;
443 fputs("# Do not edit this file!!!\n", fp);
444 cfg_save_category(cfgo, cfgo->root, fp);
445 fclose(fp);
446 #ifndef WIN32
447 chmod(cfgo->url, 0600);
448 #endif
451 return FALSE;
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,
460 config_node * root,
461 const char *class)
463 config_node *node = NULL;
464 if (root != NULL)
466 node = root->children;
468 } else
470 node = cfg->root;
472 if (node == NULL)
473 return NULL;
474 /* find the first */
475 while (node->prev != NULL)
476 node = node->prev;
478 for (; node != NULL; node = node->next)
480 if (node->type == TYPE_CATEGORY && !strcmp(node->name, class))
482 return node;
486 return NULL;
489 static config_node *cfg_get_single_value(config_obj * cfg, const char *class,
490 const char *key)
492 /* take children */
493 config_node *cur = NULL;
494 cur = cfg_get_class(cfg, class);
495 if (cur == NULL || cur->children == NULL)
497 return NULL;
499 cur = cur->children;
500 for (; cur != NULL; cur = cur->next)
502 if (!strcmp(cur->name, key))
504 return cur;
507 return NULL;
510 static char *__int_cfg_get_single_value_as_string(config_obj * cfg,
511 const char *class,
512 const char *key)
514 config_node *cur = cfg_get_single_value(cfg, class, key);
515 if (cur != NULL)
517 if (cur->type == TYPE_ITEM)
519 return g_strdup((char *)cur->value);
522 return NULL;
525 char *cfg_get_single_value_as_string(config_obj * cfg, const char *class,
526 const char *key)
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,
532 const char *class,
533 const char *key,
534 const char *def)
536 char *retv = NULL;
537 retv = __int_cfg_get_single_value_as_string(cfg, class, key);
538 if (retv == NULL)
540 __int_cfg_set_single_value_as_string(cfg, class, key, def);
541 retv = __int_cfg_get_single_value_as_string(cfg, class, key);
543 return retv;
546 static int __int_cfg_get_single_value_as_int_mm(config_obj * cfg,
547 const char *class,
548 const char *key)
550 config_node *cur = NULL;
551 int retv = CFG_INT_NOT_DEFINED;
553 cur = cfg_get_single_value(cfg, class, key);
554 if (cur != NULL)
556 if (cur->type == TYPE_ITEM)
558 retv = (int)g_ascii_strtoull(cur->value, NULL, 0);
562 return retv;
565 int cfg_get_single_value_as_int(config_obj * cfg, const char *class,
566 const char *key)
568 int retv = 0;
569 retv = __int_cfg_get_single_value_as_int_mm(cfg, class, key);
570 return retv;
573 void cfg_set_single_value_as_int(config_obj * cfg, const char *class,
574 const char *key, int value)
576 gchar *temp = NULL;
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,
583 const char *class,
584 const char *key, int value)
586 gchar *temp = NULL;
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,
594 int def)
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);
603 return retv;
606 /* float */
607 static float __int_cfg_get_single_value_as_float(config_obj * cfg,
608 const char *class,
609 const char *key)
611 float result = 0;
612 config_node *cur = NULL;
613 cur = cfg_get_single_value(cfg, class, key);
615 if (cur == NULL)
617 return CFG_INT_NOT_DEFINED;
619 /* make it return an error */
620 result = g_ascii_strtod(cur->value, NULL);
621 return result;
624 float cfg_get_single_value_as_float(config_obj * cfg, const char *class,
625 const char *key)
627 float retv = 0;
628 retv = __int_cfg_get_single_value_as_float(cfg, class, key);
629 return retv;
632 void cfg_set_single_value_as_float(config_obj * cfg, const char *class,
633 const char *key, float value)
635 char *value1 = NULL;
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,
642 const char *class,
643 const char *key, float def)
645 float retv = 0;
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 */
655 return retv;
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)
695 if (node->prev)
697 cfg->root = node->prev;
698 } else if (node->next)
700 cfg->root = node->next;
701 } else
703 cfg->root = NULL;
706 cfg->total_size -= sizeof(config_node);
707 if (node->name)
709 cfg->total_size -= strlen(node->name);
710 cfg_free_string(node->name);
712 if (node->value)
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,
721 const char *key)
723 config_node *node = NULL;
724 node = cfg_get_single_value(cfg, class, key);
725 if (node != NULL)
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)
743 return;
745 node = cfg_get_class(cfg, class);
746 if (node)
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",
752 class);
755 static void __int_cfg_set_single_value_as_string(config_obj * cfg,
756 const char *class,
757 const char *key,
758 const char *value)
760 config_node *newnode = cfg_get_single_value(cfg, class, key);
761 if (newnode == NULL)
763 config_node *node = cfg_get_class(cfg, class);
764 if (node == NULL)
766 node = cfg_add_class(cfg, NULL, class);
767 if (node == NULL)
768 return;
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 */
782 return;
784 newnode->type = TYPE_ITEM;
785 if (newnode->value)
787 cfg->total_size -= strlen(newnode->value);
788 cfg_free_string(newnode->value);
790 if (value)
792 newnode->value = g_strdup(value);
793 cfg->total_size += strlen(value);
794 } else
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;
813 if (list == NULL)
814 return;
815 /* go to first */
816 while (list->prev != NULL)
818 list = list->prev;
820 while (list != NULL)
822 if (list->value)
823 cfg_free_string(list->value);
824 if (list->key)
825 cfg_free_string(list->key);
826 if (list->next != NULL)
828 if (list->prev)
829 g_slice_free(conf_mult_obj, list->prev);
830 list = list->next;
831 } else
833 g_slice_free(conf_mult_obj, list->prev);
834 g_slice_free(conf_mult_obj, list);
835 list = NULL;
840 conf_mult_obj *cfg_get_class_list(config_obj * data)
842 conf_mult_obj *list = NULL;
843 config_node *root = NULL;
844 if (!data)
845 return NULL;
846 if (!data->root)
847 return NULL;
848 root = data->root;
849 while (root->prev != NULL)
850 root = root->prev;
853 if (root->type == TYPE_CATEGORY)
855 conf_mult_obj *temp = g_slice_new0(conf_mult_obj);
856 temp->value = NULL;
857 temp->key = g_strdup(root->name);
858 temp->next = list;
859 if (temp->next)
860 temp->next->prev = temp;
861 list = temp;
863 root = root->next;
864 } while (root != NULL);
865 while (list->prev != NULL)
866 list = list->prev;
868 return list;
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;
875 if (data == NULL)
876 return NULL;
877 if (data->root == NULL)
878 return NULL;
879 root = cfg_get_class(data, class);
880 if (!root || !root->children)
882 return NULL;
884 root = root->children;
885 while (root->prev != NULL)
886 root = root->prev;
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);
894 temp->next = list;
895 if (temp->next)
896 temp->next->prev = temp;
897 list = temp;
899 root = root->next;
900 } while (root != NULL);
901 while (list && list->prev != NULL)
902 list = list->prev;
903 return list;
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);