#include #include #include #include #include #include #include #include "gm-options.h" #include "gm-string.h" #include "gm-debug.h" extern int errno; #define GM_OPTIONS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \ GM_TYPE_OPTIONS, GmOptionsPrivate)) struct _GmOptionsPrivate { GHashTable *options; gchar *filepath; }; /* Signals */ enum { OPTION_CHANGED, NUM_SIGNALS }; #define XML_ROOT_NAME "options" #define XML_OPTION_NAME "option" static guint options_signals[NUM_SIGNALS] = {0}; G_DEFINE_TYPE(GmOptions, gm_options, G_TYPE_OBJECT) static void gm_options_finalize(GObject *object) { GmOptions *options = GM_OPTIONS(object); g_hash_table_destroy(options->priv->options); g_free(options->priv->filepath); G_OBJECT_CLASS(gm_options_parent_class)->finalize(object); } static void gm_options_class_init(GmOptionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = gm_options_finalize; options_signals[OPTION_CHANGED] = g_signal_new("option_changed", G_OBJECT_CLASS_TYPE(object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GmOptionsClass, option_changed), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); g_type_class_add_private(object_class, sizeof(GmOptionsPrivate)); } static void gm_options_init(GmOptions *options) { options->priv = GM_OPTIONS_GET_PRIVATE(options); options->priv->options = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); options->priv->filepath = NULL; } static void gm_options_load_option(GmOptions *options, xmlDocPtr doc, xmlNodePtr ptr) { xmlChar *key, *value; key = xmlGetProp(ptr, (const xmlChar *)"key"); value = xmlGetProp(ptr, (const xmlChar *)"value"); if (key == NULL) { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load_option: key not present in option"); } else if (value == NULL) { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load_option: value not present in option"); } else { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load_option: adding option %s: %s", key, value); gm_options_set(options, (gchar const *)key, (gchar const *)value); } xmlFree(key); xmlFree(value); } static void gm_options_save_option(gchar *key, gchar *value, xmlNodePtr root) { xmlNodePtr option; gm_debug_msg(DEBUG_DEFAULT, "GmOptions.SaveValue: saving %s, %s", key, value); option = xmlNewChild(root, NULL, (const xmlChar *)(XML_OPTION_NAME), NULL); xmlNewProp(option, (const xmlChar *)("key"), (const xmlChar *)(key)); xmlNewProp(option, (const xmlChar *)("value"), (const xmlChar *)(value)); } static void gm_options_dup_option(gchar *key, gchar *value, GmOptions *copy) { gm_options_set(copy, key, value); } /* Public functions */ GmOptions * gm_options_new(void) { GmOptions *options = GM_OPTIONS(g_object_new(GM_TYPE_OPTIONS, NULL)); return options; } GmOptions * gm_options_dup(GmOptions *source) { GmOptions *copy = gm_options_new(); g_hash_table_foreach(source->priv->options, (GHFunc)gm_options_dup_option, copy); return copy; } // Adds an option to opt, even if key already exists void gm_options_set(GmOptions * options, gchar const *key, gchar const *value) { gchar *trimmed = gm_string_trim(key); gchar *lookup = g_hash_table_lookup(options->priv->options, trimmed); gboolean changed = lookup != NULL && lookup != value; if (lookup != value) { g_hash_table_insert(options->priv->options, g_strdup(trimmed), g_strdup(value)); } if (changed) { g_signal_emit(options, options_signals[OPTION_CHANGED], 0, trimmed); } g_free(trimmed); } gchar const * gm_options_get(GmOptions *options, gchar const *key) { return g_hash_table_lookup(options->priv->options, key); } void gm_options_set_int(GmOptions *options, gchar const *key, int value) { gchar val[15]; g_snprintf((gchar *) &val, 15, "%d", value); gm_options_set(options, key, (gchar const *)val); } int gm_options_get_int(GmOptions *options, gchar const *key) { gchar const *val = gm_options_get(options, key); int ret; if (val && gm_string_to_int(val, &ret)) { return ret; } else { return 0; } } void gm_options_remove(GmOptions *options, gchar const *key) { g_hash_table_remove(options->priv->options, key); } void gm_options_save(GmOptions *options) { xmlDocPtr doc; xmlNodePtr root; if (options->priv->filepath == NULL) { return; } gm_debug_msg(DEBUG_DEFAULT, "GmOptions.save: saving options (%s)!", options->priv->filepath); doc = xmlNewDoc((const xmlChar *)("1.0")); root = xmlNewNode(NULL, (const xmlChar *)(XML_ROOT_NAME)); xmlDocSetRootElement(doc, root); g_hash_table_foreach(options->priv->options, (GHFunc)gm_options_save_option, root); xmlSaveFormatFileEnc(options->priv->filepath, doc, "UTF-8", 1); xmlFreeDoc(doc); // Make sure to make this only readable for the user and the group chmod(options->priv->filepath, 0660); } void gm_options_save_as(GmOptions *options, gchar const *filename) { g_free(options->priv->filepath); options->priv->filepath = g_strdup(filename); gm_options_save(options); } gboolean gm_options_load(GmOptions *options, gchar const *filename) { xmlDocPtr doc; xmlNodePtr root; gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: loading options (%s)!", filename); g_free(options->priv->filepath); options->priv->filepath = g_strdup(filename); if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: file does not exist"); return FALSE; } doc = xmlParseFile(filename); if (doc == NULL) { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: error on parsing options file"); return FALSE; } root = xmlDocGetRootElement(doc); if (root == NULL) { xmlFreeDoc(doc); return FALSE; } if (xmlStrcmp(root->name, (const xmlChar *)(XML_ROOT_NAME))) { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: invalid root node"); xmlFreeDoc(doc); return FALSE; } for (root = root->xmlChildrenNode; root; root = root->next) { if (!xmlStrcmp(root->name, (const xmlChar *)(XML_OPTION_NAME))) { gm_options_load_option(options, doc, root); } } xmlFreeDoc(doc); return TRUE; } void _gm_options_check_old_options(gchar const *xmlname) { gchar *filename; xmlDocPtr doc; xmlNodePtr root; FILE *f; gchar **keyvalue, line[1024]; gint i; filename = g_strdup(xmlname); filename[strlen(xmlname) - 4] = '\0'; if (g_file_test(xmlname, G_FILE_TEST_EXISTS) || !g_file_test(filename, G_FILE_TEST_EXISTS)) { g_free(filename); return; } doc = xmlNewDoc((const xmlChar *)("1.0")); root = xmlNewNode(NULL, (const xmlChar *)(XML_ROOT_NAME)); xmlDocSetRootElement(doc, root); if ((f = fopen(filename, "r")) != NULL) { i = 0; while (fgets((char *) &line, 1024 - 1, f) != NULL) { line[strlen((char *) &line) - 1] = '\0'; i++; if (strlen(line) != 0) { // Empty lines, we don't need to process those keyvalue = g_strsplit(line, "=", 2); // This will return at least 1 element, at most 2, we need 2 if (strncmp(keyvalue[0], "#", 1) != 0) { // Commented lines, well ignore them too if (keyvalue[1] != NULL) { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.check_old_options: converting %s, %s", keyvalue[0], keyvalue[1]); gm_options_save_option(keyvalue[0], keyvalue[1], root); } else { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: wrong " "syntax of options line in %s line %d", filename, i); } } g_strfreev(keyvalue); } } fclose(f); } else { gm_debug_msg(DEBUG_DEFAULT, "GmOptions.check_old_options: could not " "retrieve contents of file %s (%s)", filename, strerror(errno)); } xmlSaveFormatFileEnc(xmlname, doc, "UTF-8", 1); xmlFreeDoc(doc); if (g_file_test(xmlname, G_FILE_TEST_EXISTS)) { // Make sure to make this only readable for the user and the group chmod(xmlname, 0660); unlink(filename); } g_free(filename); }