This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
gnoemoe/gnoemoe/gm-options.c

331 lines
7.8 KiB
C

#include <glib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <libxml/parser.h>
#include <unistd.h>
#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);
}