/* Chrysalide - Outil d'analyse de fichiers binaires * configuration.c - éléments de configuration du programme * * Copyright (C) 2014-2017 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Chrysalide. If not, see . */ #include "configuration.h" #include #include #include #include #include #include #include #include "../common/cpp.h" #include "../common/extstr.h" #include "../common/fnv1a.h" #include "../common/io.h" #include "../common/xdg.h" #include "../common/xml.h" /* ---------------------------- ELEMENT DE CONFIGURATION ---------------------------- */ /* Valeurs supportées par les paramètres */ typedef union _param_value { bool boolean; /* Valeur booléenne */ int integer; /* Valeur entière */ unsigned long ulong; /* Valeur entière positive */ char *string; /* Chaîne de caractères */ } param_value; /* Configuration générique quelconque (instance) */ struct _GCfgParam { GObject parent; /* A laisser en premier */ char *path; /* Chemin d'accès XML */ fnv64_t hash; /* Empreinte pour accès rapide */ ConfigParamType type; /* Type de valeur */ ConfigParamState cached_state; /* Etat du paramétrage */ param_value def; /* Valeur par défaut */ bool def_empty; /* Non défini par défaut ? */ param_value cur; /* Valeur courante */ bool cur_empty; /* Actuellement non défini ? */ }; /* Configuration générique quelconque (classe) */ struct _GCfgParamClass { GObjectClass parent; /* A laisser en premier */ /* Signaux */ void (* modified) (GCfgParam *); }; /* Initialise la classe des blocs de données binaires. */ static void g_config_param_class_init(GCfgParamClass *); /* Initialise une instance de bloc de données binaires. */ static void g_config_param_init(GCfgParam *); /* Supprime toutes les références externes. */ static void g_config_param_dispose(GCfgParam *); /* Procède à la libération totale de la mémoire. */ static void g_config_param_finalize(GCfgParam *); /* Lit un paramètre de configuration depuis un fichier. */ static bool g_config_param_read(GCfgParam *, xmlXPathContextPtr); /* Ecrit un paramètre de configuration dans un fichier. */ static bool g_config_param_write(GCfgParam *, xmlDocPtr, xmlXPathContextPtr); /* --------------------- PARTIES IMPREVISIBLES DE CONFIGURATION --------------------- */ /* Groupe de paramètres non fixés à l'avance (instance) */ struct _GCfgGroup { GObject parent; /* A laisser en premier */ char *root; /* Chemin d'accès commun */ ConfigParamType type; /* Type de valeur */ }; /* Groupe de paramètres non fixés à l'avance (classe) */ struct _GCfgGroupClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe des groupes de paramètres. */ static void g_config_group_class_init(GCfgGroupClass *); /* Initialise une instance de groupe de paramètres. */ static void g_config_group_init(GCfgGroup *); /* Supprime toutes les références externes. */ static void g_config_group_dispose(GCfgGroup *); /* Procède à la libération totale de la mémoire. */ static void g_config_group_finalize(GCfgGroup *); /* Charge tous les paramètres correspondants au groupe. */ static void g_config_group_load(GCfgGroup *, GGenConfig *, xmlXPathContextPtr); /* ----------------------- GESTION GENERIQUE DE CONFIGURATION ----------------------- */ /* Configuration générique quelconque (instance) */ struct _GGenConfig { GObject parent; /* A laisser en premier */ char *filename; /* CHemin d'accès complet */ GList *groups; /* Groupes d'éléments non fixés*/ GList *params; /* Eléments de configuration */ GRWLock params_access; /* Verrou de protection */ }; /* Configuration générique quelconque (classe) */ struct _GGenConfigClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe des blocs de données binaires. */ static void g_generic_config_class_init(GGenConfigClass *); /* Initialise une instance de bloc de données binaires. */ static void g_generic_config_init(GGenConfig *); /* Supprime toutes les références externes. */ static void g_generic_config_dispose(GGenConfig *); /* Procède à la libération totale de la mémoire. */ static void g_generic_config_finalize(GGenConfig *); /* ---------------------------------------------------------------------------------- */ /* ELEMENT DE CONFIGURATION */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini par la GLib pour les configurations génériques. */ G_DEFINE_TYPE(GCfgParam, g_config_param, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des blocs de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_param_class_init(GCfgParamClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_config_param_dispose; object->finalize = (GObjectFinalizeFunc)g_config_param_finalize; g_signal_new("modified", G_TYPE_CFG_PARAM, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GCfgParamClass, modified), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } /****************************************************************************** * * * Paramètres : param = instance à initialiser. * * * * Description : Initialise une instance de bloc de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_param_init(GCfgParam *param) { } /****************************************************************************** * * * Paramètres : param = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_param_dispose(GCfgParam *param) { G_OBJECT_CLASS(g_config_param_parent_class)->dispose(G_OBJECT(param)); } /****************************************************************************** * * * Paramètres : param = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_param_finalize(GCfgParam *param) { free(param->path); switch (param->type) { case CPT_STRING: if (param->def.string != NULL) free(param->def.string); if (param->cur.string != NULL) free(param->cur.string); break; default: break; } G_OBJECT_CLASS(g_config_param_parent_class)->finalize(G_OBJECT(param)); } /****************************************************************************** * * * Paramètres : path = chemin d'accès à un paramètre en guise de clef. * * type = type de paramètre à installer. * * ... = valeur par défaut du paramètre. * * * * Description : Crée un paramètre de configuration. * * * * Retour : Elément mis en place. * * * * Remarques : - * * * ******************************************************************************/ GCfgParam *g_config_param_new(const char *path, ConfigParamType type, ...) { GCfgParam *result; /* Structure à retourner */ va_list ap; /* Liste d'arguments */ result = g_object_new(G_TYPE_CFG_PARAM, NULL); result->path = strdup(path); result->hash = fnv_64a_hash(path); result->type = type; va_start(ap, type); switch (result->type) { case CPT_BOOLEAN: result->def.boolean = va_arg(ap, /*bool*/int); break; case CPT_INTEGER: result->def.integer = va_arg(ap, int); break; case CPT_ULONG: result->def.ulong = va_arg(ap, unsigned long); break; case CPT_STRING: result->def.string = va_arg(ap, char *); if (result->def.string != NULL) result->def.string = strdup(result->def.string); break; default: g_object_unref(G_OBJECT(result)); result = NULL; assert(false); break; } va_end(ap); g_config_param_reset(result); return result; } /****************************************************************************** * * * Paramètres : path = chemin d'accès à un paramètre en guise de clef. * * type = type de paramètre à installer. * * * * Description : Crée un paramètre de configuration sans valeur. * * * * Retour : Elément mis en place. * * * * Remarques : - * * * ******************************************************************************/ GCfgParam *g_config_param_new_empty(const char *path, ConfigParamType type) { GCfgParam *result; /* Structure à retourner */ result = g_object_new(G_TYPE_CFG_PARAM, NULL); result->path = strdup(path); result->hash = fnv_64a_hash(path); result->type = type; g_config_param_make_empty(result); result->def = result->cur; result->def_empty = true; return result; } /****************************************************************************** * * * Paramètres : param = paramètre de configuration à charger. * * context = contexte de lecture d'un fichier XML. * * * * Description : Lit un paramètre de configuration depuis un fichier. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_config_param_read(GCfgParam *param, xmlXPathContextPtr context) { char *access; /* Chemin d'accès XML */ char *value; /* Valeur en chaîne de carac. */ unsigned long ulval; /* Valeur transformée */ access = strdup(param->path); access = strrpl(access, ".", "/"); access = strprep(access, "/ChrysalideConfig/"); value = get_node_text_value(context, access); if (value == NULL) goto gcpr_no_value; if (strlen(value) == 0) g_config_param_make_empty(param); else switch (param->type) { case CPT_BOOLEAN: g_config_param_set_value(param, strcmp(value, "true") == 0); break; case CPT_INTEGER: g_config_param_set_value(param, atoi(value)); break; case CPT_ULONG: ulval = strtoul(value, NULL, 10); g_config_param_set_value(param, ulval); break; case CPT_STRING: g_config_param_set_value(param, value); break; default: assert(false); break; } free(value); gcpr_no_value: free(access); return true; } /****************************************************************************** * * * Paramètres : param = paramètre de configuration à charger. * * xdoc = document XML à mettre en place. * * context = contexte de lecture d'un fichier XML. * * * * Description : Ecrit un paramètre de configuration dans un fichier. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_config_param_write(GCfgParam *param, xmlDocPtr xdoc, xmlXPathContextPtr context) { bool result; /* Bilan à retourner */ ConfigParamState state; /* Etat du paramètre */ char *access; /* Chemin d'accès XML */ char int_val[sizeof(XSTR(INT_MIN)) + 1];/* Valeur en chaîne de carac. */ char ul_val[sizeof(XSTR(ULONG_MAX)) + 1];/* Valeur en chaîne de carac. */ state = g_config_param_get_state(param); if (state & CPS_DEFAULT) return true; result = true; access = strdup(param->path); access = strrpl(access, ".", "/"); access = strprep(access, "/ChrysalideConfig/"); if (state & CPS_EMPTY) result = add_content_to_node(xdoc, context, access, ""); else switch (param->type) { case CPT_BOOLEAN: result = add_content_to_node(xdoc, context, access, param->cur.boolean ? "true" : "false"); break; case CPT_INTEGER: snprintf(int_val, sizeof(int_val), "%d", param->cur.integer); result = add_content_to_node(xdoc, context, access, int_val); break; case CPT_ULONG: snprintf(ul_val, sizeof(ul_val), "%lu", param->cur.ulong); result = add_content_to_node(xdoc, context, access, ul_val); break; case CPT_STRING: if (param->def.string != NULL && param->def.string != NULL && strcmp(param->cur.string, param->def.string) == 0) break; result = add_content_to_node(xdoc, context, access, param->cur.string != NULL ? param->cur.string : ""); break; default: assert(false); break; } free(access); return result; } /****************************************************************************** * * * Paramètres : param = paramètre de configuration à analyser. * * path = chemin d'accès à retrouver. * * * * Description : Etablit une comparaison selon un chemin d'accès. * * * * Retour : -1, 0 ou 1 selon les conclusions de la comparaison. * * * * Remarques : - * * * ******************************************************************************/ gint g_config_param_compare(const GCfgParam *param, const GCfgParam *path) { gint result; /* Bilan à renvoyer */ result = cmp_fnv_64a(param->hash, path->hash); if (result == 0) result = strcmp(param->path, path->path); return result; } /****************************************************************************** * * * Paramètres : param = paramètre de configuration à consulter. * * * * Description : Indique le chemin d'accès utilisé pour un paramètre. * * * * Retour : Clef servant de chemin d'accès. * * * * Remarques : - * * * ******************************************************************************/ const char *g_config_param_get_path(const GCfgParam *param) { return param->path; } /****************************************************************************** * * * Paramètres : param = paramètre de configuration à consulter. * * * * Description : Indique le type de valeur utilisée par un paramètre. * * * * Retour : Type de paramètre. * * * * Remarques : - * * * ******************************************************************************/ ConfigParamType g_config_param_get_ptype(const GCfgParam *param) { return param->type; } /****************************************************************************** * * * Paramètres : param = paramètre de configuration à consulter. * * * * Description : Indique le statut d'une valeur utilisée par un paramètre. * * * * Retour : Etat de paramètre. * * * * Remarques : - * * * ******************************************************************************/ ConfigParamState g_config_param_get_state(GCfgParam *param) { if (param->cached_state == CPS_UNDEFINED) { if (param->def_empty || param->cur_empty) { param->cached_state = (param->def_empty && param->cur_empty ? CPS_DEFAULT : CPS_CHANGED); param->cached_state |= CPS_EMPTY; goto gcpgs_recached; } switch (param->type) { case CPT_BOOLEAN: param->cached_state = (param->def.boolean == param->cur.boolean ? CPS_DEFAULT : CPS_CHANGED); break; case CPT_INTEGER: param->cached_state = (param->def.integer == param->cur.integer ? CPS_DEFAULT : CPS_CHANGED); break; case CPT_ULONG: param->cached_state = (param->def.ulong == param->cur.ulong ? CPS_DEFAULT : CPS_CHANGED); break; case CPT_STRING: if (param->def.string == NULL && param->cur.string == NULL) param->cached_state = CPS_DEFAULT; else if (param->def.string != NULL && param->cur.string != NULL) param->cached_state = strcmp(param->def.string, param->cur.string) == 0 ? CPS_DEFAULT : CPS_CHANGED; else param->cached_state = CPS_CHANGED; break; default: break; } gcpgs_recached:; } return param->cached_state; } /****************************************************************************** * * * Paramètres : param = paramètre à mettre à jour. * * * * Description : Efface toute valeur courante d'un paramètre de configuration.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_config_param_make_empty(GCfgParam *param) { switch (param->type) { case CPT_BOOLEAN: param->cur.boolean = false; break; case CPT_INTEGER: param->cur.integer = INT_MIN; break; case CPT_ULONG: param->cur.ulong = 0; break; case CPT_STRING: if (param->cur.string != NULL) { free(param->cur.string); param->cur.string = NULL; } break; default: break; } if (!param->cur_empty) { param->cur_empty = true; /* Réinitialisation du cache d'état... */ param->cached_state = CPS_UNDEFINED; g_signal_emit_by_name(param, "modified"); } } /****************************************************************************** * * * Paramètres : param = paramètre à mettre à jour. * * * * Description : Réinitialise la valeur d'un paramètre de configuration. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_config_param_reset(GCfgParam *param) { bool modified; /* Détermine une modification */ modified = !(g_config_param_get_state(param) & CPS_DEFAULT); switch (param->type) { case CPT_BOOLEAN: param->cur.boolean = param->def.boolean; break; case CPT_INTEGER: param->cur.integer = param->def.integer; break; case CPT_ULONG: param->cur.ulong = param->def.ulong; break; case CPT_STRING: if (param->def.string != NULL) param->cur.string = strdup(param->def.string); else param->cur.string = NULL; break; default: assert(false); break; } if (modified) { param->cur_empty = param->def_empty; /* Réinitialisation du cache d'état... */ param->cached_state = CPS_UNDEFINED; g_signal_emit_by_name(param, "modified"); } } /****************************************************************************** * * * Paramètres : param = paramètre à mettre à jour. * * ... = nouvelle valeur du paramètre. * * * * Description : Modifie la valeur courante d'un paramètre de configuration. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_config_param_set_value(GCfgParam *param, ...) { va_list ap; /* Liste d'arguments */ bool old_boolean; /* Valeur booléenne */ int old_integer; /* Valeur entière */ unsigned long old_ulong; /* Valeur entière positive */ char *old_string; /* Chaîne de caractères */ bool modified; /* Détermine une modification */ va_start(ap, param); switch (param->type) { case CPT_BOOLEAN: old_boolean = param->cur.boolean; param->cur.boolean = va_arg(ap, /*bool*/int); modified = (old_boolean != param->cur.boolean); break; case CPT_INTEGER: old_integer = param->cur.integer; param->cur.integer = va_arg(ap, int); modified = (old_integer != param->cur.integer); break; case CPT_ULONG: old_ulong = param->cur.ulong; param->cur.ulong = va_arg(ap, unsigned long); modified = (old_ulong != param->cur.ulong); break; case CPT_STRING: old_string = param->cur.string; param->cur.string = va_arg(ap, char *); if (param->cur.string != NULL) param->cur.string = strdup(param->cur.string); if (old_string == NULL && param->cur.string == NULL) modified = false; else if (old_string != NULL && param->cur.string != NULL) modified = (strcmp(old_string, param->cur.string) != 0); else modified = true; if (old_string != NULL) free(old_string); break; default: assert(false); modified = false; break; } va_end(ap); if (modified) { param->cur_empty = false; /* Réinitialisation du cache d'état... */ param->cached_state = CPS_UNDEFINED; g_signal_emit_by_name(param, "modified"); } } /****************************************************************************** * * * Paramètres : param = paramètre à consulter. * * ... = zone d'enregistrement de la valeur du paramètre. * * * * Description : Indique la valeur courante d'un paramètre de configuration. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_config_param_get_value(GCfgParam *param, ...) { va_list ap; /* Liste d'arguments */ va_start(ap, param); switch (param->type) { case CPT_BOOLEAN: *(va_arg(ap, bool *)) = param->cur.boolean; break; case CPT_INTEGER: *(va_arg(ap, int *)) = param->cur.integer; break; case CPT_ULONG: *(va_arg(ap, unsigned long *)) = param->cur.ulong; break; case CPT_STRING: *(va_arg(ap, char **)) = param->cur.string; break; default: assert(false); break; } va_end(ap); } /* ---------------------------------------------------------------------------------- */ /* PARTIES IMPREVISIBLES DE CONFIGURATION */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini par la GLib pour les groupes de paramètres non prévisibiles. */ G_DEFINE_TYPE(GCfgGroup, g_config_group, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des groupes de paramètres. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_group_class_init(GCfgGroupClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_config_group_dispose; object->finalize = (GObjectFinalizeFunc)g_config_group_finalize; } /****************************************************************************** * * * Paramètres : group = instance à initialiser. * * * * Description : Initialise une instance de groupe de paramètres. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_group_init(GCfgGroup *group) { } /****************************************************************************** * * * Paramètres : group = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_group_dispose(GCfgGroup *group) { G_OBJECT_CLASS(g_config_group_parent_class)->dispose(G_OBJECT(group)); } /****************************************************************************** * * * Paramètres : group = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_group_finalize(GCfgGroup *group) { free(group->root); G_OBJECT_CLASS(g_config_group_parent_class)->finalize(G_OBJECT(group)); } /****************************************************************************** * * * Paramètres : root = base du chemin d'accès à un groupe de paramètres. * * type = type de groupètre à installer. * * * * Description : Crée un groupe de paramètres de configuration. * * * * Retour : Groupe mis en place. * * * * Remarques : - * * * ******************************************************************************/ GCfgGroup *g_config_group_new(const char *root, ConfigParamType type) { GCfgGroup *result; /* Structure à retourner */ result = g_object_new(G_TYPE_CFG_GROUP, NULL); result->root = strdup(root); result->type = type; return result; } /****************************************************************************** * * * Paramètres : group = groupe de paramètres de configuration à étudier. * * config = ensemble de paramètres à compléter. * * context = contexte de lecture d'un fichier XML. * * * * Description : Charge tous les paramètres correspondants au groupe. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_config_group_load(GCfgGroup *group, GGenConfig *config, xmlXPathContextPtr context) { char *access; /* Chemin d'accès XML */ xmlXPathObjectPtr xpathObj; /* Cible d'une recherche */ int i; /* Boucle de parcours */ char *key; /* Clef d'accès à un paramètre */ GCfgParam *param; /* Nouveau paramètre à ajouter */ access = strdup(group->root); access = strrpl(access, ".", "/"); access = stradd(access, "/*"); access = strprep(access, "/ChrysalideConfig/"); xpathObj = get_node_xpath_object(context, access); for (i = 0; i < XPATH_OBJ_NODES_COUNT(xpathObj); i++) { asprintf(&key, "%s.%s", group->root, NODE_FROM_PATH_OBJ(xpathObj, i)->name); param = g_config_param_new(key, CPT_INTEGER, -1); _g_generic_config_add_param(config, param, false); free(key); } if (xpathObj != NULL) xmlXPathFreeObject(xpathObj); free(access); } /* ---------------------------------------------------------------------------------- */ /* GESTION GENERIQUE DE CONFIGURATION */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini par la GLib pour les configurations génériques. */ G_DEFINE_TYPE(GGenConfig, g_generic_config, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des blocs de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_generic_config_class_init(GGenConfigClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_generic_config_dispose; object->finalize = (GObjectFinalizeFunc)g_generic_config_finalize; } /****************************************************************************** * * * Paramètres : config = instance à initialiser. * * * * Description : Initialise une instance de bloc de données binaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_generic_config_init(GGenConfig *config) { g_rw_lock_init(&config->params_access); } /****************************************************************************** * * * Paramètres : config = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_generic_config_dispose(GGenConfig *config) { GList *iter; /* Boucle de parcours */ for (iter = g_list_first(config->params); iter != NULL; iter = g_list_next(iter)) { g_object_unref(G_OBJECT(iter->data)); } G_OBJECT_CLASS(g_generic_config_parent_class)->dispose(G_OBJECT(config)); } /****************************************************************************** * * * Paramètres : config = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_generic_config_finalize(GGenConfig *config) { free(config->filename); g_rw_lock_clear(&config->params_access); G_OBJECT_CLASS(g_generic_config_parent_class)->finalize(G_OBJECT(config)); } /****************************************************************************** * * * Paramètres : name = désignation de la configuration. * * * * Description : Crée une description de partie de code vierge. * * * * Retour : Elément mis en place. * * * * Remarques : - * * * ******************************************************************************/ GGenConfig *g_generic_config_new(const char *name) { GGenConfig *result; /* Structure à retourner */ char *suffix; /* Fin du nom de fichier */ result = g_object_new(G_TYPE_GEN_CONFIG, NULL); suffix = strdup("chrysalide"); suffix = stradd(suffix, G_DIR_SEPARATOR_S); suffix = stradd(suffix, name); suffix = stradd(suffix, ".xml"); result->filename = get_xdg_config_dir(suffix); free(suffix); return result; } /****************************************************************************** * * * Paramètres : config = ensemble de paramètres de configuration à consulter.* * * * Description : Indique le fichier utilisé pour l'enregistrement XML. * * * * Retour : Chemin d'accès, potentiellement non existant. * * * * Remarques : - * * * ******************************************************************************/ const char *g_generic_config_get_filename(const GGenConfig *config) { return config->filename; } /****************************************************************************** * * * Paramètres : config = configuration à mettre à jour. * * write = précise le type d'accès prévu (lecture/écriture). * * lock = indique le sens du verrouillage à mener. * * * * Description : Met à disposition un encadrement des accès aux paramètres. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_generic_config_lock_unlock(GGenConfig *config, bool write, bool lock) { if (write) { if (lock) g_rw_lock_writer_lock(&config->params_access); else g_rw_lock_writer_unlock(&config->params_access); } else { if (lock) g_rw_lock_reader_lock(&config->params_access); else g_rw_lock_reader_unlock(&config->params_access); } } /****************************************************************************** * * * Paramètres : config = ensemble de paramètres de configuration à charger. * * * * Description : Lit la configuration depuis un fichier. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_generic_config_read(GGenConfig *config) { bool result; /* Bilan à retourner */ int ret; /* Test de présence de fichier */ xmlDocPtr xdoc; /* Document XML de configurat° */ xmlXPathContextPtr context; /* Contexte de recherche XPath */ GList *iter; /* Boucle de parcours */ ret = access(config->filename, F_OK); if (ret != 0) return true; if (!open_xml_file(config->filename, &xdoc, &context)) return false; g_generic_config_rlock(config); for (iter = g_list_first(config->groups); iter != NULL; iter = g_list_next(iter)) { g_config_group_load(G_CFG_GROUP(iter->data), config, context); } for (result = true, iter = g_list_first(config->params); result && iter != NULL; iter = g_list_next(iter)) { result = g_config_param_read(G_CFG_PARAM(iter->data), context); } g_generic_config_runlock(config); close_xml_file(xdoc, context); return true; } /****************************************************************************** * * * Paramètres : config = ensemble de paramètres de configuration à décharger.* * * * Description : Ecrit la configuration dans un fichier. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_generic_config_write(GGenConfig *config) { bool result; /* Bilan à retourner */ xmlDocPtr xdoc; /* Document XML de configurat° */ xmlXPathContextPtr context; /* Contexte de recherche XPath */ GList *iter; /* Boucle de parcours */ if (!create_new_xml_file(&xdoc, &context)) return false; g_generic_config_rlock(config); for (result = true, iter = g_list_first(config->params); result && iter != NULL; iter = g_list_next(iter)) { result = g_config_param_write(G_CFG_PARAM(iter->data), xdoc, context); } g_generic_config_runlock(config); save_xml_file(xdoc, config->filename); close_xml_file(xdoc, context); return result; } /****************************************************************************** * * * Paramètres : config = configuration à mettre à jour. * * param = groupe de paramètres à prendre en compte. * * * * Description : Ajoute la définition d'un groupe à une configuration. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_generic_config_add_group(GGenConfig *config, GCfgGroup *group) { g_generic_config_wlock(config); config->groups = g_list_append(config->groups, group); g_generic_config_wunlock(config); } /****************************************************************************** * * * Paramètres : config = ensemble de paramètres de configuration. * * path = chemin d'accès au paramètre visé. * * lock = pose un verrou si ce n'est déjà fait. * * * * Description : Retrouve un élément de configuration par son chemin. * * * * Retour : Elément trouvé ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GCfgParam *_g_generic_config_search(GGenConfig *config, const char *path, bool lock) { GList *item; /* Elément générique de liste */ GCfgParam fake; /* Faux élément partiel */ fake.path = (char *)path; fake.hash = fnv_64a_hash(path); if (lock) g_generic_config_rlock(config); item = g_list_find_custom(config->params, &fake, (GCompareFunc)g_config_param_compare); if (lock) g_generic_config_runlock(config); return item ? item->data : NULL; } /****************************************************************************** * * * Paramètres : config = configuration à mettre à jour. * * param = paramètre à intégrer dans la configuration. * * lock = pose un verrou si ce n'est déjà fait. * * * * Description : Ajoute un paramètre à une configuration. * * * * Retour : Elément mis en place ou NULL en cas d'erreur. * * * * Remarques : - * * * ******************************************************************************/ GCfgParam *_g_generic_config_add_param(GGenConfig *config, GCfgParam *param, bool lock) { const char *path; /* Chemin d'accès unique */ GCfgParam *old; /* Test de présence */ path = g_config_param_get_path(param); if (lock) g_generic_config_wlock(config); old = _g_generic_config_search(config, path, false); if (old != NULL) { g_object_unref(G_OBJECT(param)); return NULL; } config->params = g_list_append(config->params, param); if (lock) g_generic_config_wunlock(config); return param; } /****************************************************************************** * * * Paramètres : config = configuration à mettre à jour. * * path = chemin d'accès au paramètre visé. * * * * Description : Retire un paramètre d'une configuration. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_generic_config_delete_param(GGenConfig *config, const char *path) { GCfgParam *old; /* Test de présence */ g_generic_config_wlock(config); old = _g_generic_config_search(config, path, false); if (old != NULL) config->params = g_list_remove(config->params, old); g_generic_config_wunlock(config); } /****************************************************************************** * * * Paramètres : config = configuration à consulter. * * * * Description : Renvoie la liste des paramètres de configuration. * * * * Retour : Liste d'éléments à parcourir. * * * * Remarques : - * * * ******************************************************************************/ GList *g_generic_config_list_params(const GGenConfig *config) { /** * Un verrou doit être posé ! * Il n'y a pas d'assert() possible pour le vérifier... */ return config->params; }