2005-11-07 10:56:25 +01:00
|
|
|
#include <glib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2006-03-29 17:42:41 +02:00
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <unistd.h>
|
2005-11-07 10:56:25 +01:00
|
|
|
|
|
|
|
#include "gm-options.h"
|
|
|
|
#include "gm-string.h"
|
|
|
|
#include "gm-debug.h"
|
|
|
|
|
|
|
|
extern int errno;
|
|
|
|
|
2006-01-02 18:54:19 +01:00
|
|
|
#define GM_OPTIONS_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), \
|
|
|
|
GM_TYPE_OPTIONS, GmOptionsPrivate))
|
2005-11-07 10:56:25 +01:00
|
|
|
|
|
|
|
struct _GmOptionsPrivate {
|
|
|
|
GHashTable *options;
|
|
|
|
gchar *filepath;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Signals */
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OPTION_CHANGED,
|
|
|
|
NUM_SIGNALS
|
|
|
|
};
|
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
#define XML_ROOT_NAME "options"
|
|
|
|
#define XML_OPTION_NAME "option"
|
|
|
|
|
2005-11-07 10:56:25 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
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");
|
2005-11-07 10:56:25 +01:00
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
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);
|
2005-11-07 10:56:25 +01:00
|
|
|
}
|
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
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
|
2005-11-07 10:56:25 +01:00
|
|
|
gm_options_dup_option(gchar *key, gchar *value, GmOptions *copy) {
|
|
|
|
gm_options_set(copy, key, value);
|
|
|
|
}
|
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
/* Public functions */
|
|
|
|
|
|
|
|
GmOptions *
|
|
|
|
gm_options_new(void) {
|
|
|
|
GmOptions *options = GM_OPTIONS(g_object_new(GM_TYPE_OPTIONS, NULL));
|
|
|
|
|
|
|
|
return options;
|
|
|
|
}
|
|
|
|
|
2005-11-07 10:56:25 +01:00
|
|
|
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
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_options_set(GmOptions * options, gchar const *key, gchar const *value) {
|
2005-11-07 10:56:25 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2006-01-02 18:54:19 +01:00
|
|
|
gchar const *
|
|
|
|
gm_options_get(GmOptions *options, gchar const *key) {
|
2005-11-07 10:56:25 +01:00
|
|
|
return g_hash_table_lookup(options->priv->options, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_options_set_int(GmOptions *options, gchar const *key, int value) {
|
2005-11-07 10:56:25 +01:00
|
|
|
gchar val[15];
|
|
|
|
|
|
|
|
g_snprintf((gchar *) &val, 15, "%d", value);
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_options_set(options, key, (gchar const *)val);
|
2005-11-07 10:56:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_options_get_int(GmOptions *options, gchar const *key) {
|
|
|
|
gchar const *val = gm_options_get(options, key);
|
2005-11-07 10:56:25 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (val && gm_string_to_int(val, &ret)) {
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-01-02 18:54:19 +01:00
|
|
|
void
|
|
|
|
gm_options_remove(GmOptions *options, gchar const *key) {
|
|
|
|
g_hash_table_remove(options->priv->options, key);
|
|
|
|
}
|
|
|
|
|
2005-11-07 10:56:25 +01:00
|
|
|
void
|
|
|
|
gm_options_save(GmOptions *options) {
|
2006-03-29 17:42:41 +02:00
|
|
|
xmlDocPtr doc;
|
|
|
|
xmlNodePtr root;
|
2005-11-07 10:56:25 +01:00
|
|
|
|
|
|
|
if (options->priv->filepath == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.save: saving options (%s)!",
|
|
|
|
options->priv->filepath);
|
2005-11-07 10:56:25 +01:00
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
doc = xmlNewDoc((const xmlChar *)("1.0"));
|
|
|
|
root = xmlNewNode(NULL, (const xmlChar *)(XML_ROOT_NAME));
|
|
|
|
xmlDocSetRootElement(doc, root);
|
2005-11-07 10:56:25 +01:00
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
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);
|
2005-11-07 10:56:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_options_save_as(GmOptions *options, gchar const *filename) {
|
2005-11-07 10:56:25 +01:00
|
|
|
g_free(options->priv->filepath);
|
|
|
|
options->priv->filepath = g_strdup(filename);
|
|
|
|
|
|
|
|
gm_options_save(options);
|
|
|
|
}
|
|
|
|
|
2006-08-12 17:07:27 +02:00
|
|
|
gboolean
|
2006-03-29 17:42:41 +02:00
|
|
|
gm_options_load(GmOptions *options, gchar const *filename) {
|
|
|
|
xmlDocPtr doc;
|
|
|
|
xmlNodePtr root;
|
|
|
|
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: loading options (%s)!",
|
|
|
|
filename);
|
|
|
|
|
2006-08-12 17:07:27 +02:00
|
|
|
g_free(options->priv->filepath);
|
|
|
|
options->priv->filepath = g_strdup(filename);
|
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: file does not exist");
|
2006-08-12 17:07:27 +02:00
|
|
|
return FALSE;
|
2006-03-29 17:42:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
doc = xmlParseFile(filename);
|
|
|
|
|
|
|
|
if (doc == NULL) {
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT,
|
|
|
|
"GmOptions.load: error on parsing options file");
|
2006-08-12 17:07:27 +02:00
|
|
|
return FALSE;
|
2006-03-29 17:42:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
root = xmlDocGetRootElement(doc);
|
|
|
|
|
|
|
|
if (root == NULL) {
|
|
|
|
xmlFreeDoc(doc);
|
2006-08-12 17:07:27 +02:00
|
|
|
return FALSE;
|
2006-03-29 17:42:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (xmlStrcmp(root->name, (const xmlChar *)(XML_ROOT_NAME))) {
|
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: invalid root node");
|
|
|
|
xmlFreeDoc(doc);
|
2006-08-12 17:07:27 +02:00
|
|
|
return FALSE;
|
2006-03-29 17:42:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2006-08-12 17:07:27 +02:00
|
|
|
return TRUE;
|
2006-03-29 17:42:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
_gm_options_check_old_options(gchar const *xmlname) {
|
|
|
|
gchar *filename;
|
|
|
|
xmlDocPtr doc;
|
|
|
|
xmlNodePtr root;
|
2005-11-07 10:56:25 +01:00
|
|
|
FILE *f;
|
|
|
|
gchar **keyvalue, line[1024];
|
2006-03-29 17:42:41 +02:00
|
|
|
gint i;
|
2005-11-07 10:56:25 +01:00
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
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);
|
2005-11-07 10:56:25 +01:00
|
|
|
|
|
|
|
if ((f = fopen(filename, "r")) != NULL) {
|
|
|
|
i = 0;
|
|
|
|
while (fgets((char *) &line, 1024 - 1, f) != NULL) {
|
|
|
|
line[strlen((char *) &line) - 1] = '\0';
|
|
|
|
i++;
|
|
|
|
|
2006-01-02 18:54:19 +01:00
|
|
|
if (strlen(line) != 0) {
|
|
|
|
// Empty lines, we don't need to process those
|
2005-11-07 10:56:25 +01:00
|
|
|
keyvalue = g_strsplit(line, "=", 2);
|
|
|
|
// This will return at least 1 element, at most 2, we need 2
|
2006-01-02 18:54:19 +01:00
|
|
|
if (strncmp(keyvalue[0], "#", 1) != 0) {
|
|
|
|
// Commented lines, well ignore them too
|
2005-11-07 10:56:25 +01:00
|
|
|
if (keyvalue[1] != NULL) {
|
2006-03-29 17:42:41 +02:00
|
|
|
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);
|
2005-11-07 10:56:25 +01:00
|
|
|
} else {
|
2006-01-02 18:54:19 +01:00
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.load: wrong "
|
|
|
|
"syntax of options line in %s line %d",
|
|
|
|
filename, i);
|
2005-11-07 10:56:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_strfreev(keyvalue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
} else {
|
2006-03-29 17:42:41 +02:00
|
|
|
gm_debug_msg(DEBUG_DEFAULT, "GmOptions.check_old_options: could not "
|
|
|
|
"retrieve contents of file %s (%s)", filename, strerror(errno));
|
2005-11-07 10:56:25 +01:00
|
|
|
}
|
|
|
|
|
2006-03-29 17:42:41 +02:00
|
|
|
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);
|
2005-11-07 10:56:25 +01:00
|
|
|
}
|