summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am4
-rw-r--r--plugins/dalvik/context.h2
-rw-r--r--plugins/pe/Makefile.am19
-rw-r--r--plugins/pe/core-int.h56
-rw-r--r--plugins/pe/core.c288
-rw-r--r--plugins/pe/core.h14
-rw-r--r--plugins/pe/format-int.c (renamed from plugins/pe/pe-int.c)28
-rw-r--r--plugins/pe/format-int.h (renamed from plugins/pe/pe-int.h)37
-rw-r--r--plugins/pe/format.c646
-rw-r--r--plugins/pe/format.h34
-rw-r--r--plugins/pe/pe-def.h (renamed from plugins/pe/pe_def.h)55
-rw-r--r--plugins/pe/python/Makefile.am8
-rw-r--r--plugins/pe/python/constants.c5
-rw-r--r--plugins/pe/python/constants.h2
-rw-r--r--plugins/pe/python/format.c205
-rw-r--r--plugins/pe/python/module.c6
-rw-r--r--plugins/pe/python/translate.c290
-rw-r--r--plugins/pe/python/translate.h10
-rw-r--r--plugins/pe/section.c73
-rw-r--r--plugins/pychrysalide/Makefile.am23
-rw-r--r--plugins/pychrysalide/analysis/content.c42
-rw-r--r--plugins/pychrysalide/arch/module.c3
-rw-r--r--plugins/pychrysalide/bindings.c1466
-rw-r--r--plugins/pychrysalide/bindings.h73
-rw-r--r--plugins/pychrysalide/constants.c1
-rw-r--r--plugins/pychrysalide/convert.c89
-rw-r--r--plugins/pychrysalide/convert.h (renamed from plugins/pychrysalide/plugins/translate.h)17
-rw-r--r--plugins/pychrysalide/core-int.h58
-rw-r--r--plugins/pychrysalide/core-ui-int.h56
-rw-r--r--plugins/pychrysalide/core-ui.c319
-rw-r--r--plugins/pychrysalide/core-ui.h64
-rw-r--r--plugins/pychrysalide/core.c1269
-rw-r--r--plugins/pychrysalide/core.h30
-rw-r--r--plugins/pychrysalide/format/executable.c22
-rw-r--r--plugins/pychrysalide/format/known.c22
-rw-r--r--plugins/pychrysalide/format/program.c62
-rw-r--r--plugins/pychrysalide/glibext/Makefile.am10
-rw-r--r--plugins/pychrysalide/glibext/comparable.c482
-rw-r--r--plugins/pychrysalide/glibext/comparable.h45
-rw-r--r--plugins/pychrysalide/glibext/hashable.c (renamed from plugins/pychrysalide/glibext/comparison.c)251
-rw-r--r--plugins/pychrysalide/glibext/hashable.h (renamed from plugins/pychrysalide/glibext/comparison.h)20
-rw-r--r--plugins/pychrysalide/glibext/module.c19
-rw-r--r--plugins/pychrysalide/glibext/objhole.c329
-rw-r--r--plugins/pychrysalide/glibext/objhole.h (renamed from plugins/pychrysalide/plugins/constants.h)22
-rw-r--r--plugins/pychrysalide/glibext/secstorage.c624
-rw-r--r--plugins/pychrysalide/glibext/secstorage.h45
-rw-r--r--plugins/pychrysalide/glibext/singleton.c510
-rw-r--r--plugins/pychrysalide/glibext/strbuilder.c542
-rw-r--r--plugins/pychrysalide/glibext/strbuilder.h45
-rw-r--r--plugins/pychrysalide/glibext/work.c18
-rw-r--r--plugins/pychrysalide/glibext/workqueue.c6
-rw-r--r--plugins/pychrysalide/helpers.c292
-rw-r--r--plugins/pychrysalide/helpers.h108
-rw-r--r--plugins/pychrysalide/plugins/Makefile.am6
-rw-r--r--plugins/pychrysalide/plugins/constants.c147
-rw-r--r--plugins/pychrysalide/plugins/module.c11
-rw-r--r--plugins/pychrysalide/plugins/plugin.c963
-rw-r--r--plugins/pychrysalide/plugins/plugin.h7
-rw-r--r--plugins/pychrysalide/plugins/python-int.h58
-rw-r--r--plugins/pychrysalide/plugins/python.c489
-rw-r--r--plugins/pychrysalide/plugins/python.h57
-rw-r--r--plugins/pychrysalide/plugins/translate.c110
-rw-r--r--plugins/pynb/Makefile.am77
-rw-r--r--plugins/pynb/core-ui-int.h56
-rw-r--r--plugins/pynb/core-ui.c363
-rw-r--r--plugins/pynb/core-ui.h43
-rw-r--r--plugins/pynb/data/images/pynb-symbolic.svg144
-rw-r--r--plugins/pynb/gresource.xml11
-rw-r--r--plugins/pynb/panel-int.h56
-rw-r--r--plugins/pynb/panel.c210
-rw-r--r--plugins/pynb/panel.h47
-rw-r--r--plugins/pynb/panel.ui21
-rw-r--r--plugins/pynb/params-int.h50
-rw-r--r--plugins/pynb/params.c171
-rw-r--r--plugins/pynb/params.h41
-rw-r--r--plugins/pynb/params.ui52
-rw-r--r--plugins/pynb/prefs-int.h48
-rw-r--r--plugins/pynb/prefs.c143
-rw-r--r--plugins/pynb/prefs.h (renamed from plugins/pe/section.h)21
-rw-r--r--plugins/pynb/prefs.ui38
80 files changed, 9158 insertions, 3048 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 9e177fa..529f4fe 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -13,7 +13,9 @@ endif
# androhelpers
SUBDIRS = \
- $(PYTHON3_SUBDIRS)
+ $(PYTHON3_SUBDIRS) \
+ pe \
+ pynb
# arm \
# bootimg \
diff --git a/plugins/dalvik/context.h b/plugins/dalvik/context.h
index f09cfa6..1954b1e 100644
--- a/plugins/dalvik/context.h
+++ b/plugins/dalvik/context.h
@@ -58,7 +58,7 @@ GType g_dalvik_context_get_type(void);
GDalvikContext *g_dalvik_context_new(void);
/* Mémorise une zone comme étant des données de branchements. */
-bool g_dalvik_context_register_switch_data(GDalvikContext *ctx, const vmpa2t *start, phys_t length);
+bool g_dalvik_context_register_switch_data(GDalvikContext *, const vmpa2t *, phys_t);
/* Mémorise une zone comme étant des données d'un tableau. */
bool g_dalvik_context_register_array_data(GDalvikContext *, const vmpa2t *, uint16_t, phys_t);
diff --git a/plugins/pe/Makefile.am b/plugins/pe/Makefile.am
index e9cd482..ddc4ee4 100644
--- a/plugins/pe/Makefile.am
+++ b/plugins/pe/Makefile.am
@@ -39,22 +39,25 @@ endif
libpe_la_SOURCES = \
core.h core.c \
- pe-int.h pe-int.c \
+ format-int.h format-int.c \
format.h format.c \
- pe_def.h \
- rich.h rich.c \
- routine.h routine.c \
- section.h section.c \
- symbols.h symbols.c
+ pe-def.h
+
+
+# rich.h rich.c \
+# routine.h routine.c \
+# symbols.h symbols.c
+
+
libpe_la_LIBADD = \
$(PYTHON3_LIBADD)
-libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src
+libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) -I$(top_srcdir)/src
libpe_la_LDFLAGS = \
-avoid-version \
- -L$(top_srcdir)/src/.libs -lchrysacore \
+ -L$(top_srcdir)/src/.libs -lchrysacore4 \
$(RUN_PATH) $(PYTHON3_LDFLAGS)
diff --git a/plugins/pe/core-int.h b/plugins/pe/core-int.h
new file mode 100644
index 0000000..1d1b817
--- /dev/null
+++ b/plugins/pe/core-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-int.h - prototypes internes pour l'intégration du support du format PE
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PE_CORE_INT_H
+#define _PLUGINS_PE_CORE_INT_H
+
+
+#include "core.h"
+
+
+#include <plugins/native-int.h>
+
+
+
+/* Greffon natif pour le support du format PE (instance) */
+struct _GPePlugin
+{
+ GNativePlugin parent; /* A laisser en premier */
+
+};
+
+
+/* Greffon natif pour le support du format PE (classe) */
+struct _GPePluginClass
+{
+ GNativePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un greffon de support PE. */
+bool g_pe_plugin_create(GPePlugin *, GModule *);
+
+
+
+#endif /* _PLUGINS_PE_CORE_INT_H */
diff --git a/plugins/pe/core.c b/plugins/pe/core.c
index ddbacf5..b752735 100644
--- a/plugins/pe/core.c
+++ b/plugins/pe/core.c
@@ -24,35 +24,244 @@
#include "core.h"
-#include <core/global.h>
+//#include <core/global.h>
#include <plugins/self.h>
+#include "core-int.h"
#include "format.h"
#ifdef INCLUDE_PYTHON3_BINDINGS
# include "python/module.h"
#endif
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des greffons de support PE. */
+static void g_pe_plugin_class_init(GPePluginClass *);
+
+/* Procède à l'initialisation de l'interface de gestion. */
+//static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *);
+
+/* Initialise une instance de greffon de support PE. */
+static void g_pe_plugin_init(GPePlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_pe_plugin_dispose(GPePlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_pe_plugin_finalize(GPePlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_pe_plugin_enable(GPePlugin *);
+
+/* Prend acte de la désactivation du greffon. */
+static bool g_pe_plugin_disable(GPePlugin *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon de liaison Python */
+G_DEFINE_TYPE_WITH_CODE(GPePlugin, g_pe_plugin, G_TYPE_NATIVE_PLUGIN,
+ /*G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pe_plugin_plugin_manager_interface_init)*/);
+
+
+NATIVE_PLUGIN_ENTRYPOINT(g_pe_plugin_new);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons de support PE. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_class_init(GPePluginClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_pe_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_pe_plugin_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_pe_plugin_enable;
+ plugin->disable = (pg_management_fc)g_pe_plugin_disable;
+
+}
+
+#if 0
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface de gestion. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface)
+{
+ iface->handle_native = (handle_native_plugins_cb)g_pe_plugin_handle_native_plugins_loaded_event;
+
+}
+
+#endif
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon de support PE. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_init(GPePlugin *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_dispose(GPePlugin *plugin)
+{
+ G_OBJECT_CLASS(g_pe_plugin_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_finalize(GPePlugin *plugin)
+{
+ G_OBJECT_CLASS(g_pe_plugin_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = extension vue du système. *
+* *
+* Description : Crée un module pour un greffon de support PE. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+GPluginModule *g_pe_plugin_new(GModule *module)
+{
+ GPePlugin *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_PE_PLUGIN, NULL);
+
+ if (!g_pe_plugin_create(result, module))
+ g_clear_object(&result);
+
+ return G_PLUGIN_MODULE(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = extension vue du système. *
+* *
+* Description : Met en place un module pour un greffon de support PE. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_pe_plugin_create(GPePlugin *plugin, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
#ifdef INCLUDE_PYTHON3_BINDINGS
-# define PG_REQ RL("PyChrysalide")
+# define PG_REQ REQ_LIST("PyChrysalide")
#else
# define PG_REQ NO_REQ
#endif
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PeFmt",
+ "PE format support",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("doc/formats"),
+ PG_REQ,
+ module);
+ return result;
-DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support",
- PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/formats"),
- PG_REQ, AL(PGA_PLUGIN_INIT, PGA_CONTENT_RESOLVER));
+}
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
/******************************************************************************
* *
* Paramètres : plugin = greffon à manipuler. *
* *
-* Description : Prend acte du chargement du greffon. *
+* Description : Prend acte de l'activation du greffon. *
* *
* Retour : - *
* *
@@ -60,7 +269,7 @@ DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support",
* *
******************************************************************************/
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
+static bool g_pe_plugin_enable(GPePlugin *plugin)
{
bool result; /* Bilan à retourner */
@@ -77,6 +286,65 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
/******************************************************************************
* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de la désactivation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_pe_plugin_disable(GPePlugin *plugin)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ return result;
+
+}
+
+
+
+#if 0
+
+/* ---------------------------------------------------------------------------------- */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = interface à manipuler. *
+* *
+* Description : Accompagne la fin du chargement des modules natifs. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_handle_native_plugins_loaded_event(GPePlugin *plugin)
+{
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+
+ gstate = PyGILState_Ensure();
+
+ load_python_plugins(G_PLUGIN_MODULE(plugin));
+
+ PyGILState_Release(gstate);
+
+}
+
+
+
+/******************************************************************************
+* *
* Paramètres : plugin = greffon à manipuler. *
* action = type d'action attendue. *
* content = contenu binaire à traiter. *
@@ -90,7 +358,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
* Remarques : - *
* *
******************************************************************************/
-
+#if 0
G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status)
{
bool test; /* Bilan des accès mémoire */
@@ -114,3 +382,7 @@ G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule
}
}
+#endif
+
+
+#endif
diff --git a/plugins/pe/core.h b/plugins/pe/core.h
index 4497cf5..5c0696f 100644
--- a/plugins/pe/core.h
+++ b/plugins/pe/core.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.h - prototypes pour l'intégration du support du format PE
*
- * Copyright (C) 2017-2018 Cyrille Bagard
+ * Copyright (C) 2017-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,16 +25,18 @@
#define _PLUGINS_PE_CORE_H
+#include <glibext/helpers.h>
#include <plugins/plugin.h>
-#include <plugins/plugin-int.h>
-/* Prend acte du chargement du greffon. */
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+#define G_TYPE_PE_PLUGIN (g_pe_plugin_get_type())
-/* Procède à une opération liée à un contenu binaire. */
-G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
+DECLARE_GTYPE(GPePlugin, g_pe_plugin, G, PE_PLUGIN);
+
+
+/* Crée un module pour un greffon de support PE. */
+GPluginModule *g_pe_plugin_new(GModule *);
diff --git a/plugins/pe/pe-int.c b/plugins/pe/format-int.c
index 4104ce1..2d1e6c8 100644
--- a/plugins/pe/pe-int.c
+++ b/plugins/pe/format-int.c
@@ -1,6 +1,6 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe-int.c - structures internes du format Portable Executable
+ * format-int.c - structures internes du format Portable Executable
*
* Copyright (C) 2010-2017 Cyrille Bagard
*
@@ -21,14 +21,14 @@
*/
-#include "pe-int.h"
+#include "format-int.h"
#include <malloc.h>
#include <string.h>
#include <i18n.h>
-#include <common/endianness.h>
+#include <common/datatypes.h>
#include <core/logs.h>
@@ -46,7 +46,7 @@
* *
******************************************************************************/
-bool read_dos_image_header(const GPeFormat *format, image_dos_header *header)
+bool read_dos_image_header(const GPeFormat *format, image_dos_header_t *header)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -104,7 +104,7 @@ bool read_dos_image_header(const GPeFormat *format, image_dos_header *header)
* *
******************************************************************************/
-bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header *header)
+bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header_t *header)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -140,13 +140,13 @@ bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header
* *
******************************************************************************/
-bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header *header)
+bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header_t *header)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
- image_optional_header_32 *hdr32; /* Version 32 bits */
- image_optional_header_64 *hdr64; /* Version 64 bits */
- image_data_directory *directories; /* Répertoires à charger */
+ image_optional_header_32_t *hdr32; /* Version 32 bits */
+ image_optional_header_64_t *hdr64; /* Version 64 bits */
+ image_data_directory_t *directories; /* Répertoires à charger */
uint32_t *number_of_rva_and_sizes; /* Quantité de ces répertoires */
uint32_t i; /* Boucle de parcours */
@@ -278,7 +278,7 @@ bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optiona
* *
******************************************************************************/
-bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t *next)
+bool read_pe_nt_header(const GPeFormat *format, image_nt_headers_t *header, vmpa2t *next)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -318,7 +318,7 @@ bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t
* *
******************************************************************************/
-bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header *section)
+bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header_t *section)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -331,7 +331,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se
for (i = 0; i < IMAGE_SIZEOF_SHORT_NAME && result; i++)
result = g_binary_content_read_u8(content, pos, (uint8_t *)&section->name[i]);
- if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->misc.physical_address);
+ if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->physical_address);
if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->virtual_address);
if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->size_of_raw_data);
@@ -361,7 +361,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se
* *
******************************************************************************/
-bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory *dir)
+bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory_t *dir)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -401,7 +401,7 @@ bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_
* *
******************************************************************************/
-bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor *desc)
+bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor_t *desc)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
diff --git a/plugins/pe/pe-int.h b/plugins/pe/format-int.h
index ebcf3f0..0b5ad4b 100644
--- a/plugins/pe/pe-int.h
+++ b/plugins/pe/format-int.h
@@ -1,6 +1,6 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe-int.h - prototypes pour les structures internes du format Portable Executable
+ * format-int.h - prototypes pour les structures internes du format Portable Executable
*
* Copyright (C) 2009-2017 Cyrille Bagard
*
@@ -21,14 +21,13 @@
*/
-#ifndef _PLUGINS_PE_PE_INT_H
-#define _PLUGINS_PE_PE_INT_H
+#ifndef _PLUGINS_PE_FORMAT_INT_H
+#define _PLUGINS_PE_FORMAT_INT_H
#include <format/executable-int.h>
-#include "pe_def.h"
#include "format.h"
@@ -36,13 +35,14 @@
/* Format d'exécutable PE (instance) */
struct _GPeFormat
{
- GExeFormat parent; /* A laisser en premier */
+ GExecutableFormat parent; /* A laisser en premier */
- image_dos_header dos_header; /* En-tête DOS */
+ image_dos_header_t dos_header; /* En-tête DOS */
mrange_t rich_header; /* En-tête enrichi */
- image_nt_headers nt_headers; /* En-tête Windows */
+ image_nt_headers_t nt_headers; /* En-tête Windows */
- image_section_header *sections; /* Liste des sections */
+ vmpa2t sections_start; /* Début de la zone de sections*/
+ image_section_header_t *sections; /* Liste des sections */
bool loaded; /* Détection partielle menée */
@@ -51,32 +51,35 @@ struct _GPeFormat
/* Format d'exécutable PE (classe) */
struct _GPeFormatClass
{
- GExeFormatClass parent; /* A laisser en premier */
+ GExecutableFormatClass parent; /* A laisser en premier */
};
+/* Met en place une nouvelle instance de format PE. */
+bool g_pe_format_create(GPeFormat *, GBinContent *);
+
/* Procède à la lecture d'un en-tête de programme DOS. */
-bool read_dos_image_header(const GPeFormat *, image_dos_header *);
+bool read_dos_image_header(const GPeFormat *, image_dos_header_t *);
/* Procède à la lecture d'un en-tête de programme PE (1). */
-bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header *);
+bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header_t *);
/* Procède à la lecture d'un en-tête de programme PE (2). */
-bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header *);
+bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header_t *);
/* Procède à la lecture d'un en-tête de programme PE. */
-bool read_pe_nt_header(const GPeFormat *, image_nt_headers *, vmpa2t *);
+bool read_pe_nt_header(const GPeFormat *, image_nt_headers_t *, vmpa2t *);
/* Procède à la lecture d'un en-tête de section PE. */
-bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header *);
+bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header_t *);
/* Procède à la lecture d'un répertoire d'exportations. */
-bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory *);
+bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory_t *);
/* Procède à la lecture d'un répertoire de programme PE. */
-bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor *);
+bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor_t *);
-#endif /* _PLUGINS_PE_PE_INT_H */
+#endif /* _PLUGINS_PE_FORMAT_INT_H */
diff --git a/plugins/pe/format.c b/plugins/pe/format.c
index 3839063..11bc116 100644
--- a/plugins/pe/format.c
+++ b/plugins/pe/format.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe.c - support du format Portable Executable
+ * format.c - support du format Portable Executable
*
- * Copyright (C) 2010-2017 Cyrille Bagard
+ * Copyright (C) 2010-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,15 +25,18 @@
#include <assert.h>
+#include <string.h>
-#include "pe-int.h"
-#include "rich.h"
-#include "section.h"
-#include "symbols.h"
+#include "format-int.h"
+//#include "rich.h"
+//#include "symbols.h"
+/* ------------------------- DEFINITION D'UN NOUVEAU FORMAT ------------------------- */
+
+
/* Initialise la classe des formats d'exécutables ELF. */
static void g_pe_format_class_init(GPeFormatClass *);
@@ -46,6 +49,11 @@ static void g_pe_format_dispose(GPeFormat *);
/* Procède à la libération totale de la mémoire. */
static void g_pe_format_finalize(GPeFormat *);
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
/* Indique la désignation interne du format. */
static char *g_pe_format_get_key(const GPeFormat *);
@@ -53,39 +61,28 @@ static char *g_pe_format_get_key(const GPeFormat *);
static char *g_pe_format_get_description(const GPeFormat *);
/* Assure l'interprétation d'un format en différé. */
-static bool g_pe_format_analyze(GPeFormat *, wgroup_id_t, GtkStatusStack *);
+static bool g_pe_format_analyze(GPeFormat *);
/* Informe quant au boutisme utilisé. */
static SourceEndian g_pe_format_get_endianness(const GPeFormat *);
/* Indique le type d'architecture visée par le format. */
-static const char *g_pe_format_get_target_machine(const GPeFormat *);
+static char *g_pe_format_get_target_machine(const GPeFormat *);
-/* Fournit l'adresse principale associée à un format Elf. */
+/* Fournit l'adresse principale associée à un format. */
static bool g_pe_format_get_main_address(GPeFormat *, vmpa2t *);
-
-#if 0
-
/* Etend la définition des portions au sein d'un binaire. */
-static void g_pe_format_refine_portions(GPeFormat *);
-
-#endif
-
-
-
-/* Fournit l'emplacement correspondant à une adresse virtuelle. */
-bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *, virt_t, vmpa2t *);
-
-
-#if 0
+static bool g_pe_format_refine_portions(GPeFormat *);
/* Fournit l'emplacement d'une section donnée. */
-static bool g_pe_format_get_section_range_by_name(const GPeFormat *, const char *, mrange_t *);
-#endif
+static bool g_pe_format_find_section_range_by_name(const GPeFormat *, const char *, mrange_t *);
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UN NOUVEAU FORMAT */
+/* ---------------------------------------------------------------------------------- */
/******************************************************************************
@@ -116,7 +113,7 @@ bool check_pe_format(const GBinContent *content)
if (result)
{
- G_KNOWN_FORMAT(&format)->content = (GBinContent *)content;
+ ((GKnownFormat *)&format)->content = (GBinContent *)content;
result = read_dos_image_header(&format, &format.dos_header);
}
@@ -137,7 +134,7 @@ bool check_pe_format(const GBinContent *content)
/* Indique le type défini pour un format d'exécutable ELF. */
-G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXE_FORMAT);
+G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXECUTABLE_FORMAT);
/******************************************************************************
@@ -156,8 +153,8 @@ static void g_pe_format_class_init(GPeFormatClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GKnownFormatClass *known; /* Version de format connu */
- GBinFormatClass *fmt; /* Version en format basique */
- GExeFormatClass *exe; /* Version en exécutable */
+ GProgramFormatClass *prgm; /* Version en format basique */
+ GExecutableFormatClass *exe; /* Version en exécutable */
object = G_OBJECT_CLASS(klass);
@@ -170,20 +167,19 @@ static void g_pe_format_class_init(GPeFormatClass *klass)
known->get_desc = (known_get_desc_fc)g_pe_format_get_description;
known->analyze = (known_analyze_fc)g_pe_format_analyze;
- fmt = G_BIN_FORMAT_CLASS(klass);
+ prgm = G_PROGRAM_FORMAT_CLASS(klass);
- fmt->get_endian = (format_get_endian_fc)g_pe_format_get_endianness;
+ prgm->get_endian = (program_get_endian_fc)g_pe_format_get_endianness;
+ prgm->find_range_by_name = (find_range_by_name_fc)g_pe_format_find_section_range_by_name;
- exe = G_EXE_FORMAT_CLASS(klass);
+ exe = G_EXECUTABLE_FORMAT_CLASS(klass);
exe->get_machine = (get_target_machine_fc)g_pe_format_get_target_machine;
exe->get_main_addr = (get_main_addr_fc)g_pe_format_get_main_address;
- //exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions;
+ exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions;
- //exe->translate_phys = (translate_phys_fc)g_exe_format_translate_offset_into_vmpa_using_portions;
- exe->translate_virt = (translate_virt_fc)g_pe_format_translate_address_into_vmpa_using_portions;
-
- //exe->get_range_by_name = (get_range_by_name_fc)g_pe_format_get_section_range_by_name;
+ exe->translate_phys = g_executable_format_translate_offset_into_vmpa_with_portions;
+ exe->translate_virt = g_executable_format_translate_address_into_vmpa_with_portions;
}
@@ -262,39 +258,41 @@ static void g_pe_format_finalize(GPeFormat *format)
* *
******************************************************************************/
-GExeFormat *g_pe_format_new(GBinContent *content)
+GPeFormat *g_pe_format_new(GBinContent *content)
{
GPeFormat *result; /* Structure à retourner */
- if (!check_pe_format(content))
- return NULL;
-
result = g_object_new(G_TYPE_PE_FORMAT, NULL);
- g_known_format_set_content(G_KNOWN_FORMAT(result), content);
+ if (!g_pe_format_create(result, content))
+ g_clear_object(&result);
- return G_EXE_FORMAT(result);
+ return result;
}
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
+* Paramètres : format = description du format connu à consulter. *
+* content = contenu binaire à parcourir. *
* *
-* Description : Indique la désignation interne du format. *
+* Description : Met en place une nouvelle instance de format PE. *
* *
-* Retour : Désignation du format. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *g_pe_format_get_key(const GPeFormat *format)
+bool g_pe_format_create(GPeFormat *format, GBinContent *content)
{
- char *result; /* Désignation à retourner */
+ bool result; /* Bilan à retourner */
- result = strdup("pe");
+ result = true;//check_pe_format(content);
+
+ if (result)
+ result = g_executable_format_create(G_EXECUTABLE_FORMAT(format), content);
return result;
@@ -303,21 +301,21 @@ static char *g_pe_format_get_key(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
+* Paramètres : format = format en place à consulter. *
* *
-* Description : Fournit une description humaine du format. *
+* Description : Présente l'en-tête MS-DOS du format chargé. *
* *
-* Retour : Description du format. *
+* Retour : Pointeur vers la description principale. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *g_pe_format_get_description(const GPeFormat *format)
+const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *format)
{
- char *result; /* Désignation à retourner */
+ const image_dos_header_t *result; /* Informations à retourner */
- result = strdup("Portable Executable");
+ result = &format->dos_header;
return result;
@@ -326,44 +324,21 @@ static char *g_pe_format_get_description(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = format chargé dont l'analyse est lancée. *
-* gid = groupe de travail dédié. *
-* status = barre de statut à tenir informée. *
+* Paramètres : format = format en place à consulter. *
* *
-* Description : Assure l'interprétation d'un format en différé. *
+* Description : Présente l'en-tête NT du format chargé. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Pointeur vers la description principale. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusStack *status)
+const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *format)
{
- bool result; /* Bilan à retourner */
- GExeFormat *exe; /* Autre version du format */
- vmpa2t section_start; /* Zone de départ des sections */
-
- exe = G_EXE_FORMAT(format);
-
- result = read_dos_image_header(format, &format->dos_header);
- if (!result) goto error;
-
- result = read_pe_nt_header(format, &format->nt_headers, &section_start);
- if (!result) goto error;
+ const image_nt_headers_t *result; /* Informations à retourner */
- format->sections = read_all_pe_sections(format, &section_start);
- if (format->sections == NULL) goto error;
-
- extract_pe_rich_header(format);
-
- result = load_pe_symbols(format, gid, status);
- if (!result) goto error;
-
- result = g_executable_format_complete_loading(exe, gid, status);
- if (!result) goto error;
-
- error:
+ result = &format->nt_headers;
return result;
@@ -372,76 +347,73 @@ static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusSta
/******************************************************************************
* *
-* Paramètres : format = informations chargées à consulter. *
+* Paramètres : format = format en place à consulter. *
* *
-* Description : Informe quant au boutisme utilisé. *
+* Description : Indique si le format PE est en 32 bits ou en 64 bits. *
* *
-* Retour : Indicateur de boutisme. *
+* Retour : true si le format est en 32 bits, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static SourceEndian g_pe_format_get_endianness(const GPeFormat *format)
+bool g_pe_format_get_is_32b(const GPeFormat *format)
{
- SourceEndian result; /* Boutisme à retourner */
+ bool result; /* Nature à retourner */
- /**
- * Sauf exception, le boutisme est généralement petit.
- *
- * Cf. https://reverseengineering.stackexchange.com/a/17923
- * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness
- */
+ assert(format->loaded);
- result = SRE_LITTLE;
+ switch (format->nt_headers.optional_header.header_32.magic)
+ {
+ case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ result = true;
+ break;
+ case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ result = false;
+ break;
+ default:
+ result = true;
+ assert(false);
+ break;
+ }
return result;
+
}
/******************************************************************************
* *
-* Paramètres : format = informations chargées à consulter. *
+* Paramètres : format = format en place à consulter. *
+* count = taille (fixe) du tableau renvoyé. [OUT] *
* *
-* Description : Indique le type d'architecture visée par le format. *
+* Description : Offre un raccourci vers les répertoires du format PE. *
* *
-* Retour : Identifiant de l'architecture ciblée par le format. *
+* Retour : Pointeur vers le tableau des répertoires. *
* *
* Remarques : - *
* *
******************************************************************************/
-static const char *g_pe_format_get_target_machine(const GPeFormat *format)
+const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *format, size_t *count)
{
- const char *result; /* Identifiant à retourner */
+ const image_data_directory_t *result; /* Liste à retourner */
- switch (format->nt_headers.file_header.machine)
+ if (g_pe_format_get_is_32b(format))
{
- case IMAGE_FILE_MACHINE_I386:
- result = "i386";
- break;
+ result = format->nt_headers.optional_header.header_32.data_directory;
- case IMAGE_FILE_MACHINE_R3000:
- case IMAGE_FILE_MACHINE_R4000:
- case IMAGE_FILE_MACHINE_R10000:
- case IMAGE_FILE_MACHINE_WCEMIPSV2:
- case IMAGE_FILE_MACHINE_MIPS16:
- case IMAGE_FILE_MACHINE_MIPSFPU:
- case IMAGE_FILE_MACHINE_MIPSFPU16:
- result = "mips";
- break;
+ if (count != NULL)
+ *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes;
- case IMAGE_FILE_MACHINE_ARM:
- case IMAGE_FILE_MACHINE_THUMB:
- case IMAGE_FILE_MACHINE_ARMNT:
- result = "armv7";
- break;
+ }
+ else
+ {
+ result = format->nt_headers.optional_header.header_64.data_directory;
- case IMAGE_FILE_MACHINE_UNKNOWN:
- default:
- result = NULL;
- break;
+ if (count != NULL)
+ *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes;
}
@@ -452,140 +424,174 @@ static const char *g_pe_format_get_target_machine(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
-* addr = adresse principale trouvée si possible. [OUT] *
+* Paramètres : format = format en place à consulter. *
+* index = indice du répertoire visé. *
* *
-* Description : Fournit l'adresse principale associée à un format Elf. *
+* Description : Extrait le contenu d'un répertoire du format PE. *
* *
-* Retour : Bilan des recherches. *
+* Retour : Pointeur vers un contenu chargé ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr)
+void *g_pe_format_get_directory(const GPeFormat *format, size_t index)
{
- bool result; /* Bilan à retourner */
- GBinSymbol *symbol; /* Point d'entrée trouvé */
- GBinFormat *base; /* Version d'instance parente */
- const mrange_t *range; /* Emplacement de ce point */
-
- result = false;
- symbol = NULL;
+ void *result; /* Données à retourner */
+ size_t max; /* Quantité de répertoires */
+ const image_data_directory_t *dir; /* Localisation du répertoire */
+ vmpa2t pos; /* Tête de lecture */
+ bool status; /* Bilan d'un traitement */
+ image_export_directory_t *export; /* Répertoire de type 0 */
+ image_import_descriptor_t *imports; /* Répertoire de type 1 */
+ size_t imported_count; /* Quantité de DLL requises */
- base = G_BIN_FORMAT(format);
+ result = NULL;
- if (g_binary_format_find_symbol_by_label(base, "main", &symbol))
- goto done;
+ dir = g_pe_format_get_directories(format, &max);
- if (g_binary_format_find_symbol_by_label(base, "_start", &symbol))
- goto done;
+ if (index >= max)
+ goto exit;
- if (g_binary_format_find_symbol_by_label(base, "entry_point", &symbol))
- goto done;
+ dir += index;
- done:
+ status = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format),
+ dir->virtual_address, &pos);
+ if (!status) goto exit;
- if (symbol != NULL)
+ switch (index)
{
- result = true;
+ case IMAGE_DIRECTORY_ENTRY_EXPORT:
+
+ export = malloc(sizeof(image_export_directory_t));
+
+ status = read_pe_image_export_directory(format, &pos, export);
+
+ if (!status)
+ {
+ free(export);
+ goto exit;
+ }
- range = g_binary_symbol_get_range(symbol);
+ result = export;
+ break;
- copy_vmpa(addr, get_mrange_addr(range));
+ case IMAGE_DIRECTORY_ENTRY_IMPORT:
- g_object_unref(G_OBJECT(symbol));
+ imports = NULL;
+ imported_count = 0;
+
+ do
+ {
+ imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor_t));
+
+ status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1));
+
+ if (!status)
+ {
+ free(imports);
+ goto exit;
+ }
+
+ }
+ while (imports[imported_count - 1].original_first_thunk != 0);
+
+ result = imports;
+ break;
}
+ exit:
+
return result;
}
-#if 0
-
/******************************************************************************
* *
-* Paramètres : format = informations chargées à consulter. *
+* Paramètres : format = format en place à consulter. *
+* count = taille (fixe) du tableau renvoyé. [OUT] *
* *
-* Description : Etend la définition des portions au sein d'un binaire. *
+* Description : Offre un raccourci vers les sections du format PE. *
* *
-* Retour : - *
+* Retour : Pointeur vers la liste des sections. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void g_pe_format_refine_portions(GPeFormat *format)
+const image_section_header_t *g_pe_format_get_sections(const GPeFormat *format, size_t *count)
{
+ const image_section_header_t *result; /* Liste à retourner */
+
+ if (count != NULL)
+ *count = format->nt_headers.file_header.number_of_sections;
+
+ result = format->sections;
+
+ return result;
}
-#endif
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
-* addr = adresse virtuelle à retrouver. *
-* pos = position correspondante. [OUT] *
* *
-* Description : Fournit l'emplacement correspondant à une adresse virtuelle. *
+* Description : Indique la désignation interne du format. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Désignation du format. *
* *
* Remarques : - *
* *
******************************************************************************/
-bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, virt_t addr, vmpa2t *pos)
+static char *g_pe_format_get_key(const GPeFormat *format)
{
- bool result; /* Bilan à retourner */
- uint16_t i; /* Boucle de parcours */
- const image_section_header *section; /* Section à consulter */
- phys_t diff; /* Décallage à appliquer */
-
- result = false;
-
- for (i = 0; i < format->nt_headers.file_header.number_of_sections && !result; i++)
- {
- section = &format->sections[i];
+ char *result; /* Désignation à retourner */
- if (addr < section->virtual_address)
- continue;
+ result = strdup("pe");
- if (addr >= (section->virtual_address + section->size_of_raw_data))
- continue;
+ return result;
- diff = addr - section->virtual_address;
+}
- init_vmpa(pos, section->pointer_to_raw_data + diff, addr);
- result = true;
+/******************************************************************************
+* *
+* Paramètres : format = description de l'exécutable à consulter. *
+* *
+* Description : Fournit une description humaine du format. *
+* *
+* Retour : Description du format. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- }
+static char *g_pe_format_get_description(const GPeFormat *format)
+{
+ char *result; /* Désignation à retourner */
- //printf(" // trans // %x -> %x (valid? %d)\n", (unsigned int)addr, (unsigned int)pos->physical, result);
+ result = strdup("Portable Executable");
return result;
}
-
-
-#if 0
-
-
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
-* name = nom de la section recherchée. *
-* range = emplacement en mémoire à renseigner. [OUT] *
+* Paramètres : format = format chargé dont l'analyse est lancée. *
* *
-* Description : Fournit l'emplacement d'une section donnée. *
+* Description : Assure l'interprétation d'un format en différé. *
* *
* Retour : Bilan de l'opération. *
* *
@@ -593,51 +599,82 @@ bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, v
* *
******************************************************************************/
-static bool g_pe_format_get_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range)
+static bool g_pe_format_analyze(GPeFormat *format)
{
bool result; /* Bilan à retourner */
- phys_t offset; /* Position physique de section*/
- phys_t size; /* Taille de la section trouvée*/
- virt_t address; /* Adresse virtuelle de section*/
- vmpa2t tmp; /* Adresse à initialiser */
+ vmpa2t start; /* Zone de départ des sections */
+ uint16_t count; /* Quantité de sections */
+ uint16_t i; /* Boucle de parcours */
- result = find_elf_section_content_by_name(format, name, &offset, &size, &address);
+ result = read_dos_image_header(format, &format->dos_header);
+ if (!result) goto error;
- if (result)
+ result = read_pe_nt_header(format, &format->nt_headers, &start);
+ if (!result) goto error;
+
+ /* Chargement des définitions des sections déclarées */
+
+ copy_vmpa(&format->sections_start, &start);
+
+ count = format->nt_headers.file_header.number_of_sections;
+
+ format->sections = malloc(count * sizeof(image_section_header_t));
+
+ for (i = 0; i < count; i++)
{
- init_vmpa(&tmp, offset, address);
- init_mrange(range, &tmp, size);
+ result = read_pe_image_section_header(format, &start, &format->sections[i]);
+ if (!result) goto error;
}
- return result;
+ /* Passage de relais */
-}
-#endif
+ if (result)
+ result = G_KNOWN_FORMAT_CLASS(g_pe_format_parent_class)->analyze(G_KNOWN_FORMAT(format));
+
+
+
+
+#if 0
+ extract_pe_rich_header(format);
+ result = load_pe_symbols(format, gid, status);
+ if (!result) goto error;
+
+#endif
+ error:
+ return result;
+}
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
+* Paramètres : format = informations chargées à consulter. *
* *
-* Description : Présente l'en-tête MS-DOS du format chargé. *
+* Description : Informe quant au boutisme utilisé. *
* *
-* Retour : Pointeur vers la description principale. *
+* Retour : Indicateur de boutisme. *
* *
* Remarques : - *
* *
******************************************************************************/
-const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format)
+static SourceEndian g_pe_format_get_endianness(const GPeFormat *format)
{
- const image_dos_header *result; /* Informations à retourner */
+ SourceEndian result; /* Boutisme à retourner */
- result = &format->dos_header;
+ /**
+ * Sauf exception, le boutisme est généralement petit.
+ *
+ * Cf. https://reverseengineering.stackexchange.com/a/17923
+ * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness
+ */
+
+ result = SRE_LITTLE;
return result;
@@ -646,21 +683,43 @@ const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
+* Paramètres : format = description de l'exécutable à consulter. *
+* name = nom de la section recherchée. *
+* range = emplacement en mémoire à renseigner. [OUT] *
* *
-* Description : Présente l'en-tête NT du format chargé. *
+* Description : Fournit l'emplacement d'une section donnée. *
* *
-* Retour : Pointeur vers la description principale. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format)
+static bool g_pe_format_find_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range)
{
- const image_nt_headers *result; /* Informations à retourner */
+ bool result; /* Bilan à retourner */
+ uint16_t i; /* Boucle de parcours */
+ image_section_header_t *section; /* Section à traiter */
+ char tmp[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */
+ vmpa2t addr; /* Emplacement dans le binaire */
- result = &format->nt_headers;
+ result = false;
+
+ for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++)
+ {
+ memcpy(tmp, section->name, IMAGE_SIZEOF_SHORT_NAME);
+ tmp[IMAGE_SIZEOF_SHORT_NAME] = '\0';
+
+ if (strcmp(name, tmp) != 0)
+ continue;
+
+ init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address);
+ init_mrange(range, &addr, section->size_of_raw_data);
+
+ result = true;
+ break;
+
+ }
return result;
@@ -669,75 +728,78 @@ const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
+* Paramètres : format = informations chargées à consulter. *
* *
-* Description : Indique si le format PE est en 32 bits ou en 64 bits. *
+* Description : Indique le type d'architecture visée par le format. *
* *
-* Retour : true si le format est en 32 bits, false sinon. *
+* Retour : Identifiant de l'architecture ciblée par le format. *
* *
* Remarques : - *
* *
******************************************************************************/
-bool g_pe_format_get_is_32b(const GPeFormat *format)
+static char *g_pe_format_get_target_machine(const GPeFormat *format)
{
- bool result; /* Nature à retourner */
-
- assert(format->loaded);
+ char *result; /* Identifiant à retourner */
- switch (format->nt_headers.optional_header.header_32.magic)
+ switch (format->nt_headers.file_header.machine)
{
- case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
- result = true;
+ case IMAGE_FILE_MACHINE_I386:
+ result = strdup("i386");
break;
- case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
- result = false;
+
+ case IMAGE_FILE_MACHINE_R3000:
+ case IMAGE_FILE_MACHINE_R4000:
+ case IMAGE_FILE_MACHINE_R10000:
+ case IMAGE_FILE_MACHINE_WCEMIPSV2:
+ case IMAGE_FILE_MACHINE_MIPS16:
+ case IMAGE_FILE_MACHINE_MIPSFPU:
+ case IMAGE_FILE_MACHINE_MIPSFPU16:
+ result = strdup("mips");
break;
+
+ case IMAGE_FILE_MACHINE_ARM:
+ case IMAGE_FILE_MACHINE_THUMB:
+ case IMAGE_FILE_MACHINE_ARMNT:
+ result = strdup("armv7");
+ break;
+
+ case IMAGE_FILE_MACHINE_UNKNOWN:
default:
- result = true;
- assert(false);
+ result = NULL;
break;
+
}
return result;
-
}
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
-* count = taille (fixe) du tableau renvoyé. [OUT] *
+* Paramètres : format = description de l'exécutable à consulter. *
+* addr = adresse principale trouvée si possible. [OUT] *
* *
-* Description : Offre un raccourci vers les répertoires du format PE. *
+* Description : Fournit l'adresse principale associée à un format. *
* *
-* Retour : Pointeur vers le tableau des répertoires. *
+* Retour : Bilan des recherches. *
* *
* Remarques : - *
* *
******************************************************************************/
-const image_data_directory *g_pe_format_get_directories(const GPeFormat *format, size_t *count)
+static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr)
{
- const image_data_directory *result; /* Liste à retourner */
+ bool result; /* Bilan à retourner */
+ virt_t ep; /* Point d'entrée */
if (g_pe_format_get_is_32b(format))
- {
- result = format->nt_headers.optional_header.header_32.data_directory;
-
- if (count != NULL)
- *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes;
-
- }
+ ep = format->nt_headers.optional_header.header_32.address_of_entry_point;
else
- {
- result = format->nt_headers.optional_header.header_64.data_directory;
-
- if (count != NULL)
- *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes;
+ ep = format->nt_headers.optional_header.header_64.address_of_entry_point;
- }
+ result = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format), ep, addr);
return result;
@@ -746,83 +808,75 @@ const image_data_directory *g_pe_format_get_directories(const GPeFormat *format,
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
-* index = indice du répertoire visé. *
+* Paramètres : format = informations chargées à consulter. *
* *
-* Description : Extrait le contenu d'un répertoire du format PE. *
+* Description : Etend la définition des portions au sein d'un binaire. *
* *
-* Retour : Pointeur vers un contenu chargé ou NULL. *
+* Retour : Bilan des définitions de portions. *
* *
* Remarques : - *
* *
******************************************************************************/
-void *g_pe_format_get_directory(const GPeFormat *format, size_t index)
+static bool g_pe_format_refine_portions(GPeFormat *format)
{
- void *result; /* Données à retourner */
- size_t max; /* Quantité de répertoires */
- const image_data_directory *dir; /* Localisation du répertoire */
- vmpa2t pos; /* Tête de lecture */
- bool status; /* Bilan d'un traitement */
- image_export_directory *export; /* Répertoire de type 0 */
- image_import_descriptor *imports; /* Répertoire de type 1 */
- size_t imported_count; /* Quantité de DLL requises */
+ bool result; /* Bilan à retourner */
+ vmpa2t origin; /* Origine d'une définition */
+ uint16_t i; /* Boucle de parcours */
+ image_section_header_t *section; /* Section à traiter */
+ vmpa2t addr; /* Emplacement dans le binaire */
+ GBinaryPortion *portion; /* Nouvelle portion de binaire */
+ char name[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */
+ PortionAccessRights rights; /* Droits d'accès à la section */
- result = NULL;
+ result = true;
- dir = g_pe_format_get_directories(format, &max);
+ copy_vmpa(&origin, &format->sections_start);
- if (index >= max)
- goto exit;
+ for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++)
+ {
+ if (section->pointer_to_raw_data == 0)
+ continue;
- dir += index;
+ /* Emplacement */
- status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), dir->virtual_address, &pos);
- if (!status) goto exit;
+ init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address);
- switch (index)
- {
- case IMAGE_DIRECTORY_ENTRY_EXPORT:
+ portion = g_binary_portion_new(&addr, section->size_of_raw_data);
- export = malloc(sizeof(image_export_directory));
+ /* Nom */
- status = read_pe_image_export_directory(format, &pos, export);
+ memcpy(name, section->name, IMAGE_SIZEOF_SHORT_NAME);
+ name[IMAGE_SIZEOF_SHORT_NAME] = '\0';
- if (!status)
- {
- free(export);
- goto exit;
- }
+ g_binary_portion_set_desc(portion, name);
- result = export;
- break;
+ /* Droits d'accès */
- case IMAGE_DIRECTORY_ENTRY_IMPORT:
+ rights = PAC_NONE;
- imports = NULL;
- imported_count = 0;
+ if (section->characteristics & IMAGE_SCN_MEM_EXECUTE)
+ rights |= PAC_EXEC;
- do
- {
- imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor));
+ if (section->characteristics & IMAGE_SCN_MEM_READ)
+ rights |= PAC_READ;
- status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1));
+ if (section->characteristics & IMAGE_SCN_MEM_WRITE)
+ rights |= PAC_WRITE;
- if (!status)
- {
- free(imports);
- goto exit;
- }
+ g_binary_portion_set_rights(portion, rights);
- }
- while (imports[imported_count - 1].original_first_thunk != 0);
+ /* Inclusion finale */
- result = imports;
- break;
+ result = g_executable_format_include_portion(G_EXECUTABLE_FORMAT(format), portion, &origin);
- }
+ unref_object(portion);
- exit:
+ if (!result) break;
+
+ advance_vmpa(&origin, sizeof(image_section_header_t));
+
+ }
return result;
diff --git a/plugins/pe/format.h b/plugins/pe/format.h
index a48e04d..829f4de 100644
--- a/plugins/pe/format.h
+++ b/plugins/pe/format.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe.h - prototypes pour le support du format Portable Executable
+ * format.h - prototypes pour le support du format Portable Executable
*
- * Copyright (C) 2010-2017 Cyrille Bagard
+ * Copyright (C) 2010-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,31 +25,20 @@
#define _PLUGINS_PE_FORMAT_H
-#include <glib-object.h>
#include <stdbool.h>
#include <analysis/content.h>
-#include <format/executable.h>
+#include <glibext/helpers.h>
-#include "pe_def.h"
+#include "pe-def.h"
-#define G_TYPE_PE_FORMAT g_pe_format_get_type()
-#define G_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PE_FORMAT, GPeFormat))
-#define G_IS_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PE_FORMAT))
-#define G_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PE_FORMAT, GPeFormatClass))
-#define G_IS_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PE_FORMAT))
-#define G_PE_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PE_FORMAT, GPeFormatClass))
+#define G_TYPE_PE_FORMAT (g_pe_format_get_type())
-
-/* Format d'exécutable PE (instance) */
-typedef struct _GPeFormat GPeFormat;
-
-/* Format d'exécutable PE (classe) */
-typedef struct _GPeFormatClass GPeFormatClass;
+DECLARE_GTYPE(GPeFormat, g_pe_format, G, PE_FORMAT);
/* Valide un contenu comme étant un format PE. */
@@ -59,23 +48,26 @@ bool check_pe_format(const GBinContent *);
GType g_pe_format_get_type(void);
/* Prend en charge un nouveau format PE. */
-GExeFormat *g_pe_format_new(GBinContent *);
+GPeFormat *g_pe_format_new(GBinContent *);
/* Présente l'en-tête MS-DOS du format chargé. */
-const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *);
+const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *);
/* Présente l'en-tête NT du format chargé. */
-const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *);
+const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *);
/* Indique si le format PE est en 32 bits ou en 64 bits. */
bool g_pe_format_get_is_32b(const GPeFormat *);
/* Offre un raccourci vers les répertoires du format PE. */
-const image_data_directory *g_pe_format_get_directories(const GPeFormat *, size_t *);
+const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *, size_t *);
/* Extrait le contenu d'un répertoire du format PE. */
void *g_pe_format_get_directory(const GPeFormat *, size_t);
+/* Offre un raccourci vers les sections du format PE. */
+const image_section_header_t *g_pe_format_get_sections(const GPeFormat *, size_t *);
+
#endif /* _PLUGINS_PE_FORMAT_H */
diff --git a/plugins/pe/pe_def.h b/plugins/pe/pe-def.h
index 62b4607..4812897 100644
--- a/plugins/pe/pe_def.h
+++ b/plugins/pe/pe-def.h
@@ -31,6 +31,7 @@
/**
* Références :
*
+ * - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
* - https://fr.wikipedia.org/wiki/Portable_Executable#En-tête_MZ_sous_MS-DOS
* - https://www.nirsoft.net/kernel_struct/vista/IMAGE_DOS_HEADER.html
*
@@ -42,7 +43,7 @@
/* En-tête DOS */
-typedef struct _image_dos_header
+typedef struct _image_dos_header_t
{
uint16_t e_magic; /* Numéro magique */
uint16_t e_cblp; /* Octets de la dernière page */
@@ -64,7 +65,7 @@ typedef struct _image_dos_header
uint16_t e_res2[10]; /* Mots réservés */
uint32_t e_lfanew; /* Décalage de bon en-tête */
-} image_dos_header;
+} image_dos_header_t;
/* Archtectures supportées */
@@ -123,7 +124,7 @@ typedef struct _image_dos_header
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 /* Octets inv. ; obsolète */
/* Première en-tête du "vrai" format */
-typedef struct _image_file_header
+typedef struct _image_file_header_t
{
uint16_t machine; /* Type de machine visée */
uint16_t number_of_sections; /* Nombre de sections */
@@ -133,7 +134,7 @@ typedef struct _image_file_header
uint16_t size_of_optional_header; /* Taille de l'en-tête n°2 */
uint16_t characteristics; /* Propriétés de l'image */
-} image_file_header;
+} image_file_header_t;
@@ -148,12 +149,12 @@ typedef struct _image_file_header
*/
/* Zone de données Windows */
-typedef struct _image_data_directory
+typedef struct _image_data_directory_t
{
uint32_t virtual_address; /* Adresse de la table */
uint32_t size; /* Taille de la table */
-} image_data_directory;
+} image_data_directory_t;
// Directory Entries
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
@@ -182,7 +183,7 @@ typedef struct _image_data_directory
/* Seconde en-tête, optionnelle */
-typedef struct _image_optional_header_32
+typedef struct _image_optional_header_32_t
{
uint16_t magic; /* Type de binaire manipulé */
uint8_t major_linker_version; /* Version majeure du linker */
@@ -214,11 +215,11 @@ typedef struct _image_optional_header_32
uint32_t size_of_heap_commit; /* Taille de tas au démarrage */
uint32_t loader_flags; /* Champ obslète */
uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */
- image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
-} image_optional_header_32;
+} image_optional_header_32_t;
-typedef struct _image_optional_header_64
+typedef struct _image_optional_header_64_t
{
uint16_t magic; /* Type de binaire manipulé */
@@ -250,16 +251,16 @@ typedef struct _image_optional_header_64
uint64_t size_of_heap_commit; /* Taille de tas au démarrage */
uint32_t loader_flags; /* Champ obslète */
uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */
- image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
-} image_optional_header_64;
+} image_optional_header_64_t;
-typedef union _image_optional_header
+typedef union _image_optional_header_t
{
- image_optional_header_32 header_32; /* Version 32 bits */
- image_optional_header_64 header_64; /* Version 64 bits */
+ image_optional_header_32_t header_32; /* Version 32 bits */
+ image_optional_header_64_t header_64; /* Version 64 bits */
-} image_optional_header;
+} image_optional_header_t;
@@ -302,13 +303,13 @@ typedef union _image_optional_header
/* Résumé global */
-typedef struct _image_nt_headers
+typedef struct _image_nt_headers_t
{
uint32_t signature; /* Numéro magique */
- image_file_header file_header; /* En-tête n°1 */
- image_optional_header optional_header; /* En-tête n°2 */
+ image_file_header_t file_header; /* En-tête n°1 */
+ image_optional_header_t optional_header;/* En-tête n°2 */
-} image_nt_headers;
+} image_nt_headers_t;
@@ -322,7 +323,7 @@ typedef struct _image_nt_headers
#define IMAGE_SIZEOF_SHORT_NAME 8
/* Description d'une section */
-typedef struct _image_section_header
+typedef struct _image_section_header_t
{
char name[IMAGE_SIZEOF_SHORT_NAME]; /* Nom de la section */
@@ -331,7 +332,7 @@ typedef struct _image_section_header
uint32_t physical_address; /* Adresse physique */
uint32_t virtual_size; /* Taille en mémoire */
- } misc;
+ };
uint32_t virtual_address; /* Adresse en mémoire */
uint32_t size_of_raw_data; /* Taille de données définies */
@@ -342,7 +343,7 @@ typedef struct _image_section_header
uint16_t number_of_line_numbers; /* Quantité de numéros de ligne*/
uint32_t characteristics; /* Caractéristiques */
-} image_section_header;
+} image_section_header_t;
/* Détails des caractéristiques d'une image (champ 'characteristics') */
#define IMAGE_SCN_UNKNOWN_0 0x00000000 /* Réservé */
@@ -400,7 +401,7 @@ typedef struct _image_section_header
*/
/* Répertoire des importations */
-typedef struct _image_export_directory
+typedef struct _image_export_directory_t
{
uint32_t characteristics; /* Zéro !? */
uint32_t time_date_stamp; /* Date de création du fichier */
@@ -414,7 +415,7 @@ typedef struct _image_export_directory
uint32_t address_of_names; /* Liste de RVA de noms */
uint32_t address_of_name_ordinals; /* Liste de RVA d'ordinaux */
-} image_export_directory;
+} image_export_directory_t;
/**
@@ -427,7 +428,7 @@ typedef struct _image_export_directory
*/
/* Point de départ de la chaîne des importations */
-typedef struct _image_import_descriptor
+typedef struct _image_import_descriptor_t
{
uint32_t original_first_thunk;
uint32_t time_date_stamp;
@@ -435,7 +436,7 @@ typedef struct _image_import_descriptor
uint32_t module_name;
uint32_t first_thunk;
-} image_import_descriptor;
+} image_import_descriptor_t;
diff --git a/plugins/pe/python/Makefile.am b/plugins/pe/python/Makefile.am
index 5949821..4a70769 100644
--- a/plugins/pe/python/Makefile.am
+++ b/plugins/pe/python/Makefile.am
@@ -1,14 +1,16 @@
noinst_LTLIBRARIES = libpepython.la
+# libpepython_la_SOURCES = \
+# constants.h constants.c \
+# routine.h routine.c
+
libpepython_la_SOURCES = \
- constants.h constants.c \
format.h format.c \
module.h module.c \
- routine.h routine.c \
translate.h translate.c
-libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
-I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
diff --git a/plugins/pe/python/constants.c b/plugins/pe/python/constants.c
index 9b4942d..bb10a7e 100644
--- a/plugins/pe/python/constants.c
+++ b/plugins/pe/python/constants.c
@@ -29,7 +29,7 @@
#include "../pe_def.h"
-#include "../routine.h"
+//#include "../routine.h"
@@ -101,7 +101,7 @@ bool define_python_pe_format_constants(PyTypeObject *type)
* Remarques : - *
* *
******************************************************************************/
-
+#if 0
bool define_python_pe_exported_routine_constants(PyTypeObject *type)
{
bool result; /* Bilan à retourner */
@@ -141,3 +141,4 @@ bool define_python_pe_exported_routine_constants(PyTypeObject *type)
return result;
}
+#endif
diff --git a/plugins/pe/python/constants.h b/plugins/pe/python/constants.h
index 25b0adb..fe4293c 100644
--- a/plugins/pe/python/constants.h
+++ b/plugins/pe/python/constants.h
@@ -35,7 +35,7 @@
bool define_python_pe_format_constants(PyTypeObject *);
/* Définit les constantes pour les routines du format PE. */
-bool define_python_pe_exported_routine_constants(PyTypeObject *);
+//bool define_python_pe_exported_routine_constants(PyTypeObject *);
diff --git a/plugins/pe/python/format.c b/plugins/pe/python/format.c
index 4bbb99a..71d3305 100644
--- a/plugins/pe/python/format.c
+++ b/plugins/pe/python/format.c
@@ -28,25 +28,22 @@
#include <pygobject.h>
-#include <format/known.h>
-#include <plugins/dt.h>
#include <plugins/pychrysalide/helpers.h>
#include <plugins/pychrysalide/analysis/content.h>
#include <plugins/pychrysalide/format/executable.h>
-#include "constants.h"
+//#include "constants.h"
#include "translate.h"
-#include "../format.h"
-#include "../rich.h"
+#include "../format-int.h"
+//#include "../rich.h"
/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
-/* Accompagne la création d'une instance dérivée en Python. */
-static PyObject *py_pe_format_new(PyTypeObject *, PyObject *, PyObject *);
+CREATE_DYN_CONSTRUCTOR(pe_format, G_TYPE_PE_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_pe_format_init(PyObject *, PyObject *, PyObject *);
@@ -59,9 +56,15 @@ static int py_pe_format_init(PyObject *, PyObject *, PyObject *);
/* Présente l'en-tête MS-DOS du format chargé. */
static PyObject *py_pe_format_get_dos_header(PyObject *, void *);
+/* Présente l'en-tête NT du format chargé. */
+static PyObject *py_pe_format_get_nt_headers(PyObject *, void *);
+
/* Offre un raccourci vers les répertoires du format PE. */
static PyObject *py_pe_format_get_directories(PyObject *, void *);
+/* Offre un raccourci vers les sections du format PE. */
+static PyObject *py_pe_format_get_sections(PyObject *, void *);
+
/* Présente l'en-tête enrichi du format chargé. */
static PyObject *py_pe_format_get_rich_header(PyObject *, void *);
@@ -80,66 +83,6 @@ static PyObject *py_pe_format_get_comp_ids(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : type = type du nouvel objet à mettre en place. *
-* args = éventuelle liste d'arguments. *
-* kwds = éventuel dictionnaire de valeurs mises à disposition. *
-* *
-* Description : Accompagne la création d'une instance dérivée en Python. *
-* *
-* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_pe_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
- PyObject *result; /* Objet à retourner */
- PyTypeObject *base; /* Type de base à dériver */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
-
- /* Validations diverses */
-
- base = get_python_pe_format_type();
-
- if (type == base)
- goto simple_way;
-
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(G_TYPE_PE_FORMAT, type->tp_name, NULL, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
- /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
-
- simple_way:
-
- result = PyType_GenericNew(type, args, kwds);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : self = objet à initialiser (théoriquement). *
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
@@ -183,7 +126,8 @@ static int py_pe_format_init(PyObject *self, PyObject *args, PyObject *kwds)
format = G_PE_FORMAT(pygobject_get(self));
- g_known_format_set_content(G_KNOWN_FORMAT(format), content);
+ if (!g_pe_format_create(format, content))
+ return -1;
return 0;
@@ -223,7 +167,6 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure)
"\n" \
"The provided information is composed of the following" \
" properties :\n" \
- "\n" \
"* e_magic;\n" \
"* e_cblp;\n" \
"* e_cp;\n" \
@@ -259,6 +202,52 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure)
* Paramètres : self = format en place à consulter. *
* closure = non utilisé ici. *
* *
+* Description : Présente l'en-tête NT du format chargé. *
+* *
+* Retour : Structure Python créée pour l'occasion. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_pe_format_get_nt_headers(PyObject *self, void *closure)
+{
+ PyObject *result; /* Trouvaille à retourner */
+ GPeFormat *format; /* Version GLib du format */
+
+#define PE_FORMAT_NT_HEADERS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ nt_headers, py_pe_format, \
+ "NT headers of the file format.\n" \
+ "\n" \
+ "This property is a pychrysalide.StructObject instance." \
+ "\n" \
+ "The provided information is composed of the following" \
+ " properties :\n" \
+ "* signature;\n" \
+ "* file_header;\n" \
+ "* optional_header.\n" \
+ "\n" \
+ "The last two fields are pychrysalide.StructObject" \
+ " which contain more fields. These fields can be" \
+ " enumerated with the keys() method (for instance:" \
+ " *mype.nt_headers.file_header.keys()*).\n" \
+)
+
+ format = G_PE_FORMAT(pygobject_get(self));
+
+ result = translate_pe_nt_headers_to_python(format, g_pe_format_get_nt_headers(format));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = format en place à consulter. *
+* closure = non utilisé ici. *
+* *
* Description : Offre un raccourci vers les répertoires du format PE. *
* *
* Retour : Structure Python créée pour l'occasion. *
@@ -272,7 +261,7 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure)
PyObject *result; /* Trouvaille à retourner */
GPeFormat *format; /* Version GLib du format */
size_t count; /* Quantité de répertoires */
- const image_data_directory *directories; /* Répertoires à exporter */
+ const image_data_directory_t *directories; /* Répertoires à exporter */
size_t i; /* Boucle de parcours */
PyObject *item; /* Elément de tableau */
int ret; /* Bilan d'une mise en place */
@@ -323,6 +312,76 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure)
* Paramètres : self = format en place à consulter. *
* closure = non utilisé ici. *
* *
+* Description : Offre un raccourci vers les sections du format PE. *
+* *
+* Retour : Structure Python créée pour l'occasion. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_pe_format_get_sections(PyObject *self, void *closure)
+{
+ PyObject *result; /* Trouvaille à retourner */
+ GPeFormat *format; /* Version GLib du format */
+ size_t count; /* Quantité de répertoires */
+ const image_section_header_t *sections; /* Sections à exporter */
+ size_t i; /* Boucle de parcours */
+ PyObject *item; /* Elément de tableau */
+ int ret; /* Bilan d'une mise en place */
+
+#define PE_FORMAT_SECTIONS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ sections, py_pe_format, \
+ "Shortcut to the definitions of all PE format sections.\n" \
+ "\n" \
+ "This property is a pychrysalide.StructObject instance.\n" \
+ "\n" \
+ "Each returned item is composed of the following properties :\n"\
+ "\n" \
+ "* name;\n" \
+ "* misc.virtual_size;\n" \
+ "* virtual_address;\n" \
+ "* size_of_raw_data;\n" \
+ "* pointer_to_raw_data;\n" \
+ "* pointer_to_relocations;\n" \
+ "* pointer_to_line_numbers;\n" \
+ "* number_of_relocations;\n" \
+ "* number_of_line_numbers;\n" \
+ "* characteristics." \
+)
+
+ format = G_PE_FORMAT(pygobject_get(self));
+
+ sections = g_pe_format_get_sections(format, &count);
+
+ result = PyTuple_New(count);
+
+ for (i = 0; i < count; i++)
+ {
+ item = translate_pe_section_header_to_python(format, sections + i);
+
+ ret = PyTuple_SetItem(result, i, item);
+
+ if (ret != 0)
+ {
+ Py_DECREF(result);
+ result = NULL;
+ break;
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = format en place à consulter. *
+* closure = non utilisé ici. *
+* *
* Description : Présente l'en-tête enrichi du format chargé. *
* *
* Retour : Tableau de valeurs brutes d'information. *
@@ -487,7 +546,9 @@ PyTypeObject *get_python_pe_format_type(void)
static PyGetSetDef py_pe_format_getseters[] = {
PE_FORMAT_DOS_HEADER_ATTRIB,
+ PE_FORMAT_NT_HEADERS_ATTRIB,
PE_FORMAT_DIRECTORIES_ATTRIB,
+ PE_FORMAT_SECTIONS_ATTRIB,
PE_FORMAT_RICH_HEADER_ATTRIB,
PE_FORMAT_RICH_HEADER_CHECKSUM_ATTRIB,
PE_FORMAT_COMP_IDS_ATTRIB,
@@ -545,8 +606,8 @@ bool register_python_pe_format(PyObject *module)
if (!register_class_for_pygobject(dict, G_TYPE_PE_FORMAT, type))
return false;
- if (!define_python_pe_format_constants(type))
- return false;
+ //if (!define_python_pe_format_constants(type))
+ // return false;
return true;
diff --git a/plugins/pe/python/module.c b/plugins/pe/python/module.c
index 93b1337..ce0c8d7 100644
--- a/plugins/pe/python/module.c
+++ b/plugins/pe/python/module.c
@@ -33,7 +33,7 @@
#include "format.h"
-#include "routine.h"
+//#include "routine.h"
@@ -83,8 +83,8 @@ bool add_format_pe_module_to_python_module(void)
result = (module != NULL);
if (result) result = register_python_pe_format(module);
- if (result) result = register_python_pe_exported_routine(module);
- if (result) result = register_python_pe_imported_routine(module);
+ //if (result) result = register_python_pe_exported_routine(module);
+ //if (result) result = register_python_pe_imported_routine(module);
assert(result);
diff --git a/plugins/pe/python/translate.c b/plugins/pe/python/translate.c
index c01a337..1b4b3ce 100644
--- a/plugins/pe/python/translate.c
+++ b/plugins/pe/python/translate.c
@@ -45,7 +45,7 @@
* *
******************************************************************************/
-PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header *header)
+PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header_t *header)
{
PyObject *result; /* Construction à retourner */
PyTypeObject *base; /* Modèle d'objet à créer */
@@ -64,6 +64,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
{ \
attrib = PyLong_FromUnsignedLongLong(header->e_ ## _f); \
ret = PyDict_SetItemString(result, "e_" #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
@@ -79,8 +80,13 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
ret = PyTuple_SetItem(attrib, i, item); \
if (ret != 0) break; \
} \
- if (ret != 0) goto failed; \
+ if (i < _n) \
+ { \
+ Py_DECREF(attrib); \
+ goto failed; \
+ } \
ret = PyDict_SetItemString(result, "e_" #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
@@ -119,6 +125,215 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
/******************************************************************************
* *
* Paramètres : format = format PE chargé sur lequel s'appuyer. *
+* header = en-tête NT à décrire en Python. *
+* *
+* Description : Traduit un en-tête PE en Python. *
+* *
+* Retour : Structure mise en place ou NULL en cas d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *translate_pe_nt_headers_to_python(GPeFormat *format, const image_nt_headers_t *header)
+{
+ PyObject *result; /* Construction à retourner */
+ PyTypeObject *base; /* Modèle d'objet à créer */
+ PyObject *attrib; /* Attribut à constituer */
+ int ret; /* Bilan d'une mise en place */
+ PyObject *sub; /* Sous-construction #1 */
+ bool is_32b; /* Format en version 32 bits ? */
+ const image_data_directory_t *directories; /* Répertoires à charger */
+ uint32_t number_of_rva_and_sizes; /* Quantité de ces répertoires */
+ uint32_t i; /* Boucle de parcours */
+ PyObject *dirs; /* Répertoires de données */
+ PyObject *subsub; /* Sous-construction #2 */
+
+ base = get_python_py_struct_type();
+
+ result = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(result != NULL);
+
+#define TRANSLATE_IMAGE_NT_HEADERS_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->_f); \
+ ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) goto failed; \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_NT_HEADERS_FIELD(signature);
+
+ /* Partie file_header */
+
+ sub = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(sub != NULL);
+
+#define TRANSLATE_IMAGE_FILE_HEADER_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->file_header._f); \
+ ret = PyDict_SetItemString(sub, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) \
+ { \
+ Py_DECREF(sub); \
+ goto failed; \
+ } \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(machine);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_sections);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(time_date_stamp);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(pointer_to_symbol_table);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_symbols);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(size_of_optional_header);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(characteristics);
+
+ ret = PyDict_SetItemString(result, "file_header", sub);
+ Py_DECREF(sub);
+ if (ret != 0) goto failed;
+
+ /* Partie optional_header */
+
+ sub = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(sub != NULL);
+
+ is_32b = g_pe_format_get_is_32b(format);
+
+#define TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(_f) \
+ do \
+ { \
+ if (is_32b) \
+ attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \
+ else \
+ attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_64._f); \
+ ret = PyDict_SetItemString(sub, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) \
+ { \
+ Py_DECREF(sub); \
+ goto failed; \
+ } \
+ } \
+ while (0);
+
+#define TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \
+ ret = PyDict_SetItemString(sub, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) \
+ { \
+ Py_DECREF(sub); \
+ goto failed; \
+ } \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(magic);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_linker_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_linker_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_code);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_initialized_data);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_uninitialized_data);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(address_of_entry_point);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(base_of_code);
+ if (is_32b) TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(base_of_data);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(image_base);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(section_alignment);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(file_alignment);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_operating_system_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_operating_system_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_image_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_image_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_subsystem_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_subsystem_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(win32_version_value);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_image);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_headers);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(checksum);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(subsystem);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(dll_characteristics);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_reserve);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_commit);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_reserve);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_commit);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(loader_flags);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(number_of_rva_and_sizes);
+
+ ret = PyDict_SetItemString(result, "optional_header", sub);
+ Py_DECREF(sub);
+ if (ret != 0) goto failed;
+
+ /* Répertoires de données */
+
+ if (is_32b)
+ {
+ directories = header->optional_header.header_32.data_directory;
+ number_of_rva_and_sizes = header->optional_header.header_32.number_of_rva_and_sizes;
+ }
+ else
+ {
+ directories = header->optional_header.header_64.data_directory;
+ number_of_rva_and_sizes = header->optional_header.header_64.number_of_rva_and_sizes;
+ }
+
+ dirs = PyTuple_New(number_of_rva_and_sizes);
+
+ for (i = 0; i < number_of_rva_and_sizes; i++)
+ {
+ subsub = translate_pe_image_data_directory_to_python(format, directories + i);
+ if (subsub == NULL) break;
+
+ ret = PyTuple_SetItem(dirs, i, subsub);
+ if (ret != 0) break;
+
+ }
+
+ if (i < number_of_rva_and_sizes)
+ goto failed_with_dirs;
+
+ /**
+ * La fonction PyTuple_SetItem() comporte le prologue suivant :
+ *
+ * if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
+ * Py_XDECREF(newitem);
+ * PyErr_BadInternalCall();
+ * return -1;
+ * }
+ *
+ * Comme l'appel à PyDict_SetItemString() incrémente le compte de référence
+ * de dirs, il convient de le réaliser après la consitution de la liste.
+ */
+
+ ret = PyDict_SetItemString(sub, "directories", dirs);
+ if (ret != 0) goto failed_with_dirs;
+
+ Py_DECREF(dirs);
+
+ return result;
+
+ failed_with_dirs:
+
+ Py_DECREF(dirs);
+
+ failed:
+
+ Py_DECREF(result);
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = format PE chargé sur lequel s'appuyer. *
* dir = répertoire PE à décrire en Python. *
* *
* Description : Traduit un répertoire PE en Python. *
@@ -129,7 +344,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
* *
******************************************************************************/
-PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory *dir)
+PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory_t *dir)
{
PyObject *result; /* Construction à retourner */
PyTypeObject *base; /* Modèle d'objet à créer */
@@ -146,6 +361,7 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i
{ \
attrib = PyLong_FromUnsignedLongLong(dir->_f); \
ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
@@ -167,6 +383,73 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i
/******************************************************************************
* *
* Paramètres : format = format PE chargé sur lequel s'appuyer. *
+* header = en-tête de section à décrire en Python. *
+* *
+* Description : Traduit une section PE en Python. *
+* *
+* Retour : Structure mise en place ou NULL en cas d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *translate_pe_section_header_to_python(GPeFormat *format, const image_section_header_t *header)
+{
+ PyObject *result; /* Construction à retourner */
+ PyTypeObject *base; /* Modèle d'objet à créer */
+ PyObject *attrib; /* Attribut à constituer */
+ int ret; /* Bilan d'une mise en place */
+
+ base = get_python_py_struct_type();
+
+ result = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(result != NULL);
+
+ /* Nom de la section */
+
+ attrib = PyBytes_FromStringAndSize(header->name, IMAGE_SIZEOF_SHORT_NAME);
+
+ ret = PyDict_SetItemString(result, "name", attrib);
+ Py_DECREF(attrib);
+
+ if (ret != 0) goto failed;
+
+ /* Eléments classiques */
+
+#define TRANSLATE_IMAGE_SECTION_HEADER_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->_f); \
+ ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) goto failed; \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_size);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_address);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(size_of_raw_data);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_raw_data);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_relocations);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_line_numbers);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_relocations);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_line_numbers);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(characteristics);
+
+ return result;
+
+ failed:
+
+ Py_DECREF(result);
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = format PE chargé sur lequel s'appuyer. *
* id = ensemble d'informations à décrire en Python. *
* *
* Description : Traduit une série d'informations enrichies en Python. *
@@ -194,6 +477,7 @@ PyObject *translate_pe_comp_id_to_python(GPeFormat *format, const comp_id_t *id)
{ \
attrib = PyLong_FromUnsignedLongLong(id->_f); \
ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
diff --git a/plugins/pe/python/translate.h b/plugins/pe/python/translate.h
index dbde6c8..e12b4ae 100644
--- a/plugins/pe/python/translate.h
+++ b/plugins/pe/python/translate.h
@@ -35,10 +35,16 @@
/* Traduit un en-tête MS-DOS en Python. */
-PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header *);
+PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header_t *);
+
+/* Traduit un en-tête PE en Python. */
+PyObject *translate_pe_nt_headers_to_python(GPeFormat *, const image_nt_headers_t *);
/* Traduit un répertoire PE en Python. */
-PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory *);
+PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory_t *);
+
+/* Traduit une section PE en Python. */
+PyObject *translate_pe_section_header_to_python(GPeFormat *, const image_section_header_t *);
/* Traduit une série d'informations enrichies en Python. */
PyObject *translate_pe_comp_id_to_python(GPeFormat *, const comp_id_t *);
diff --git a/plugins/pe/section.c b/plugins/pe/section.c
deleted file mode 100644
index 732b35c..0000000
--- a/plugins/pe/section.c
+++ /dev/null
@@ -1,73 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * section.h - prototypes pour la gestion des sections d'un PE
- *
- * Copyright (C) 2010-2021 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 <http://www.gnu.org/licenses/>.
- */
-
-
-#include "section.h"
-
-
-#include <malloc.h>
-
-
-#include "pe-int.h"
-
-
-
-/******************************************************************************
-* *
-* Paramètres : format = description de l'exécutable à consulter. *
-* pos = tête de lecture positionnée. [OUT] *
-* *
-* Description : Recherche une section donnée au sein de binaire par indice. *
-* *
-* Retour : Liste de sections reconstituées ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-image_section_header *read_all_pe_sections(const GPeFormat *format, vmpa2t *pos)
-{
- image_section_header *result; /* Liste à retourner */
- uint16_t count; /* Quantité de sections */
- uint16_t i; /* Boucle de parcours */
- bool status; /* Bilan d'une lecture */
-
- count = format->nt_headers.file_header.number_of_sections;
-
- result = malloc(count * sizeof(image_section_header));
-
- for (i = 0; i < count; i++)
- {
- status = read_pe_image_section_header(format, pos, &result[i]);
-
- if (!status)
- {
- free(result);
- result = NULL;
- break;
- }
-
- }
-
- return result;
-
-}
diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am
index 183a4ef..d1bf457 100644
--- a/plugins/pychrysalide/Makefile.am
+++ b/plugins/pychrysalide/Makefile.am
@@ -1,7 +1,7 @@
DEFAULT_INCLUDES = -I$(top_builddir) -idirafter.
-lib_LTLIBRARIES = pychrysalide.la
+lib_LTLIBRARIES = pychrysalide.la pychrysalideui.la
libdir = $(pluginslibdir)
@@ -32,7 +32,10 @@ endif
pychrysalide_la_SOURCES = \
access.h access.c \
+ bindings.h bindings.c \
constants.h constants.c \
+ convert.h convert.c \
+ core-int.h \
core.h core.c \
helpers.h helpers.c \
star.h star.c \
@@ -71,6 +74,24 @@ pychrysalide_la_LDFLAGS = \
$(RUN_PATH)
+EXTRA_pychrysalideui_la_DEPENDENCIES = pychrysalide.la
+
+pychrysalideui_la_SOURCES = \
+ core-ui-int.h \
+ core-ui.h core-ui.c
+
+pychrysalideui_la_LIBADD =
+
+# -ldl: dladdr(), dlerror()
+pychrysalideui_la_LDFLAGS = \
+ -module -avoid-version -ldl \
+ $(LIBPYTHON_INTERPRETER_LIBS) \
+ $(LIBPYGOBJECT_LIBS) \
+ -L.libs -l:pychrysalide.so \
+ -L$(top_srcdir)/src/.libs -lchrysacoreui\
+ $(RUN_PATH)
+
+
devdir = $(includedir)/chrysalide/$(subdir)
dev_HEADERS = $(pychrysalide_la_SOURCES:%c=)
diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c
index dd9c1c1..c271139 100644
--- a/plugins/pychrysalide/analysis/content.c
+++ b/plugins/pychrysalide/analysis/content.c
@@ -50,9 +50,9 @@
/* Initialise la classe générique des contenus de binaire. */
-static void py_binary_content_init_gclass(GBinContentClass *, gpointer);
+static int py_binary_content_init_gclass(GBinContentClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT, py_binary_content_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_binary_content_init(PyObject *, PyObject *, PyObject *);
@@ -163,37 +163,39 @@ static PyObject *py_binary_content_get_data(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe générique des contenus de binaire. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_binary_content_init_gclass(GBinContentClass *class, gpointer unused)
+static int py_binary_content_init_gclass(GBinContentClass *gclass, PyTypeObject *pyclass)
{
- class->describe = py_binary_content_describe_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->describe, py_binary_content_describe_wrapper);
+
+ PY_CLASS_SET_WRAPPER(gclass->compute_checksum, py_binary_content_compute_checksum_wrapper);
- class->compute_checksum = py_binary_content_compute_checksum_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->compute_size, py_binary_content_compute_size_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->compute_start_pos, py_binary_content_compute_start_pos_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->compute_end_pos, py_binary_content_compute_end_pos_wrapper);
- class->compute_size = py_binary_content_compute_size_wrapper;
- class->compute_start_pos = py_binary_content_compute_start_pos_wrapper;
- class->compute_end_pos = py_binary_content_compute_end_pos_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->seek, py_binary_content_seek_wrapper);
- class->seek = py_binary_content_seek_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->read_raw, py_binary_content_read_raw_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u8, py_binary_content_read_u8_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u16, py_binary_content_read_u16_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u32, py_binary_content_read_u32_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u64, py_binary_content_read_u64_wrapper);
- class->read_raw = py_binary_content_read_raw_wrapper;
- class->read_u8 = py_binary_content_read_u8_wrapper;
- class->read_u16 = py_binary_content_read_u16_wrapper;
- class->read_u32 = py_binary_content_read_u32_wrapper;
- class->read_u64 = py_binary_content_read_u64_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->read_uleb128, py_binary_content_read_uleb128_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_leb128, py_binary_content_read_leb128_wrapper);
- class->read_uleb128 = py_binary_content_read_uleb128_wrapper;
- class->read_leb128 = py_binary_content_read_leb128_wrapper;
+ return 0;
}
@@ -2248,6 +2250,8 @@ bool ensure_python_binary_content_is_registered(void)
dict = PyModule_GetDict(module);
+ pyg_register_class_init(G_TYPE_BIN_CONTENT, (PyGClassInitFunc)py_binary_content_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_BIN_CONTENT, type))
return false;
diff --git a/plugins/pychrysalide/arch/module.c b/plugins/pychrysalide/arch/module.c
index bbdaf76..0127348 100644
--- a/plugins/pychrysalide/arch/module.c
+++ b/plugins/pychrysalide/arch/module.c
@@ -28,9 +28,6 @@
#include <assert.h>
-#include <arch/archbase.h>
-
-
/*
#include "context.h"
#include "instriter.h"
diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c
new file mode 100644
index 0000000..7e87e27
--- /dev/null
+++ b/plugins/pychrysalide/bindings.c
@@ -0,0 +1,1466 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bindings.c - éléments d'un socle commun aux fonctionnalités graphiques et non graphiques
+ *
+ * Copyright (C) 2024 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "bindings.h"
+
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <pygobject.h>
+#include <stddef.h>
+#include <stdio.h>
+
+
+#include <common/cpp.h>
+#include <common/extstr.h>
+#include <core/core.h>
+#include <plugins/pglist.h>
+#include <plugins/self.h>
+
+
+#include "access.h"
+#include "constants.h"
+#include "helpers.h"
+#include "star.h"
+//#include "strenum.h"
+#include "struct.h"
+#include "analysis/module.h"
+#include "arch/module.h"
+#include "common/module.h"
+#include "core/module.h"
+#include "glibext/module.h"
+/* #include "debug/module.h" */
+#include "format/module.h"
+/* #ifdef INCLUDE_GTK_SUPPORT */
+/* # include "gtkext/module.h" */
+/* # include "gui/module.h" */
+/* #endif */
+/* #include "mangling/module.h" */
+#include "plugins/module.h"
+
+
+
+
+
+/* ------------------------ FONCTIONNALITES POUR CODE PYTHON ------------------------ */
+
+
+#define PYCHRYSALIDE_NAME "pychrysalide"
+
+#define PYCHRYSALIDE_DOC \
+ "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \
+ "\n" \
+ "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \
+ "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \
+ "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
+ "\n" \
+ "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \
+ "how the API works.\n" \
+ "\n" \
+ "These plugins are located in the 'plugins/python' directory.\n" \
+ "\n" \
+ "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup."
+
+
+/* Fournit la révision du programme global. */
+static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
+
+/* Fournit la version du programme global. */
+static PyObject *py_chrysalide_version(PyObject *, PyObject *);
+
+/* Fournit la version du greffon pour Python. */
+static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
+
+
+
+/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */
+
+
+/* Détermine si l'interpréteur lancé est celui pris en compte. */
+static bool is_current_abi_suitable(void);
+
+/* Assure une pleine initialisation des objets de Python-GI. */
+static bool install_metaclass_for_python_gobjects(void);
+
+/* Met en place un environnement pour l'extension Python. */
+static bool setup_python_context(void);
+
+/* Intègre les éventuelles fonctions natives des interfaces. */
+static void inherit_interface_slots(PyObject *);
+
+/**
+ * Conservation d'anciens pointeurs remplacés
+ */
+static initproc __old_gobject_meta_base_init = NULL;
+static initproc __old_gobject_meta_init = NULL;
+
+/**
+ * La fonction unhook_pygobject_behaviour(), inversant les opérations de la fonction
+ * unhook_pygobject_behaviour() manipulerait volontiers les fonctions PyImport_ImportModule()
+ * et PyObject_GetAttrString().
+ *
+ * Cependant, les appels à ces dernières depuis la clôture organisée par la fonction
+ * PyExit_pychrysalide() provoque l'erreur suivante :
+ *
+ * Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
+ *
+ * Les accès nécessaires sont donc conservés ici.
+ */
+static PyTypeObject *__gobject_meta_base = NULL;
+static PyTypeObject *__gobject_meta = NULL;
+
+/* Interceptionne une initialisation pour types gi._gi.GObject. */
+static int hook_gobject_meta_base_init(PyObject *, PyObject *, PyObject *);
+
+/* Interceptionne une initialisation pour types GObject.Object. */
+static int hook_gobject_meta_init(PyObject *, PyObject *, PyObject *);
+
+/* Modifie légèrement le comportement des GObjects en Python. */
+static bool hook_pygobject_behaviour(void);
+
+/* Restaure le comportement d'origine des GObjects en Python. */
+static void unhook_pygobject_behaviour(void);
+
+/* Assure la définition d'un type GObject pour Python adapté. */
+static void ensure_native_pygobject_type(PyTypeObject **);
+
+/* Fournit la référence à un éventuel module déjà en place. */
+static PyObject *get_existing_modules(void);
+
+/* Définit les différents modules du support Python. */
+static PyObject *create_basic_modules(void);
+
+/* Inscrit les défintions des objets Python de Chrysalide. */
+static bool populate_python_modules(const pyinit_details_t *);
+
+/* Restore une ancienne définition de type GObject au besoin. */
+static void restore_original_pygobject_type(PyTypeObject *);
+
+
+/* ------------------------ FONCTIONS GLOBALES DE CHRYSALIDE ------------------------ */
+
+
+/* Assure le plein chargement dans un interpréteur Python. */
+static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *);
+
+/* Point de sortie pour l'initialisation de Python. */
+static void PyExit_pychrysalide(void);
+
+
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONNALITES POUR CODE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la révision du programme global. *
+* *
+* Retour : Numéro de révision. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+
+#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \
+( \
+ revision, "/", \
+ METH_NOARGS, py_chrysalide, \
+ "Provide the revision number of Chrysalide.\n" \
+ "\n" \
+ "The returned value is provided as a string, for instance: 'r1665'." \
+)
+
+ result = PyUnicode_FromString("r" XSTR(REVISION));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la version du programme global. *
+* *
+* Retour : Numéro de version. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_version(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ int major; /* Numéro de version majeur */
+ int minor; /* Numéro de version mineur */
+ int revision; /* Numéro de révision */
+ char version[16]; /* Conservation temporaire */
+
+#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \
+( \
+ version, "/", \
+ METH_NOARGS, py_chrysalide, \
+ "Provide the version number of Chrysalide.\n" \
+ "\n" \
+ "The returned value is provided as a string, for instance: '1.6.65'." \
+)
+
+ major = REVISION / 1000;
+ minor = (REVISION - (major * 1000)) / 100;
+ revision = REVISION % 100;
+
+ snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
+
+ result = PyUnicode_FromString(version);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la version du greffon pour Python. *
+* *
+* Retour : Numéro de version. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ char version[16]; /* Conservation temporaire */
+
+#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \
+( \
+ mod_version, "/", \
+ METH_NOARGS, py_chrysalide, \
+ "Provide the version number of Chrysalide module for Python.\n" \
+ "\n" \
+ "The returned value is provided as a string, for instance: '0.1.0'." \
+)
+
+ snprintf(version, sizeof(version), "%s", "x.x.x");// FIXME _chrysalide_plugin.version);
+
+ result = PyUnicode_FromString(version);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONNALITES DE MISE EN PLACE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Détermine si l'interpréteur lancé est celui pris en compte. *
+* *
+* Retour : true si l'exécution peut se poursuivre, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool is_current_abi_suitable(void)
+{
+ bool result;
+ int fds[2];
+ int ret;
+ char cmds[128];
+ char content[64];
+ ssize_t got;
+
+#define GRAB_ABI_FLAGS_IN_PYTHON \
+ "import sys" "\n" \
+ "import os" "\n" \
+ "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \
+ "os.write(%d, data)" "\n"
+
+ result = false;
+
+ ret = pipe(fds);
+ if (ret == -1)
+ {
+ perror("pipe()");
+ goto exit;
+ }
+
+ snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
+
+ ret = PyRun_SimpleString(cmds);
+ if (ret != 0) goto exit_with_pipe;
+
+ got = read(fds[0], content, sizeof(content));
+ if (got < 0)
+ {
+ perror("read()");
+ goto exit_with_pipe;
+ }
+
+ content[got] = '\0';
+
+ result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
+
+ exit_with_pipe:
+
+ close(fds[0]);
+ close(fds[1]);
+
+ exit:
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
+ "the ones of the Python library used during the module compilation.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Assure une pleine initialisation des objets de Python-GI. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool install_metaclass_for_python_gobjects(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *gi_types_mod; /* Module Python-GObject */
+
+ /**
+ * Les extensions Python sont chargées à partir de la fonction load_python_plugins(),
+ * qui fait appel à create_python_plugin(). Une instance y est construite via un
+ * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad
+ * dans le fichier __init__.py présent dans chaque module d'extension.
+ *
+ * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique
+ * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction
+ * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c.
+ * Le code de cette dernière comprend notamment la portion suivante :
+ *
+ * [...]
+ * Py_SET_TYPE(type, PyGObject_MetaType);
+ * [...]
+ * if (PyType_Ready(type) < 0) {
+ * g_warning ("couldn't make the type `%s' ready", type->tp_name);
+ * return;
+ * }
+ * [...]
+ *
+ * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c
+ * et commence par :
+ *
+ * int PyType_Ready(PyTypeObject *type)
+ * {
+ * if (type->tp_flags & Py_TPFLAGS_READY) {
+ * assert(_PyType_CheckConsistency(type));
+ * return 0;
+ * }
+ * [...]
+ * }
+ *
+ * La vérification de cohérencce commence par analyser le type et son propre
+ * type :
+ *
+ * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c :
+ *
+ * int _PyType_CheckConsistency(PyTypeObject *type)
+ * {
+ * [...]
+ * CHECK(!_PyObject_IsFreed((PyObject *)type));
+ * [...]
+ * }
+ *
+ * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c :
+ *
+ * int _PyObject_IsFreed(PyObject *op)
+ * {
+ * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
+ * return 1;
+ * }
+ *
+ * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL.
+ *
+ * Or le type du type est écrasé dans la fonction pygobject_register_class()
+ * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est
+ * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c :
+ *
+ * static PyObject *
+ * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass)
+ * {
+ * Py_INCREF(metaclass);
+ * PyGObject_MetaType = metaclass;
+ * Py_INCREF(metaclass);
+ *
+ * Py_SET_TYPE(&PyGObject_Type, metaclass);
+ *
+ * Py_INCREF(Py_None);
+ * return Py_None;
+ * }
+ *
+ * Afin de valider la vérification de _PyType_CheckConsistency() pour les
+ * modules externes qui entraînent un enregistrement tout en portant le drapeau
+ * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut
+ * initialiser au besoin la variable PyGObject_MetaType.
+ *
+ * Une ligne suffit donc à enregistrer le type intermédiaire :
+ *
+ * from _gi import types
+ *
+ * On simule ici une déclaration similaire si nécessaire, selon la valeur
+ * portée par PyGObject_Type.ob_base.ob_base.ob_type.tp_name :
+ * - "type" (PyType_Type) : état initial ;
+ * - "_GObjectMetaBase" : état revu.
+ */
+
+ /**
+ * PyGObject_Type.ob_base.ob_base.ob_type != &PyType_Type ?
+ */
+ result = (PyType_CheckExact(&PyGObject_Type) == 0);
+
+ if (!result)
+ {
+ gi_types_mod = PyImport_ImportModule("gi.types");
+
+ result = (PyErr_Occurred() == NULL);
+
+ if (result)
+ result = (PyType_CheckExact(&PyGObject_Type) == 0);
+
+ Py_XDECREF(gi_types_mod);
+
+ }
+
+#ifndef NDEBUG
+ if (result)
+ assert(strcmp(PyGObject_Type.ob_base.ob_base.ob_type->tp_name, "_GObjectMetaBase") == 0);
+#endif
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Met en place un environnement pour l'extension Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool setup_python_context(void)
+{
+ bool result; /* Bilan à retourner */
+
+ result = false;
+
+ /**
+ * Un message d'erreur doit être défini en cas d'échec de l'initialisation,
+ * via un appel à PyErr_SetString().
+ */
+
+ if (!is_current_abi_suitable())
+ goto exit;
+
+ if (pygobject_init(-1, -1, -1) == NULL)
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
+ goto exit;
+ }
+
+ if (!install_metaclass_for_python_gobjects())
+ goto exit;
+
+ result = true;
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cls = classe instanciée pour la construction d'un objet. *
+* *
+* Description : Intègre les éventuelles fonctions natives des interfaces. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void inherit_interface_slots(PyObject *cls)
+{
+ GType gtype; /* Type GObject lié au Python */
+ GType *ifaces; /* Interfaces implémentées */
+ guint ifaces_count; /* Nombre de ces interfaces */
+ guint i; /* Boucle de parcours */
+ PyTypeObject *iface_type; /* Type Python pour interface */
+ size_t k; /* Boucle de parcours */
+ size_t offset; /* Position dans une structure */
+ void *src_slot; /* Eventuelle fonction idéale */
+ void *dst_slot; /* Eventuelle fonction en place*/
+
+ static size_t slot_offsets[] = { /* Emplacements à actualiser */
+ //offsetof(PyTypeObject, tp_str),
+ offsetof(PyTypeObject, tp_hash),
+ offsetof(PyTypeObject, tp_richcompare),
+ };
+
+ /**
+ * Cette fonction reprend les principes de la fonction d'importation de
+ * PyGObject pygobject_inherit_slots().
+ *
+ * Cependant, cette dernière n'est appelée que depuis les fonctions :
+ * - pygobject_register_class() (send C -> Python), qui peut écraser des
+ * slots existants ;
+ * - pygobject_new_with_interfaces() / pygobject_lookup_class(), qui ne
+ * remplace pas les fonctions par défaut déjà en place.
+ *
+ * Pour mémoire, les types créés dynamiquement depuis des scripts (sens
+ * Python -> C) passent par la fonction _wrap_pyg_type_register().
+ */
+
+ gtype = pyg_type_from_object(cls);
+ assert(gtype != G_TYPE_INVALID);
+
+ ifaces = g_type_interfaces(gtype, &ifaces_count);
+
+ for (i = 0; i < ifaces_count; i++)
+ {
+ iface_type = pygobject_lookup_class(ifaces[i]);
+
+#define PYTYPE_SLOT(tp, off) \
+ *(void **)(void *)(((char *)tp) + off)
+
+ for (k = 0; k < ARRAY_SIZE(slot_offsets); k++)
+ {
+ offset = slot_offsets[k];
+
+ src_slot = PYTYPE_SLOT(iface_type, offset);
+
+ if (src_slot == NULL)
+ continue;
+
+ if (src_slot == PYTYPE_SLOT(&PyBaseObject_Type, offset)
+ || src_slot == PYTYPE_SLOT(&PyGObject_Type, offset))
+ continue;
+
+ dst_slot = PYTYPE_SLOT(cls, offset);
+
+ if (src_slot == dst_slot)
+ continue;
+
+ if (dst_slot != NULL)
+ {
+ if (dst_slot != PYTYPE_SLOT(&PyBaseObject_Type, offset)
+ && dst_slot != PYTYPE_SLOT(&PyGObject_Type, offset))
+ continue;
+ }
+
+ /**
+ * Usage du *(void **)(void *)
+ */
+ PYTYPE_SLOT(cls, offset) = src_slot;
+
+ }
+
+ }
+
+ g_free(ifaces);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Interceptionne une initialisation pour types gi._gi.GObject. *
+* *
+* Retour : Bilan de l'initialisation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int hook_gobject_meta_base_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int result; /* Bilan à retourner */
+
+ /**
+ * Le type de self (self->ob_type->tp_name) est ici _GObjectMetaBase.
+ */
+
+ result = __old_gobject_meta_base_init(self, args, kwds);
+
+ if (result == 0)
+ inherit_interface_slots(self);
+
+ return result;
+
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Interceptionne une initialisation pour types GObject.Object. *
+* *
+* Retour : Bilan de l'initialisation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int hook_gobject_meta_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int result; /* Bilan à retourner */
+
+ /**
+ * Le type de self (self->ob_type->tp_name) est ici GObjectMeta.
+ */
+
+ result = __old_gobject_meta_init(self, args, kwds);
+
+ if (result == 0)
+ inherit_interface_slots(self);
+
+ return result;
+
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Modifie légèrement le comportement des GObjects en Python. *
+* *
+* Retour : Bilan de l'initialisation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool hook_pygobject_behaviour(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *gi_types_mod; /* Module Python-GObject */
+
+ result = false;
+
+ /**
+ * Validation des accès.
+ *
+ * Les références prises sur les attributs sont restituées dans
+ * unhook_pygobject_behaviour().
+ */
+
+ gi_types_mod = PyImport_ImportModule("gi.types");
+ if (gi_types_mod == NULL) goto exit;
+
+ __gobject_meta_base = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "_GObjectMetaBase");
+ assert(__gobject_meta_base != NULL);
+ if (__gobject_meta_base == NULL) goto exit_with_mod;
+
+ __gobject_meta = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "GObjectMeta");
+ assert(__gobject_meta != NULL);
+ if (__gobject_meta == NULL) goto exit_with_mod;
+
+ /**
+ * Modification des comportements.
+ */
+
+ __old_gobject_meta_base_init = __gobject_meta_base->tp_init;
+
+ __gobject_meta_base->tp_init = hook_gobject_meta_base_init;
+
+ __old_gobject_meta_init = __gobject_meta->tp_init;
+
+ __gobject_meta->tp_init = hook_gobject_meta_init;
+
+ result = true;
+
+ exit_with_mod:
+
+ Py_DECREF(gi_types_mod);
+
+ exit:
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "unable to hook the GObject behaviour in Python.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Restaure le comportement d'origine des GObjects en Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void unhook_pygobject_behaviour(void)
+{
+ /**
+ * Le déclenchement de la fonction PyExit_pychrysalide() appelante est
+ * programmé depuis init_python_pychrysalide_module(), appelée si
+ * hook_pygobject_behaviour() a opéré avec réussite.
+ */
+ assert(__gobject_meta_base != NULL);
+ assert(__gobject_meta != NULL);
+
+ __gobject_meta_base->tp_init = __old_gobject_meta_base_init;
+
+ Py_XDECREF(__gobject_meta_base);
+
+ __gobject_meta->tp_init = __old_gobject_meta_init;
+
+ Py_XDECREF(__gobject_meta);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : namespace = module particulier à charger à partir de gi. *
+* version = idenfiant de la version à stipuler. *
+* *
+* Description : Charge un module GI dans Python avec une version attendue. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool import_namespace_from_gi_repository(const char *namespace, const char *version)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *module; /* Module Python-GObject */
+ PyObject *args; /* Arguments à fournir */
+ int ret; /* Bilan d'une mise en place */
+
+ result = false;
+
+ /* Sélection d'une version */
+
+ module = PyImport_ImportModule("gi");
+
+ if (module != NULL)
+ {
+ args = Py_BuildValue("ss", namespace, version);
+
+ run_python_method(module, "require_version", args);
+
+ result = (PyErr_Occurred() == NULL);
+
+ Py_DECREF(args);
+ Py_DECREF(module);
+
+ }
+
+ /* Importation du module visé */
+
+ if (result)
+ {
+ args = PyTuple_New(1);
+
+ ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace));
+ if (ret != 0)
+ {
+ result = false;
+ goto args_error;
+ }
+
+ module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args);
+
+ result = (module != NULL);
+
+ Py_XDECREF(module);
+
+ args_error:
+
+ Py_DECREF(args);
+
+ }
+
+ // TODO : message d'erreur ?
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : py_gobj_def = définition de type actuelle. [OUT] *
+* *
+* Description : Assure la définition d'un type GObject pour Python adapté. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void ensure_native_pygobject_type(PyTypeObject **py_gobj_def)
+{
+ GQuark pygobject_class_key; /* Copie d'un accès GI interne */
+
+ /**
+ * Les appels suivants procèdent à l'enregistrement de différents éléments
+ * dans l'espace de noms Python pour Chrysalide.
+ *
+ * Une majeure partie de ces éléments est constituée d'objets dérivés de
+ * GObject. Ce type d'objet (G_TYPE_OBJECT) est représenté par deux types
+ * en Python :
+ *
+ * - gi._gi.GObject, mis en place lors de l'importation du module gi.
+ *
+ * Ce dernier lance automatiquement l'importation du module natif gi._gi,
+ * lequel, via son initialisation dans la fonction PyInit__gi()
+ * (cf. ./gi/gimodule.c) lance un appel à pyi_object_register_types()
+ * qui procède à l'enregistrement du type "GObject" porté par la structure
+ * PyGObject_Type en correspondance au type GLib G_TYPE_OBJECT (cf. appel
+ * à pygobject_register_class() dans gi/pygobject-object.c).
+ *
+ * - gi.repository.GObject.Object, qui vient surclasser le type précédent
+ * lors d'un appel d'initialisation : from gi.repository import GObject.
+ *
+ * Cette seconde définition est destinée à apporter une représentation
+ * de l'introspection GObject de plus haut niveau pour l'utilisateur par
+ * rapport à celle de bas niveau gi._gi.
+ *
+ * Il demeure que la seconde définition est entièrement implémentée en Python
+ * et porte ainsi le fanion Py_TPFLAGS_HEAPTYPE, imposant cette même dernière
+ * propriétée à tous les objets qui en dérivent.
+ *
+ * Les définitions de Chrysalide sont cependant toutes statiques et donc
+ * incompatibles avec une définition gi.repository.GObject.Object, comme le
+ * pointent les validations opérées par PyType_Ready().
+ *
+ * Une solution consiste ici à restaurer au besoin la définition gi._gi.GObject
+ * brute, effectuer les enregistrements de Chrysalide sur cette base, et
+ * remettre en place la définition éventuellement remplacée ensuite.
+ */
+
+ *py_gobj_def = pygobject_lookup_class(G_TYPE_OBJECT);
+
+ if (*py_gobj_def != &PyGObject_Type)
+ {
+ Py_INCREF((PyObject *)*py_gobj_def);
+
+ /* Définition récupérée de pyi_object_register_types() */
+ pygobject_class_key = g_quark_from_static_string("PyGObject::class");
+
+ g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, &PyGObject_Type);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit la référence à un éventuel module déjà en place. *
+* *
+* Retour : Pointeur vers le module mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *get_existing_modules(void)
+{
+ PyObject *result; /* Module Python à retourner */
+
+ /**
+ * Vérification préalable : dans le cas où on est embarqué directement dans
+ * un interpréteur Python, le module se charge et termine par charger à leur
+ * tour les différentes extensions trouvées, via load_remaning_plugins() puis
+ * chrysalide_plugin_on_native_loaded().
+ *
+ * Lesquelles vont très probablement charger le module pychrysalide.
+ *
+ * Comme le chargement de ce dernier n'est alors pas encore terminé,
+ * Python va relancer cette procédure, et register_access_to_python_module()
+ * va détecter un doublon.
+ */
+
+ result = get_access_to_python_module(PYCHRYSALIDE_NAME);
+
+ Py_XINCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Définit les différents modules du support Python. *
+* *
+* Retour : Pointeur vers le module mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *create_basic_modules(void)
+{
+ PyObject *result; /* Module Python à retourner */
+ bool status; /* Bilan des inclusions */
+
+ static PyMethodDef py_chrysalide_methods[] = {
+ PY_CHRYSALIDE_REVISION_METHOD,
+ PY_CHRYSALIDE_VERSION_METHOD,
+ PY_CHRYSALIDE_MOD_VERSION_METHOD,
+ { NULL }
+ };
+
+ static PyModuleDef py_chrysalide_module = {
+
+ .m_base = PyModuleDef_HEAD_INIT,
+
+ .m_name = PYCHRYSALIDE_NAME,
+ .m_doc = PYCHRYSALIDE_DOC,
+
+ .m_size = -1,
+
+ .m_methods = py_chrysalide_methods
+
+ };
+
+ result = PyModule_Create(&py_chrysalide_module);
+
+ register_access_to_python_module(py_chrysalide_module.m_name, result);
+
+ status = true;
+
+ /**
+ * Réceptacle pour objets et constantes : à laisser en premier ajout donc.
+ */
+ if (status) status = add_features_module(result);
+
+ if (status) status = define_data_types_constants(result);
+
+ if (status) status = add_analysis_module(result);
+ if (status) status = add_arch_module(result);
+ if (status) status = add_common_module(result);
+ if (status) status = add_glibext_module(result);
+ if (status) status = add_core_module(result);
+ /*
+ if (status) status = add_debug_module(result);
+ */
+ if (status) status = add_format_module(result);
+ /*
+#ifdef INCLUDE_GTK_SUPPORT
+ if (status) status = add_gtkext_module(result);
+ if (status) status = add_gui_module(result);
+#endif
+ if (status) status = add_mangling_module(result);
+ */
+ if (status) status = add_plugins_module(result);
+
+ if (!status)
+ {
+ Py_DECREF(result);
+ result = NULL;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : details = précisions de chargement complémentaires. *
+* *
+* Description : Inscrit les défintions des objets Python de Chrysalide. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool populate_python_modules(const pyinit_details_t *details)
+{
+ bool result; /* Bilan à retourner */
+
+ /**
+ * Les chargements de types supplémentaires, apportés par la version UI, ne
+ * peuvent être forcés depuis les mises en places des versions non-UI via
+ * les classiques appels ensure_xxx().
+ *
+ * L'assurance d'un chargement préalable est ainsi réalisée ici, via
+ * un chargement préliminaire, si besoin est.
+ */
+
+ if (details->populate_extra)
+ result = details->populate_extra();
+ else
+ result = true;
+
+ /*
+ if (result) result = ensure_python_string_enum_is_registered();
+ */
+ if (result) result = ensure_python_py_struct_is_registered();
+
+ if (result) result = populate_analysis_module();
+ if (result) result = populate_arch_module();
+ if (result) result = populate_glibext_module();
+ if (result) result = populate_common_module();
+ if (result) result = populate_core_module();
+ /*
+ if (result) result = populate_debug_module();
+ */
+ if (result) result = populate_format_module();
+ /*
+#ifdef INCLUDE_GTK_SUPPORT
+ if (result) result = populate_gtkext_module();
+ if (result) result = populate_gui_module();
+#endif
+ if (result) result = populate_mangling_module();
+ */
+ if (result) result = populate_plugins_module();
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : py_gobj_def = définition de type actuelle. [OUT] *
+* *
+* Description : Restore une ancienne définition de type GObject au besoin. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void restore_original_pygobject_type(PyTypeObject *py_gobj_def)
+{
+ GQuark pygobject_class_key; /* Copie d'un accès GI interne */
+
+ if (py_gobj_def != &PyGObject_Type)
+ {
+ /* Définition récupérée de pyi_object_register_types() */
+ pygobject_class_key = g_quark_from_static_string("PyGObject::class");
+
+ g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def);
+
+ Py_DECREF((PyObject *)py_gobj_def);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : details = précisions de chargement complémentaires. *
+* *
+* Description : Implémente le point d'entrée pour l'initialisation de Python.*
+* *
+* Retour : Module mis en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *init_python_pychrysalide_module(const pyinit_details_t *details)
+{
+ PyObject *result; /* Module Python à retourner */
+ PyTypeObject *py_gobj_def; /* Définition GObject courante */
+ bool status; /* Bilan des inclusions */
+
+ result = get_existing_modules();
+
+ if (result != NULL)
+ return result;
+
+ if (!setup_python_context())
+ goto exit;
+
+ if (!hook_pygobject_behaviour())
+ goto exit;
+
+ /**
+ * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil,
+ * à savoir des types convertis de façon incomplète. Par exemple, pour une
+ * structure GChecksum, le type à l'exécution est :
+ *
+ * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>]
+ *
+ * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>]
+ *
+ * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec
+ * la version debug de Python3 (version de python3-gi : 3.42.2-3) :
+ *
+ * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module()
+ *
+ * Code de reproduction dans un interpréteur classique :
+ *
+ * import gi
+ * gi.require_version('GLib', '2.0')
+ * from gi.repository import GLib
+ *
+ */
+
+ if (!import_namespace_from_gi_repository("GLib", "2.0"))
+ goto exit;
+
+ /* Mise en place des fonctionnalités offertes */
+
+ ensure_native_pygobject_type(&py_gobj_def);
+
+ result = create_basic_modules();
+
+ if (result == NULL)
+ PyErr_SetString(PyExc_SystemError, "failed to create all PyChrysalide modules.");
+
+ else
+ {
+ status = populate_python_modules(details);
+
+ if (!status)
+ PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
+
+ else if (details->standalone)
+ status = init_python_interpreter_for_standalone_mode(details);
+
+ if (!status)
+ {
+ Py_DECREF(result);
+ result = NULL;
+ }
+
+ }
+
+ restore_original_pygobject_type(py_gobj_def);
+
+ exit:
+
+ if (result == NULL && !details->standalone)
+ log_pychrysalide_exception("Python bindings loading failed");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.*
+* *
+* Description : Présente dans le journal une exception survenue. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void log_pychrysalide_exception(const char *prefix, ...)
+{
+ va_list ap; /* Compléments argumentaires */
+ char *msg; /* Message complet à imprimer */
+ PyObject *err_type; /* Type d'erreur Python */
+ PyObject *err_value; /* Instance Python d'erreur */
+ PyObject *err_traceback; /* Trace Python associée */
+ PyObject *err_string; /* Description Python d'erreur */
+ const char *err_msg; /* Représentation humaine */
+
+ assert(PyGILState_Check() == 1);
+
+ if (PyErr_Occurred())
+ {
+ /* Base de la communication */
+
+ va_start(ap, prefix);
+
+ vasprintf(&msg, prefix, ap);
+
+ va_end(ap);
+
+ /* Détails complémentaires */
+
+ PyErr_Fetch(&err_type, &err_value, &err_traceback);
+
+ PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+ if (err_traceback == NULL)
+ {
+ err_traceback = Py_None;
+ Py_INCREF(err_traceback);
+ }
+
+ PyException_SetTraceback(err_value, err_traceback);
+
+ if (err_value == NULL)
+ msg = stradd(msg, _(": no extra information is provided..."));
+
+ else
+ {
+ err_string = PyObject_Str(err_value);
+ err_msg = PyUnicode_AsUTF8(err_string);
+
+ msg = stradd(msg, ": ");
+ msg = stradd(msg, err_msg);
+
+ Py_DECREF(err_string);
+
+ }
+
+ /**
+ * Bien que la documentation précise que la fonction PyErr_Fetch()
+ * transfère la propritété des éléments retournés, la pratique
+ * montre que le programme plante à la terminaison en cas d'exception.
+ *
+ * C'est par exemple le cas quand un greffon Python ne peut se lancer
+ * correctement ; l'exception est alors levée à partir de la fonction
+ * create_python_plugin() et le plantage intervient en sortie d'exécution,
+ * au moment de la libération de l'extension Python :
+ *
+ * ==14939== Jump to the invalid address stated on the next line
+ * ==14939== at 0x1A8FCBC9: ???
+ * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
+ * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
+ * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153)
+ * ==14939== by 0x10AD19: main (main.c:440)
+ * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
+ *
+ * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
+ * appel à PyErr_PrintEx(0) ne change rien.
+ *
+ * La seule différence de l'instruction set_sys_last_vars réside en quelques
+ * lignes dans le code de l'interpréteur Python :
+ *
+ * if (set_sys_last_vars) {
+ * _PySys_SetObjectId(&PyId_last_type, exception);
+ * _PySys_SetObjectId(&PyId_last_value, v);
+ * _PySys_SetObjectId(&PyId_last_traceback, tb);
+ * }
+ *
+ * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
+ * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
+ * à ces éléments.
+ *
+ * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
+ */
+
+ PySys_SetObject("last_type", err_type);
+ PySys_SetObject("last_value", err_value);
+ PySys_SetObject("last_traceback", err_traceback);
+
+ Py_XDECREF(err_traceback);
+ Py_XDECREF(err_value);
+ Py_XDECREF(err_type);
+
+ log_plugin_simple_message(LMT_EXT_ERROR, msg);
+
+ free(msg);
+
+ }
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONS GLOBALES DE CHRYSALIDE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : details = précisions de chargement complémentaires. *
+* *
+* Description : Assure le plein chargement dans un interpréteur Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *details)
+{
+ bool result; /* Bilan à retourner */
+ int ret; /* Bilan de préparatifs */
+ Dl_info info; /* Informations dynamiques */
+ GModule *module; /* Structure de chargement GLib*/
+ GPluginModule *self; /* Représentation interne */
+
+ result = false;
+
+ ret = Py_AtExit(PyExit_pychrysalide);
+ if (ret == -1)
+ {
+ PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
+ goto exit;
+ }
+
+ if (!load_core_components(ACC_ALL_COMPONENTS))
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to load core components.");
+ goto exit;
+ }
+
+ /**
+ * Le module chargé par Python n'apparaît pas dans la liste des greffons de
+ * Chrysalide et ne peut donc pas être référencé comme dépendance par d'autres
+ * extensions.
+ *
+ * Par ailleurs, lors de la recherche d'autres greffons via l'appel à la
+ * fonction init_all_plugins() ci-après, il faut que le nom du greffon soit
+ * déjà réservé pour faire échouer le second chargement du greffon courant
+ * lors du parcours des répertoires conservant les fichiers d'extensions.
+ */
+
+ ret = dladdr(__FUNCTION__, &info);
+ if (ret == 0)
+ {
+ LOG_ERROR_DL_N("dladdr");
+
+ PyErr_SetString(PyExc_SystemError, "failed to force bindings registration.");
+ goto exit;
+
+ }
+
+ module = g_module_open(info.dli_fname, G_MODULE_BIND_LAZY);
+ assert(module != NULL);
+
+ self = details->create_self(module);
+
+ /* A ce stade, le greffon a été chargé correctement */
+ g_plugin_module_override_flags(self, PSF_LOADED);
+
+ register_plugin(self);
+
+ unref_object(self);
+
+ /**
+ * Intégration des fonctionnalités portées par d'autres greffons.
+ */
+
+ result = true;
+
+ init_all_plugins(true);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Point de sortie pour l'initialisation de Python. *
+* *
+* Retour : ? *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void PyExit_pychrysalide(void)
+{
+ unhook_pygobject_behaviour();
+
+ exit_all_plugins();
+
+ unload_core_components(ACC_ALL_COMPONENTS);
+
+}
diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h
new file mode 100644
index 0000000..1758747
--- /dev/null
+++ b/plugins/pychrysalide/bindings.h
@@ -0,0 +1,73 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bindings.h - prototypes pour les éléments d'un socle commun aux fonctionnalités graphiques et non graphiques
+ *
+ * Copyright (C) 2024 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_BINDINGS_H
+#define _PLUGINS_PYCHRYSALIDE_BINDINGS_H
+
+
+/**
+ * Note:
+ * Since Python may define some pre-processor definitions which affect the standard headers
+ * on some systems, you must include Python.h before any standard headers are included.
+ *
+ * cf. https://docs.python.org/3.4/c-api/intro.html
+ */
+#include <Python.h>
+
+
+#include <gmodule.h>
+#include <stdbool.h>
+
+
+#include <plugins/plugin.h>
+
+
+
+/* Charge un module GI dans Python avec une version attendue. */
+bool import_namespace_from_gi_repository(const char *, const char *);
+
+/* Raffinements pour la mise en place du module Python */
+typedef struct _pyinit_details_t
+{
+ bool standalone; /* Chargement depuis Python ? */
+
+ bool (* populate_extra) (void); /* Ajout de types ? */
+
+ /**
+ * Prototype de la fonction de création, à garder synchronisé avec
+ * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h).
+ */
+ GPluginModule * (* create_self) (GModule *);
+
+} pyinit_details_t;
+
+/* Implémente le point d'entrée pour l'initialisation de Python. */
+PyObject *init_python_pychrysalide_module(const pyinit_details_t *);
+
+/* Présente dans le journal une exception survenue. */
+void log_pychrysalide_exception(const char *, ...);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_H */
diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c
index 8142284..34caa12 100644
--- a/plugins/pychrysalide/constants.c
+++ b/plugins/pychrysalide/constants.c
@@ -25,7 +25,6 @@
#include "constants.h"
-#include <arch/archbase.h>
#include <common/datatypes.h>
diff --git a/plugins/pychrysalide/convert.c b/plugins/pychrysalide/convert.c
new file mode 100644
index 0000000..c67c8ba
--- /dev/null
+++ b/plugins/pychrysalide/convert.c
@@ -0,0 +1,89 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * convert.c - conversion d'arguments en éléments usuels externes
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "convert.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+#ifndef NDEBUG
+# include <stdbool.h>
+#endif
+#include <gio/gio.h>
+
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en instance GSettings. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_gsettings(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+ GType type; /* Type obtenu ou 0 */
+
+ result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type);
+
+ if (result == 1)
+ {
+ type = pyg_type_from_object(arg);
+
+ if (type != G_TYPE_SETTINGS)
+ result = 0;
+
+ }
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GSetting instance");
+ break;
+
+ case 1:
+ *((GSettings **)dst) = G_SETTINGS(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/plugins/translate.h b/plugins/pychrysalide/convert.h
index facffd2..7bdf7da 100644
--- a/plugins/pychrysalide/plugins/translate.h
+++ b/plugins/pychrysalide/convert.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * translate.h - prototypes pour la conversion de structures liées aux greffons en objets Python
+ * convert.h - prototypes pour la conversion d'arguments en éléments usuels externes
*
- * Copyright (C) 2021 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,20 +22,17 @@
*/
-#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H
-#define _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H
+#ifndef _PLUGINS_PYCHRYSALIDE_CONVERT_H
+#define _PLUGINS_PYCHRYSALIDE_CONVERT_H
#include <Python.h>
-#include <plugins/plugin-def.h>
+/* Tente de convertir en instance GSettings. */
+int convert_to_gsettings(PyObject *, void *);
-/* Traduit une description d'interface de greffon. */
-PyObject *translate_plugin_interface_to_python(const plugin_interface *);
-
-
-#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H */
+#endif /* _PLUGINS_PYCHRYSALIDE_CONVERT_H */
diff --git a/plugins/pychrysalide/core-int.h b/plugins/pychrysalide/core-int.h
new file mode 100644
index 0000000..2b8fcc8
--- /dev/null
+++ b/plugins/pychrysalide/core-int.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-int.h - prototypes internes pour le plugin permettant des extensions en Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_CORE_INT_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_INT_H
+
+
+#include "core.h"
+
+
+#include <plugins/native-int.h>
+
+
+
+/* Greffon natif pour la liaison Python de Chrysalide (instance) */
+struct _GPyChrysalidePlugin
+{
+ GNativePlugin parent; /* A laisser en premier */
+
+ PyObject *py_module; /* Réceptacle de chargement */
+
+};
+
+
+/* Greffon natif pour la liaison Python de Chrysalide (classe) */
+struct _GPyChrysalidePluginClass
+{
+ GNativePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un greffon de support Python. */
+bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *, GModule *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_CORE_INT_H */
diff --git a/plugins/pychrysalide/core-ui-int.h b/plugins/pychrysalide/core-ui-int.h
new file mode 100644
index 0000000..7cecc13
--- /dev/null
+++ b/plugins/pychrysalide/core-ui-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui-int.h - prototypes internes pour le plugin permettant des extensions UI en Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H
+
+
+#include "core-ui.h"
+
+
+#include "core-int.h"
+
+
+
+/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (instance) */
+struct _GPyChrysalidePluginUI
+{
+ GPyChrysalidePlugin parent; /* A laisser en premier */
+
+};
+
+
+/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (classe) */
+struct _GPyChrysalidePluginUIClass
+{
+ GPyChrysalidePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un greffon de support Python. */
+bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *, GModule *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H */
diff --git a/plugins/pychrysalide/core-ui.c b/plugins/pychrysalide/core-ui.c
new file mode 100644
index 0000000..1b332b7
--- /dev/null
+++ b/plugins/pychrysalide/core-ui.c
@@ -0,0 +1,319 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.c - plugin permettant des extensions UI en Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "core-ui.h"
+
+
+#include <stdbool.h>
+
+
+#include <i18n.h>
+#include <plugins/self.h>
+
+
+#include "bindings.h"
+#include "core-ui-int.h"
+
+
+
+/* Note la nature du chargement */
+static bool _standalone = true;
+
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des greffons de support Python. */
+static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *);
+
+/* Initialise une instance de greffon de support Python. */
+static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *);
+
+/* Supprime toutes les références externes. */
+static void g_pychrysalide_plugin_ui_dispose(GPyChrysalidePluginUI *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_pychrysalide_plugin_ui_finalize(GPyChrysalidePluginUI *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_pychrysalide_plugin_ui_enable(GPyChrysalidePluginUI *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon de liaison Python */
+G_DEFINE_TYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G_TYPE_PYCHRYSALIDE_PLUGIN);
+
+
+NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_ui_new);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons de support Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_pychrysalide_plugin_ui_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_pychrysalide_plugin_ui_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_pychrysalide_plugin_ui_enable;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon de support Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *plugin)
+{
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_dispose(GPyChrysalidePluginUI *plugin)
+{
+ G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_finalize(GPyChrysalidePluginUI *plugin)
+{
+ G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = nom du fichier à charger. *
+* *
+* Description : Crée un module pour un greffon de support Python. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+GPluginModule *g_pychrysalide_plugin_ui_new(GModule *module)
+{
+ GPyChrysalidePluginUI *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN_UI, NULL);
+
+ if (!g_pychrysalide_plugin_ui_create(result, module))
+ g_clear_object(&result);
+
+ return G_PLUGIN_MODULE(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = module système correspondant. *
+* *
+* Description : Met en place un module pour un greffon de support Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *plugin, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_pychrysalide_plugin_create(G_PYCHRYSALIDE_PLUGIN(plugin), module);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de l'activation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_pychrysalide_plugin_ui_enable(GPyChrysalidePluginUI *plugin)
+{
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ int ret; /* Bilan de préparatifs */
+
+ _standalone = false;
+
+ /* Chargement du module pour Python */
+
+ ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalideui);
+
+ if (ret == -1)
+ {
+ g_plugin_module_log_simple_message(G_PLUGIN_MODULE(plugin),
+ LMT_ERROR,
+ _("Can not extend the existing table of Python built-in modules."));
+
+ result = false;
+ goto done;
+
+ }
+
+ Py_Initialize();
+
+ gstate = PyGILState_Ensure();
+
+ G_PYCHRYSALIDE_PLUGIN(plugin)->py_module = PyImport_ImportModule("pychrysalide");
+
+ /**
+ * Pour mémoire, une situation concrête conduisant à un échec :
+ * le paquet python3-gi-dbg n'est pas installé alors que le
+ * programme est compilé en mode débogage.
+ *
+ * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize()
+ * le laisse rien filtrer...
+ *
+ * En mode autonome, le shell Python remonte bien l'erreur par contre.
+ */
+
+ // TODO : check (2025)
+
+ result = (G_PYCHRYSALIDE_PLUGIN(plugin)->py_module != NULL);
+
+ PyGILState_Release(gstate);
+
+ done:
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* POINT D'ENTREE POUR PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Point d'entrée pour l'initialisation de Python. *
+* *
+* Retour : ? *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyMODINIT_FUNC PyInit_pychrysalideui(void)
+{
+ PyObject *result; /* Module Python à retourner */
+ pyinit_details_t details; /* Détails de chargement */
+
+ details.standalone = _standalone;
+
+ details.populate_extra = NULL;
+ details.create_self = g_pychrysalide_plugin_ui_new;
+
+ result = init_python_pychrysalide_module(&details);
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/core-ui.h b/plugins/pychrysalide/core-ui.h
new file mode 100644
index 0000000..658aa19
--- /dev/null
+++ b/plugins/pychrysalide/core-ui.h
@@ -0,0 +1,64 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.h - prototypes pour le plugin permettant des extensions UI en Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_CORE_UI_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_UI_H
+
+
+/**
+ * Note:
+ * Since Python may define some pre-processor definitions which affect the standard headers
+ * on some systems, you must include Python.h before any standard headers are included.
+ *
+ * cf. https://docs.python.org/3.4/c-api/intro.html
+ */
+#include <Python.h>
+
+
+#include <glibext/helpers.h>
+#include <plugins/plugin.h>
+
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+#define G_TYPE_PYCHRYSALIDE_PLUGIN_UI (g_pychrysalide_plugin_ui_get_type())
+
+DECLARE_GTYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G, PYCHRYSALIDE_PLUGIN_UI);
+
+
+/* Crée un module pour un greffon de support Python. */
+GPluginModule *g_pychrysalide_plugin_ui_new(GModule *);
+
+
+
+/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */
+
+
+/* Point d'entrée pour l'initialisation de Python. */
+PyMODINIT_FUNC PyInit_pychrysalideui(void);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_H */
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index 8d69933..0e72b46 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.c - plugin permettant des extensions en Python
*
- * Copyright (C) 2018-2019 Cyrille Bagard
+ * Copyright (C) 2018-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,6 +21,15 @@
*/
+#include "core.h"
+
+
+#include "core-int.h"
+
+
+
+
+
#undef NO_IMPORT_PYGOBJECT
#include <pygobject.h>
#define NO_IMPORT_PYGOBJECT
@@ -32,9 +41,8 @@
#include <assert.h>
#include <errno.h>
#include <malloc.h>
-#include <pygobject.h>
+//#include <pygobject.h>
#include <stdarg.h>
-#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
@@ -42,741 +50,402 @@
#include <i18n.h>
#include <gleak.h>
-#include <common/cpp.h>
#include <common/environment.h>
#include <common/extstr.h>
#include <core/core.h>
#include <core/logs.h>
#include <core/paths.h>
-#include <plugins/pglist.h>
+#include <plugins/manager-int.h>
+#include <plugins/plugin.h>
#include <plugins/self.h>
+
+
#include "access.h"
-#include "constants.h"
-#include "helpers.h"
-#include "star.h"
-#include "strenum.h"
-#include "struct.h"
-#include "analysis/module.h"
-#include "arch/module.h"
-#include "common/module.h"
-#include "core/module.h"
-#include "glibext/module.h"
-/* #include "debug/module.h" */
-#include "format/module.h"
-/* #ifdef INCLUDE_GTK_SUPPORT */
-/* # include "gtkext/module.h" */
-/* # include "gui/module.h" */
-/* #endif */
-/* #include "mangling/module.h" */
-#include "plugins/module.h"
-#include "plugins/plugin.h"
-
-
-
-DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Chrysalide bindings to Python",
- PACKAGE_VERSION, CHRYSALIDE_WEBSITE("api/python/pychrysalide"),
- NO_REQ, AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT,
- PGA_NATIVE_PLUGINS_LOADED, PGA_TYPE_BUILDING));
+#include "bindings.h"
+
/* Note la nature du chargement */
static bool _standalone = true;
-/* Réceptacle pour le chargement forcé */
-static PyObject *_chrysalide_module = NULL;
-/* Fournit la révision du programme global. */
-static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
-/* Fournit la version du programme global. */
-static PyObject *py_chrysalide_version(PyObject *, PyObject *);
-/* Fournit la version du greffon pour Python. */
-static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
-/* Détermine si l'interpréteur lancé est celui pris en compte. */
-static bool is_current_abi_suitable(void);
-/* Assure une pleine initialisation des objets de Python-GI. */
-static bool install_metaclass_for_python_gobjects(void);
-/* Définit la version attendue de GTK à charger dans Python. */
-#ifdef INCLUDE_GTK_SUPPORT
-static bool set_version_for_gtk_namespace(const char *);
-#endif
-/* Point de sortie pour l'initialisation de Python. */
-static void PyExit_pychrysalide(void);
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des greffons de support Python. */
+static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *);
+
+/* Procède à l'initialisation de l'interface de gestion. */
+static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *);
+
+/* Initialise une instance de greffon de support Python. */
+static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_pychrysalide_plugin_finalize(GPyChrysalidePlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *);
+
+/* Prend acte de la désactivation du greffon. */
+static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *);
+
+
+
+/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */
+
/* Complète les chemins de recherches de Python. */
static void extend_python_path(const char *);
+/* Crée un greffon à partir de code Python. */
+static GPluginModule *create_python_plugin(const char *, const char *);
+
/* Charge autant de greffons composés en Python que possible. */
static void load_python_plugins(GPluginModule *);
-/* Efface un type Python pour greffon de la mémoire. */
-static void free_native_plugin_type(PyTypeObject *);
+/* Prend acte du chargement de l'ensemble des greffons natifs. */
+static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPyChrysalidePlugin *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon de liaison Python */
+G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePlugin, g_pychrysalide_plugin, G_TYPE_NATIVE_PLUGIN,
+ G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_plugin_manager_interface_init));
+
+NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_new);
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : class = classe à initialiser. *
* *
-* Description : Fournit la révision du programme global. *
+* Description : Initialise la classe des greffons de support Python. *
* *
-* Retour : Numéro de révision. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *class)
{
- PyObject *result; /* Valeur à retourner */
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
-#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \
-( \
- revision, "/", \
- METH_NOARGS, py_chrysalide, \
- "Provide the revision number of Chrysalide.\n" \
- "\n" \
- "The returned value is provided as a string, for instance: 'r1665'." \
-)
+ object = G_OBJECT_CLASS(class);
- result = PyUnicode_FromString("r" XSTR(REVISION));
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_pychrysalide_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_pychrysalide_plugin_finalize;
- return result;
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_pychrysalide_plugin_enable;
+ plugin->disable = (pg_management_fc)g_pychrysalide_plugin_disable;
}
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : iface = interface GLib à initialiser. *
* *
-* Description : Fournit la version du programme global. *
+* Description : Procède à l'initialisation de l'interface de gestion. *
* *
-* Retour : Numéro de version. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_version(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface)
{
- PyObject *result; /* Valeur à retourner */
- int major; /* Numéro de version majeur */
- int minor; /* Numéro de version mineur */
- int revision; /* Numéro de révision */
- char version[16]; /* Conservation temporaire */
-
-#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \
-( \
- version, "/", \
- METH_NOARGS, py_chrysalide, \
- "Provide the version number of Chrysalide.\n" \
- "\n" \
- "The returned value is provided as a string, for instance: '1.6.65'." \
-)
-
- major = REVISION / 1000;
- minor = (REVISION - (major * 1000)) / 100;
- revision = REVISION % 100;
-
- snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
-
- result = PyUnicode_FromString(version);
-
- return result;
+ iface->handle_native = (handle_native_plugins_cb)g_pychrysalide_plugin_handle_native_plugins_loaded_event;
}
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : plugin = instance à initialiser. *
* *
-* Description : Fournit la version du greffon pour Python. *
+* Description : Initialise une instance de greffon de support Python. *
* *
-* Retour : Numéro de version. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *plugin)
{
- PyObject *result; /* Valeur à retourner */
- char version[16]; /* Conservation temporaire */
+ STORE_PLUGIN_ABI(plugin);
-#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \
-( \
- mod_version, "/", \
- METH_NOARGS, py_chrysalide, \
- "Provide the version number of Chrysalide module for Python.\n" \
- "\n" \
- "The returned value is provided as a string, for instance: '0.1.0'." \
-)
-
- snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version);
-
- result = PyUnicode_FromString(version);
-
- return result;
+ plugin->py_module = NULL;
}
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
* *
-* Description : Détermine si l'interpréteur lancé est celui pris en compte. *
+* Description : Supprime toutes les références externes. *
* *
-* Retour : true si l'exécution peut se poursuivre, false sinon. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool is_current_abi_suitable(void)
+static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *plugin)
{
- bool result;
- int fds[2];
- int ret;
- char cmds[128];
- char content[64];
- ssize_t got;
-
-#define GRAB_ABI_FLAGS_IN_PYTHON \
- "import sys" "\n" \
- "import os" "\n" \
- "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \
- "os.write(%d, data)" "\n"
-
- result = false;
-
- ret = pipe(fds);
- if (ret == -1)
- {
- perror("pipe()");
- return false;
- }
-
- snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
-
- ret = PyRun_SimpleString(cmds);
- if (ret != 0) goto icas_exit;
-
- got = read(fds[0], content, sizeof(content));
- if (got < 0)
- {
- perror("read()");
- goto icas_exit;
- }
-
- content[got] = '\0';
-
- result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
-
- icas_exit:
-
- if (!result)
- PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
- "the ones of the Python library used during the module compilation.");
-
- return result;
+ G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->dispose(G_OBJECT(plugin));
}
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
* *
-* Description : Assure une pleine initialisation des objets de Python-GI. *
+* Description : Procède à la libération totale de la mémoire. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool install_metaclass_for_python_gobjects(void)
+static void g_pychrysalide_plugin_finalize(GPyChrysalidePlugin *plugin)
{
- bool result; /* Bilan à retourner */
- PyObject *gi_types_mod; /* Module Python-GObject */
-
- /**
- * Les extensions Python sont chargées à partir de la fonction load_python_plugins(),
- * qui fait appel à create_python_plugin(). Une instance y est construite via un
- * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad
- * dans le fichier __init__.py présent dans chaque module d'extension.
- *
- * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique
- * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction
- * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c.
- * Le code de cette dernière comprend notamment la portion suivante :
- *
- * [...]
- * Py_SET_TYPE(type, PyGObject_MetaType);
- * [...]
- * if (PyType_Ready(type) < 0) {
- * g_warning ("couldn't make the type `%s' ready", type->tp_name);
- * return;
- * }
- * [...]
- *
- * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c
- * et commence par :
- *
- * int PyType_Ready(PyTypeObject *type)
- * {
- * if (type->tp_flags & Py_TPFLAGS_READY) {
- * assert(_PyType_CheckConsistency(type));
- * return 0;
- * }
- * [...]
- * }
- *
- * La vérification de cohérencce commence par analyser le type et son propre
- * type :
- *
- * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c :
- *
- * int _PyType_CheckConsistency(PyTypeObject *type)
- * {
- * [...]
- * CHECK(!_PyObject_IsFreed((PyObject *)type));
- * [...]
- * }
- *
- * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c :
- *
- * int _PyObject_IsFreed(PyObject *op)
- * {
- * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
- * return 1;
- * }
- *
- * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL.
- *
- * Or le type du type est écrasé dans la fonction pygobject_register_class()
- * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est
- * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c :
- *
- * static PyObject *
- * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass)
- * {
- * Py_INCREF(metaclass);
- * PyGObject_MetaType = metaclass;
- * Py_INCREF(metaclass);
- *
- * Py_SET_TYPE(&PyGObject_Type, metaclass);
- *
- * Py_INCREF(Py_None);
- * return Py_None;
- * }
- *
- * Afin de valider la vérification de _PyType_CheckConsistency() pour les
- * modules externes qui entraînent un enregistrement tout en portant le drapeau
- * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut
- * initialiser au besoin la variable PyGObject_MetaType.
- *
- * Une ligne suffit donc à enregistrer le type intermédiaire :
- *
- * from _gi import types
- *
- * On simule ici une déclaration similaire si nécessaire
- */
-
- result = false;
-
- if (PyType_CheckExact(&PyGObject_Type))
- {
- gi_types_mod = PyImport_ImportModule("gi.types");
-
- result = (PyErr_Occurred() == NULL);
-
- if (result)
- result = (PyType_CheckExact(&PyGObject_Type) == 0);
-
- Py_XDECREF(gi_types_mod);
-
- }
-
- return result;
+ G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->finalize(G_OBJECT(plugin));
}
/******************************************************************************
* *
-* Paramètres : version = idenfiant de la version de GTK à stipuler. *
+* Paramètres : filename = nom du fichier à charger. *
* *
-* Description : Définit la version attendue de GTK à charger dans Python. *
+* Description : Crée un module pour un greffon de support Python. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Adresse de la structure mise en place. *
* *
-* Remarques : - *
+* Remarques : Le transfert de propriétée du module est total. *
* *
******************************************************************************/
-#ifdef INCLUDE_GTK_SUPPORT
-static bool set_version_for_gtk_namespace(const char *version)
-{
- bool result; /* Bilan à retourner */
- PyObject *gi_mod; /* Module Python-GObject */
- PyObject *args; /* Arguments à fournir */
-
- result = false;
-
- /**
- * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement :
- *
- *
- * PyGIWarning: Gtk was imported without specifying a version first. \
- * Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded.
- *
- */
-
- gi_mod = PyImport_ImportModule("gi");
-
- if (gi_mod != NULL)
- {
- args = Py_BuildValue("ss", "Gtk", version);
-
- run_python_method(gi_mod, "require_version", args);
- result = (PyErr_Occurred() == NULL);
+GPluginModule *g_pychrysalide_plugin_new(GModule *module)
+{
+ GPyChrysalidePlugin *result; /* Structure à retourner */
- Py_DECREF(args);
- Py_DECREF(gi_mod);
+ result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL);
- }
+ if (!g_pychrysalide_plugin_create(result, module))
+ g_clear_object(&result);
- return result;
+ return G_PLUGIN_MODULE(result);
}
-#endif
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = module système correspondant. *
* *
-* Description : Point de sortie pour l'initialisation de Python. *
+* Description : Met en place un module pour un greffon de support Python. *
* *
-* Retour : ? *
+* Retour : Bilan de l'opération. *
* *
-* Remarques : - *
+* Remarques : Le transfert de propriétée du module est total. *
* *
******************************************************************************/
-static void PyExit_pychrysalide(void)
+bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module)
{
- assert(_standalone);
-
- /*
- extern void set_current_project(void *project);
+ bool result; /* Bilan à retourner */
- set_current_project(NULL);
- */
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PyChrysalide",
+ "Chrysalide bindings to Python",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("api/python/pychrysalide"),
+ NO_REQ,
+ module);
-#ifdef TRACK_GOBJECT_LEAKS
- remember_gtypes_for_leaks();
-#endif
+ return result;
- exit_all_plugins();
+}
- //unload_all_core_components(true);
-#ifdef TRACK_GOBJECT_LEAKS
- dump_remaining_gtypes();
-#endif
-}
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = greffon à manipuler. *
* *
-* Description : Point d'entrée pour l'initialisation de Python. *
+* Description : Prend acte de l'activation du greffon. *
* *
-* Retour : ? *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-#define PYCHRYSALIDE_DOC \
- "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \
- "\n" \
- "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \
- "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \
- "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
- "\n" \
- "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \
- "how the API works.\n" \
- "\n" \
- "These plugins are located in the 'plugins/python' directory.\n" \
- "\n" \
- "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup."
-
-PyMODINIT_FUNC PyInit_pychrysalide(void)
+static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *plugin)
{
- PyObject *result; /* Module Python à retourner */
- bool status; /* Bilan des inclusions */
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
int ret; /* Bilan de préparatifs */
-#ifdef PYTHON_PACKAGE
- Dl_info info; /* Informations dynamiques */
-#endif
- GPluginModule *self; /* Représentation interne */
- PluginStatusFlags self_flags; /* Fanions à mettre à jour */
-
- static PyMethodDef py_chrysalide_methods[] = {
- PY_CHRYSALIDE_REVISION_METHOD,
- PY_CHRYSALIDE_VERSION_METHOD,
- PY_CHRYSALIDE_MOD_VERSION_METHOD,
- { NULL }
- };
-
- static PyModuleDef py_chrysalide_module = {
-
- .m_base = PyModuleDef_HEAD_INIT,
-
- .m_name = "pychrysalide",
- .m_doc = PYCHRYSALIDE_DOC,
- .m_size = -1,
+ _standalone = false;
- .m_methods = py_chrysalide_methods
+ /* Chargement du module pour Python */
- };
+ ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
- /**
- * Vérification préalable : dans le cas où on est embarqué directement dans
- * un interpréteur Python, le module se charge et termine par charger à leur
- * tour les différentes extensions trouvées, via load_remaning_plugins() puis
- * chrysalide_plugin_on_native_loaded().
- *
- * Lesquelles vont très probablement charger le module pychrysalide.
- *
- * Comme le chargement de ce dernier n'est alors pas encore terminé,
- * Python va relancer cette procédure, et register_access_to_python_module()
- * va détecter un doublon.
- */
+ if (ret == -1)
+ {
+ g_plugin_module_log_simple_message(G_PLUGIN_MODULE(plugin),
+ LMT_ERROR,
+ _("Can not extend the existing table of Python built-in modules."));
- result = get_access_to_python_module(py_chrysalide_module.m_name);
+ result = false;
+ goto done;
- if (result != NULL)
- {
- Py_INCREF(result);
- return result;
}
- if (!is_current_abi_suitable())
- goto exit;
+ Py_Initialize();
- if (pygobject_init(-1, -1, -1) == NULL)
- {
- PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
- goto exit;
- }
+ gstate = PyGILState_Ensure();
- if (!install_metaclass_for_python_gobjects())
- goto exit;
+ plugin->py_module = PyImport_ImportModule("pychrysalide");
/**
- * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil,
- * à savoir des types convertis de façon incomplète. Par exemple, pour une
- * structure GChecksum, le type à l'exécution est :
- *
- * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>]
- *
- * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>]
- *
- * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec
- * la version debug de Python3 (version de python3-gi : 3.42.2-3) :
- *
- * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module()
- *
- * Code de reproduction dans un interpréteur classique :
+ * Pour mémoire, une situation concrête conduisant à un échec :
+ * le paquet python3-gi-dbg n'est pas installé alors que le
+ * programme est compilé en mode débogage.
*
- * import gi
- * gi.require_version('GLib', '2.0')
- * from gi.repository import GLib
+ * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize()
+ * le laisse rien filtrer...
*
+ * En mode autonome, le shell Python remonte bien l'erreur par contre.
*/
- if (!import_namespace_from_gi_repository("GLib", "2.0"))
- goto exit;
-
-#if 0
-#ifdef INCLUDE_GTK_SUPPORT
- if (!set_version_for_gtk_namespace("3.0"))
- goto exit;
-#endif
-#endif
- if (!load_core_components(ACC_GLOBAL_VARS))
- {
- PyErr_SetString(PyExc_SystemError, "unable to load all basic components.");
- goto exit;
- }
-
- /* Mise en place des fonctionnalités offertes */
-
- result = PyModule_Create(&py_chrysalide_module);
+ // TODO : check (2025)
- register_access_to_python_module(py_chrysalide_module.m_name, result);
+ result = (plugin->py_module != NULL);
- status = true;
-
- if (status) status = add_features_module(result);
-
- if (status) status = add_analysis_module(result);
- if (status) status = add_arch_module(result);
- if (status) status = add_common_module(result);
- if (status) status = add_glibext_module(result);
- if (status) status = add_core_module(result);
- /*
- if (status) status = add_debug_module(result);
- */
- if (status) status = add_format_module(result);
- /*
-#ifdef INCLUDE_GTK_SUPPORT
- if (status) status = add_gtkext_module(result);
- if (status) status = add_gui_module(result);
-#endif
- if (status) status = add_mangling_module(result);
- */
- if (status) status = add_plugins_module(result);
-
- /*
- if (status) status = ensure_python_string_enum_is_registered();
- */
- if (status) status = ensure_python_py_struct_is_registered();
-
- if (status) status = define_data_types_constants(result);
-
- if (status) status = populate_analysis_module();
- if (status) status = populate_arch_module();
- if (status) status = populate_glibext_module();
- if (status) status = populate_common_module();
- if (status) status = populate_core_module();
- /*
- if (status) status = populate_debug_module();
- */
- if (status) status = populate_format_module();
- /*
-#ifdef INCLUDE_GTK_SUPPORT
- if (status) status = populate_gtkext_module();
- if (status) status = populate_gui_module();
-#endif
- if (status) status = populate_mangling_module();
- */
- if (status) status = populate_plugins_module();
+ PyGILState_Release(gstate);
- if (!status)
- {
- PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
- Py_DECREF(result);
- result = NULL;
- goto exit;
- }
+ done:
- if (_standalone)
- {
- ret = Py_AtExit(PyExit_pychrysalide);
+ return result;
- if (ret == -1)
- {
- PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
- Py_DECREF(result);
- result = NULL;
- goto exit;
- }
+}
- /**
- * Comme les sources locales sont prioritaires, le fichier "core/global.h"
- * du greffon masque la fonction suivante, issue du corps principal du
- * programme.
- *
- * On la déclare donc à la main.
- */
- /*
- extern void set_batch_mode(void);
-
- set_batch_mode();
- */
-
- /**
- * Si cette extension pour Python est chargée depuis un dépôt Python,
- * elle ne se trouve pas dans le répertoire classique des extensions et
- * n'est donc pas chargée et enregistrée comme attendu.
- *
- * Cet enregistrement est donc forcé ici.
- */
-
-#ifdef PYTHON_PACKAGE
-
- ret = dladdr(__FUNCTION__, &info);
- if (ret == 0)
- {
- LOG_ERROR_DL_N("dladdr");
- Py_DECREF(result);
- result = NULL;
- goto exit;
- }
- self = g_plugin_module_new(info.dli_fname);
- assert(self != NULL);
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de la désactivation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- register_plugin(self);
+static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *plugin)
+{
+ bool result; /* Bilan à retourner */
+ bool standalone; /* Nature du chargement */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
-#endif
+ result = true;
- init_all_plugins(false);
+ /**
+ * Le champ plugin->py_module n'est défini que via la fonction
+ * g_pychrysalide_plugin_enable(), qui n'est pas sollicitée lorsque
+ * le module PyChrysalide est mis en place directement par Python.
+ *
+ * L'analyse de ce champ pour retrouver la situation courante est
+ * plus fiable que celle du champ _standalone, potentiellement
+ * cohérent dans la version UI du greffon et resté à son état
+ * initial ici.
+ */
- lock_plugin_list_for_reading();
+ standalone = (plugin->py_module == NULL);
- self = get_plugin_by_name("PyChrysalide", NULL);
- assert(self != NULL);
+ /**
+ * Si on se trouve embarqué dans un interpréteur Python, le déchargement
+ * des greffons est organisé à partir de la fonction PyExit_pychrysalide(),
+ * directement appelée depuis un contexte Python.
+ *
+ * Un verrou n'est alors pas souhaité ici :
+ *
+ * python3d: ../Python/pystate.c:1687: PyGILState_Ensure: Assertion `gilstate->autoInterpreterState' failed.
+ *
+ * Avec :
+ *
+ * $ python3d --version
+ * Python 3.11.2
+ *
+ */
- self_flags = g_plugin_module_get_flags(self);
- self_flags &= ~(PSF_FAILURE | PSF_LOADED);
- self_flags |= (status ? PSF_LOADED : PSF_FAILURE);
+ if (!standalone)
+ gstate = PyGILState_Ensure();
- g_plugin_module_override_flags(self, self_flags);
+ clear_all_accesses_to_python_modules();
- unlock_plugin_list_for_reading();
+ Py_XDECREF(plugin->py_module);
+ plugin->py_module = NULL;
- load_remaning_plugins();
+ if (!standalone)
+ PyGILState_Release(gstate);
- /**
- * On laisse fuir ici la référence sur self afin d'avoir
- * l'assurance que le greffon se déchargera toujours en dernier.
- *
- * La fuite mémoire est au final évitée dans PyExit_pychrysalide().
- */
+ return result;
- }
+}
- exit:
- if (result == NULL && !_standalone)
- log_pychrysalide_exception("Loading failed");
- return result;
+/* ---------------------------------------------------------------------------------- */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* ---------------------------------------------------------------------------------- */
-}
/******************************************************************************
* *
@@ -812,6 +481,81 @@ static void extend_python_path(const char *path)
/******************************************************************************
* *
+* Paramètres : modname = nom du module à charger. *
+* filename = chemin d'accès au code Python à charger. *
+* *
+* Description : Crée un greffon à partir de code Python. *
+* *
+* Retour : Adresse de la structure mise en place ou NULL si erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GPluginModule *create_python_plugin(const char *modname, const char *filename)
+{
+ GPluginModule *result; /* Structure à retourner */
+ PyObject *name; /* Chemin d'accès pour Python */
+ PyObject *module; /* Script Python chargé */
+ PyObject *dict; /* Dictionnaire associé */
+ PyObject *class; /* Classe à instancier */
+ PyObject *instance; /* Instance Python du greffon */
+
+ name = PyUnicode_FromString(modname);
+ if (name == NULL) goto bad_exit;
+
+ module = PyImport_Import(name);
+ Py_DECREF(name);
+
+ if (module == NULL) goto no_import;
+
+ dict = PyModule_GetDict(module);
+ class = PyDict_GetItemString(dict, "AutoLoad");
+
+ if (class == NULL) goto no_class;
+ if (!PyType_Check(class->ob_type)) goto no_class;
+
+ instance = PyObject_CallFunction(class, NULL);
+ if (instance == NULL) goto no_instance;
+
+ result = G_PLUGIN_MODULE(pygobject_get(instance));
+
+ /**
+ * L'instance Python et l'objet GLib résultant sont un même PyGObject.
+ *
+ * Donc pas besoin de toucher au comptage des références ici, la libération
+ * se réalisera à la fin, quand l'objet GLib sera libéré.
+ */
+
+ Py_DECREF(module);
+
+ return result;
+
+ no_instance:
+
+ log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance"));
+
+ no_class:
+
+ if (class == NULL)
+ log_plugin_simple_message(LMT_ERROR,
+ _("An error occured when looking for the 'AutoLoad': item not found!"));
+
+ no_import:
+
+ Py_XDECREF(module);
+
+ log_pychrysalide_exception(_("An error occured when importing '%s'"), modname);
+
+ bad_exit:
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : plugin = instance représentant le greffon Python d'origine. *
* *
* Description : Charge autant de greffons composés en Python que possible. *
@@ -836,7 +580,6 @@ static void load_python_plugins(GPluginModule *plugin)
char *filename; /* Chemin d'accès reconstruit */
GPluginModule *pyplugin; /* Lien vers un grffon Python */
bool status; /* Bilan d'une opération */
- //GGenConfig *config; /* Configuration à charger */
/* Définition des zones d'influence */
@@ -847,19 +590,19 @@ static void load_python_plugins(GPluginModule *plugin)
#else
edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python");
+
dir = opendir(edir);
- free(edir);
if (dir != NULL)
{
closedir(dir);
- edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python");
extend_python_path(edir);
- free(edir);
}
+ free(edir);
+
#endif
g_plugin_module_log_variadic_message(plugin, LMT_INFO,
@@ -923,8 +666,7 @@ static void load_python_plugins(GPluginModule *plugin)
goto done_with_plugin;
}
- //g_plugin_module_create_config(pyplugin);
-
+ /*
status = g_plugin_module_manage(pyplugin, PGA_PLUGIN_LOADED);
if (!status)
@@ -934,18 +676,19 @@ static void load_python_plugins(GPluginModule *plugin)
g_object_unref(G_OBJECT(pyplugin));
goto done_with_plugin;
}
-
- /*
- config = g_plugin_module_get_config(pyplugin);
- g_generic_config_read(config);
- g_object_unref(G_OBJECT(config));
*/
g_plugin_module_log_variadic_message(plugin, LMT_PROCESS,
_("Loaded the Python plugin found in the '<b>%s</b>' directory"),
filename);
- _register_plugin(pyplugin);
+ printf(" -> BUG // %p\n", pyplugin);
+
+ printf(" -> BUG // %u\n", ((GObject *)pyplugin)->ref_count);
+
+ //register_plugin(pyplugin);
+
+ /////////unref_object(pyplugin);
done_with_plugin:
@@ -965,116 +708,7 @@ static void load_python_plugins(GPluginModule *plugin)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à manipuler. *
-* *
-* Description : Prend acte du chargement du greffon. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
-{
- bool result; /* Bilan à retourner */
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- int ret; /* Bilan de préparatifs */
-
- _standalone = false;
-
- /* Chargement du module pour Python */
-
- ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
-
- if (ret == -1)
- {
- log_plugin_simple_message(LMT_ERROR, _("Can not extend the existing table of Python built-in modules."));
- result = false;
- goto cpi_done;
- }
-
- Py_Initialize();
-
- gstate = PyGILState_Ensure();
-
- _chrysalide_module = PyImport_ImportModule("pychrysalide");
-
- /**
- * Pour mémoire, une situation concrête conduisant à un échec :
- * le paquet python3-gi-dbg n'est pas installé alors que le
- * programme est compilé en mode débogage.
- *
- * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize()
- * le laisse rien filtrer...
- *
- * En mode autonome, le shell Python remonte bien l'erreur par contre.
- */
-
- result = (_chrysalide_module != NULL);
-
- PyGILState_Release(gstate);
-
- cpi_done:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* *
-* Description : Prend acte du déchargement du greffon. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
-{
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
-
- gstate = PyGILState_Ensure();
-
- clear_all_accesses_to_python_modules();
-
- Py_XDECREF(_chrysalide_module);
-
- PyGILState_Release(gstate);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : type = informations à libérer de la mémoire. *
-* *
-* Description : Efface un type Python pour greffon de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void free_native_plugin_type(PyTypeObject *type)
-{
- free((char *)type->tp_name);
- free((char *)type->tp_doc);
-
- free(type);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
+* Paramètres : plugin = interface à manipuler. *
* *
* Description : Accompagne la fin du chargement des modules natifs. *
* *
@@ -1084,308 +718,49 @@ static void free_native_plugin_type(PyTypeObject *type)
* *
******************************************************************************/
-G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action)
+static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPyChrysalidePlugin *plugin)
{
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- size_t count; /* Quantité de greffons chargés*/
- PyObject *module; /* Module à recompléter */
- PyObject *dict; /* Dictionnaire du module */
- GPluginModule **list; /* Ensemble de ces greffons */
- size_t i; /* Boucle de parcours */
- char *name; /* Désignation complète */
- char *doc; /* Description adaptée */
- int ret; /* Bilan d'un appel */
- PyTypeObject *type; /* Nouveau type dynamique */
gstate = PyGILState_Ensure();
- if (action == PGA_NATIVE_PLUGINS_LOADED)
- {
- /* Intégration des greffons natifs en Python */
-
- if (ensure_python_plugin_module_is_registered())
- {
- module = get_access_to_python_module("pychrysalide.plugins");
- assert(module != NULL);
-
- dict = PyModule_GetDict(module);
-
- list = get_all_plugins(&count);
-
- for (i = 0; i < count; i++)
- {
- ret = asprintf(&name, "pychrysalide.plugins.%s", G_OBJECT_TYPE_NAME(list[i]) + 1);
- if (ret == -1)
- {
- LOG_ERROR_N("asprintf");
- continue;
- }
-
- ret = asprintf(&doc, "Place holder for the native plugin %s documentation",
- G_OBJECT_TYPE_NAME(list[i]) + 1);
- if (ret == -1)
- {
- LOG_ERROR_N("asprintf");
- free(name);
- continue;
- }
-
- type = calloc(1, sizeof(PyTypeObject));
-
- type->tp_name = name;
- type->tp_doc = doc;
- type->tp_flags = Py_TPFLAGS_DEFAULT;
- type->tp_new = no_python_constructor_allowed;
-
- if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type))
- g_object_set_data_full(G_OBJECT(list[i]), "python_type", type,
- (GDestroyNotify)free_native_plugin_type);
-
- else
- free_native_plugin_type(type);
-
- }
-
- if (list != NULL)
- free(list);
-
- }
-
- /* Chargement des extensions purement Python */
-
- load_python_plugins(plugin);
-
- }
+ load_python_plugins(G_PLUGIN_MODULE(plugin));
PyGILState_Release(gstate);
}
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
-* type = type d'objet à mettre en place. *
-* *
-* Description : Crée une instance à partir d'un type dynamique externe. *
-* *
-* Retour : Instance d'objet gérée par l'extension ou NULL. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type)
-{
- gpointer result; /* Instance à retourner */
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- PyTypeObject *pytype; /* Classe Python concernée */
- PyObject *instance; /* Initialisation forcée */
-
- result = NULL;
-
- gstate = PyGILState_Ensure();
-
- pytype = pygobject_lookup_class(type);
-
- if (pytype != NULL)
- {
- instance = PyObject_CallObject((PyObject *)pytype, NULL);
- assert(instance != NULL);
-
- result = pygobject_get(instance);
-
- }
-
- PyGILState_Release(gstate);
-
- return result;
-
-}
+/* ---------------------------------------------------------------------------------- */
+/* POINT D'ENTREE POUR PYTHON */
+/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
-* Paramètres : namespace = module particulier à charger à partir de gi. *
-* version = idenfiant de la version à stipuler. *
+* Paramètres : - *
* *
-* Description : Charge un module GI dans Python avec une version attendue. *
+* Description : Point d'entrée pour l'initialisation de Python. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : ? *
* *
* Remarques : - *
* *
******************************************************************************/
-bool import_namespace_from_gi_repository(const char *namespace, const char *version)
+PyMODINIT_FUNC PyInit_pychrysalide(void)
{
- bool result; /* Bilan à retourner */
- PyObject *module; /* Module Python-GObject */
- PyObject *args; /* Arguments à fournir */
- int ret; /* Bilan d'une mise en place */
-
- result = false;
-
- /* Sélection d'une version */
-
- module = PyImport_ImportModule("gi");
-
- if (module != NULL)
- {
- args = Py_BuildValue("ss", namespace, version);
-
- run_python_method(module, "require_version", args);
-
- result = (PyErr_Occurred() == NULL);
-
- Py_DECREF(args);
- Py_DECREF(module);
-
- }
-
- /* Importation du module visé */
-
- if (result)
- {
- args = PyTuple_New(1);
-
- ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace));
- if (ret != 0)
- {
- result = false;
- goto args_error;
- }
-
- module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args);
-
- result = (module != NULL);
-
- Py_XDECREF(module);
+ PyObject *result; /* Module Python à retourner */
+ pyinit_details_t details; /* Détails de chargement */
- args_error:
+ details.standalone = _standalone;
- Py_DECREF(args);
+ details.populate_extra = NULL;
+ details.create_self = g_pychrysalide_plugin_new;
- }
+ result = init_python_pychrysalide_module(&details);
return result;
}
-
-
-/******************************************************************************
-* *
-* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.*
-* *
-* Description : Présente dans le journal une exception survenue. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void log_pychrysalide_exception(const char *prefix, ...)
-{
- va_list ap; /* Compléments argumentaires */
- char *msg; /* Message complet à imprimer */
- PyObject *err_type; /* Type d'erreur Python */
- PyObject *err_value; /* Instance Python d'erreur */
- PyObject *err_traceback; /* Trace Python associée */
- PyObject *err_string; /* Description Python d'erreur */
- const char *err_msg; /* Représentation humaine */
-
- assert(PyGILState_Check() == 1);
-
- if (PyErr_Occurred())
- {
- /* Base de la communication */
-
- va_start(ap, prefix);
-
- vasprintf(&msg, prefix, ap);
-
- va_end(ap);
-
- /* Détails complémentaires */
-
- PyErr_Fetch(&err_type, &err_value, &err_traceback);
-
- PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
-
- if (err_traceback == NULL)
- {
- err_traceback = Py_None;
- Py_INCREF(err_traceback);
- }
-
- PyException_SetTraceback(err_value, err_traceback);
-
- if (err_value == NULL)
- msg = stradd(msg, _(": no extra information is provided..."));
-
- else
- {
- err_string = PyObject_Str(err_value);
- err_msg = PyUnicode_AsUTF8(err_string);
-
- msg = stradd(msg, ": ");
- msg = stradd(msg, err_msg);
-
- Py_DECREF(err_string);
-
- }
-
- /**
- * Bien que la documentation précise que la fonction PyErr_Fetch()
- * transfère la propritété des éléments retournés, la pratique
- * montre que le programme plante à la terminaison en cas d'exception.
- *
- * C'est par exemple le cas quand un greffon Python ne peut se lancer
- * correctement ; l'exception est alors levée à partir de la fonction
- * create_python_plugin() et le plantage intervient en sortie d'exécution,
- * au moment de la libération de l'extension Python :
- *
- * ==14939== Jump to the invalid address stated on the next line
- * ==14939== at 0x1A8FCBC9: ???
- * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
- * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
- * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153)
- * ==14939== by 0x10AD19: main (main.c:440)
- * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
- *
- * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
- * appel à PyErr_PrintEx(0) ne change rien.
- *
- * La seule différence de l'instruction set_sys_last_vars réside en quelques
- * lignes dans le code de l'interpréteur Python :
- *
- * if (set_sys_last_vars) {
- * _PySys_SetObjectId(&PyId_last_type, exception);
- * _PySys_SetObjectId(&PyId_last_value, v);
- * _PySys_SetObjectId(&PyId_last_traceback, tb);
- * }
- *
- * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
- * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
- * à ces éléments.
- *
- * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
- */
-
- PySys_SetObject("last_type", err_type);
- PySys_SetObject("last_value", err_value);
- PySys_SetObject("last_traceback", err_traceback);
-
- Py_XDECREF(err_traceback);
- Py_XDECREF(err_value);
- Py_XDECREF(err_type);
-
- log_plugin_simple_message(LMT_ERROR, msg);
-
- free(msg);
-
- }
-
-}
diff --git a/plugins/pychrysalide/core.h b/plugins/pychrysalide/core.h
index 5d25d3d..60c6c93 100644
--- a/plugins/pychrysalide/core.h
+++ b/plugins/pychrysalide/core.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.h - prototypes pour le plugin permettant des extensions en Python
*
- * Copyright (C) 2018-2019 Cyrille Bagard
+ * Copyright (C) 2018-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -35,31 +35,29 @@
#include <Python.h>
+#include <glibext/helpers.h>
#include <plugins/plugin.h>
-#include <plugins/plugin-int.h>
-/* Point d'entrée pour l'initialisation de Python. */
-PyMODINIT_FUNC PyInit_pychrysalide(void);
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+#define G_TYPE_PYCHRYSALIDE_PLUGIN (g_pychrysalide_plugin_get_type())
-/* Prend acte du chargement du greffon. */
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+DECLARE_GTYPE(GPyChrysalidePlugin, g_pychrysalide_plugin, G, PYCHRYSALIDE_PLUGIN);
-/* Prend acte du déchargement du greffon. */
-G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *);
-/* Accompagne la fin du chargement des modules natifs. */
-G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *, PluginAction);
+/* Crée un module pour un greffon de support Python. */
+GPluginModule *g_pychrysalide_plugin_new(GModule *);
-/* Crée une instance à partir d'un type dynamique externe. */
-G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *, PluginAction, GType);
-/* Charge un module GI dans Python avec une version attendue. */
-bool import_namespace_from_gi_repository(const char *, const char *);
-/* Présente dans le journal une exception survenue. */
-void log_pychrysalide_exception(const char *, ...);
+/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */
+
+
+/* Point d'entrée pour l'initialisation de Python. */
+PyMODINIT_FUNC PyInit_pychrysalide(void);
diff --git a/plugins/pychrysalide/format/executable.c b/plugins/pychrysalide/format/executable.c
index 7d05578..f0d3d6b 100644
--- a/plugins/pychrysalide/format/executable.c
+++ b/plugins/pychrysalide/format/executable.c
@@ -47,9 +47,9 @@
/* Initialise la classe des formats exécutables. */
-static void py_executable_format_init_gclass(GExecutableFormatClass *, gpointer);
+static int py_executable_format_init_gclass(GExecutableFormatClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT, py_executable_format_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_executable_format_init(PyObject *, PyObject *, PyObject *);
@@ -95,23 +95,25 @@ static PyObject *py_executable_format_get_portions(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des formats exécutables. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_executable_format_init_gclass(GExecutableFormatClass *class, gpointer unused)
+static int py_executable_format_init_gclass(GExecutableFormatClass *gclass, PyTypeObject *pyclass)
{
- class->get_machine = py_executable_format_get_target_machine_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_machine, py_executable_format_get_target_machine_wrapper);
- class->get_main_addr = py_executable_format_get_main_address_wrapper;
- class->refine_portions = py_executable_format_refine_portions_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_main_addr, py_executable_format_get_main_address_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->refine_portions, py_executable_format_refine_portions_wrapper);
+
+ return 0;
}
@@ -784,6 +786,8 @@ bool ensure_python_executable_format_is_registered(void)
if (!ensure_python_program_format_is_registered())
return false;
+ pyg_register_class_init(G_TYPE_EXECUTABLE_FORMAT, (PyGClassInitFunc)py_executable_format_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_EXECUTABLE_FORMAT, type))
return false;
diff --git a/plugins/pychrysalide/format/known.c b/plugins/pychrysalide/format/known.c
index 5df2a8f..856c087 100644
--- a/plugins/pychrysalide/format/known.c
+++ b/plugins/pychrysalide/format/known.c
@@ -42,9 +42,9 @@
/* Initialise la classe des descriptions de fichier binaire. */
-static void py_known_format_init_gclass(GKnownFormatClass *, gpointer);
+static int py_known_format_init_gclass(GKnownFormatClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT, py_known_format_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_known_format_init(PyObject *, PyObject *, PyObject *);
@@ -84,23 +84,25 @@ static PyObject *py_known_format_get_content(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des formats connus. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_known_format_init_gclass(GKnownFormatClass *class, gpointer unused)
+static int py_known_format_init_gclass(GKnownFormatClass *gclass, PyTypeObject *pyclass)
{
- class->get_key = py_known_format_get_key_wrapper;
- class->get_desc = py_known_format_get_description_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_key, py_known_format_get_key_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->get_desc, py_known_format_get_description_wrapper);
- class->analyze = py_known_format_analyze_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->analyze, py_known_format_analyze_wrapper);
+
+ return 0;
}
@@ -635,6 +637,8 @@ bool ensure_python_known_format_is_registered(void)
dict = PyModule_GetDict(module);
+ pyg_register_class_init(G_TYPE_KNOWN_FORMAT, (PyGClassInitFunc)py_known_format_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_FORMAT, type))
return false;
diff --git a/plugins/pychrysalide/format/program.c b/plugins/pychrysalide/format/program.c
index 28c1540..57a359a 100644
--- a/plugins/pychrysalide/format/program.c
+++ b/plugins/pychrysalide/format/program.c
@@ -55,9 +55,9 @@
/* Initialise la classe des formats de programmes. */
-static void py_program_format_init_gclass(GProgramFormatClass *, gpointer);
+static int py_program_format_init_gclass(GProgramFormatClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT, py_program_format_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_program_format_init(PyObject *, PyObject *, PyObject *);
@@ -66,7 +66,7 @@ static int py_program_format_init(PyObject *, PyObject *, PyObject *);
static SourceEndian py_program_format_get_endianness_wrapper(const GProgramFormat *);
/* Fournit l'emplacement d'une section donnée. */
-static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *);
+static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *);
@@ -86,7 +86,7 @@ static PyObject *py_program_format_has_flag(PyObject *, PyObject *);
/* Fournit l'emplacement d'une section donnée. */
-static PyObject *py_program_format_get_section_range_by_name(PyObject *, PyObject *);
+static PyObject *py_program_format_find_section_range_by_name(PyObject *, PyObject *);
#if 0
@@ -140,21 +140,23 @@ static PyObject *py_program_format_get_errors(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des formats de programmes. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_program_format_init_gclass(GProgramFormatClass *class, gpointer unused)
+static int py_program_format_init_gclass(GProgramFormatClass *gclass, PyTypeObject *pyclass)
{
- class->get_endian = py_program_format_get_endianness_wrapper;
- class->get_range_by_name = py_program_format_get_section_range_by_name_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_endian, py_program_format_get_endianness_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->find_range_by_name, py_program_format_find_section_range_by_name_wrapper);
+
+ return 0;
}
@@ -188,7 +190,7 @@ static int py_program_format_init(PyObject *self, PyObject *args, PyObject *kwds
"* pychrysalide.format.ProgramFormat._get_endianness().\n" \
"\n" \
"Other optional method may be defined for new classes:\n" \
- "* pychrysalide.format.ProgramFormat._get_section_range_by_name().\n" \
+ "* pychrysalide.format.ProgramFormat._find_section_range_by_name().\n" \
"\n" \
"Calls to the *__init__* constructor of this abstract object expect" \
" only one argument: a binary content, provided as a" \
@@ -295,7 +297,7 @@ static SourceEndian py_program_format_get_endianness_wrapper(const GProgramForma
* *
******************************************************************************/
-static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range)
+static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range)
{
bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
@@ -303,15 +305,15 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo
PyObject *pyret; /* Valeur retournée */
int ret; /* Bilan d'une conversion */
-#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _get_section_range_by_name_wrapper, "$self, name", \
- METH_VARARGS, \
- "Abstract method used to compute the area of a section identified by" \
- " its name.\n" \
- "\n" \
- "The expected returned value is a pychrysalide.arch.mrange instance or" \
- " *None* in case of failure." \
+#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _find_section_range_by_name_wrapper, "$self, name", \
+ METH_VARARGS, \
+ "Abstract method used to compute the area of a section identified by" \
+ " its name.\n" \
+ "\n" \
+ "The expected returned value is a pychrysalide.arch.mrange instance or" \
+ " *None* in case of failure." \
)
result = false;
@@ -320,9 +322,9 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo
pyobj = pygobject_new(G_OBJECT(format));
- if (has_python_method(pyobj, "_get_section_range_by_name"))
+ if (has_python_method(pyobj, "_find_section_range_by_name"))
{
- pyret = run_python_method(pyobj, "_get_section_range_by_name", NULL);
+ pyret = run_python_method(pyobj, "_find_section_range_by_name", NULL);
if (pyret != NULL)
{
@@ -520,7 +522,7 @@ static PyObject *py_program_format_has_flag(PyObject *self, PyObject *args)
* *
******************************************************************************/
-static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyObject *args)
+static PyObject *py_program_format_find_section_range_by_name(PyObject *self, PyObject *args)
{
PyObject *result; /* Emplacement à retourner */
const char *name; /* Nom de section ciblée */
@@ -529,9 +531,9 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO
mrange_t range; /* Emplacement obtenu ? */
bool status; /* Bilan de l'opération */
-#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \
+#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \
( \
- get_section_range_by_name, "$self, name, /", \
+ find_section_range_by_name, "$self, name, /", \
METH_VARARGS, py_program_format, \
"Compute the area of a section identified by its name.\n" \
"\n" \
@@ -546,7 +548,7 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO
format = G_PROGRAM_FORMAT(pygobject_get(self));
- status = g_program_format_get_section_range_by_name(format, name, &range);
+ status = g_program_format_find_section_range_by_name(format, name, &range);
if (status)
result = build_from_internal_mrange(&range);
@@ -1194,8 +1196,8 @@ PyTypeObject *get_python_program_format_type(void)
{
static PyMethodDef py_program_format_methods[] = {
PROGRAM_FORMAT_GET_ENDIANNESS_WRAPPER,
- PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER,
- PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD,
+ PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER,
+ PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD,
/*
PROGRAM_FORMAT_SET_FLAG_METHOD,
PROGRAM_FORMAT_UNSET_FLAG_METHOD,
@@ -1273,6 +1275,8 @@ bool ensure_python_program_format_is_registered(void)
if (!ensure_python_known_format_is_registered())
return false;
+ pyg_register_class_init(G_TYPE_PROGRAM_FORMAT, (PyGClassInitFunc)py_program_format_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_PROGRAM_FORMAT, type))
return false;
diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am
index 8b021bb..af1d9f2 100644
--- a/plugins/pychrysalide/glibext/Makefile.am
+++ b/plugins/pychrysalide/glibext/Makefile.am
@@ -5,12 +5,10 @@ noinst_LTLIBRARIES = libpychrysaglibext.la
# binarycursor.h binarycursor.c \
# buffercache.h buffercache.c \
# bufferline.h bufferline.c \
-# comparison.h comparison.c \
# configuration.h configuration.c \
# linecursor.h linecursor.c \
# linegen.h linegen.c \
-# module.h module.c \
-# singleton.h singleton.c
+# module.h module.c
# if BUILD_GTK_SUPPORT
@@ -22,9 +20,15 @@ noinst_LTLIBRARIES = libpychrysaglibext.la
# endif
libpychrysaglibext_la_SOURCES = \
+ comparable.h comparable.c \
constants.h constants.c \
+ hashable.h hashable.c \
module.h module.c \
+ objhole.h objhole.c \
portion.h portion.c \
+ secstorage.h secstorage.c \
+ singleton.h singleton.c \
+ strbuilder.h strbuilder.c \
work.h work.c \
workqueue.h workqueue.c
diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c
new file mode 100644
index 0000000..e4982d7
--- /dev/null
+++ b/plugins/pychrysalide/glibext/comparable.c
@@ -0,0 +1,482 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable.c - équivalent Python du fichier "glibext/comparable.c"
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "comparable.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/comparable-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Procède à l'initialisation de l'interface de détermination. */
+static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *);
+
+/* Réalise une comparaison étendue entre objets. */
+static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Transmet le statut d'une comparaison effectuée par le parent. */
+static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *);
+
+/* Effectue une comparaison avec un objet 'ComparableObject'. */
+static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* unused = adresse non utilisée ici. *
+* *
+* Description : Procède à l'initialisation de l'interface de détermination. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused)
+{
+#define COMPARABLE_OBJECT_DOC \
+ "The ComparableObject class provides an interface to compare" \
+ " objects.\n" \
+ "\n" \
+ "A typical class declaration for a new implementation looks like:\n" \
+ "\n" \
+ " class NewImplem(GObject.Object, ComparableObject):\n" \
+ " ...\n" \
+ "\n" \
+ "The following method has to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.ComparableObject._compare().\n"
+
+ iface->compare = py_comparable_object_compare_wrapper;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = premier objet à consulter pour une comparaison. *
+* other = second objet à consulter pour une comparaison. *
+* *
+* Description : Réalise une comparaison étendue entre objets. *
+* *
+* Retour : Bilan de la comparaison. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other)
+{
+ int result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *args; /* Arguments pour l'appel */
+ PyObject *pyret; /* Bilan de consultation */
+
+#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _compare, "$self, other", \
+ METH_VARARGS, \
+ "Abstract method allowing to compare two objects implementing" \
+ " the interface. This method is used to handle rich comparisons"\
+ " automatically.\n" \
+ "\n" \
+ "The result has to be an integer lesser than, equal to, or" \
+ " greater than zero if *self* is found, respectively, to be" \
+ " lesser than, to match, or to be greater than *other*.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " an integer." \
+)
+
+ result = 0;
+
+ gstate = PyGILState_Ensure();
+
+ pyobj = pygobject_new(G_OBJECT(object));
+
+ args = PyTuple_New(1);
+ PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other)));
+
+ pyret = run_python_method(pyobj, "_compare", args);
+
+ if (pyret != NULL)
+ {
+ if (PyLong_Check(pyret))
+ result = PyLong_AsLong(pyret);
+
+ else
+ PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer"));
+
+ }
+
+ Py_XDECREF(pyret);
+
+ Py_DECREF(args);
+
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet dont l'instance se veut unique. *
+* args = adresse non utilisée ici. *
+* *
+* Description : Transmet le statut d'une comparaison effectuée par le parent.*
+* *
+* Retour : Bilan de la comparaison. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ GComparableObject *other; /* Second objet à comparer */
+ int ret; /* Bilan de lecture des args. */
+ GComparableObject *object; /* Mécanismes natifs */
+ GComparableObjectInterface *iface; /* Interface utilisée */
+ GComparableObjectInterface *parent_iface; /* Interface parente */
+ int status; /* Bilan d'une comparaison */
+
+#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \
+( \
+ parent_compare, "$sel, otherf", \
+ METH_VARARGS, py_comparable_object, \
+ "Provide the comparison status defined by the interface" \
+ " implementation from the object native parent.\n" \
+ "\n" \
+ "The result is a signed integer.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the object parent does" \
+ " not implement the pychrysalide.glibext.ComparableObject" \
+ " interface.\n" \
+ "\n" \
+ "A *RuntimeError* exception is raised if the direct parent type"\
+ " of the object has not a native implementation. For Python" \
+ " implementations, the super()._compare() function has to be" \
+ " used instead." \
+)
+
+ if (!check_for_native_parent(self))
+ return NULL;
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other);
+ if (!ret) return NULL;
+
+ object = G_COMPARABLE_OBJECT(pygobject_get(self));
+
+ iface = G_COMPARABLE_OBJECT_GET_IFACE(object);
+
+ parent_iface = g_type_interface_peek_parent(iface);
+
+ if (parent_iface == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface"));
+
+ result = NULL;
+
+ }
+ else
+ {
+ status = parent_iface->compare(object, other);
+
+ result = PyLong_FromLong(status);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : a = premier object Python à consulter. *
+* b = second object Python à consulter. *
+* op = type de comparaison menée. *
+* *
+* Description : Effectue une comparaison avec un objet 'ComparableObject'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op)
+{
+ PyObject *result; /* Bilan à retourner */
+ int ret; /* Bilan de lecture des args. */
+ GComparableObject *obj_a; /* Instance à manipuler #1 */
+ GComparableObject *obj_b; /* Instance à manipuler #2 */
+ int status; /* Bilan d'une comparaison */
+
+ ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type());
+ if (!ret)
+ {
+ result = Py_NotImplemented;
+ goto cmp_done;
+ }
+
+ obj_a = G_COMPARABLE_OBJECT(pygobject_get(a));
+ obj_b = G_COMPARABLE_OBJECT(pygobject_get(b));
+
+ status = g_comparable_object_compare(obj_a, obj_b);
+
+ switch (op)
+ {
+ case Py_LT:
+ result = (status < 0 ? Py_True : Py_False);
+ break;
+
+ case Py_LE:
+ result = (status <= 0 ? Py_True : Py_False);
+ break;
+
+ case Py_EQ:
+ result = (status == 0 ? Py_True : Py_False);
+ break;
+
+ case Py_NE:
+ result = (status != 0 ? Py_True : Py_False);
+ break;
+
+ case Py_GT:
+ result = (status > 0 ? Py_True : Py_False);
+ break;
+
+ case Py_GE:
+ result = (status >= 0 ? Py_True : Py_False);
+ break;
+
+ default:
+ assert(false);
+ result = Py_NotImplemented;
+ break;
+
+ }
+
+ cmp_done:
+
+ Py_INCREF(result);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ return result;
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_comparable_object_type(void)
+{
+ static PyMethodDef py_comparable_object_methods[] = {
+ COMPARABLE_OBJECT_COMPARE_WRAPPER,
+ COMPARABLE_OBJECT_PARENT_COMPARE_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_comparable_object_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_comparable_object_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.ComparableObject",
+ .tp_basicsize = sizeof(PyObject),
+
+ /**
+ * Une valeur de .tp_richcompare non nulle écarte la définition du
+ * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type
+ * dans les préparatifs de la fonction Python inherit_slots().
+ *
+ * Ces préparatifs se poursuivent avec type_ready_set_hash(),
+ * qui initialise .tp_hash avec PyObject_HashNotImplemented(),
+ * qui n'est donc pas un comportement par défaut.
+ *
+ * Côté PyGObject, la fonction pygobject_inherit_slots() y voit
+ * une implémentation de .tp_hash personnalisée, ce qui bloque
+ * la défintion d'autres personnalisations, comme celle de
+ * l'interface HashableObject.
+ *
+ * La valeur nominale nulle est ainsi écartée au préalable ici.
+ */
+ .tp_hash = (hashfunc)_Py_HashPointer,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = COMPARABLE_OBJECT_DOC,
+
+ .tp_richcompare = py_comparable_object_richcompare,
+
+ .tp_methods = py_comparable_object_methods,
+ .tp_getset = py_comparable_object_getseters
+
+ };
+
+ return &py_comparable_object_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_comparable_object_is_registered(void)
+{
+ PyTypeObject *type; /* Type 'ComparableObject' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ static GInterfaceInfo info = { /* Paramètres d'inscription */
+
+ .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init,
+ .interface_finalize = NULL,
+ .interface_data = NULL,
+
+ };
+
+ type = get_python_comparable_object_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en interface d'objet comparable. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_comparable_object(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable object");
+ break;
+
+ case 1:
+ *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h
new file mode 100644
index 0000000..d4c6ecf
--- /dev/null
+++ b/plugins/pychrysalide/glibext/comparable.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h"
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_comparable_object_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */
+bool ensure_python_comparable_object_is_registered(void);
+
+/* Tente de convertir en interface d'objet comparable. */
+int convert_to_comparable_object(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */
diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/hashable.c
index 548f700..c870d55 100644
--- a/plugins/pychrysalide/glibext/comparison.c
+++ b/plugins/pychrysalide/glibext/hashable.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison.c - équivalent Python du fichier "glibext/comparison.h"
+ * hashable.c - équivalent Python du fichier "glibext/hashable.c"
*
- * Copyright (C) 2018-2019 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,38 +22,40 @@
*/
-#include "comparison.h"
+#include "hashable.h"
+#include <assert.h>
#include <pygobject.h>
-#include <glibext/comparison-int.h>
+#include <glibext/hashable-int.h>
-#include "constants.h"
#include "../access.h"
#include "../helpers.h"
-#include "../analysis/content.h"
/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
-/* Procède à l'initialisation de l'interface de comparaison. */
-static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *);
+/* Procède à l'initialisation de l'interface de détermination. */
+static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *);
-/* Réalise une comparaison entre objets selon un critère précis. */
-static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *);
+/* Calcule l'empreinte sur 32 bits d'un objet. */
+static guint py_hashable_object_hash_wrapper(const GHashableObject *);
/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
-/* Effectue une comparaison avec un objet 'ComparableItem'. */
-static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int);
+/* Transmet l'empreinte d'un objet calculée par son parent. */
+static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *);
+
+/* Calcule l'empreinte sur 32 bits d'un objet. */
+static Py_hash_t py_hashable_object_hash(PyObject *);
@@ -67,7 +69,7 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int);
* Paramètres : iface = interface GLib à initialiser. *
* unused = adresse non utilisée ici. *
* *
-* Description : Procède à l'initialisation de l'interface de comparaison. *
+* Description : Procède à l'initialisation de l'interface de détermination. *
* *
* Retour : - *
* *
@@ -75,72 +77,78 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int);
* *
******************************************************************************/
-static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused)
+static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused)
{
-
-#define COMPARABLE_ITEM_DOC \
- "ComparableItem provides an interface to compare objects.\n" \
+#define HASHABLE_OBJECT_DOC \
+ "The HashableObject class defines a interface ensuring that a" \
+ " customized hashing method is available for an object.\n" \
"\n" \
"A typical class declaration for a new implementation looks like:\n" \
"\n" \
- " class NewImplem(GObject.Object, ComparableItem):\n" \
+ " class NewImplem(GObject.Object, HashableObject):\n" \
" ...\n" \
- "\n"
+ "\n" \
+ "The following method has to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.HashableObject._hash().\n"
- iface->cmp_rich = py_comparable_item_compare_rich;
+ iface->hash = py_hashable_object_hash_wrapper;
}
/******************************************************************************
* *
-* Paramètres : item = premier objet à cnsulter pour une comparaison. *
-* other = second objet à cnsulter pour une comparaison. *
-* op = opération de comparaison à réaliser. *
-* status = bilan des opérations de comparaison. [OUT] *
+* Paramètres : object = objet dont l'instance est à consulter. *
* *
-* Description : Réalise une comparaison entre objets selon un critère précis.*
+* Description : Calcule l'empreinte sur 32 bits d'un objet. *
* *
-* Retour : true si la comparaison a pu être effectuée, false sinon. *
+* Retour : Valeur de représentation, unique pour l'objet ou non. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status)
+static guint py_hashable_object_hash_wrapper(const GHashableObject *object)
{
- bool result; /* Etat à retourner */
+ guint result; /* Valeur à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- PyObject *pyitem; /* Objet Python concerné #1 */
- PyObject *pyother; /* Objet Python concerné #2 */
+ PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan de consultation */
- int ret; /* Bilan d'une conversion */
- result = false;
+#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _hash, "$self", \
+ METH_NOARGS, \
+ "Abstract method computing a hash from an object which is used" \
+ " as the default implementation of the __hash__() method.\n" \
+ "\n" \
+ "The result has to be an unsigned integer.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " an integer." \
+)
+
+ result = 0;
gstate = PyGILState_Ensure();
- pyitem = pygobject_new(G_OBJECT(item));
- pyother = pygobject_new(G_OBJECT(other));
+ pyobj = pygobject_new(G_OBJECT(object));
- pyret = PyObject_RichCompare(pyitem, pyother, op);
+ pyret = run_python_method(pyobj, "_hash", NULL);
if (pyret != NULL)
{
- ret = PyBool_Check(pyret);
+ if (PyLong_Check(pyret))
+ result = PyLong_AsUnsignedLong(pyret);
- if (ret)
- {
- *status = (pyret == Py_True);
- result = true;
- }
-
- Py_DECREF(pyret);
+ else
+ PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer"));
}
- Py_DECREF(pyother);
- Py_DECREF(pyitem);
+ Py_XDECREF(pyret);
+
+ Py_DECREF(pyobj);
PyGILState_Release(gstate);
@@ -157,47 +165,97 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G
/******************************************************************************
* *
-* Paramètres : a = premier object Python à consulter. *
-* b = second object Python à consulter. *
-* op = type de comparaison menée. *
+* Paramètres : self = objet dont l'instance se veut unique. *
+* args = adresse non utilisée ici. *
* *
-* Description : Effectue une comparaison avec un objet 'ComparableItem'. *
+* Description : Transmet l'empreinte d'un objet calculée par son parent. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Valeur de représentation, unique pour l'objet ou non. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op)
+static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args)
{
- PyObject *result; /* Bilan à retourner */
- int ret; /* Bilan de lecture des args. */
- GComparableItem *item_a; /* Instance à manipuler #1 */
- GComparableItem *item_b; /* Instance à manipuler #2 */
- bool valid; /* Indication de validité */
- bool status; /* Résultat d'une comparaison */
-
- ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type());
- if (!ret)
+ PyObject *result; /* Valeur à retourner */
+ GHashableObject *object; /* Mécanismes natifs */
+ GHashableObjectInterface *iface; /* Interface utilisée */
+ GHashableObjectInterface *parent_iface; /* Interface parente */
+ guint hash; /* Valeur d'empreitne */
+
+#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \
+( \
+ parent_hash, "$self", \
+ METH_NOARGS, py_hashable_object, \
+ "Provide the hash value defined by the interface implementation"\
+ " from the object native parent.\n" \
+ "\n" \
+ "The result is an unsigned integer.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the object parent does" \
+ " not implement the pychrysalide.glibext.HashableObject" \
+ " interface.\n" \
+ "\n" \
+ "A *RuntimeError* exception is raised if the direct parent type"\
+ " of the object has not a native implementation. For Python" \
+ " implementations, the super()._hash() function has to be used" \
+ " instead." \
+)
+
+ if (!check_for_native_parent(self))
+ return NULL;
+
+ object = G_HASHABLE_OBJECT(pygobject_get(self));
+
+ iface = G_HASHABLE_OBJECT_GET_IFACE(object);
+
+ parent_iface = g_type_interface_peek_parent(iface);
+
+ if (parent_iface == NULL)
{
- result = Py_NotImplemented;
- goto cmp_done;
+ PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface"));
+
+ result = NULL;
+
}
+ else
+ {
+ hash = parent_iface->hash(object);
- item_a = G_COMPARABLE_ITEM(pygobject_get(a));
- item_b = G_COMPARABLE_ITEM(pygobject_get(b));
+ result = PyLong_FromUnsignedLong(hash);
- valid = g_comparable_item_compare_rich(item_a, item_b, op, &status);
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ }
+
+ return result;
+
+}
- if (valid)
- result = status ? Py_True : Py_False;
- else
- result = Py_NotImplemented;
- cmp_done:
+/******************************************************************************
+* *
+* Paramètres : self = objet manipulé ici. *
+* *
+* Description : Calcule l'empreinte sur 32 bits d'un objet. *
+* *
+* Retour : Valeur de représentation, unique pour l'objet ou non. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- Py_INCREF(result);
+static Py_hash_t py_hashable_object_hash(PyObject *self)
+{
+ Py_hash_t result; /* Empreinte à retourner */
+ GHashableObject *object; /* Mécanismes natifs */
+
+ object = G_HASHABLE_OBJECT(pygobject_get(self));
+
+ result = g_hashable_object_hash(object);
+
+ UPDATE_RESULT_IF_RAISED_EXCEPTION(-1);
return result;
@@ -216,44 +274,46 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op
* *
******************************************************************************/
-PyTypeObject *get_python_comparable_item_type(void)
+PyTypeObject *get_python_hashable_object_type(void)
{
- static PyMethodDef py_comparable_item_methods[] = {
+ static PyMethodDef py_hashable_object_methods[] = {
+ HASHABLE_OBJECT_HASH_WRAPPER,
+ HASHABLE_OBJECT_PARENT_HASH_METHOD,
{ NULL }
};
- static PyGetSetDef py_comparable_item_getseters[] = {
+ static PyGetSetDef py_hashable_object_getseters[] = {
{ NULL }
};
- static PyTypeObject py_comparable_item_type = {
+ static PyTypeObject py_hashable_object_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "pychrysalide.glibext.ComparableItem",
+ .tp_name = "pychrysalide.glibext.HashableObject",
.tp_basicsize = sizeof(PyObject),
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_hash = py_hashable_object_hash,
- .tp_doc = COMPARABLE_ITEM_DOC,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .tp_richcompare = py_comparable_item_richcompare,
+ .tp_doc = HASHABLE_OBJECT_DOC,
- .tp_methods = py_comparable_item_methods,
- .tp_getset = py_comparable_item_getseters,
+ .tp_methods = py_hashable_object_methods,
+ .tp_getset = py_hashable_object_getseters,
};
- return &py_comparable_item_type;
+ return &py_hashable_object_type;
}
/******************************************************************************
* *
-* Paramètres : module = module dont la définition est à compléter. *
+* Paramètres : - *
* *
-* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. *
+* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. *
* *
* Retour : Bilan de l'opération. *
* *
@@ -261,21 +321,21 @@ PyTypeObject *get_python_comparable_item_type(void)
* *
******************************************************************************/
-bool ensure_python_comparable_item_is_registered(void)
+bool ensure_python_hashable_object_is_registered(void)
{
- PyTypeObject *type; /* Type Python 'ComparableItem' */
+ PyTypeObject *type; /* Type Python 'HashableObject'*/
PyObject *module; /* Module à recompléter */
PyObject *dict; /* Dictionnaire du module */
static GInterfaceInfo info = { /* Paramètres d'inscription */
- .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init,
+ .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init,
.interface_finalize = NULL,
.interface_data = NULL,
};
- type = get_python_comparable_item_type();
+ type = get_python_hashable_object_type();
if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
{
@@ -283,10 +343,7 @@ bool ensure_python_comparable_item_is_registered(void)
dict = PyModule_GetDict(module);
- if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info))
- return false;
-
- if (!define_comparable_item_constants(type))
+ if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info))
return false;
}
@@ -301,7 +358,7 @@ bool ensure_python_comparable_item_is_registered(void)
* Paramètres : arg = argument quelconque à tenter de convertir. *
* dst = destination des valeurs récupérées en cas de succès. *
* *
-* Description : Tente de convertir en élément comparable. *
+* Description : Tente de convertir en interface d'objet réductible. *
* *
* Retour : Bilan de l'opération, voire indications supplémentaires. *
* *
@@ -309,11 +366,11 @@ bool ensure_python_comparable_item_is_registered(void)
* *
******************************************************************************/
-int convert_to_comparable_item(PyObject *arg, void *dst)
+int convert_to_hashable_object(PyObject *arg, void *dst)
{
int result; /* Bilan à retourner */
- result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type());
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type());
switch (result)
{
@@ -323,11 +380,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst)
break;
case 0:
- PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item");
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object");
break;
case 1:
- *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg));
+ *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg));
break;
default:
diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h
index 79f7092..8583118 100644
--- a/plugins/pychrysalide/glibext/comparison.h
+++ b/plugins/pychrysalide/glibext/hashable.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h"
+ * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h"
*
- * Copyright (C) 2018 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,8 +22,8 @@
*/
-#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H
-#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H
#include <Python.h>
@@ -32,14 +32,14 @@
/* Fournit un accès à une définition de type à diffuser. */
-PyTypeObject *get_python_comparable_item_type(void);
+PyTypeObject *get_python_hashable_object_type(void);
-/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */
-bool ensure_python_comparable_item_is_registered(void);
+/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */
+bool ensure_python_hashable_object_is_registered(void);
-/* Tente de convertir en élément comparable. */
-int convert_to_comparable_item(PyObject *, void *);
+/* Tente de convertir en interface d'objet réductible. */
+int convert_to_hashable_object(PyObject *, void *);
-#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */
diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c
index e62d587..6ce0709 100644
--- a/plugins/pychrysalide/glibext/module.c
+++ b/plugins/pychrysalide/glibext/module.c
@@ -33,15 +33,19 @@
#include "buffercache.h"
#include "bufferline.h"
#include "bufferview.h"
-#include "comparison.h"
#include "configuration.h"
#include "linecursor.h"
#include "linegen.h"
#include "loadedpanel.h"
#include "named.h"
-#include "singleton.h"
*/
+#include "comparable.h"
+#include "hashable.h"
+#include "objhole.h"
#include "portion.h"
+#include "secstorage.h"
+#include "singleton.h"
+#include "strbuilder.h"
#include "work.h"
#include "workqueue.h"
#include "../helpers.h"
@@ -110,12 +114,19 @@ bool populate_glibext_module(void)
result = true;
+ if (result) result = ensure_python_comparable_object_is_registered();
+ if (result) result = ensure_python_hashable_object_is_registered();
+ if (result) result = ensure_python_singleton_candidate_is_registered();
+ if (result) result = ensure_python_string_builder_is_registered();
+
+ if (result) result = ensure_python_thick_object_is_registered();
if (result) result = ensure_python_binary_portion_is_registered();
if (result) result = ensure_python_generic_work_is_registered();
+ if (result) result = ensure_python_secret_storage_is_registered();
+ if (result) result = ensure_python_singleton_factory_is_registered();
if (result) result = ensure_python_work_queue_is_registered();
/*
- if (result) result = ensure_python_singleton_candidate_is_registered();
if (result) result = ensure_python_binary_cursor_is_registered();
if (result) result = ensure_python_buffer_cache_is_registered();
@@ -123,7 +134,6 @@ bool populate_glibext_module(void)
#ifdef INCLUDE_GTK_SUPPORT
if (result) result = ensure_python_buffer_view_is_registered();
#endif
- if (result) result = ensure_python_comparable_item_is_registered();
if (result) result = ensure_python_config_param_is_registered();
if (result) result = ensure_python_config_param_iterator_is_registered();
if (result) result = ensure_python_generic_config_is_registered();
@@ -133,7 +143,6 @@ bool populate_glibext_module(void)
if (result) result = ensure_python_loaded_panel_is_registered();
if (result) result = ensure_python_named_widget_is_registered();
#endif
- if (result) result = ensure_python_singleton_factory_is_registered();
*/
assert(result);
diff --git a/plugins/pychrysalide/glibext/objhole.c b/plugins/pychrysalide/glibext/objhole.c
new file mode 100644
index 0000000..2a3ad6f
--- /dev/null
+++ b/plugins/pychrysalide/glibext/objhole.c
@@ -0,0 +1,329 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * objhole.c - équivalent Python du fichier "glibext/objhole.c"
+ *
+ * Copyright (C) 2024 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "objhole.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <glibext/objhole-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(thick_object, G_TYPE_THICK_OBJECT);
+
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_thick_object_init(PyObject *, PyObject *, PyObject *);
+
+/* Indique le nombre de bits accaparés par la GLib. */
+static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *, void *);
+
+/* Fournit la valeur courante de la zone de stockage d'un objet. */
+static PyObject *py_thick_object_get_extra(PyObject *, void *);
+
+/* Définit la valeur courante de la zone de stockage d'un objet. */
+static int py_thick_object_set_extra(PyObject *, PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_thick_object_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define THICK_OBJECT_DOC \
+ "The ThickObject class is an alternative version of the GObject class." \
+ " It provides an access to a 32-bit wide memory hole inside the native" \
+ " GObject structure in order to exploit the area as various values" \
+ " storage.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " ThickObject()"
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique le nombre de bits accaparés par la GLib. *
+* *
+* Retour : Nombre de bits, à priori inférieur à 32. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *self, void *closure)
+{
+ PyObject *result; /* Résultat à retourner */
+
+#define THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ _GOBJECT_RESERVED_EXTRA_BITS, py_thick_object, \
+ "Quantity of lower bits used by the GLib inside the memory hole.\n" \
+ "\n" \
+ "The returned value should be less then 32 and is provided for" \
+ " pychrysalide.glibext.ThickObject subclass implementation." \
+)
+
+ result = PyLong_FromUnsignedLong(GOBJECT_RESERVED_EXTRA_BITS);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit la valeur courante de la zone de stockage d'un objet.*
+* *
+* Retour : Valeur de 32 lues et expurgées des bits GLib. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_thick_object_get_extra(PyObject *self, void *closure)
+{
+ PyObject *result; /* Résultat à retourner */
+ GThickObject *obj; /* Version GLib de l'instance */
+ guint extra; /* Valeur à transmettre */
+
+#define THICK_OBJECT_EXTRA_ATTRIB PYTHON_GETSET_DEF_FULL \
+( \
+ extra, py_thick_object, \
+ "Data stored inside the structure memory hole of all GObject" \
+ " native instances.\n" \
+ "\n" \
+ "Less than 32 bits are available for storing arbitrary values." \
+)
+
+ obj = G_THICK_OBJECT(pygobject_get(self));
+
+ extra = g_thick_object_get_extra(obj);
+
+ result = PyLong_FromUnsignedLong(extra);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* value = valeur fournie à intégrer ou prendre en compte. *
+* closure = non utilisé ici. *
+* *
+* Description : Définit la valeur courante de la zone de stockage d'un objet.*
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_thick_object_set_extra(PyObject *self, PyObject *value, void *closure)
+{
+ unsigned long extra; /* Valeur à inscrire */
+ GThickObject *obj; /* Version GLib de l'instance */
+
+ if (!PyLong_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, _("Invalid integer value"));
+ return -1;
+ }
+
+ extra = PyLong_AsUnsignedLong(value);
+
+ obj = G_THICK_OBJECT(pygobject_get(self));
+
+ g_thick_object_set_extra(obj, extra);
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_thick_object_type(void)
+{
+ static PyMethodDef py_thick_object_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_thick_object_getseters[] = {
+ THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB,
+ THICK_OBJECT_EXTRA_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_thick_object_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.ThickObject",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = THICK_OBJECT_DOC,
+
+ .tp_methods = py_thick_object_methods,
+ .tp_getset = py_thick_object_getseters,
+
+ .tp_init = py_thick_object_init,
+ .tp_new = py_thick_object_new,
+
+ };
+
+ return &py_thick_object_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_thick_object_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'ThickObject' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_thick_object_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_THICK_OBJECT, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en objet dense. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_thick_object(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_thick_object_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to thick object");
+ break;
+
+ case 1:
+ *((GThickObject **)dst) = G_THICK_OBJECT(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/plugins/constants.h b/plugins/pychrysalide/glibext/objhole.h
index 56612c9..b541b70 100644
--- a/plugins/pychrysalide/plugins/constants.h
+++ b/plugins/pychrysalide/glibext/objhole.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * constants.h - prototypes pour l'ajout des constantes principales
+ * objhole.h - prototypes pour l'équivalent Python du fichier "glibext/objhole.h"
*
- * Copyright (C) 2020 Cyrille Bagard
+ * Copyright (C) 2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,20 +22,24 @@
*/
-#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H
-#define _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H
#include <Python.h>
#include <stdbool.h>
-/* Définit les constantes relatives aux greffons Python. */
-bool define_plugin_module_constants(PyTypeObject *);
-/* Tente de convertir en constante PluginAction. */
-int convert_to_plugin_action(PyObject *, void *);
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_thick_object_type(void);
+/* Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. */
+bool ensure_python_thick_object_is_registered(void);
+/* Tente de convertir en objet dense. */
+int convert_to_thick_object(PyObject *, void *);
-#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H */
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H */
diff --git a/plugins/pychrysalide/glibext/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c
new file mode 100644
index 0000000..b5adb7c
--- /dev/null
+++ b/plugins/pychrysalide/glibext/secstorage.c
@@ -0,0 +1,624 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage.c - équivalent Python du fichier "glibext/secstorage.c"
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "secstorage.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/secstorage-int.h>
+
+
+#include "../access.h"
+#include "../convert.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_secret_storage_init(PyObject *, PyObject *, PyObject *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Définit un mot de passe pour protéger une clef maître. */
+static PyObject *py_secret_storage_set_password(PyObject *, PyObject *);
+
+/* Déverrouille la clef de chiffrement maître. */
+static PyObject *py_secret_storage_unlock(PyObject *, PyObject *);
+
+/* Verrouille la clef de chiffrement maître. */
+static PyObject *py_secret_storage_lock(PyObject *, PyObject *);
+
+/* Chiffre des données avec la clef de chiffrement maître. */
+static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *);
+
+/* Déchiffre des données avec la clef de chiffrement maître. */
+static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *);
+
+/* Détermine si une clef de chiffrement protégée est en place. */
+static PyObject *py_secret_storage_has_key(PyObject *, void *);
+
+/* Détermine si la clef de chiffrement maître est vérouillée. */
+static PyObject *py_secret_storage_is_locked(PyObject *, void *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GSettings *settings; /* Configuration à considérer */
+ int ret; /* Bilan de lecture des args. */
+ GSecretStorage *storage; /* Stockage natif à manipuler */
+
+#define SECRET_STORAGE_DOC \
+ "SecretStorage acts as guardian for secrets using ecryption," \
+ " mainly for sensitive information stored as configuration" \
+ " parameters.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " SecretStorage(settings)" \
+ "\n" \
+ "The *settings* arguement must point to a GSettings intance;" \
+ " the main configuration settings are used by default." \
+
+ settings = NULL;
+
+ ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings, &settings);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ if (!g_secret_storage_create(storage, settings))
+ return -1;
+
+ return 0;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Définit un mot de passe pour protéger une clef maître. *
+* *
+* Retour : Bilan de la mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *passwd; /* Mot de passe associé */
+ int ret; /* Bilan de lecture des args. */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+
+#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \
+( \
+ set_password, "/, password=''", \
+ METH_VARARGS, py_secret_storage, \
+ "Create a master key used for protecting secrets. This key is" \
+ " itself protected by the provided password.\n" \
+ "\n" \
+ "The supplied *password* has to be a string.\n" \
+ "\n" \
+ "The result is a boolean status: *True* if the operation successed,"\
+ " *False* otherwise." \
+)
+
+ passwd = "";
+
+ ret = PyArg_ParseTuple(args, "|s", &passwd);
+ if (!ret) return NULL;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_set_password(storage, passwd);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Déverrouille la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *passwd; /* Mot de passe associé */
+ int ret; /* Bilan de lecture des args. */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+
+#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \
+( \
+ unlock, "/, password=''", \
+ METH_VARARGS, py_secret_storage, \
+ "Decrypt in memory the master key used for protecting secrets.\n" \
+ "\n" \
+ "The supplied *password* is the primary password used to protect" \
+ " this key.\n" \
+ "\n" \
+ "The result is a boolean status: *True* if the operation successed" \
+ " or if the master key is already unlocked, *False* otherwise." \
+)
+
+ passwd = "";
+
+ ret = PyArg_ParseTuple(args, "|s", &passwd);
+ if (!ret) return NULL;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_unlock(storage, passwd);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Verrouille la clef de chiffrement maître. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+
+#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \
+( \
+ lock, "", \
+ METH_NOARGS, py_secret_storage, \
+ "Clear from memory the master key used for protecting secrets." \
+)
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ g_secret_storage_lock(storage);
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Chiffre des données avec la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *data_in; /* Données d'entrée à chiffrer */
+ Py_ssize_t size_in; /* Quantité de ces données */
+ int ret; /* Bilan de lecture des args. */
+ sized_binary_t in; /* Données à chiffer */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+ sized_binary_t out; /* Données chiffrées */
+
+#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \
+( \
+ encrypt_data, "data", \
+ METH_VARARGS, py_secret_storage, \
+ "Encrypt data using an unlocked the master key.\n" \
+ "\n" \
+ "The *data* arguement points to bytes to process." \
+ "\n" \
+ "The result is either encrypted *data* as bytes in case of success,"\
+ " or *None* in case of failure." \
+)
+
+ ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in);
+ if (!ret) return NULL;
+
+ in.static_data = data_in;
+ in.size = size_in;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_encrypt_data(storage, &in, &out);
+
+ if (status)
+ {
+ result = PyBytes_FromStringAndSize(out.static_data, out.size);
+ exit_sized_binary(&out);
+ }
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Déchiffre des données avec la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *data_in; /* Données d'entrée à chiffrer */
+ Py_ssize_t size_in; /* Quantité de ces données */
+ int ret; /* Bilan de lecture des args. */
+ sized_binary_t in; /* Données à chiffer */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+ sized_binary_t out; /* Données chiffrées */
+
+#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \
+( \
+ decrypt_data, "data", \
+ METH_VARARGS, py_secret_storage, \
+ "Decrypt data using an unlocked the master key.\n" \
+ "\n" \
+ "The *data* arguement points to bytes to process." \
+ "\n" \
+ "The result is either decrypted *data* as bytes in case of success,"\
+ " or *None* in case of failure." \
+)
+
+ ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in);
+ if (!ret) return NULL;
+
+ in.static_data = data_in;
+ in.size = size_in;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_decrypt_data(storage, &in, &out);
+
+ if (status)
+ {
+ result = PyBytes_FromStringAndSize(out.static_data, out.size);
+ exit_sized_binary(&out);
+ }
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Détermine si une clef de chiffrement protégée est en place. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_has_key(PyObject *self, void *closure)
+{
+ PyObject *result; /* Instance Python à retourner */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de consultation */
+
+#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \
+( \
+ key, py_secret_storage, \
+ "Indicate if a master key used for protecting secrets seems to have"\
+ " been defined. Without any unlocking attempt, the test only relies"\
+ " on the length of saved master data.\n" \
+ "\n" \
+ "The returned status is a boolean status: *True* if the master key" \
+ " seems to exist, *False* otherwise." \
+)
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_has_key(storage);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Détermine si la clef de chiffrement maître est vérouillée. *
+* *
+* Retour : Bilan de la détermination. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure)
+{
+ PyObject *result; /* Instance Python à retourner */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de consultation */
+
+#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \
+( \
+ locked, py_secret_storage, \
+ "Indicate if the master key used for protecting secrets is" \
+ " currently decrypted in memory.\n" \
+ "\n" \
+ "The *settings* arguement must point to a GSettings intance; the" \
+ " main configuration settings are used by default.\n" \
+ "\n" \
+ "The returned status is a boolean status: *True* if the master key" \
+ " is unlocked and ready for use, *False* otherwise." \
+)
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_is_locked(storage);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_secret_storage_type(void)
+{
+ static PyMethodDef py_secret_storage_methods[] = {
+ SECRET_STORAGE_SET_PASSWORD_METHOD,
+ SECRET_STORAGE_UNLOCK_METHOD,
+ SECRET_STORAGE_LOCK_METHOD,
+ SECRET_STORAGE_ENCRYPT_DATA_METHOD,
+ SECRET_STORAGE_DECRYPT_DATA_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_secret_storage_getseters[] = {
+ PY_SECRET_STORAGE_HAS_KEY_ATTRIB,
+ SECRET_STORAGE_IS_LOCKED_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_secret_storage_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.SecretStorage",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = SECRET_STORAGE_DOC,
+
+ .tp_methods = py_secret_storage_methods,
+ .tp_getset = py_secret_storage_getseters,
+
+ .tp_init = py_secret_storage_init,
+ .tp_new = py_secret_storage_new,
+
+ };
+
+ return &py_secret_storage_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_secret_storage_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'SecretStorage' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_secret_storage_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en gardien des secrets avec stockage. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_secret_storage(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to secret storage");
+ break;
+
+ case 1:
+ *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h
new file mode 100644
index 0000000..68726c3
--- /dev/null
+++ b/plugins/pychrysalide/glibext/secstorage.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h"
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_secret_storage_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */
+bool ensure_python_secret_storage_is_registered(void);
+
+/* Tente de convertir en gardien des secrets avec stockage. */
+int convert_to_secret_storage(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */
diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c
index 8491473..8712506 100644
--- a/plugins/pychrysalide/glibext/singleton.c
+++ b/plugins/pychrysalide/glibext/singleton.c
@@ -30,7 +30,6 @@
#include <glibext/singleton-int.h>
-#include <plugins/dt.h>
#include "../access.h"
@@ -42,7 +41,7 @@
/* Procède à l'initialisation de l'interface de candidature. */
-static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *);
+static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *, gpointer *);
/* Fournit une liste de candidats embarqués par un candidat. */
static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *);
@@ -50,37 +49,27 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper
/* Met à jour une liste de candidats embarqués par un candidat. */
static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t);
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *);
-
-/* Détermine si deux candidats à l'unicité sont identiques. */
-static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *);
-
/* Marque un candidat comme figé. */
-static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *);
+static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *);
/* Indique si le candidat est figé. */
-static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *);
-
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *);
-
-/* Fournit une liste de candidats embarqués par un candidat. */
-static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *);
+static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *);
/* Indique si le candidat est figé. */
-static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *);
+static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *);
+
+/* Crée une copie modifiable d'un object unique. */
+static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *);
-/* Effectue une comparaison avec un objet 'SingletonCandidate'. */
-static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int);
+/* Crée une copie modifiable d'un object unique. */
+static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *);
/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */
-/* Accompagne la création d'une instance dérivée en Python. */
-static PyObject *py_singleton_factory_new(PyTypeObject *, PyObject *, PyObject *);
+CREATE_DYN_CONSTRUCTOR(singleton_factory, G_TYPE_SINGLETON_FACTORY);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_singleton_factory_init(PyObject *, PyObject *, PyObject *);
@@ -108,37 +97,44 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *);
* *
******************************************************************************/
-static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused)
+static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *iface, gpointer *unused)
{
#define SINGLETON_CANDIDATE_DOC \
"The SingletonCandidate class is a required interface for objects" \
" aiming at becoming singleton instances. All shared singletons are" \
" registered within a pychrysalide.glibext.SingletonFactory object.\n" \
"\n" \
+ "Implementations of the pychrysalide.glibext.HashableObject and" \
+ " pychrysalide.glibext.ComparableObject interfaces are required for" \
+ " types implementing the SingletonCandidate interface.\n" \
+ "\n" \
"The main implemantations come with types derived from" \
- " pychrysalide.analysis.DataType.\n" \
+ " pychrysalide.analysis.DataType (with possible recursivity) or from" \
+ " pychrysalide.arch.ArchOperand.\n" \
"\n" \
"A typical class declaration for a new implementation looks like:\n" \
"\n" \
- " class NewImplem(GObject.Object, SingletonCandidate):\n" \
+ " class NewImplem(GObject.Object, HashableObject, ComparableObject," \
+ " SingletonCandidate):\n" \
" ...\n" \
"\n" \
"The following methods have to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \
+ "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \
+ "* pychrysalide.glibext.SingletonCandidate._dup().\n" \
+ "\n" \
+ "The following methods may bbe defined for new implementations if" \
+ " inner SingletonCandidate objets are carried:\n" \
"* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \
- "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\
- "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \
- "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \
- "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \
- "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n"
+ "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n"
iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper;
iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper;
- iface->hash = py_singleton_candidate___hash__wrapper;
- iface->is_equal = py_singleton_candidate___eq__wrapper;
+ iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper;
+ iface->is_ro = py_singleton_candidate_is_read_only_wrapper;
- iface->set_ro = py_singleton_candidate_set_ro_wrapper;
- iface->is_ro = py_singleton_candidate_is_ro_wrapper;
+ iface->dup = py_singleton_candidate_dup_wrapper;
}
@@ -313,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
* *
-* Description : Fournit l'empreinte d'un candidat à une centralisation. *
+* Description : Marque un candidat comme figé. *
* *
-* Retour : Empreinte de l'élément représenté. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate)
+static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate)
{
- guint result; /* Empreinte à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan de consultation */
-#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- __hash__, "$self, /", \
- METH_NOARGS, \
- "Abstract method used to produce a hash of the object.\n" \
- "\n" \
- "The result must be an integer value up to 64 bits." \
- "\n" \
- "Inner instances which are listed through the" \
- " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \
- " method do not need to get processed here as they are handled" \
- " automatically by the interface core." \
+#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _mark_as_read_only, "$self", \
+ METH_NOARGS, \
+ "Abstract method used to seal the object as unmodifiable.\n" \
+ "\n" \
+ "No result is expected." \
)
- result = 0;
-
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(candidate));
- if (has_python_method(pyobj, "__hash__"))
- {
- pyret = run_python_method(pyobj, "__hash__", NULL);
+ pyret = run_python_method(pyobj, "_mark_as_read_only", NULL);
- if (pyret != NULL)
- {
- if (PyLong_Check(pyret))
- result = PyLong_AsUnsignedLongMask(pyret);
-
- Py_DECREF(pyret);
-
- }
-
- }
+ Py_XDECREF(pyret);
Py_DECREF(pyobj);
PyGILState_Release(gstate);
- return result;
-
}
/******************************************************************************
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
-* other = second élément à analyser. *
* *
-* Description : Détermine si deux candidats à l'unicité sont identiques. *
+* Description : Indique si le candidat est figé. *
* *
-* Retour : Bilan de la comparaison. *
+* Retour : true si le contenu du candidat ne peut plus être modifié. *
* *
* Remarques : - *
* *
******************************************************************************/
-static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other)
+static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate)
{
- guint result; /* Empreinte à retourner */
+ bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
- PyObject *args; /* Arguments pour l'appel */
PyObject *pyret; /* Bilan de consultation */
-#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- __eq__, "$self, other, /", \
- METH_NOARGS, \
- "Abstract method used to provide the *__eq__* method for" \
- " rich comparison.\n" \
- "\n" \
- "The expected result is a boolean value." \
+#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _is_read_only, "$self", \
+ METH_NOARGS, \
+ "Abstract method used to provide the state of the object: are" \
+ " its properties frozen (*True*) or can it be modified" \
+ " (*False*)?\n" \
+ "\n" \
+ "The result has to be a boolean status.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " a boolean." \
)
- result = 0;
+ result = false;
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(candidate));
- if (has_python_method(pyobj, "__eq__"))
- {
- args = PyTuple_New(1);
- PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other)));
-
- pyret = run_python_method(pyobj, "__eq__", args);
-
- if (pyret != NULL)
- {
- if (PyLong_Check(pyret))
- result = PyLong_AsUnsignedLong(pyret);
+ pyret = run_python_method(pyobj, "_is_read_only", NULL);
- Py_DECREF(pyret);
-
- }
+ if (pyret != NULL)
+ {
+ if (PyBool_Check(pyret))
+ result = (pyret == Py_True);
- Py_DECREF(args);
+ else
+ PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value"));
}
+ Py_XDECREF(pyret);
+
Py_DECREF(pyobj);
PyGILState_Release(gstate);
@@ -442,80 +413,38 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
* *
-* Description : Marque un candidat comme figé. *
+* Description : Crée une copie modifiable d'un object unique. *
* *
-* Retour : - *
+* Retour : Nouvelle instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate)
+static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate)
{
+ GSingletonCandidate *result; /* Instance à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan de consultation */
+ PyObject *state; /* Validation du mode */
-#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \
+#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \
( \
- _set_read_only, "$self, /", \
+ _dup, "$self", \
METH_NOARGS, \
- "Abstract method used to mark the content of a singleton" \
- " candidate as read-only.\n" \
+ "Abstract method used to create a copy of the object. This" \
+ " has to be able to get modified (ie. its" \
+ " pychrysalide.glibext.SingletonCandidate.read_only status" \
+ " has to be *False*).\n" \
"\n" \
- "The read-only state is mandatory once the candidate is" \
- " registered inside a pychrysalide.glibext.SingletonFactory"\
- " instance as official singleton." \
-)
-
- gstate = PyGILState_Ensure();
-
- pyobj = pygobject_new(G_OBJECT(candidate));
-
- if (has_python_method(pyobj, "_set_read_only"))
- {
- pyret = run_python_method(pyobj, "_set_read_only", NULL);
-
- Py_XDECREF(pyret);
-
- }
-
- Py_DECREF(pyobj);
-
- PyGILState_Release(gstate);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : candidate = objet dont l'instance se veut unique. *
-* *
-* Description : Indique si le candidat est figé. *
-* *
-* Retour : true si le contenu du candidat ne peut plus être modifié. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *candidate)
-{
- bool result; /* Etat à retourner */
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- PyObject *pyobj; /* Objet Python concerné */
- PyObject *pyret; /* Bilan de consultation */
-
-#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _is_read_only, "$self, /", \
- METH_NOARGS, \
- "Abstract method used to retrieve the status of the data" \
- " contained by a singleton candidate.\n" \
+ "The result has to be a new intance of type(self).\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the type of the" \
+ " return value is different from the type of self.\n" \
"\n" \
- "The retured value is *True* if the candidate is" \
- " registered inside a pychrysalide.glibext.SingletonFactory"\
- " instance as official singleton, *False* otherwise." \
+ "A *ValueError* exception is raised of the return object" \
+ " is in read-only mode." \
)
result = false;
@@ -524,16 +453,32 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand
pyobj = pygobject_new(G_OBJECT(candidate));
- if (has_python_method(pyobj, "_is_read_only"))
+ pyret = run_python_method(pyobj, "_dup", NULL);
+
+ if (pyret != NULL)
{
- pyret = run_python_method(pyobj, "_is_read_only", NULL);
+ if (Py_TYPE(pyret) != Py_TYPE(pyobj))
+ PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type"));
- result = (pyret == Py_True);
+ else
+ {
+ state = py_singleton_candidate_is_read_only(pyret, NULL);
- Py_XDECREF(pyret);
+ if (state != NULL)
+ {
+ if (state != Py_False)
+ PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode"));
+
+ Py_DECREF(state);
+
+ }
+
+ }
}
+ Py_XDECREF(pyret);
+
Py_DECREF(pyobj);
PyGILState_Release(gstate);
@@ -545,97 +490,51 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand
/******************************************************************************
* *
-* Paramètres : self = objet dont l'instance se veut unique. *
+* Paramètres : self = objet manipulé ici. *
* args = adresse non utilisée ici. *
* *
-* Description : Fournit l'empreinte d'un candidat à une centralisation. *
+* Description : Crée une copie modifiable d'un object unique. *
* *
-* Retour : Empreinte de l'élément représenté. *
+* Retour : Nouvelle instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args)
+static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args)
{
PyObject *result; /* Emplacement à retourner */
GSingletonCandidate *candidate; /* Mécanismes natifs */
- guint hash; /* Valeur d'empreitne */
+ GSingletonCandidate *copy; /* Copie des mécanismes natifs */
-#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \
-( \
- hash, "$self", \
- METH_NOARGS, py_singleton_candidate, \
- "Compute the hash value of the singleton candidate.\n" \
- "\n" \
- "The method relies on the interface core to include in the" \
- " process the optional embedded instances which may become" \
- " singletons.\n" \
- "\n" \
- "The result is an integer value.\n" \
- "\n" \
- "Even if the Python *hash()* method, relying on the" \
- " pychrysalide.glibext.SingletonCandidate.__hash__()" \
- " implementation, provides values up to 64 bits, the final" \
- " hashes processed by the native GLib hash methods are" \
- " limited to 32 bits values." \
+#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \
+( \
+ dup, "$self", \
+ METH_NOARGS, py_singleton_candidate, \
+ "Create a copy of the object. This has to be able to get" \
+ " modified (ie. its" \
+ " pychrysalide.glibext.SingletonCandidate.read_only status" \
+ " has to be *False*).\n" \
+ "\n" \
+ "The result has to be a new intance of type(self)." \
)
candidate = G_SINGLETON_CANDIDATE(pygobject_get(self));
- hash = g_singleton_candidate_hash(candidate);
+ copy = g_singleton_candidate_dup(candidate);
- result = PyLong_FromUnsignedLong(hash);
+ if (copy == NULL)
+ result = NULL;
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : self = objet Python concerné par l'appel. *
-* closure = non utilisé ici. *
-* *
-* Description : Fournit une liste de candidats embarqués par un candidat. *
-* *
-* Retour : Liste de candidats internes, vide si aucun. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure)
-{
- PyObject *result; /* Valeur à retourner */
- GSingletonCandidate *candidate; /* Mécanismes natifs */
- size_t count; /* Quantité d'objets internes */
- GSingletonCandidate **instances; /* Liste des embarqués */
- size_t i; /* Boucle de parcours */
-
-#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \
-( \
- inner_instances, py_singleton_candidate, \
- "List of optional internal singleton candidate instances.\n" \
- "\n" \
- "The result has to be a tuple containing zero or more" \
- " pychrysalide.glibext.SingletonCandidate instances." \
-)
-
- candidate = G_SINGLETON_CANDIDATE(pygobject_get(self));
-
- instances = g_singleton_candidate_list_inner_instances(candidate, &count);
+ else
+ {
+ result = pygobject_new(G_OBJECT(candidate));
- result = PyTuple_New(count);
+ unref_object(copy);
- for (i = 0; i < count; i++)
- {
- PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i])));
- g_object_unref(G_OBJECT(instances[i]));
}
- if (instances != NULL)
- free(instances);
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
return result;
@@ -649,87 +548,33 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void
* *
* Description : Indique si le candidat est figé. *
* *
-* Retour : true si le contenu du candidat ne peut plus être modifié. *
+* Retour : True si le contenu du candidat ne peut plus être modifié. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure)
+static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GSingletonCandidate *candidate; /* Mécanismes natifs */
- bool status; /* Etat de l'élément consulté */
+ bool state; /* Etat de l'objet courant */
-#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \
-( \
- read_only, py_singleton_candidate, \
- "State of the singleton candidate content.\n" \
- "\n" \
- "The result is a boolean: *True* if the object is registered" \
- " as singleton, *False* otherwise.\n" \
- "\n" \
- "Once a singleton, the object must not change its content as" \
- " it is a shared instance." \
+#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \
+( \
+ read_only, py_singleton_candidate, \
+ "Boolean state of the object: *True* if all its properties are" \
+ " frozen, *False* if the object can be modified." \
)
candidate = G_SINGLETON_CANDIDATE(pygobject_get(self));
- status = g_singleton_candidate_is_read_only(candidate);
+ state = g_singleton_candidate_is_read_only(candidate);
- result = status ? Py_True : Py_False;
+ result = state ? Py_True : Py_False;
Py_INCREF(result);
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : a = premier object Python à consulter. *
-* b = second object Python à consulter. *
-* op = type de comparaison menée. *
-* *
-* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op)
-{
- PyObject *result; /* Bilan à retourner */
- int ret; /* Bilan de lecture des args. */
- GSingletonCandidate *cand_a; /* Premier élément à traiter */
- GSingletonCandidate *cand_b; /* Second élément à traiter */
- gboolean status; /* Résultat d'une comparaison */
-
- if (op != Py_EQ)
- {
- result = Py_NotImplemented;
- goto cmp_done;
- }
-
- ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type());
- if (!ret)
- {
- result = Py_NotImplemented;
- goto cmp_done;
- }
-
- cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a));
- cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b));
-
- status = g_singleton_candidate_is_equal(cand_a, cand_b);
-
- result = (status ? Py_True : Py_False);
-
- cmp_done:
-
- Py_INCREF(result);
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
return result;
@@ -753,16 +598,14 @@ PyTypeObject *get_python_singleton_candidate_type(void)
static PyMethodDef py_singleton_candidate_methods[] = {
SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER,
SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER,
- SINGLETON_CANDIDATE_HASH_WRAPPER,
- SINGLETON_CANDIDATE_EQ_WRAPPER,
- SINGLETON_CANDIDATE_SET_RO_WRAPPER,
- SINGLETON_CANDIDATE_IS_RO_WRAPPER,
- SINGLETON_CANDIDATE_HASH_METHOD,
+ SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER,
+ SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER,
+ SINGLETON_CANDIDATE_DUP_WRAPPER,
+ SINGLETON_CANDIDATE_DUP_METHOD,
{ NULL }
};
static PyGetSetDef py_singleton_candidate_getseters[] = {
- SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB,
SINGLETON_CANDIDATE_READ_ONLY_ATTRIB,
{ NULL }
};
@@ -778,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void)
.tp_doc = SINGLETON_CANDIDATE_DOC,
- .tp_richcompare = py_singleton_candidate_richcompare,
-
.tp_methods = py_singleton_candidate_methods,
.tp_getset = py_singleton_candidate_getseters
@@ -804,7 +645,7 @@ PyTypeObject *get_python_singleton_candidate_type(void)
bool ensure_python_singleton_candidate_is_registered(void)
{
- PyTypeObject *type; /* Type Python 'BinContent' */
+ PyTypeObject *type; /* Type 'SingletonCandidate' */
PyObject *module; /* Module à recompléter */
PyObject *dict; /* Dictionnaire du module */
@@ -887,66 +728,6 @@ int convert_to_singleton_candidate(PyObject *arg, void *dst)
/******************************************************************************
* *
-* Paramètres : type = type du nouvel objet à mettre en place. *
-* args = éventuelle liste d'arguments. *
-* kwds = éventuel dictionnaire de valeurs mises à disposition. *
-* *
-* Description : Accompagne la création d'une instance dérivée en Python. *
-* *
-* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_singleton_factory_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
- PyObject *result; /* Objet à retourner */
- PyTypeObject *base; /* Type de base à dériver */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
-
- /* Validations diverses */
-
- base = get_python_singleton_factory_type();
-
- if (type == base)
- goto simple_way;
-
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(G_TYPE_SINGLETON_FACTORY, type->tp_name, NULL, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
- /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
-
- simple_way:
-
- result = PyType_GenericNew(type, args, kwds);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : self = objet à initialiser (théoriquement). *
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
@@ -973,11 +754,6 @@ static int py_singleton_factory_init(PyObject *self, PyObject *args, PyObject *k
"\n" \
"The first processed instance defines the type handled by the factory."
- /* Récupération des paramètres */
-
- ret = PyArg_ParseTuple(args, "");
- if (!ret) return -1;
-
/* Initialisation d'un objet GLib */
ret = forward_pygobjet_init(self);
diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c
new file mode 100644
index 0000000..a6de0f0
--- /dev/null
+++ b/plugins/pychrysalide/glibext/strbuilder.c
@@ -0,0 +1,542 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c"
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "strbuilder.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/strbuilder-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Procède à l'initialisation de l'interface d'exportation. */
+static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *);
+
+/* Exporte une chaîne de caractères à partir d'un objet. */
+static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Transmet la description d'un objet définie par son parent. */
+static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *);
+
+/* Exporte une chaîne de caractères à partir d'un objet. */
+static PyObject *py_string_builder_to_string(PyObject *, PyObject *);
+
+/* Fournit une représentation de l'objet exportable. */
+static PyObject *py_string_builder_str(PyObject *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* unused = adresse non utilisée ici. *
+* *
+* Description : Procède à l'initialisation de l'interface d'exportation. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpointer *unused)
+{
+#define STRING_BUILDER_DOC \
+ "The StringBuilder class defines a interface for native objects aiming" \
+ " at providing a string representation of themselves.\n" \
+ "\n" \
+ "A typical class declaration for a new implementation looks like:\n" \
+ "\n" \
+ " class NewImplem(GObject.Object, StringBuilder):\n" \
+ " ...\n" \
+ "\n" \
+ "The following method has to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.StringBuilder._to_string().\n"
+
+ iface->to_string = py_string_builder_to_string_wrapper;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : builder = objet dont l'instance est exportable. *
+* flags = éventuelles indications pour l'opération. *
+* out = chaîne de caractères mise en place. [OUT] *
+* *
+* Description : Exporte une chaîne de caractères à partir d'un objet. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après *
+* usage. *
+* *
+******************************************************************************/
+
+static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out)
+{
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *args; /* Arguments pour l'appel */
+ PyObject *pyret; /* Bilan de consultation */
+ const char *utf8; /* Chaîne UTF-8 portée */
+ Py_ssize_t size; /* Taille de ces données */
+
+#define STRING_BUILDER_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _to_string, "$self, /, flags=0", \
+ METH_VARARGS, \
+ "Abstract method providing a string representation for the" \
+ " object which is used as the default implementation of the" \
+ " __repr__() method.\n" \
+ "\n" \
+ "The optional *flags* argument define hints for the operation" \
+ " (for instance the Intel or AT&T flavor for x86 assembly).\n" \
+ "\n" \
+ "The result has to be a string." \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " a string." \
+)
+
+ result = false;
+
+ gstate = PyGILState_Ensure();
+
+ pyobj = pygobject_new(G_OBJECT(builder));
+
+ args = PyTuple_New(1);
+ PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags));
+
+ pyret = run_python_method(pyobj, "_to_string", args);
+
+ if (pyret != NULL)
+ {
+ if (PyUnicode_Check(pyret))
+ {
+ utf8 = PyUnicode_AsUTF8AndSize(pyret, &size);
+
+ if (utf8 != NULL)
+ {
+ assert(size >= 0);
+
+ add_to_sized_binary(out, utf8, size);
+ result = true;
+
+ }
+
+ }
+
+ if (!result)
+ PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value"));
+
+ }
+
+ Py_XDECREF(pyret);
+
+ Py_DECREF(args);
+
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet dont l'instance se veut unique. *
+* args = adresse non utilisée ici. *
+* *
+* Description : Transmet la description d'un objet définie par son parent. *
+* *
+* Retour : Présentation de l'élément construite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ unsigned int flags; /* Eventuelles indications */
+ int ret; /* Bilan de lecture des args. */
+ GStringBuilder *builder; /* Mécanismes natifs */
+ GStringBuilderInterface *iface; /* Interface utilisée */
+ GStringBuilderInterface *parent_iface; /* Interface parente */
+ sized_binary_t out; /* Description construite */
+ bool status; /* Bilan de l'opération */
+
+#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \
+( \
+ parent_to_string, "$self, /, flags=0", \
+ METH_VARARGS, py_string_builder, \
+ "Provide a string representation defined by the interface" \
+ " implementation from the object native parent.\n" \
+ "\n" \
+ "The result is a string.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the object parent does" \
+ " not implement the pychrysalide.glibext.StringBuilder" \
+ " interface." \
+ "\n" \
+ "A *RuntimeError* exception is raised if the direct parent type"\
+ " of the object has not a native implementation. For Python" \
+ " implementations, the super()._to_string() function has to be" \
+ " used instead.\n" \
+ "\n" \
+ "A *BufferError* exception is raised if the description has" \
+ " not been able to get created." \
+)
+
+ if (!check_for_native_parent(self))
+ return NULL;
+
+ flags = 0;
+
+ ret = PyArg_ParseTuple(args, "|I", &flags);
+ if (!ret) return NULL;
+
+ builder = G_STRING_BUILDER(pygobject_get(self));
+
+ iface = G_STRING_BUILDER_GET_IFACE(builder);
+
+ parent_iface = g_type_interface_peek_parent(iface);
+
+ if (parent_iface == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface"));
+
+ result = NULL;
+
+ }
+ else
+ {
+ init_sized_binary(&out);
+
+ status = parent_iface->to_string(builder, flags, &out);
+
+ if (status)
+ result = PyUnicode_FromStringAndSize(out.data, out.size);
+
+ else
+ {
+ result = NULL;
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_BufferError, _("unable to create a description"));
+
+ }
+
+ exit_sized_binary(&out);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet manipulé ici. *
+* args = adresse non utilisée ici. *
+* *
+* Description : Exporte une chaîne de caractères à partir d'un objet. *
+* *
+* Retour : Présentation de l'élément construite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Emplacement à retourner */
+ unsigned int flags; /* Eventuelles indications */
+ int ret; /* Bilan de lecture des args. */
+ GStringBuilder *builder; /* Mécanismes natifs */
+ sized_binary_t out; /* Description construite */
+ bool status; /* Bilan de l'opération */
+
+#define STRING_BUILDER_TO_STRING_METHOD PYTHON_METHOD_DEF \
+( \
+ to_string, "$self, /, flags=0", \
+ METH_VARARGS, py_string_builder, \
+ "Provide a string representation for the object which is used" \
+ " as the default implementation of the __repr__() method.\n" \
+ "\n" \
+ "The optional *flags* argument define hints for the operation" \
+ " (for instance the Intel or AT&T flavor for x86 assembly).\n" \
+ "\n" \
+ "The result is a string.\n" \
+ "\n" \
+ "A *BufferError* exception is raised if the description has" \
+ " not been able to get created." \
+)
+
+ flags = 0;
+
+ ret = PyArg_ParseTuple(args, "|I", &flags);
+ if (!ret) return NULL;
+
+ builder = G_STRING_BUILDER(pygobject_get(self));
+
+ init_sized_binary(&out);
+
+ status = g_string_builder_to_string(builder, flags, &out);
+
+ if (status)
+ result = PyUnicode_FromStringAndSize(out.data, out.size);
+
+ else
+ {
+ result = NULL;
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_BufferError, _("unable to create a description"));
+
+ }
+
+ exit_sized_binary(&out);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet manipulé ici. *
+* *
+* Description : Fournit une représentation de l'objet exportable. *
+* *
+* Retour : Présentation de l'élément construite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_string_builder_str(PyObject *self)
+{
+ PyObject *result; /* Emplacement à retourner */
+ GStringBuilder *builder; /* Mécanismes natifs */
+ sized_binary_t out; /* Description construite */
+ bool status; /* Bilan de l'opération */
+
+ builder = G_STRING_BUILDER(pygobject_get(self));
+
+ init_sized_binary(&out);
+
+ status = g_string_builder_to_string(builder, 0, &out);
+
+ if (status)
+ result = PyUnicode_FromStringAndSize(out.data, out.size);
+
+ else
+ {
+ result = NULL;
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_BufferError, _("unable to create a description"));
+
+ }
+
+ exit_sized_binary(&out);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_string_builder_type(void)
+{
+ static PyMethodDef py_string_builder_methods[] = {
+ STRING_BUILDER_TO_STRING_WRAPPER,
+ STRING_BUILDER_PARENT_TO_STRING_METHOD,
+ STRING_BUILDER_TO_STRING_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_string_builder_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_string_builder_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.StringBuilder",
+ .tp_basicsize = sizeof(PyObject),
+
+ .tp_str = py_string_builder_str,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = STRING_BUILDER_DOC,
+
+ .tp_methods = py_string_builder_methods,
+ .tp_getset = py_string_builder_getseters
+
+ };
+
+ return &py_string_builder_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_string_builder_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'StringBuilder' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ static GInterfaceInfo info = { /* Paramètres d'inscription */
+
+ .interface_init = (GInterfaceInitFunc)py_string_builder_interface_init,
+ .interface_finalize = NULL,
+ .interface_data = NULL,
+
+ };
+
+ type = get_python_string_builder_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_interface_for_pygobject(dict, G_TYPE_STRING_BUILDER, type, &info))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en interface d'exportation en chaîne. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_string_builder(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_string_builder_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to string builder");
+ break;
+
+ case 1:
+ *((GStringBuilder **)dst) = G_STRING_BUILDER(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h
new file mode 100644
index 0000000..1881cae
--- /dev/null
+++ b/plugins/pychrysalide/glibext/strbuilder.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h"
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_string_builder_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'. */
+bool ensure_python_string_builder_is_registered(void);
+
+/* Tente de convertir en interface d'exportation en chaîne. */
+int convert_to_string_builder(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H */
diff --git a/plugins/pychrysalide/glibext/work.c b/plugins/pychrysalide/glibext/work.c
index 6a15984..e6791e3 100644
--- a/plugins/pychrysalide/glibext/work.c
+++ b/plugins/pychrysalide/glibext/work.c
@@ -41,9 +41,9 @@
/* Initialise la classe des travaux programmés. */
-static void py_generic_work_init_gclass(GGenericWorkClass *, gpointer);
+static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK, py_generic_work_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_generic_work_init(PyObject *, PyObject *, PyObject *);
@@ -68,20 +68,22 @@ static PyObject *py_generic_work_process(PyObject *, PyObject *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des travaux programmés. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_generic_work_init_gclass(GGenericWorkClass *class, gpointer unused)
+static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass)
{
- class->run = py_generic_work_run_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper);
+
+ return 0;
}
@@ -296,6 +298,8 @@ bool ensure_python_generic_work_is_registered(void)
dict = PyModule_GetDict(module);
+ pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type))
return false;
diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c
index d8126be..ca6c73c 100644
--- a/plugins/pychrysalide/glibext/workqueue.c
+++ b/plugins/pychrysalide/glibext/workqueue.c
@@ -94,9 +94,9 @@ static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds)
{
int ret; /* Bilan de lecture des args. */
-#define WORK_QUEUE_DOC \
- "WorkQueue defines a basic work aimed to get processed in a" \
- " thread setup by a pychrysalide.glibext.WorkQueue instance.\n" \
+#define WORK_QUEUE_DOC \
+ "WorkQueue creates threads in order to process" \
+ " pychrysalide.glibext.Work instances.\n" \
"\n" \
"Instances can be created using the following constructor:\n" \
"\n" \
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index d361c3e..0c84278 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* helpers.c - simplification des interactions de base avec Python
*
- * Copyright (C) 2018-2020 Cyrille Bagard
+ * Copyright (C) 2018-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -39,7 +39,6 @@
#include <i18n.h>
#include <common/extstr.h>
-#include <plugins/dt.h>
#include "access.h"
@@ -217,6 +216,53 @@ bool has_python_method(PyObject *module, const char *method)
* *
* Paramètres : target = propriétaire de la routine visée. *
* method = désignation de la fonction à appeler. *
+* *
+* Description : Indique si une routine Python possède une implémentation. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool has_python_implementation_method(PyObject *module, const char *method)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *func; /* Fonction visée */
+ const PyMethodDef *def; /* Définition de la fonction */
+
+ result = (PyObject_HasAttrString(module, method) == 1);
+
+ if (result)
+ {
+ func = PyObject_GetAttrString(module, method);
+ assert(func != NULL);
+
+ result = PyCallable_Check(func);
+
+ if (func->ob_type == &PyCFunction_Type)
+ {
+ def = ((PyCFunctionObject *)func)->m_ml;
+
+ assert(strcmp(def->ml_name, method) == 0);
+
+ result = (def->ml_meth != not_yet_implemented_method);
+
+ }
+
+ Py_DECREF(func);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : target = propriétaire de la routine visée. *
+* method = désignation de la fonction à appeler. *
* args = arguments à associer à l'opération. *
* *
* Description : Appelle une routine Python. *
@@ -517,75 +563,10 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type)
* *
******************************************************************************/
-PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds)
-{
- PyObject *result; /* Objet à retourner */
- PyTypeObject *base; /* Type parent version Python */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
-
- /* Validations diverses */
-
- base = pygobject_lookup_class(gbase);
-
- if (type == base)
- goto simple_way;
-
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(gbase, type->tp_name, NULL, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
- /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
-
- simple_way:
-
- result = PyType_GenericNew(type, args, kwds);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : type = type du nouvel objet à mettre en place. *
-* gbase = type de base natif. *
-* cinit = procédure d'initialisation de la classe associée. *
-* args = éventuelle liste d'arguments. *
-* kwds = éventuel dictionnaire de valeurs mises à disposition.*
-* *
-* Description : Accompagne la création d'une instance dérivée en Python. *
-* *
-* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds)
+PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds)
{
PyObject *result; /* Objet à retourner */
PyTypeObject *base; /* Type parent version Python */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
/* Validations diverses */
@@ -598,24 +579,6 @@ PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GTy
goto exit;
}
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(gbase, type->tp_name, cinit, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
/* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
result = PyType_GenericNew(type, args, kwds);
@@ -1159,6 +1122,60 @@ int forward_pygobjet_init(PyObject *self)
/******************************************************************************
* *
+* Paramètres : type = type Python à ausculter. *
+* *
+* Description : Détermine si un type Python est implémenté en C ou non. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool pytype_has_native_implementation(const PyTypeObject *type)
+{
+ bool result; /* Bilan à retourner */
+ GType gtype; /* Type associé à la classe */
+
+ static GQuark pygobject_custom_key = 0; /* Clef d'accès direct */
+
+ /**
+ * Dans les sources de PyGObject, la fonction pyg_type_register() de
+ * gi/gimodule.c, appelée depuis gi/types.py, contient la bribe de code
+ * suivante :
+ *
+ * // Mark this GType as a custom python type
+ * g_type_set_qdata(instance_type, pygobject_custom_key,
+ * GINT_TO_POINTER (1));
+ *
+ * La fonction pyi_object_register_types() de gi/pygobject-object.c indique
+ * la clef associée au Quark :
+ *
+ * pygobject_custom_key = g_quark_from_static_string("PyGObject::custom");
+ *
+ * Enfin, une fonction inspirante est codée dans le fichier gi/pygi-type.c :
+ *
+ * gboolean pyg_gtype_is_custom(GType gtype)
+ * {
+ * return g_type_get_qdata (gtype, pygobject_custom_key) != NULL;
+ * }
+ *
+ */
+
+ if (pygobject_custom_key == 0)
+ pygobject_custom_key = g_quark_from_static_string("PyGObject::custom");
+
+ gtype = pyg_type_from_object((PyObject *)type);
+
+ result = (g_type_get_qdata(gtype, pygobject_custom_key) == NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : arg = argument quelconque à tenter de convertir. *
* dst = destination des valeurs récupérées en cas de succès. *
* *
@@ -1486,6 +1503,109 @@ int convert_to_gdk_rgba(PyObject *arg, void *dst)
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en tableau de chaînes de caractères. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_sequence_to_charp_array(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+ charp_array_t *array; /* Tableau à constituer */
+ size_t i; /* Boucle de parcours */
+ PyObject *value; /* Valeur brute d'un élément */
+
+ array = (charp_array_t *)dst;
+
+ /* Nettoyage ? */
+ if (arg == NULL)
+ {
+ result = 1;
+ goto clean;
+ }
+
+ else
+ {
+ result = 0;
+
+ if (PySequence_Check(arg) != 1)
+ goto done;
+
+ array->length = PySequence_Length(arg);
+
+ array->values = calloc(array->length, sizeof(char *));
+
+ for (i = 0; i < array->length; i++)
+ {
+ value = PySequence_ITEM(arg, i);
+
+ if (!PyUnicode_Check(value))
+ {
+ Py_DECREF(value);
+ goto clean;
+ }
+
+ array->values[i] = strdup(PyUnicode_DATA(value));
+
+ Py_DECREF(value);
+
+ }
+
+ result = Py_CLEANUP_SUPPORTED;
+
+ }
+
+ done:
+
+ return result;
+
+ clean:
+
+ clean_charp_array(array);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = tableau de chaînes de caractères à traiter. *
+* *
+* Description : Libère de la mémoire un tableau de chaînes de caractères. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void clean_charp_array(charp_array_t *array)
+{
+ size_t i; /* Boucle de parcours */
+
+ for (i = 0; i < array->length; i++)
+ if (array->values[i] != NULL)
+ free(array->values[i]);
+
+ if (array->values != NULL)
+ free(array->values);
+
+ array->values = NULL;
+ array->length = 0;
+
+}
+
+
+
/* ---------------------------------------------------------------------------------- */
/* TRANSFERT DES VALEURS CONSTANTES */
/* ---------------------------------------------------------------------------------- */
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index cc82bba..745d013 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* helpers.h - prototypes pour la simplification des interactions de base avec Python
*
- * Copyright (C) 2018-2020 Cyrille Bagard
+ * Copyright (C) 2018-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -34,6 +34,9 @@
#endif
+#include <i18n.h>
+
+
/* ---------------------- ACCELERATEURS POUR PYTHON UNIQUEMENT ---------------------- */
@@ -47,6 +50,9 @@ int convert_to_callable(PyObject *, void *);
/* Indique si une routine Python existe ou non. */
bool has_python_method(PyObject *, const char *);
+/* Indique si une routine Python possède une implémentation. */
+bool has_python_implementation_method(PyObject *, const char *);
+
/* Appelle une routine Python. */
PyObject *run_python_method(PyObject *, const char *, PyObject *);
@@ -126,7 +132,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
PYTHON_GETSET_DEF("is_" #name, base ## _is_ ## name, NULL, ATTRIB_RO doc, NULL)
#define PYTHON_HAS_DEF_FULL(name, base, doc) \
- PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL)
+ PYTHON_GETSET_DEF("has_" #name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL)
#define PYTHON_RAWGET_DEF_FULL(name, base, doc) \
PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, ATTRIB_RO doc, NULL)
@@ -152,14 +158,11 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
*
* Cf. http://stackoverflow.com/questions/20432335/can-python-abstract-base-classes-inherit-from-c-extensions
*/
-#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new
+#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new // REMME
/* Accompagne la création d'une instance dérivée en Python. */
-PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *, GType, PyObject *, PyObject *);
-
-/* Accompagne la création d'une instance dérivée en Python. */
-PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *, GType, GClassInitFunc, PyObject *, PyObject *);
+PyObject *python_abstract_constructor(PyTypeObject *, GType, PyObject *, PyObject *);
#define CREATE_DYN_CONSTRUCTOR(pyname, gbase) \
@@ -167,22 +170,41 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *);
static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \
{ \
PyObject *result; /* Objet à retourner */ \
- result = python_constructor_with_dynamic_gtype(type, gbase, args, kwds); \
+ result = PyType_GenericNew(type, args, kwds); \
return result; \
}
-#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase, cinit) \
+#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase) \
static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); \
static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \
{ \
PyObject *result; /* Objet à retourner */ \
- result = python_abstract_constructor_with_dynamic_gtype(type, gbase, (GClassInitFunc)cinit, \
- args, kwds); \
+ result = python_abstract_constructor(type, gbase, args, kwds); \
return result; \
}
+/**
+ * Les initialisations de classes engagées par les appels à pyg_register_class_init()
+ * ne se déclenchent qu'après les initialisations complètes des classes côté GObject.
+ *
+ * Typiquement, pour une déclinaison Python du type PythonModule, sont appelées
+ * successivement les fonctions suivantes :
+ * - g_plugin_module_class_init() ;
+ * - g_python_plugin_class_init() ;
+ * - py_plugin_module_init_gclass().
+ *
+ * Il est alors impératif de considérer les pointeurs de fonction déjà en place
+ * afin de ne par remplacer les implémentations de GPythonPlugin par les
+ * wrappers par défaut de PythonModule.
+ */
+
+#define PY_CLASS_SET_WRAPPER(field, ptr) \
+ if (field == NULL) \
+ field = ptr;
+
+
/* Marque l'interdiction d'une instanciation depuis Python. */
PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *);
@@ -206,6 +228,41 @@ PyTypeObject *define_python_dynamic_type(const PyTypeObject *);
/**
+ * Prise en compte d'éventuelles exceptions levées dans les implémentations.
+ *
+ * Par exemple :
+ * - du code Python exécute une fonction implémentée en C ;
+ * - cette dernière fait appel à un Wrapper C qui sollicite du code
+ * d'implémentation Python.
+ *
+ * Cette seconde étape peut lever une exception (impletation manquante ou
+ * implémentation levant une exception.
+ *
+ * Les codes C des étapes 1 et 2 ne dispose pas de mécanismes pour transmettre
+ * le détail des éventuelles exceptions, mais Python le mémorise.
+ */
+
+#define UPDATE_RESULT_IF_RAISED_EXCEPTION(val) \
+ do \
+ { \
+ if (PyErr_Occurred() != NULL) \
+ result = val; \
+ } \
+ while (0)
+
+#define CLEAN_RESULT_IF_RAISED_EXCEPTION(val) \
+ do \
+ { \
+ if (PyErr_Occurred() != NULL) \
+ { \
+ Py_XDECREF(result); \
+ result = NULL; \
+ } \
+ } \
+ while (0)
+
+
+/**
* pygobject_new() prend en compte les références flottantes au moment de la
* construction d'un objet Python.
*
@@ -231,11 +288,23 @@ bool register_class_for_pygobject(PyObject *, GType, PyTypeObject *);
bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *);
/* Enregistre un type Python dérivant d'un type GLib dynamique. */
-bool register_class_for_dynamic_pygobject(GType, PyTypeObject *);
+bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); // REMME
/* Fait suivre à la partie GObject une initialisation nouvelle. */
int forward_pygobjet_init(PyObject *);
+/* Détermine si un type Python est implémenté en C ou non. */
+bool pytype_has_native_implementation(const PyTypeObject *);
+
+#define check_for_native_parent(obj) \
+ ({ \
+ bool __result; \
+ __result = pytype_has_native_implementation((obj)->ob_type->tp_base); \
+ if (!__result) \
+ PyErr_SetString(PyExc_RuntimeError, _("object parent is not a native type")); \
+ __result; \
+ })
+
/* Tente de convertir en valeur GType. */
int convert_to_gtype(PyObject *, void *);
@@ -283,6 +352,21 @@ int convert_to_gdk_rgba(PyObject *, void *);
#endif
+/* Tableau de chaînes de caractères converti */
+typedef struct _charp_array_t
+{
+ char **values; /* Liste de chaînes textuelles */
+ size_t length; /* Taille de cette liste */
+
+} charp_array_t;
+
+/* Tente de convertir en tableau de chaînes de caractères. */
+int convert_to_sequence_to_charp_array(PyObject *, void *);
+
+/* Libère de la mémoire un tableau de chaînes de caractères. */
+void clean_charp_array(charp_array_t *);
+
+
/* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */
diff --git a/plugins/pychrysalide/plugins/Makefile.am b/plugins/pychrysalide/plugins/Makefile.am
index bb9ed5d..abadb7f 100644
--- a/plugins/pychrysalide/plugins/Makefile.am
+++ b/plugins/pychrysalide/plugins/Makefile.am
@@ -2,10 +2,10 @@
noinst_LTLIBRARIES = libpychrysaplugins.la
libpychrysaplugins_la_SOURCES = \
- constants.h constants.c \
- plugin.h plugin.c \
module.h module.c \
- translate.h translate.c
+ plugin.h plugin.c \
+ python-int.h \
+ python.h python.c
libpychrysaplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
-I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
diff --git a/plugins/pychrysalide/plugins/constants.c b/plugins/pychrysalide/plugins/constants.c
deleted file mode 100644
index 7e20e4c..0000000
--- a/plugins/pychrysalide/plugins/constants.c
+++ /dev/null
@@ -1,147 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * constants.c - ajout des constantes principales
- *
- * Copyright (C) 2020 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include "constants.h"
-
-
-#include <plugins/plugin-def.h>
-
-
-#include "../helpers.h"
-
-
-
-/******************************************************************************
-* *
-* Paramètres : type = type dont le dictionnaire est à compléter. *
-* *
-* Description : Définit les constantes relatives aux greffons Python. *
-* *
-* Retour : true en cas de succès de l'opération, false sinon. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool define_plugin_module_constants(PyTypeObject *type)
-{
- bool result; /* Bilan à retourner */
- PyObject *values; /* Groupe de valeurs à établir */
-
- result = true;
-
- values = PyDict_New();
-
- if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE);
- if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT);
- if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED);
- if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT);
- if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED);
- if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED);
- if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING);
- if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME);
- if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION);
- if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING);
- if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER);
- if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER);
- if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED);
- if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED);
- if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD);
- if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG);
- if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED);
- if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED);
- if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED);
- if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS);
-
- if (!result)
- {
- Py_DECREF(values);
- goto exit;
- }
-
- result = attach_constants_group_to_type(type, true, "PluginAction", values,
- "Features with which plugins can extend the core of Chrysalide.");
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : arg = argument quelconque à tenter de convertir. *
-* dst = destination des valeurs récupérées en cas de succès. *
-* *
-* Description : Tente de convertir en constante PluginAction. *
-* *
-* Retour : Bilan de l'opération, voire indications supplémentaires. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-int convert_to_plugin_action(PyObject *arg, void *dst)
-{
- int result; /* Bilan à retourner */
- unsigned long value; /* Valeur récupérée */
-
- result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
-
- switch (result)
- {
- case -1:
- /* L'exception est déjà fixée par Python */
- result = 0;
- break;
-
- case 0:
- PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to PluginAction");
- break;
-
- case 1:
- value = PyLong_AsUnsignedLong(arg);
- *((PluginAction *)dst) = value;
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- return result;
-
-}
diff --git a/plugins/pychrysalide/plugins/module.c b/plugins/pychrysalide/plugins/module.c
index 1c7b326..ce94611 100644
--- a/plugins/pychrysalide/plugins/module.c
+++ b/plugins/pychrysalide/plugins/module.c
@@ -33,8 +33,8 @@
#include <plugins/pglist.h>
-#include "constants.h"
#include "plugin.h"
+#include "python.h"
#include "../helpers.h"
@@ -46,7 +46,7 @@ static PyObject *py_plugins_get_plugin_by_name(PyObject *, PyObject *);
static PyObject *py_plugins_get_all_plugins(PyObject *, PyObject *);
/* Fournit les greffons offrant le service demandé. */
-static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *);
+//static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *);
@@ -162,7 +162,7 @@ static PyObject *py_plugins_get_all_plugins(PyObject *self, PyObject *args)
}
-
+#if 0
/******************************************************************************
* *
* Paramètres : self = NULL car méthode statique. *
@@ -219,7 +219,7 @@ static PyObject *py_plugins_get_all_plugins_for_action(PyObject *self, PyObject
return result;
}
-
+#endif
/******************************************************************************
* *
@@ -248,7 +248,7 @@ bool add_plugins_module(PyObject *super)
static PyMethodDef py_plugins_methods[] = {
PY_PLUGINS_GET_PLUGIN_BY_NAME_METHOD,
PY_PLUGINS_GET_ALL_PLUGINS_METHOD,
- PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD,
+ //PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD,
{ NULL }
};
@@ -293,6 +293,7 @@ bool populate_plugins_module(void)
result = true;
if (result) result = ensure_python_plugin_module_is_registered();
+ if (result) result = ensure_python_python_plugin_is_registered();
assert(result);
diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c
index de070cb..78f57ba 100644
--- a/plugins/pychrysalide/plugins/plugin.c
+++ b/plugins/pychrysalide/plugins/plugin.c
@@ -25,22 +25,17 @@
#include "plugin.h"
-#include <assert.h>
-#include <libgen.h>
#include <malloc.h>
#include <pygobject.h>
#include <string.h>
-#include <common/extstr.h>
-#include <plugins/dt.h>
+#include <common/compiler.h>
#include <plugins/plugin-int.h>
#include <plugins/pglist.h>
#include <plugins/self.h>
-#include "constants.h"
-#include "translate.h"
#include "../access.h"
#include "../core.h"
#include "../helpers.h"
@@ -52,85 +47,53 @@
/* Initialise la classe des greffons d'extension. */
-static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer);
+static int py_plugin_module_init_gclass(GPluginModuleClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE, py_plugin_module_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds);
-/* Encadre une étape de la vie d'un greffon. */
-static bool py_plugin_module_manage_wrapper(GPluginModule *);
-
-/* Assiste la désactivation d'un greffon. */
-static bool py_plugin_module_exit(GPluginModule *);
-
-/* Accompagne la fin du chargement des modules natifs. */
-static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction);
-
-/* Fournit le nom brut associé au greffon par défaut. */
-static PyObject *py_plugin_module_get_modname_by_default(PyObject *, PyObject *);
+/* Pointe le fichier contenant le greffon manipulé. */
+static char *py_plugin_module_get_filename_wrapper(const GPluginModule *);
/* Fournit le nom brut associé au greffon. */
static char *py_plugin_module_get_modname_wrapper(const GPluginModule *);
-#if 0
-
-#ifdef INCLUDE_GTK_SUPPORT
-
-/* Complète une liste de resources pour thème. */
-static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *);
-
-/* Rend compte de la création d'un panneau. */
-static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *);
-
-/* Rend compte d'un affichage ou d'un retrait de panneau. */
-static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool);
-
-#endif
-
-/* Procède à une opération liée à un contenu binaire. */
-static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
-
-/* Procède à une opération liée à un contenu chargé. */
-static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *);
+/* Prend acte de l'activation du greffon. */
+static bool py_plugin_module_enable_wrapper(GPluginModule *);
-/* Procède à une opération liée à l'analyse d'un format. */
-static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *);
+/* Prend acte de la désactivation du greffon. */
+static bool py_plugin_module_disable_wrapper(GPluginModule *);
-/* Procède à un préchargement de format de fichier. */
-static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *);
-/* Procède au rattachement d'éventuelles infos de débogage. */
-static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *);
-/* Exécute une action pendant un désassemblage de binaire. */
-static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *);
-
-/* Effectue la détection d'effets d'outils externes. */
-static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *);
-
-#endif
+/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */
+/* Affiche un message dans le journal des messages système. */
+static PyObject *py_plugin_module_log_message(PyObject *, PyObject *);
-/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */
+/* Indique le nom associé à un greffon. */
+static PyObject *py_plugin_module_get_name(PyObject *, void *);
+/* Fournit une description fonctionnelle d'un greffon. */
+static PyObject *py_plugin_module_get_desc(PyObject *, void *);
-/* Construit le nom d'un fichier de configuration du greffon. */
-static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *);
+/* Fournit la version d'un greffon et de ses fonctionnalités. */
+static PyObject *py_plugin_module_get_version(PyObject *, void *);
-/* Affiche un message dans le journal des messages système. */
-static PyObject *py_plugin_module_log_message(PyObject *, PyObject *);
+/* Fournit l'URL des ressources en ligne liées à un greffon. */
+static PyObject *py_plugin_module_get_url(PyObject *, void *);
-/* Fournit le nom brut associé au greffon. */
-static PyObject *py_plugin_module_get_modname(PyObject *, void *);
+/* Fournit la liste des dépendances d'un greffon donné. */
+static PyObject *py_plugin_module_get_requirements(PyObject *, void *);
/* Indique le fichier contenant le greffon manipulé. */
static PyObject *py_plugin_module_get_filename(PyObject *, void *);
-/* Fournit la description du greffon dans son intégralité. */
-static PyObject *py_plugin_module_get_interface(PyObject *, void *);
+/* Fournit le nom brut associé au greffon. */
+static PyObject *py_plugin_module_get_modname(PyObject *, void *);
@@ -141,47 +104,26 @@ static PyObject *py_plugin_module_get_interface(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des greffons d'extension. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused)
+static int py_plugin_module_init_gclass(GPluginModuleClass *gclass, PyTypeObject *pyclass)
{
- class->init = NULL;
- class->manage = py_plugin_module_manage_wrapper;
- class->exit = py_plugin_module_exit;
+ PY_CLASS_SET_WRAPPER(gclass->get_filename, py_plugin_module_get_filename_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->get_modname, py_plugin_module_get_modname_wrapper);
- class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->enable, py_plugin_module_enable_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->disable, py_plugin_module_disable_wrapper);
- class->get_modname = py_plugin_module_get_modname_wrapper;
-
-#if 0
-
-#ifdef INCLUDE_GTK_SUPPORT
- class->include_theme = py_plugin_module_include_theme_wrapper;
- class->notify_panel = py_plugin_module_notify_panel_creation_wrapper;
- class->notify_docking = py_plugin_module_notify_panel_docking_wrapper;
-#endif
-
- class->handle_content = py_plugin_module_handle_binary_content_wrapper;
- class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper;
-
- class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper;
- class->preload_format = py_plugin_module_preload_binary_format_wrapper;
- class->attach_debug = py_plugin_module_attach_debug_format_wrapper;
-
- class->process_disass = py_plugin_module_process_disassembly_event_wrapper;
-
- class->detect = py_plugin_module_detect_external_tools_wrapper;
-
-#endif
+ return 0;
}
@@ -202,45 +144,56 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu
static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
{
+ const char *desc; /* Description plus loquace */
+ const char *version; /* Version du greffon */
+ const char *url; /* Site Web associé */
+ const char *name; /* Désignation humaine courte */
+ charp_array_t required; /* Liste des dépendances */
int ret; /* Bilan d'un appel */
GPluginModule *plugin; /* Greffon à manipuler */
- plugin_interface *iface; /* Interface à constituer */
- GPluginModule *dependency; /* Module nécessaire */
- PyObject *value; /* Valeur à présence imposée */
- size_t i; /* Boucle de parcours */
- PyObject *action; /* Identifiant d'une action */
+
+ static char *kwlist[] = { "name", "desc", "version", "url", "required", NULL };
#define PLUGIN_MODULE_DOC \
- "PythonModule is the class allowing the creation of Chrysalide plugins" \
- " for Python." \
+ "PluginModule is the core class handling the Chrysalide plugins." \
+ " Python plugins should inherit the pychrysalide.plugins.PythonPlugin" \
+ " instead of this one." \
"\n" \
"Calls to the *__init__* constructor of this abstract object expect" \
- " no particular argument.\n" \
+ " the following arguments as keyword parameters:\n" \
+ "* *name*: a string providing a key name for the plugin;\n" \
+ "* *desc* (optional): a string for a human readable description of the" \
+ " features provided by the plugin;\n" \
+ "* *version* (optional): a string providing the version of the plugin;" \
+ " Version format is free;\n" \
+ "* *url* (optional): a string for the homepage describing the plugin;\n"\
+ "* *required* (optional): dependencies of the plugin." \
"\n" \
- "Several items have to be defined as class attributes in the final" \
- " class:\n" \
- "* *_name*: a string providing a small name for the plugin;\n" \
- "* *_desc*: a string for a human readable description of the plugin;\n" \
- "* *_version*: a string providing the version of the plugin;\n" \
- "* *_url*: a string for the homepage describing the plugin;\n" \
- "* *_actions*: a tuple of" \
- " pychrysalide.plugins.PluginModule.PluginAction defining the features" \
- " the plugin is bringing; this list can be empty.\n" \
+ "Dependency to the *PyChrysalide* plugin is added automatically if not" \
+ " specified.\n" \
"\n" \
- "Depending on the implemented actions, some of the following methods" \
- " have to be defined for new classes:\n" \
- "* pychrysalide.plugins.PluginModule._init_config();\n" \
- "* pychrysalide.plugins.PluginModule._notify_plugins_loaded();\n" \
- "* pychrysalide.plugins.PluginModule._include_theme();\n" \
- "* pychrysalide.plugins.PluginModule._on_panel_creation;\n" \
- "* pychrysalide.plugins.PluginModule._on_panel_docking();\n" \
- "* pychrysalide.plugins.PluginModule._handle_binary_content();\n" \
- "* pychrysalide.plugins.PluginModule._handle_loaded_content();\n" \
- "* pychrysalide.plugins.PluginModule._handle_format_analysis();\n" \
- "* pychrysalide.plugins.PluginModule._preload_format();\n" \
- "* pychrysalide.plugins.PluginModule._attach_debug_format();\n" \
- "* pychrysalide.plugins.PluginModule._process_disassembly_event();\n" \
- "* pychrysalide.plugins.PluginModule._detect_external_tools()."
+ "The following methods have to be defined for new classes:\n" \
+ "* pychrysalide.plugins.PluginModule._get_filename();\n" \
+ "* pychrysalide.plugins.PluginModule._get_modname();\n" \
+ "* pychrysalide.plugins.PluginModule._enable();\n" \
+ "* pychrysalide.plugins.PluginModule._disable()."
+
+ /* Récupération des paramètres */
+
+ desc = NULL;
+ version = NULL;
+ url = NULL;
+ required.values = NULL;
+ required.length = 0;
+
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|sssO&", kwlist,
+ &name, &desc, &version, &url,
+ convert_to_sequence_to_charp_array, &required);
+ if (!ret) return -1;
+
+ required.values = realloc(required.values, ++required.length * sizeof(char *));
+
+ required.values[required.length - 1] = strdup("PyChrysalide");
/* Initialisation d'un objet GLib */
@@ -251,105 +204,17 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- iface = calloc(1, sizeof(plugin_interface));
- plugin->interface = iface;
-
-#define LOAD_PYTHON_IFACE(attr) \
- do \
- { \
- value = PyObject_GetAttrString(self, "_" #attr); \
- if (value == NULL) \
- { \
- PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \
- return -1; \
- } \
- if (PyUnicode_Check(value)) \
- { \
- iface->attr = strdup(PyUnicode_AsUTF8(value)); \
- Py_DECREF(value); \
- } \
- else \
- { \
- Py_DECREF(value); \
- PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \
- return -1; \
- } \
- assert(iface->attr != NULL); \
- } \
- while (0);
-
- LOAD_PYTHON_IFACE(name);
- LOAD_PYTHON_IFACE(desc);
- LOAD_PYTHON_IFACE(version);
- LOAD_PYTHON_IFACE(url);
-
- iface->container = false;
-
- /**
- * Comme le greffon n'est pas passé par la résolution des dépendances,
- * orchestrée par la fonction g_plugin_module_resolve_dependencies(),
- * on simule l'effet attendu en obtenant une référence par un appel à
- * get_plugin_by_name().
- *
- * L'incrémentation des références doit coller au plus près de
- * l'inscription nominative du greffon : en cas de sortie impromptue
- * (lorsqu'une erreur intervient pendant un chargement par exemple),
- * l'état de l'ensemble est ainsi cohérent au moment du retrait du
- * greffon fautif via la fonction g_plugin_module_dispose().
- */
-
- lock_plugin_list_for_reading();
- dependency = get_plugin_by_name("PyChrysalide", NULL);
- unlock_plugin_list_for_reading();
-
- assert(dependency != NULL);
-
- if (dependency == NULL)
- {
- PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!"));
- return -1;
- }
-
- iface->required = malloc(sizeof(char *));
- iface->required[0] = "PyChrysalide";
- iface->required_count = 1;
+ STORE_PLUGIN_ABI(plugin);
- /* Validation du reste de l'interface */
-
- value = PyObject_GetAttrString(self, "_actions");
-
- if (value == NULL)
+ if (!g_plugin_module_create(plugin, name, desc, version, url,
+ CONST_ARRAY_CAST(required.values, char), required.length))
{
- PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing."));
+ clean_charp_array(&required);
+ PyErr_SetString(PyExc_ValueError, _("Unable to create plugin module."));
return -1;
}
- if (!PyTuple_Check(value))
- {
- Py_DECREF(value);
- PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple."));
- return -1;
- }
-
- iface->actions_count = PyTuple_Size(value);
- iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t));
-
- for (i = 0; i < iface->actions_count; i++)
- {
- action = PyTuple_GetItem(value, i);
-
- if (!PyLong_Check(action))
- {
- Py_DECREF(value);
- PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action."));
- return -1;
- }
-
- iface->actions[i] = PyLong_AsUnsignedLong(action);
-
- }
-
- Py_DECREF(value);
+ clean_charp_array(&required);
return 0;
@@ -358,56 +223,55 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à manipuler. *
+* Paramètres : plugin = greffon à consulter. *
* *
-* Description : Encadre une étape de la vie d'un greffon. *
+* Description : Pointe le fichier contenant le greffon manipulé. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Chemin d'accès au greffon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_plugin_module_manage_wrapper(GPluginModule *plugin)
+static char *py_plugin_module_get_filename_wrapper(const GPluginModule *plugin)
{
- bool result; /* Bilan à faire remonter */
+ char *result; /* Désignation brute à renvoyer*/
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
- PyObject *args; /* Arguments pour l'appel */
PyObject *pyret; /* Bilan d'exécution */
-#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _manage, "$self, action, /", \
- METH_VARARGS, \
- "Abstract method called to react to several steps of the plugin" \
- " life.\n" \
- "\n" \
- "The expected action is a" \
- " pychrysalide.plugins.PluginModule.PluginAction value.\n" \
- "\n" \
- "This method has to be defined in order to handle actions such as" \
- " *PLUGIN_LOADED*." \
+#define PLUGIN_MODULE_GET_FILENAME_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _get_filename, "$self, /", \
+ METH_NOARGS, \
+ "Abstract method providing the filename of the script.\n" \
+ "\n" \
+ "The result should be the string value pointing to the plugin" \
+ " file path.\n" \
)
- result = true;
+ result = NULL;
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(plugin));
- if (has_python_method(pyobj, "_manage"))
+ if (has_python_method(pyobj, "_get_filename"))
{
- args = PyTuple_New(1);
+ pyret = run_python_method(pyobj, "_get_filename", NULL);
- PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED));
+ if (pyret != NULL)
+ {
+ if (!PyUnicode_Check(pyret))
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("The returned raw name must be a string"));
- pyret = run_python_method(pyobj, "_manage", args);
+ else
+ result = strdup(PyUnicode_DATA(pyret));
- result = (pyret == Py_True);
+ }
Py_XDECREF(pyret);
- Py_DECREF(args);
}
@@ -422,44 +286,62 @@ static bool py_plugin_module_manage_wrapper(GPluginModule *plugin)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à manipuler. *
+* Paramètres : plugin = greffon à valider. *
* *
-* Description : Assiste la désactivation d'un greffon. *
+* Description : Fournit le nom brut associé au greffon. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Désignation brute du greffon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_plugin_module_exit(GPluginModule *plugin)
+static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
{
- bool result; /* Bilan à faire remonter */
- plugin_interface *final; /* Interface finale conservée */
+ char *result; /* Désignation brute à renvoyer*/
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *pyret; /* Bilan d'exécution */
- result = true;
+#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _get_modname, "$self, /", \
+ METH_NOARGS, \
+ "Abstract method providing the raw module name of the loaded" \
+ " plugin.\n" \
+ "\n" \
+ "The result should be a short string value.\n" \
+)
- final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface;
+ result = NULL;
+
+ gstate = PyGILState_Ensure();
- if (final != NULL)
+ pyobj = pygobject_new(G_OBJECT(plugin));
+
+ if (has_python_method(pyobj, "_get_modname"))
{
- if (final->name != NULL) free(final->name);
- if (final->desc != NULL) free(final->desc);
- if (final->version != NULL) free(final->version);
- if (final->url != NULL) free(final->url);
+ pyret = run_python_method(pyobj, "_get_modname", NULL);
- assert(final->required_count == 1);
+ if (pyret != NULL)
+ {
+ if (!PyUnicode_Check(pyret))
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("The returned raw name must be a string"));
- if (final->required != NULL)
- free(final->required);
+ else
+ result = strdup(PyUnicode_DATA(pyret));
- if (final->actions != NULL)
- free(final->actions);
+ }
- free(final);
+ Py_XDECREF(pyret);
}
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
return result;
}
@@ -468,51 +350,45 @@ static bool py_plugin_module_exit(GPluginModule *plugin)
/******************************************************************************
* *
* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
* *
-* Description : Accompagne la fin du chargement des modules natifs. *
+* Description : Prend acte de l'activation du greffon. *
* *
-* Retour : - *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action)
+static bool py_plugin_module_enable_wrapper(GPluginModule *plugin)
{
+ bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
- PyObject *args; /* Arguments pour l'appel */
PyObject *pyret; /* Bilan d'exécution */
-#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _notify_plugins_loaded, "$self, action, /", \
- METH_VARARGS, \
- "Abstract method called once all the (native?) plugins are" \
- " loaded.\n" \
- "\n" \
- "The expected action is a" \
- " pychrysalide.plugins.PluginModule.PluginAction value.\n" \
- "\n" \
- "This method has to be defined in order to handle actions such as" \
- " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \
+#define PLUGIN_MODULE_ENABLE_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _enable, "$self, /", \
+ METH_VARARGS, \
+ "Abstract method called when the plugin gets enabled.\n" \
+ "\n" \
+ "The result is a boolean status: *False* if an implementation" \
+ " failed, *True* otherwise." \
)
+ result = true;
+
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(plugin));
- if (has_python_method(pyobj, "_notify_plugins_loaded"))
+ if (has_python_implementation_method(pyobj, "_enable"))
{
- args = PyTuple_New(1);
-
- PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+ pyret = run_python_method(pyobj, "_enable", NULL);
- pyret = run_python_method(pyobj, "_notify_plugins_loaded", args);
+ result = (pyret == Py_True);
Py_XDECREF(pyret);
- Py_DECREF(args);
}
@@ -520,36 +396,6 @@ static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin
PyGILState_Release(gstate);
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : self = objet Python concerné par l'appel. *
-* args = arguments fournis à l'appel. *
-* *
-* Description : Fournit le nom brut associé au greffon par défaut. *
-* *
-* Retour : Désignation brute du greffon. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObject *args)
-{
- PyObject *result; /* Bilan à retourner */
- GPluginModule *plugin; /* Version native du greffon */
- char *path; /* Chemin à traiter */
-
- plugin = G_PLUGIN_MODULE(pygobject_get(self));
-
- path = strdup(g_plugin_module_get_filename(plugin));
-
- result = PyUnicode_FromString(basename(path));
-
- free(path);
-
return result;
}
@@ -557,52 +403,44 @@ static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObjec
/******************************************************************************
* *
-* Paramètres : plugin = greffon à valider. *
+* Paramètres : plugin = greffon à manipuler. *
* *
-* Description : Fournit le nom brut associé au greffon. *
+* Description : Prend acte de la désactivation du greffon. *
* *
-* Retour : Désignation brute du greffon. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
+static bool py_plugin_module_disable_wrapper(GPluginModule *plugin)
{
- char *result; /* Désignation brute à renvoyer*/
+ bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan d'exécution */
-#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF_WITH \
-( \
- _get_modname, "$self, /", \
- METH_VARARGS, py_plugin_module_get_modname_by_default, \
- "(Abstract) method providing the raw module name of the plugin.\n" \
- " loaded.\n" \
- "\n" \
- "The result should be a short string value.\n" \
- "\n" \
- "A default implementation builds the module name from the Python" \
- " script filename." \
+#define PLUGIN_MODULE_DISABLE_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _disable, "$self, /", \
+ METH_VARARGS, \
+ "Abstract method called when the plugin gets disabled.\n" \
+ "\n" \
+ "The result is a boolean status: *False* if an implementation" \
+ " failed, *True* otherwise." \
)
- result = NULL;
+ result = true;
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(plugin));
- if (has_python_method(pyobj, "_get_modname"))
+ if (has_python_implementation_method(pyobj, "_disable"))
{
- pyret = run_python_method(pyobj, "_get_modname", NULL);
+ pyret = run_python_method(pyobj, "_disable", NULL);
- if (!PyUnicode_Check(pyret))
- g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
- _("The returned raw name must be a string"));
-
- else
- result = strdup(PyUnicode_DATA(pyret));
+ result = (pyret == Py_True);
Py_XDECREF(pyret);
@@ -617,6 +455,15 @@ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
}
+
+
+
+
+
+
+
+
+
#if 0
#ifdef INCLUDE_GTK_SUPPORT
@@ -1382,51 +1229,42 @@ static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *
* Paramètres : self = objet Python concerné par l'appel. *
* args = arguments fournis à l'appel. *
* *
-* Description : Construit le nom d'un fichier de configuration du greffon. *
+* Description : Affiche un message dans le journal des messages système. *
* *
-* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. *
+* Retour : Rien en équivalent Python. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args)
+static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
{
PyObject *result; /* Bilan à retourner */
- const char *final; /* Suffixe de fichier imposé */
- int create; /* Volonté de création */
- char *filename; /* Nom de fichier déterminé */
+ LogMessageType type; /* Espèce du message */
+ const char *msg; /* Contenu du message */
-#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \
+#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \
( \
- build_config_filename, "final, /, create=False", \
+ log_message, "type, msg, /", \
METH_VARARGS, py_plugin_module, \
- "Build a filename suitable for the plugin configuration, ending with" \
- " the *final* suffix.\n" \
+ "Display a message in the log window, in graphical mode, or in the" \
+ " console output if none.\n" \
"\n" \
- "If the *create* parameter is set, the path to this filename is" \
- " created.\n" \
+ "The type of the message has to be a pychrysalide.core.LogMessageType" \
+ " value." \
"\n" \
- "The result is a string or None on failure." \
+ "The only difference with the main pychrysalide.core.log_message()" \
+ " function is that messages are automatically prefixed with the plugin" \
+ " name here." \
)
- create = 0;
-
- if (!PyArg_ParseTuple(args, "s|p", &final, &create))
+ if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg))
return NULL;
- filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create);
+ g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg);
- if (filename != NULL)
- {
- result = PyUnicode_FromString(filename);
- free(filename);
- }
- else
- {
- result = Py_None;
- Py_INCREF(result);
- }
+ result = Py_None;
+ Py_INCREF(result);
return result;
@@ -1435,45 +1273,35 @@ static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject
/******************************************************************************
* *
-* Paramètres : self = objet Python concerné par l'appel. *
-* args = arguments fournis à l'appel. *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
* *
-* Description : Affiche un message dans le journal des messages système. *
+* Description : Indique le nom associé à un greffon. *
* *
-* Retour : Rien en équivalent Python. *
+* Retour : Désignation interne de l'extension, pour référence(s). *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
+static PyObject *py_plugin_module_get_name(PyObject *self, void *closure)
{
- PyObject *result; /* Bilan à retourner */
- LogMessageType type; /* Espèce du message */
- const char *msg; /* Contenu du message */
+ PyObject *result; /* Valeur à retourner */
+ GPluginModule *plugin; /* Version native du greffon */
+ const char *name; /* Nom attribué au greffon */
-#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \
-( \
- log_message, "type, msg, /", \
- METH_VARARGS, py_plugin_module, \
- "Display a message in the log window, in graphical mode, or in the" \
- " console output if none.\n" \
- "\n" \
- "The type of the message has to be a pychrysalide.core.LogMessageType" \
- " value." \
- "\n" \
- "The only difference with the main pychrysalide.core.log_message()" \
- " function is that messages are automatically prefixed with the plugin" \
- " name here." \
+#define PLUGIN_MODULE_NAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ name, py_plugin_module, \
+ "Name of the plugin. This string value is used to" \
+ " reference the plugin as dependency." \
)
- if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg))
- return NULL;
+ plugin = G_PLUGIN_MODULE(pygobject_get(self));
- g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg);
+ name = g_plugin_module_get_name(plugin);
- result = Py_None;
- Py_INCREF(result);
+ result = PyUnicode_FromString(name);
return result;
@@ -1485,32 +1313,31 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Fournit le nom brut associé au greffon. *
+* Description : Fournit une description fonctionnelle d'un greffon. *
* *
-* Retour : Désignation brute du greffon. *
+* Retour : Description textuelle associée à une extension ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_desc(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- char *modname; /* Désignation brute */
+ const char *desc; /* Description du greffon */
-#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \
-( \
- modname, py_plugin_module, \
- "Raw module name of the plugin." \
+#define PLUGIN_MODULE_DESC_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ desc, py_plugin_module, \
+ "Optional plugin description as string or *None*." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- modname = g_plugin_module_get_modname(plugin);
- result = PyUnicode_FromString(modname);
+ desc = g_plugin_module_get_desc(plugin);
- free(modname);
+ result = PyUnicode_FromString(desc);
return result;
@@ -1522,30 +1349,32 @@ static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure)
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Indique le fichier contenant le greffon manipulé. *
+* Description : Fournit la version d'un greffon et de ses fonctionnalités. *
* *
-* Retour : Chemin d'accès au greffon. *
+* Retour : Version sous forme de chaîne de caractères ou None. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_version(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- const char *filename; /* Chemin d'accès associé */
+ const char *version; /* Version du greffon */
-#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \
+#define PLUGIN_MODULE_VERSION_ATTRIB PYTHON_GET_DEF_FULL \
( \
- filename, py_plugin_module, \
- "Filename of the plugin." \
+ version, py_plugin_module, \
+ "Optional plugin version, in free format, as string," \
+ " or *None*." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- filename = g_plugin_module_get_filename(plugin);
- result = PyUnicode_FromString(filename);
+ version = g_plugin_module_get_version(plugin);
+
+ result = PyUnicode_FromString(version);
return result;
@@ -1557,113 +1386,204 @@ static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure)
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Fournit la description du greffon dans son intégralité. *
+* Description : Fournit l'URL des ressources en ligne liées à un greffon. *
* *
-* Retour : Interfaçage renseigné. *
+* Retour : URL de renvoi associée à une extension ou None. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_interface(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_url(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- const plugin_interface *iface; /* Interface liée à traduire */
+ const char *url; /* URL associée au greffon */
-#define PLUGIN_MODULE_INTERFACE_ATTRIB PYTHON_GET_DEF_FULL \
+#define PLUGIN_MODULE_URL_ATTRIB PYTHON_GET_DEF_FULL \
( \
- interface, py_plugin_module, \
- "Interface exported by the plugin..\n" \
- "\n" \
- "This property is a pychrysalide.StructObject instance." \
- "\n" \
- "The provided information is composed of the following" \
- " properties :\n" \
- "\n" \
- "* gtp_name;\n" \
- "* name;\n" \
- "* desc;\n" \
- "* version;\n" \
- "* url;\n" \
- "* container;\n" \
- "* required;\n" \
- "* actions.\n" \
- "\n" \
- "The *gtp_name* value may be *None* for non-native plugin." \
- " All other fields carry a string value except:\n" \
- "* *container*: a boolean status indicating if the plugin" \
- " can embed other plugins;\n" \
- "* *required*: a tuple of depedencies names;\n" \
- "* *actions*: a tuple of available features from the plugin"\
- " coded as pychrysalide.plugins.PluginModule.PluginAction" \
- " values." \
+ url, py_plugin_module, \
+ "Optional URL pointing to the plugin homepage as string" \
+ " or *None*." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- iface = g_plugin_module_get_interface(plugin);
- result = translate_plugin_interface_to_python(iface);
+ url = g_plugin_module_get_url(plugin);
+
+ result = PyUnicode_FromString(url);
return result;
}
-#if 0
+
/******************************************************************************
* *
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Fournit la configuration mise en place pour le greffon. *
+* Description : Fournit la liste des dépendances d'un greffon donné. *
* *
-* Retour : Configuration dédiée à l'extension. *
+* Retour : Liste des noms d'extensions requises pour une extension. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_config(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_requirements(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- GGenConfig *config; /* Configuration associée */
+ const char * const *required; /* Liste de dépendances */
+ size_t count; /* Nombre de ces dépendances */
+ size_t i; /* Boucle de parcours */
-#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \
+#define PLUGIN_MODULE_REQUIREMENTS_ATTRIB PYTHON_GET_DEF_FULL \
( \
- config, py_plugin_module, \
- "Dedicated configuration for the plugin." \
- "\n" \
- "The value is a pychrysalide.glibext.GenConfig instance" \
- " or None if the configuration is not yet created.\n" \
- "\n" \
- "As configuration storage path depends on the plugin name," \
- " all plugin properties have to get fully loaded by the" \
- " core before the configuration can be setup." \
- "automatically" \
+ requirements, py_plugin_module, \
+ "Tuple of the plugin dependencies." \
+)
+
+ plugin = G_PLUGIN_MODULE(pygobject_get(self));
+
+ required = g_plugin_module_get_requirements(plugin, &count);
+
+ result = PyTuple_New(count);
+
+ for (i = 0; i < count; i++)
+ PyTuple_SetItem(result, i, PyUnicode_FromString(required[i]));
+
+ return result;
+
+}
+
+
+
+
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique le fichier contenant le greffon manipulé. *
+* *
+* Retour : Chemin d'accès au greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GPluginModule *plugin; /* Version native du greffon */
+ char *filename; /* Chemin d'accès associé */
+
+#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ filename, py_plugin_module, \
+ "Filename of the plugin." \
+)
+
+ plugin = G_PLUGIN_MODULE(pygobject_get(self));
+
+ filename = g_plugin_module_get_filename(plugin);
+
+ if (filename != NULL)
+ {
+ result = PyUnicode_FromString(filename);
+
+ free(filename);
+
+ }
+
+ else
+ {
+ /**
+ * La méthode de classe sollicitée a renvoyé une valeur nulle.
+ *
+ * Si cette méthode correspond à une implémentation Python
+ * (avec un appel à not_yet_implemented_method()), une exception
+ * est déjà en place.
+ *
+ * Si aucune exception n'a été prévue, un rattrapage est effectué ici.
+ */
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as filename"));
+
+ result = NULL;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit le nom brut associé au greffon. *
+* *
+* Retour : Désignation brute du greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GPluginModule *plugin; /* Version native du greffon */
+ char *modname; /* Désignation brute */
+
+#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ modname, py_plugin_module, \
+ "Raw module name of the plugin." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- config = g_plugin_module_get_config(plugin);
- if (config == NULL)
+ modname = g_plugin_module_get_modname(plugin);
+
+ if (modname != NULL)
{
- result = Py_None;
- Py_INCREF(result);
+ result = PyUnicode_FromString(modname);
+
+ free(modname);
+
}
else
{
- result = pygobject_new(G_OBJECT(config));
+ /**
+ * La méthode de classe sollicitée a renvoyé une valeur nulle.
+ *
+ * Si cette méthode correspond à une implémentation Python
+ * (avec un appel à not_yet_implemented_method()), une exception
+ * est déjà en place.
+ *
+ * Si aucune exception n'a été prévue, un rattrapage est effectué ici.
+ */
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as modname"));
- g_object_unref(G_OBJECT(config));
+ result = NULL;
}
return result;
}
-#endif
/******************************************************************************
@@ -1681,33 +1601,22 @@ static PyObject *py_plugin_module_get_config(PyObject *self, void *closure)
PyTypeObject *get_python_plugin_module_type(void)
{
static PyMethodDef py_plugin_module_methods[] = {
- PLUGIN_MODULE_MANAGE_WRAPPER,
- PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER,
+ PLUGIN_MODULE_GET_FILENAME_WRAPPER,
PLUGIN_MODULE_GET_MODNAME_WRAPPER,
-#if 0
-#ifdef INCLUDE_GTK_SUPPORT
- PLUGIN_MODULE_INCLUDE_THEME_WRAPPER,
- PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER,
- PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER,
-#endif
- PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER,
- PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER,
- PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER,
- PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER,
- PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER,
- PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER,
- PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER,
- PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD,
-#endif
+ PLUGIN_MODULE_ENABLE_WRAPPER,
+ PLUGIN_MODULE_DISABLE_WRAPPER,
PLUGIN_MODULE_LOG_MESSAGE_METHOD,
{ NULL }
};
static PyGetSetDef py_plugin_module_getseters[] = {
- PLUGIN_MODULE_MODNAME_ATTRIB,
+ PLUGIN_MODULE_NAME_ATTRIB,
+ PLUGIN_MODULE_DESC_ATTRIB,
+ PLUGIN_MODULE_VERSION_ATTRIB,
+ PLUGIN_MODULE_URL_ATTRIB,
+ PLUGIN_MODULE_REQUIREMENTS_ATTRIB,
PLUGIN_MODULE_FILENAME_ATTRIB,
- PLUGIN_MODULE_INTERFACE_ATTRIB,
- //PLUGIN_MODULE_CONFIG_ATTRIB,
+ PLUGIN_MODULE_MODNAME_ATTRIB,
{ NULL }
};
@@ -1761,10 +1670,9 @@ bool ensure_python_plugin_module_is_registered(void)
dict = PyModule_GetDict(module);
- if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type))
- return false;
+ pyg_register_class_init(G_TYPE_PLUGIN_MODULE, (PyGClassInitFunc)py_plugin_module_init_gclass);
- if (!define_plugin_module_constants(type))
+ if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type))
return false;
}
@@ -1772,80 +1680,3 @@ bool ensure_python_plugin_module_is_registered(void)
return true;
}
-
-
-/******************************************************************************
-* *
-* Paramètres : modname = nom du module à charger. *
-* filename = chemin d'accès au code Python à charger. *
-* *
-* Description : Crée un greffon à partir de code Python. *
-* *
-* Retour : Adresse de la structure mise en place ou NULL si erreur. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GPluginModule *create_python_plugin(const char *modname, const char *filename)
-{
- GPluginModule *result; /* Structure à retourner */
- PyObject *name; /* Chemin d'accès pour Python */
- PyObject *module; /* Script Python chargé */
- PyObject *dict; /* Dictionnaire associé */
- PyObject *class; /* Classe à instancier */
- PyObject *instance; /* Instance Python du greffon */
-
- name = PyUnicode_FromString(modname);
- if (name == NULL) goto bad_exit;
-
- module = PyImport_Import(name);
- Py_DECREF(name);
-
- if (module == NULL) goto no_import;
-
- dict = PyModule_GetDict(module);
- class = PyDict_GetItemString(dict, "AutoLoad");
-
- if (class == NULL) goto no_class;
- if (!PyType_Check(class->ob_type)) goto no_class;
-
- instance = PyObject_CallFunction(class, NULL);
- if (instance == NULL) goto no_instance;
-
- result = G_PLUGIN_MODULE(pygobject_get(instance));
-
- result->filename = strdup(filename);
-
- /**
- * L'instance Python et l'objet GLib résultante sont un même PyGObject.
- *
- * Donc pas besoin de toucher au comptage des références ici, la libération
- * se réalisera à la fin, quand l'objet GLib sera libéré.
- */
-
- Py_DECREF(module);
-
- return result;
-
- no_instance:
-
- log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance"));
-
- no_class:
-
- if (class == NULL)
- log_plugin_simple_message(LMT_ERROR,
- _("An error occured when looking for the 'AutoLoad': item not found!"));
-
- no_import:
-
- Py_XDECREF(module);
-
- log_pychrysalide_exception(_("An error occured when importing '%s'"), modname);
-
- bad_exit:
-
- return NULL;
-
-}
diff --git a/plugins/pychrysalide/plugins/plugin.h b/plugins/pychrysalide/plugins/plugin.h
index ad54b8e..fd3d239 100644
--- a/plugins/pychrysalide/plugins/plugin.h
+++ b/plugins/pychrysalide/plugins/plugin.h
@@ -27,13 +27,9 @@
#include <Python.h>
-#include <glib-object.h>
#include <stdbool.h>
-#include <plugins/plugin.h>
-
-
/* Fournit un accès à une définition de type à diffuser. */
PyTypeObject *get_python_plugin_module_type(void);
@@ -41,9 +37,6 @@ PyTypeObject *get_python_plugin_module_type(void);
/* Prend en charge l'objet 'pychrysalide.plugins.PluginModule'. */
bool ensure_python_plugin_module_is_registered(void);
-/* Crée un greffon à partir de code Python. */
-GPluginModule *create_python_plugin(const char *, const char *);
-
#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H */
diff --git a/plugins/pychrysalide/plugins/python-int.h b/plugins/pychrysalide/plugins/python-int.h
new file mode 100644
index 0000000..7408d18
--- /dev/null
+++ b/plugins/pychrysalide/plugins/python-int.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * python-int.h - prototypes internes pour la déclinaison Python de greffons
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H
+#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H
+
+
+#include "python.h"
+
+
+#include <plugins/plugin-int.h>
+
+
+
+/* Greffon Python pour Chrysalide (instance) */
+struct _GPythonPlugin
+{
+ GPluginModule parent; /* A laisser en premier */
+
+ char *file; /* Valeur initiale de __file__ */
+
+};
+
+
+/* Greffon Python pour Chrysalide (classe) */
+struct _GPythonPluginClass
+{
+ GPluginModuleClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un greffon Python. */
+bool g_python_plugin_create(GPythonPlugin *, const char *, const char *, const char *, const char *, char ***, size_t *, const char *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H */
diff --git a/plugins/pychrysalide/plugins/python.c b/plugins/pychrysalide/plugins/python.c
new file mode 100644
index 0000000..d6b9281
--- /dev/null
+++ b/plugins/pychrysalide/plugins/python.c
@@ -0,0 +1,489 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * python.c - déclinaison Python de greffons
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "python.h"
+
+
+#include <malloc.h>
+#include <pygobject.h>
+
+
+#include <common/compiler.h>
+#include <common/cpp.h>
+
+
+#include "plugin.h"
+#include "python-int.h"
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */
+
+
+/* Initialise la classe des greffons Python. */
+static void g_python_plugin_class_init(GPythonPluginClass *);
+
+/* Initialise une instance de greffon Python. */
+static void g_python_plugin_init(GPythonPlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_python_plugin_dispose(GPythonPlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_python_plugin_finalize(GPythonPlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Pointe le fichier contenant le greffon manipulé. */
+static char *g_python_plugin_get_filename(const GPythonPlugin *);
+
+/* Fournit le nom brut associé au greffon. */
+static char *g_python_plugin_get_modname(const GPythonPlugin *);
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon Python. */
+G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_class_init(GPythonPluginClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->get_filename = (get_plugin_filename_fc)g_python_plugin_get_filename;
+ plugin->get_modname = (get_plugin_modname_fc)g_python_plugin_get_modname;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_init(GPythonPlugin *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
+
+ plugin->file = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_dispose(GPythonPlugin *plugin)
+{
+ G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_finalize(GPythonPlugin *plugin)
+{
+ if (plugin->file != NULL)
+ free(plugin->file);
+
+ G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* name = nom du greffon pour référence, principalement. *
+* desc = présentation éventuelle à destination humaine. *
+* version = indication de version éventuelle. *
+* url = référence vers une ressource en ligne. *
+* required = liste de dépendances éventuelles ou NULL. *
+* count = taille de cette liste. *
+* file = emplacement du script considéré. *
+* *
+* Description : Met en place un greffon Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_python_plugin_create(GPythonPlugin *plugin, const char *name, const char *desc, const char *version, const char *url, char ***required, size_t *count, const char *file)
+{
+ bool result; /* Bilan à retourner */
+
+ /* Ajout imposé d'une dépendance à Python */
+
+ *required = realloc(*required, ++(*count) * sizeof(char *));
+
+ (*required)[*count - 1] = strdup("PyChrysalide");
+
+ /* Poursuite de la mise en place */
+
+ result = g_plugin_module_create(G_PLUGIN_MODULE(plugin),
+ name, desc, version, url,
+ CONST_ARRAY_CAST(*required, char), *count);
+
+ if (result)
+ plugin->file = strdup(file);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à consulter. *
+* *
+* Description : Pointe le fichier contenant le greffon manipulé. *
+* *
+* Retour : Chemin d'accès au greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_python_plugin_get_filename(const GPythonPlugin *plugin)
+{
+ char *result; /* Chemin d'accès à renvoyer */
+
+ result = strdup(plugin->file);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à valider. *
+* *
+* Description : Fournit le nom brut associé au greffon. *
+* *
+* Retour : Désignation brute du greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_python_plugin_get_modname(const GPythonPlugin *plugin)
+{
+ char *result; /* Désignation brute à renvoyer*/
+ char *filename; /* Chemin du script Python */
+ char *name; /* Nom du fichier associé */
+ size_t length; /* Taille du nom */
+ int ret; /* Bilan d'une comparaison */
+
+ filename = g_python_plugin_get_filename(plugin);
+
+ name = basename(filename);
+
+ length = strlen(name);
+
+#define PYTHON_SUFFIX ".py"
+
+ ret = strncmp(&name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)],
+ PYTHON_SUFFIX,
+ STATIC_STR_SIZE(PYTHON_SUFFIX));
+
+ if (ret == 0)
+ name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)] = '\0';
+
+ result = strdup(name);
+
+ free(filename);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ const char *version; /* Version du greffon */
+ const char *url; /* Site Web associé */
+ charp_array_t required; /* Liste des dépendances */
+ const char *file; /* Emplacement du script */
+ int ret; /* Bilan de lecture des args. */
+ PyObject *value; /* Valeur d'un arg. implicite */
+ const char *name; /* Désignation humaine courte */
+ const char *desc; /* Description plus loquace */
+ GPythonPlugin *plugin; /* Greffon à manipuler */
+
+ static char *kwlist[] = { "file", "version", "url", "required", NULL };
+
+#define PYTHON_PLUGIN_DOC \
+ "The PythonPlugin class helps to build custom Python plugins:\n" \
+ "* some required information (*name* and *desc* for the parent" \
+ " constructor) is automatically extracted from the final class name" \
+ " or its documentation;\n" \
+ "* implementations for pychrysalide.plugins.PluginModule._get_filename()" \
+ " and pychrysalide.plugins.PluginModule._get_modname() are provided," \
+ " relying on the *file* argument.\n" \
+ "\n" \
+ "Calls to the *__init__* constructor of this abstract object expect the" \
+ " following arguments as keyword parameters:\n" \
+ "* *file*: path to the Python script; the value should be equal to the" \
+ " *__file__* keyword;\n" \
+ "* *version* (optional): a string providing the version of the plugin;" \
+ " Version format is free;\n" \
+ "* *url* (optional): a string for the homepage describing the plugin;\n" \
+ "* *required* (optional): dependencies of the plugin."
+
+ /* Récupération des paramètres */
+
+ version = NULL;
+ url = NULL;
+ required.values = NULL;
+ required.length = 0;
+
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|ssO&", kwlist,
+ &file, &version, &url,
+ convert_to_sequence_to_charp_array, &required);
+ if (!ret) return -1;
+
+ name = self->ob_type->tp_name;
+
+ value = PyObject_GetAttrString(self, "__doc__");
+
+ if (value != NULL && PyUnicode_Check(value))
+ desc = PyUnicode_AsUTF8(value);
+ else
+ desc = NULL;
+
+ Py_XDECREF(value);
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ plugin = G_PYTHON_PLUGIN(pygobject_get(self));
+
+ STORE_PLUGIN_ABI(plugin);
+
+ if (!g_python_plugin_create(plugin, name, desc, version, url,
+ &required.values, &required.length,
+ file))
+ {
+ clean_charp_array(&required);
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Python plugin."));
+ return -1;
+ }
+
+ clean_charp_array(&required);
+
+ return 0;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION POUR SUPPORT PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_python_plugin_type(void)
+{
+ static PyMethodDef py_python_plugin_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_python_plugin_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_python_plugin_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.PythonPlugin",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = PYTHON_PLUGIN_DOC,
+
+ .tp_methods = py_python_plugin_methods,
+ .tp_getset = py_python_plugin_getseters,
+
+ .tp_init = py_python_plugin_init,
+ .tp_new = py_python_plugin_new,
+
+ };
+
+ return &py_python_plugin_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_python_plugin_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'PythonPlugin' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_python_plugin_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_plugin_module_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type))
+ return false;
+
+ }
+
+ return true;
+
+}
diff --git a/plugins/pychrysalide/plugins/python.h b/plugins/pychrysalide/plugins/python.h
new file mode 100644
index 0000000..8613bd1
--- /dev/null
+++ b/plugins/pychrysalide/plugins/python.h
@@ -0,0 +1,57 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * python.h - prototypes pour la déclinaison Python de greffons
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H
+#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+#include <glibext/helpers.h>
+
+
+
+/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */
+
+
+#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type())
+
+DECLARE_GTYPE(GPythonPlugin, g_python_plugin, G, PYTHON_PLUGIN);
+
+
+
+/* ------------------------- DEFINITION POUR SUPPORT PYTHON ------------------------- */
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_python_plugin_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. */
+bool ensure_python_python_plugin_is_registered(void);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H */
diff --git a/plugins/pychrysalide/plugins/translate.c b/plugins/pychrysalide/plugins/translate.c
deleted file mode 100644
index 055788c..0000000
--- a/plugins/pychrysalide/plugins/translate.c
+++ /dev/null
@@ -1,110 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * translate.c - conversion de structures liées aux greffons en objets Python
- *
- * Copyright (C) 2021 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include "translate.h"
-
-
-#include <assert.h>
-
-
-#include "plugin.h"
-#include "../helpers.h"
-#include "../struct.h"
-
-
-
-/******************************************************************************
-* *
-* Paramètres : iface = ensemble d'informations à transcrire en Python. *
-* *
-* Description : Traduit une description d'interface de greffon. *
-* *
-* Retour : Structure mise en place ou NULL en cas d'erreur. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PyObject *translate_plugin_interface_to_python(const plugin_interface *iface)
-{
- PyObject *result; /* Construction à retourner */
- PyTypeObject *base; /* Modèle d'objet à créer */
- bool status; /* Bilan d'une traduction */
- PyObject *array; /* Tableau à insérer */
- size_t i; /* Boucle de parcours */
- PyTypeObject *itype; /* Type d'élément à créer */
- PyObject *item; /* Elément mis en place */
-
- base = get_python_py_struct_type();
-
- result = PyObject_CallFunction((PyObject *)base, NULL);
- assert(result != NULL);
-
- status = true;
-
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, gtp_name);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, name);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, desc);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, version);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, url);
-
- if (status) status = TRANSLATE_BOOLEAN_FIELD(result, iface, container);
-
- if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, required, &array);
-
- if (status)
- {
- itype = get_python_plugin_module_type();
-
- for (i = 0; i < iface->required_count; i++)
- {
- item = PyUnicode_FromString(iface->required[i]);
- PyTuple_SetItem(array, i, item);
- }
-
- }
-
- if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, actions, &array);
-
- if (status)
- {
- itype = get_python_plugin_module_type();
-
- for (i = 0; i < iface->actions_count; i++)
- {
- item = cast_with_constants_group_from_type(itype, "PluginAction", iface->actions[i]);
- PyTuple_SetItem(array, i, item);
- }
-
- }
-
- if (!status)
- {
- Py_DECREF(result);
- result = NULL;
- }
-
- return result;
-
-}
diff --git a/plugins/pynb/Makefile.am b/plugins/pynb/Makefile.am
new file mode 100644
index 0000000..50e549f
--- /dev/null
+++ b/plugins/pynb/Makefile.am
@@ -0,0 +1,77 @@
+
+BUILT_SOURCES = resources.h resources.c
+
+
+lib_LTLIBRARIES = libpynbui.la
+
+libdir = $(pluginslibdir)
+
+
+# if BUILD_PYTHON3_BINDINGS
+
+# PYTHON3_LIBADD = python/libpynbpython.la
+
+# if BUILD_DISCARD_LOCAL
+
+# PYTHON3_LDFLAGS = -Wl,-rpath,$(pluginslibdir) \
+# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+# else
+
+# PYTHON3_LDFLAGS = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs \
+# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+# endif
+
+# PYTHON3_SUBDIRS = python
+
+# endif
+
+
+libpynbui_la_SOURCES = \
+ core-ui-int.h \
+ core-ui.h core-ui.c \
+ panel-int.h \
+ panel.h panel.c \
+ params-int.h \
+ params.h params.c \
+ prefs-int.h \
+ prefs.h prefs.c \
+ resources.h resources.c
+
+libpynbui_la_LIBADD = \
+ $(PYTHON3_LIBADD)
+
+libpynbui_la_CFLAGS = $(LIBGTK4_CFLAGS)
+
+libpynbui_la_LDFLAGS = \
+ $(LIBGTK4_LIBS) $(PYTHON3_LDFLAGS)
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libpynbui_la_SOURCES:%c=)
+
+
+RES_FILES = \
+ panel.ui \
+ params.ui \
+ prefs.ui \
+ data/images/pynb-symbolic.svg
+
+
+resources.c: gresource.xml $(RES_FILES)
+ glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name plugins_pynb gresource.xml
+
+resources.h: gresource.xml
+ glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name plugins_pynb gresource.xml
+
+
+CLEANFILES = resources.h resources.c
+
+EXTRA_DIST = gresource.xml $(RES_FILES)
+
+
+AM_CPPFLAGS = -I$(top_srcdir)/src $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
+
+SUBDIRS = $(PYTHON3_SUBDIRS)
diff --git a/plugins/pynb/core-ui-int.h b/plugins/pynb/core-ui-int.h
new file mode 100644
index 0000000..caf5713
--- /dev/null
+++ b/plugins/pynb/core-ui-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui-int.h - prototypes internes pour le plugin présentant des notes avec code Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_CORE_UI_INT_H
+#define _PLUGINS_PYNB_CORE_UI_INT_H
+
+
+#include <plugins/native-int.h>
+
+
+#include "core-ui.h"
+
+
+
+/* Greffon natif pour la présentation de notes avec texte et code Python (instance) */
+struct _GPythonNotebookPluginUI
+{
+ GNativePlugin parent; /* A laisser en premier */
+
+};
+
+
+/* Greffon natif pour la présentation de notes avec texte et code Python (classe) */
+struct _GPythonNotebookPluginUIClass
+{
+ GNativePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un module pour présentation. */
+bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *, GModule *);
+
+
+
+#endif /* _PLUGINS_PYNB_CORE_UI_INT_H */
diff --git a/plugins/pynb/core-ui.c b/plugins/pynb/core-ui.c
new file mode 100644
index 0000000..dd49a83
--- /dev/null
+++ b/plugins/pynb/core-ui.c
@@ -0,0 +1,363 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.c - présentation de notes sous forme de texte et de code Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "core-ui.h"
+
+
+#include <i18n.h>
+#include <gui/core/panels.h>
+#include <plugins/self.h>
+#include <plugins/tweakable-int.h>
+
+
+#include "core-ui-int.h"
+#include "panel.h"
+#include "params.h"
+#include "prefs.h"
+
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des recherches et identifications. */
+static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *);
+
+/* Procède à l'initialisation de l'interface d'intervention. */
+static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *);
+
+/* Initialise une instance de recherches et identifications. */
+static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *);
+
+/* Supprime toutes les références externes. */
+static void g_python_notebook_plugin_ui_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_python_notebook_plugin_ui_finalize(GObject *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *);
+
+/* Prend acte de la désactivation du greffon. */
+static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *);
+
+
+
+/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */
+
+
+/* Fournit une liste de sections de configuration. */
+static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *, size_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une présentation de notes texte et code Python. */
+G_DEFINE_TYPE_WITH_CODE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G_TYPE_NATIVE_PLUGIN,
+ G_IMPLEMENT_INTERFACE(G_TYPE_TWEAKABLE_PLUGIN, g_python_notebook_plugin_ui_tweakable_plugin_interface_init));
+
+
+NATIVE_PLUGIN_ENTRYPOINT(g_python_notebook_plugin_ui_new);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des recherches et identifications. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = g_python_notebook_plugin_ui_dispose;
+ object->finalize = g_python_notebook_plugin_ui_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_python_notebook_plugin_ui_enable;
+ plugin->disable = (pg_management_fc)g_python_notebook_plugin_ui_disable;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface d'intervention. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *iface)
+{
+ iface->get_info = g_python_notebook_plugin_ui_get_tweak_info;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de recherches et identifications. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = extension vue du système. *
+* *
+* Description : Crée un module pour présentation de notes. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+GPluginModule *g_python_notebook_plugin_ui_new(GModule *module)
+{
+ GPythonNotebookPluginUI *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI, NULL);
+
+ if (!g_python_notebook_plugin_ui_create(result, module))
+ g_clear_object(&result);
+
+ return G_PLUGIN_MODULE(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = extension vue du système. *
+* *
+* Description : Met en place un module pour un module pour présentation. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *plugin, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
+#ifdef INCLUDE_PYTHON3_BINDINGS
+# define PG_REQ REQ_LIST("PyChrysalide")
+#else
+# define PG_REQ NO_REQ
+#endif
+
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PythonNotebook",
+ "Edit notebook with text and code to support binary analysis",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("doc/plugins/pynb"),
+ PG_REQ,
+ module);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de l'activation du greffon. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *plugin)
+{
+ bool result; /* Bilan à retourner */
+ panel_info_t info; /* Infos d'enregistrement */
+
+ info.category = "Main";
+
+ info.image = "pynb-symbolic";
+ info.title = _("Python notebook");
+ info.desc = _("Edit notebook with text and code to support binary analysis");
+
+ info.personality = FPP_MAIN_PANEL;
+
+ info.panel_type = GTK_TYPE_PYTHON_NOTEBOOK_PANEL;
+ info.params_type = GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS;
+
+ result = register_framework_panel_definition(&info);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de la désactivation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *plugin)
+{
+ bool result; /* Bilan à retourner */
+
+
+ // TODO : unregister
+
+ result = true;
+
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* INTEGRATION DANS L'EDITION DES PREFERENCES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = interface à manipuler. *
+* count = taille de la liste renvoyée. [OUT] *
+* *
+* Description : Fournit une liste de sections de configuration. *
+* *
+* Retour : Définition(s) de section de configuration ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *plugin, size_t *count)
+{
+ tweak_info_t *result; /* Liste à renvoyer */
+
+ tweak_info_t infos[] = {
+ TWEAK_SIMPLE_DEF("root", "Basics",
+ "pynb-symbolic", "pynotebook", "Notebook", GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL),
+ };
+
+ *count = 1;
+ result = malloc(*count * sizeof(tweak_info_t));
+
+ memcpy(result, infos, *count * sizeof(tweak_info_t));
+
+ return result;
+
+}
diff --git a/plugins/pynb/core-ui.h b/plugins/pynb/core-ui.h
new file mode 100644
index 0000000..8ab9dd9
--- /dev/null
+++ b/plugins/pynb/core-ui.h
@@ -0,0 +1,43 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.h - prototypes pour la présentation de notes sous forme de texte et de code Python
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_FIDO_CORE_UI_H
+#define _PLUGINS_FIDO_CORE_UI_H
+
+
+#include <plugins/plugin.h>
+#include <plugins/plugin-int.h>
+
+
+
+#define G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI (g_python_notebook_plugin_ui_get_type())
+
+DECLARE_GTYPE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G, PYTHON_NOTEBOOK_PLUGIN_UI);
+
+
+/* Crée un module pour présentation de notes. */
+GPluginModule *g_python_notebook_plugin_ui_new(GModule *);
+
+
+
+#endif /* _PLUGINS_FIDO_CORE_UI_H */
diff --git a/plugins/pynb/data/images/pynb-symbolic.svg b/plugins/pynb/data/images/pynb-symbolic.svg
new file mode 100644
index 0000000..f8cae60
--- /dev/null
+++ b/plugins/pynb/data/images/pynb-symbolic.svg
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="256"
+ height="256"
+ viewBox="0 0 67.73333 67.733333"
+ version="1.1"
+ id="svg2759"
+ sodipodi:docname="pynotebook.svg"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview2761"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="false"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="px"
+ showgrid="false"
+ inkscape:zoom="2.9329997"
+ inkscape:cx="126.83261"
+ inkscape:cy="128.1964"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1" />
+ <defs
+ id="defs2756">
+ <linearGradient
+ id="b"
+ x1="28.809"
+ y1="28.882"
+ x2="45.803001"
+ y2="45.162998"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)">
+ <stop
+ stop-color="#FFE052"
+ id="stop3586" />
+ <stop
+ offset="1"
+ stop-color="#FFC331"
+ id="stop3588" />
+ </linearGradient>
+ <linearGradient
+ id="a"
+ x1="19.075001"
+ y1="18.782"
+ x2="34.897999"
+ y2="34.658001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)">
+ <stop
+ stop-color="#387EB8"
+ id="stop3581" />
+ <stop
+ offset="1"
+ stop-color="#366994"
+ id="stop3583" />
+ </linearGradient>
+ </defs>
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3524"
+ transform="translate(2.9562937,-0.62320885)"
+ sodipodi:insensitive="true"
+ style="display:none">
+ <path
+ id="rect2966"
+ style="fill:#3a1616;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 49.272443,10.836031 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 V 57.03476 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 16.30288 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 14.59084 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z"
+ sodipodi:nodetypes="csssscsssssc" />
+ <path
+ id="rect2964"
+ style="display:inline;fill:#a44040;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 13.657273,5.3857002 h 29.21476 c 2.168053,0 3.913452,1.7453645 3.913452,3.9133733 V 54.388991 c 0,2.168009 -1.745399,3.913373 -3.913452,3.913373 h -29.21476 c -1.084026,0 -2.062389,-0.436341 -2.769745,-1.143683 -0.707357,-0.707342 -1.1437069,-1.685685 -1.1437069,-2.76969 V 9.2990735 c 0,-2.1680088 1.7453999,-3.9133733 3.9134519,-3.9133733 z"
+ sodipodi:nodetypes="ssssssssss" />
+ <path
+ id="rect3518"
+ style="fill:#2d3a16;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 10.887528,57.158681 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ <g
+ id="g3807"
+ transform="matrix(0.275,0,0,0.27644269,10.179141,20.324238)"
+ style="display:none;stroke-width:3.62685">
+ <path
+ d="m 76.135321,-13.223349 c -26.868437,0 -25.19164,11.6515879 -25.19164,11.6515879 l 0.03307,12.0716141 h 25.638125 v 3.621485 h -35.82789 c 0,0 -17.187994,-1.951302 -17.187994,25.161874 0,27.106562 15.005182,26.147447 15.005182,26.147447 h 8.956145 V 52.849722 c 0,0 -0.482864,-15.005182 14.767057,-15.005182 h 25.426458 c 0,0 14.287506,0.231511 14.287506,-13.807942 V 0.82271803 c 0,0 2.16958,-14.04606703 -25.906021,-14.04606703 z m -14.138672,8.1160932 c 2.549922,0 4.613672,2.0637499 4.613672,4.61367177 0,2.54992183 -2.06375,4.61367183 -4.613672,4.61367183 a 4.6070572,4.6070572 0 0 1 -4.613672,-4.61367183 c 0,-2.54992187 2.06375,-4.61367177 4.613672,-4.61367177 z"
+ fill="url(#a)"
+ id="path3577"
+ style="fill:url(#a);stroke-width:11.9951" />
+ <path
+ d="m 76.895998,92.057664 c 26.868432,0 25.191642,-11.651588 25.191642,-11.651588 l -0.0331,-12.071615 h -25.6381 v -3.621484 h 35.82458 c 0,0 17.1913,1.951302 17.1913,-25.158567 0,-27.109869 -15.00518,-26.147447 -15.00518,-26.147447 H 105.471 v 12.57763 c 0,0 0.48286,15.005182 -14.76706,15.005182 H 65.277482 c 0,0 -14.287499,-0.231511 -14.287499,13.807942 v 23.21388 c 0,0 -2.169584,14.046067 25.906015,14.046067 z m 14.138671,-8.116093 a 4.6070572,4.6070572 0 0 1 -4.613671,-4.613672 c 0,-2.546615 2.06375,-4.610365 4.613671,-4.610365 2.549922,0 4.613672,2.060443 4.613672,4.610365 0,2.553229 -2.06375,4.613672 -4.613672,4.613672 z"
+ fill="url(#b)"
+ id="path3579"
+ style="fill:url(#b);stroke-width:11.9951" />
+ </g>
+ <g
+ id="g3859"
+ style="display:none;fill:#000000;fill-opacity:1">
+ <path
+ id="path3544"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 52.228737,10.212822 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 v 45.089753 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 19.259174 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 13.967631 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z"
+ sodipodi:nodetypes="csssscsssssc" />
+ <path
+ id="path3546"
+ style="display:inline;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 16.613456,4.7624999 C 14.445408,4.7625 12.7,6.5079516 12.7,8.675956 v 45.089753 c 0,1.084003 0.436244,2.062515 1.143599,2.769857 0.707354,0.70734 1.685833,1.143599 2.769857,1.143599 h 29.214753 c 2.168049,0 3.913456,-1.745451 3.913456,-3.913456 V 8.675956 c 0,-2.1680044 -1.745407,-3.9134561 -3.913456,-3.9134561 z M 31.116446,16.66875 c 7.720775,0 7.124113,3.882967 7.124113,3.882967 v 6.417179 c 0,3.881096 -3.928959,3.817338 -3.928959,3.817338 h -6.992338 c -4.19372,0 -4.061251,4.148067 -4.061251,4.148067 v 3.477824 h -2.462898 c 0,0 -4.126363,0.264897 -4.126363,-7.2285 0,-7.495223 4.726843,-6.955647 4.726843,-6.955647 h 9.852628 v -1.000973 h -7.050215 l -0.0093,-3.337264 c 0,0 -0.461064,-3.220991 6.927742,-3.220991 z m -3.888135,2.24379 c -0.701226,0 -1.268656,0.570469 -1.268656,1.275374 a 1.2669408,1.2735872 0 0 0 1.268656,1.275375 c 0.701229,0 1.268657,-0.570467 1.268657,-1.275375 0,-0.704905 -0.567428,-1.275374 -1.268657,-1.275374 z m 14.46475,5.117 c 0.453342,-0.0018 4.079854,0.203094 4.079854,7.229016 0,7.494318 -4.727359,6.955131 -4.727359,6.955131 h -9.852112 v 1.000973 h 7.050732 l 0.0093,3.337263 c 0,0 0.461069,3.220992 -6.927742,3.220992 -7.720783,0 -7.124113,-3.882967 -7.124113,-3.882967 v -6.417179 c 0,-3.881101 3.928959,-3.817338 3.928959,-3.817338 h 6.992338 c 4.193725,0 4.060734,-4.148067 4.060734,-4.148067 v -3.47679 h 2.462898 c 0,0 0.01629,-9.15e-4 0.04651,-0.001 z M 35.21387,40.97941 c -0.701227,0 -1.269173,0.570348 -1.269173,1.27434 a 1.2669408,1.2735872 0 0 0 1.269173,1.275375 c 0.701228,0 1.268657,-0.569554 1.268657,-1.275375 0,-0.704906 -0.567429,-1.27434 -1.268657,-1.27434 z" />
+ <path
+ id="path3548"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 13.843822,56.535472 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ <path
+ id="rect3934"
+ style="display:none;fill:#00ba2b;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ id="path4924"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 24.551 4.7625 C 22.3829 4.7625 20.1615 6.50795 19.5702 8.67596 L 7.27299 53.7657 C 6.97735 54.8497 7.14673 55.8282 7.66117 56.5356 L 11.5111 61.8277 L 11.5147 61.8277 C 11.6056 61.9525 11.7049 62.0706 11.8167 62.1776 C 12.3371 62.6758 13.0763 62.9708 13.9676 62.9708 L 43.1824 62.9708 C 45.3504 62.9708 47.5719 61.2254 48.1631 59.0574 L 60.4603 13.9676 C 60.9465 12.1852 60.1745 10.6888 58.6799 10.2128 C 58.687 10.5642 58.6413 10.9362 58.5361 11.3218 L 46.2389 56.4115 C 45.6476 58.5795 43.4262 60.325 41.2581 60.325 L 15.7098 60.325 L 13.7851 57.6792 L 39.3339 57.6792 C 41.5019 57.6792 43.7234 55.9337 44.3147 53.7657 L 56.6119 8.67596 C 57.2031 6.50795 55.9338 4.7625 53.7657 4.7625 L 24.551 4.7625 Z M 35.8068 16.6688 C 43.5276 16.6688 41.8719 20.5517 41.8719 20.5517 L 40.1218 26.9689 C 39.0633 30.85 35.1517 30.7862 35.1517 30.7862 L 28.1594 30.7862 C 23.9657 30.7862 22.9668 34.9343 22.9668 34.9343 L 22.0183 38.4121 L 19.5554 38.4121 C 19.5554 38.4121 15.3568 38.677 17.4005 31.1836 C 19.4446 23.6884 24.0243 24.228 24.0243 24.228 L 33.877 24.228 L 34.1499 23.227 L 27.0997 23.227 L 28.0006 19.8897 C 28.0006 19.8897 28.418 16.6688 35.8068 16.6688 Z M 31.3067 18.9125 C 30.6055 18.9125 29.8825 19.483 29.6902 20.1879 C 29.5975 20.5263 29.6424 20.851 29.8152 21.0902 C 29.9879 21.3295 30.2743 21.4637 30.611 21.4633 C 31.3123 21.4633 32.0353 20.8928 32.2275 20.1879 C 32.4198 19.483 32.0079 18.9125 31.3067 18.9125 Z M 44.3759 24.0295 C 44.8297 24.0277 48.4004 24.2326 46.4842 31.2586 C 44.4403 38.7529 39.86 38.2137 39.86 38.2137 L 30.0079 38.2137 L 29.7349 39.2147 L 36.7856 39.2147 L 35.8848 42.5519 C 35.8848 42.5519 35.4674 45.7729 28.0786 45.7729 C 20.3578 45.7729 22.0135 41.8899 22.0135 41.8899 L 23.7636 35.4728 C 24.8221 31.5917 28.7336 31.6554 28.7336 31.6554 L 35.726 31.6554 C 39.9197 31.6554 40.918 27.5074 40.918 27.5074 L 41.8662 24.0306 L 44.3291 24.0306 C 44.3291 24.0306 44.3457 24.0296 44.3759 24.0295 Z M 33.274 40.9794 C 32.5728 40.9794 31.8493 41.5498 31.6573 42.2538 C 31.5645 42.5922 31.6095 42.917 31.7824 43.1563 C 31.9553 43.3956 32.2418 43.5297 32.5787 43.5291 C 33.2799 43.5291 34.0026 42.9596 34.1951 42.2538 C 34.3874 41.5488 33.9753 40.9794 33.274 40.9794 Z" />
+ <path
+ style="display:none;fill:#af00ba;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z"
+ id="path5158"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+</svg>
diff --git a/plugins/pynb/gresource.xml b/plugins/pynb/gresource.xml
new file mode 100644
index 0000000..71eb8ed
--- /dev/null
+++ b/plugins/pynb/gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/re/chrysalide/framework/gui/panels">
+ <file compressed="true" alias="pynb-panel.ui">panel.ui</file>
+ <file compressed="true" alias="pynb-params.ui">params.ui</file>
+ <file compressed="true" alias="pynb-prefs.ui">prefs.ui</file>
+ </gresource>
+ <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions">
+ <file compressed="true" alias="pynb-symbolic.svg">data/images/pynb-symbolic.svg</file>
+ </gresource>
+</gresources>
diff --git a/plugins/pynb/panel-int.h b/plugins/pynb/panel-int.h
new file mode 100644
index 0000000..23884c7
--- /dev/null
+++ b/plugins/pynb/panel-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * panel-int.h - prototypes internes pour le panneau dédié à la présentation de notes
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYNB_PANEL_INT_H
+#define _PLUGINS_PYNB_PANEL_INT_H
+
+
+#include <gtkext/panel-int.h>
+
+
+#include "panel.h"
+
+
+
+/* Panneau de cartopgraphie des dispositions d'échantillons (instance) */
+struct _GtkPythonNotebookPanel
+{
+ GtkTiledPanel parent; /* A laisser en premier */
+
+};
+
+/* Panneau de cartopgraphie des dispositions d'échantillons (classe) */
+struct _GtkPythonNotebookPanelClass
+{
+ GtkTiledPanelClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place nouvelle instance de panneau de présentation. */
+bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *);
+
+
+
+#endif /* _PLUGINS_PYNB_PANEL_INT_H */
diff --git a/plugins/pynb/panel.c b/plugins/pynb/panel.c
new file mode 100644
index 0000000..8ce9cdb
--- /dev/null
+++ b/plugins/pynb/panel.c
@@ -0,0 +1,210 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * panel.c - panneau dédié à la présentation de notes
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "panel.h"
+
+
+#include <gtkext/helpers.h>
+
+
+#include "panel-int.h"
+
+
+
+/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
+
+
+/* Initialise la classe des panneaux pour binaires. */
+static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *);
+
+/* Initialise une instance de panneau pour binaire. */
+static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *);
+
+/* Supprime toutes les références externes. */
+static void gtk_python_notebook_panel_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_python_notebook_panel_finalize(GObject *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Fournit les composants adaptés pour la barre de titre. */
+//static GListStore *gtk_python_notebook_panel_get_title_widgets(GtkTiledPanel *, bool);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COEUR D'UN PANNEAU D'AFFICHAGE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un panneau de présentation de notes. */
+G_DEFINE_TYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK_TYPE_TILED_PANEL);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des panneaux pour binaires. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+ GtkTiledPanelClass *panel; /* Version parente de classe */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_python_notebook_panel_dispose;
+ object->finalize = gtk_python_notebook_panel_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-panel.ui");
+
+ //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookPanel, summary);
+
+ panel = GTK_TILED_PANEL_CLASS(class);
+
+ //panel->get_widgets = (get_tiled_panel_widgets_cb)gtk_python_notebook_panel_get_title_widgets;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = instance à initialiser. *
+* *
+* Description : Initialise une instance de panneau pour binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *panel)
+{
+ gtk_widget_init_template(GTK_WIDGET(panel));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PANEL);
+
+ G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Crée une nouvelle instance de panneau de présentation. *
+* *
+* Retour : Composant GTK mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GtkTiledPanel *gtk_python_notebook_panel_new(void)
+{
+ GtkTiledPanel *result; /* Instance à retourner */
+
+ result = g_object_new(GTK_TYPE_PYTHON_NOTEBOOK_PANEL, NULL);
+
+ if (!gtk_python_notebook_panel_create(GTK_PYTHON_NOTEBOOK_PANEL(result)))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = panneau de recherche et récupération à remplir. *
+* *
+* Description : Met en place nouvelle instance de panneau de présentation. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *panel)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ return result;
+
+}
diff --git a/plugins/pynb/panel.h b/plugins/pynb/panel.h
new file mode 100644
index 0000000..f9f8016
--- /dev/null
+++ b/plugins/pynb/panel.h
@@ -0,0 +1,47 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * panel.h - prototypes pour le panneau dédié à la présentation de notes
+ *
+ * Copyright (C) 2025 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYNB_PANEL_H
+#define _PLUGINS_PYNB_PANEL_H
+
+
+#include <gtk/gtk.h>
+
+
+#include <glibext/helpers.h>
+#include <gtkext/panel.h>
+
+
+
+#define GTK_TYPE_PYTHON_NOTEBOOK_PANEL (gtk_python_notebook_panel_get_type())
+
+DECLARE_GTYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK, PYTHON_NOTEBOOK_PANEL);
+
+
+/* Crée une nouvelle instance de panneau de présentation. */
+GtkTiledPanel *gtk_python_notebook_panel_new(void);
+
+
+
+#endif /* _PLUGINS_PYNB_PANEL_H */
diff --git a/plugins/pynb/panel.ui b/plugins/pynb/panel.ui
new file mode 100644
index 0000000..d16af80
--- /dev/null
+++ b/plugins/pynb/panel.ui
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkPythonNotebookPanel" parent="GtkTiledPanel">
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hscrollbar-policy">automatic</property>
+ <property name="vscrollbar-policy">automatic</property>
+ <property name="hexpand">true</property>
+ <property name="vexpand">true</property>
+ <property name="has-frame">true</property>
+ <property name="margin-start">8</property>
+ <property name="margin-top">8</property>
+ <property name="margin-end">8</property>
+ <property name="margin-bottom">8</property>
+
+ </object>
+ </child>
+ </template>
+
+</interface>
diff --git a/plugins/pynb/params-int.h b/plugins/pynb/params-int.h
new file mode 100644
index 0000000..7f3bc8b
--- /dev/null
+++ b/plugins/pynb/params-int.h
@@ -0,0 +1,50 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * params-int.h - définitions internes pour l'édition des paramètres initiaux d'une présentation de notes
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PARAMS_INT_H
+#define _PLUGINS_PYNB_PARAMS_INT_H
+
+
+#include "params.h"
+
+
+
+/* Composant pour les paramètres de chargement d'un binaire (instance) */
+struct _GtkPythonNotebookParameters
+{
+ GtkGrid parent; /* A laisser en premier */
+
+ //GtkEntry *filename; /* CHemin d'un binaire */
+
+};
+
+/* Composant pour les paramètres de chargement d'un binaire (classe) */
+struct _GtkPythonNotebookParametersClass
+{
+ GtkGridClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* _PLUGINS_PYNB_PARAMS_INT_H */
diff --git a/plugins/pynb/params.c b/plugins/pynb/params.c
new file mode 100644
index 0000000..690ff95
--- /dev/null
+++ b/plugins/pynb/params.c
@@ -0,0 +1,171 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * params.c - édition des paramètres initiaux d'une présentation de notes
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "params.h"
+
+
+#include "panel.h"
+#include "params-int.h"
+#include <gtkext/helpers.h>
+#include <gui/window.h>
+
+
+
+/* Initialise la classe des composants d'édition de paramètres. */
+static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *);
+
+/* Initialise une instance de composant d'édition de paramètres. */
+static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *);
+
+/* Supprime toutes les références externes. */
+static void gtk_python_notebook_parameters_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_python_notebook_parameters_finalize(GObject *);
+
+/* Réagit à une demande de création de notes. */
+static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *, GtkPythonNotebookParameters *);
+
+
+
+/* Indique le type du composant d'édition des paramètres de chargement. */
+G_DEFINE_TYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK_TYPE_GRID);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe des composants d'édition de paramètres. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_python_notebook_parameters_dispose;
+ object->finalize = gtk_python_notebook_parameters_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-params.ui");
+
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_parameters_on_create_clicked));
+
+ //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookParameters, filename);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : params = composant GTK à initialiser. *
+* *
+* Description : Initialise une instance de composant d'édition de paramètres.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *params)
+{
+ gtk_widget_init_template(GTK_WIDGET(params));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS);
+
+ G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : button = bouton GTK concerné par l'appel. *
+* params = paramètres du panneau à mettre en place. *
+* *
+* Description : Réagit à une demande de création de notes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *button, GtkPythonNotebookParameters *params)
+{
+ GtkRoot *root; /* Racine du composant */
+ GtkTiledPanel *tiled; /* Panneau d'affichage complet */
+
+ root = gtk_widget_get_root(GTK_WIDGET(button));
+
+ tiled = gtk_python_notebook_panel_new();
+
+ gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled);
+
+}
diff --git a/plugins/pynb/params.h b/plugins/pynb/params.h
new file mode 100644
index 0000000..2d4b39b
--- /dev/null
+++ b/plugins/pynb/params.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * params.h - prototypes pour l'édition des paramètres initiaux d'une présentation de notes
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PARAMS_H
+#define _PLUGINS_PYNB_PARAMS_H
+
+
+#include <gtk/gtk.h>
+
+
+#include <glibext/helpers.h>
+
+
+
+#define GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS (gtk_python_notebook_parameters_get_type())
+
+DECLARE_GTYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK, PYTHON_NOTEBOOK_PARAMETERS);
+
+
+
+#endif /* _PLUGINS_PYNB_PARAMS_H */
diff --git a/plugins/pynb/params.ui b/plugins/pynb/params.ui
new file mode 100644
index 0000000..21a4788
--- /dev/null
+++ b/plugins/pynb/params.ui
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkPythonNotebookParameters" parent="GtkGrid">
+ <property name="margin-bottom">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">12</property>
+ <property name="column-spacing">12</property>
+ <property name="row-spacing">8</property>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Load and analyze a new notebook file:</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEntry" id="filename">
+ <property name="secondary-icon-name">document-open-symbolic</property>
+ <property name="placeholder-text">File location</property>
+ <property name="hexpand">TRUE</property>
+ <property name="hexpand-set">TRUE</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ <style>
+ <class name="background"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="create">
+ <property name="label">Create</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ </layout>
+ <signal name="clicked" handler="gtk_python_notebook_parameters_on_create_clicked"/>
+ </object>
+ </child>
+
+ </template>
+
+</interface>
diff --git a/plugins/pynb/prefs-int.h b/plugins/pynb/prefs-int.h
new file mode 100644
index 0000000..9c44c68
--- /dev/null
+++ b/plugins/pynb/prefs-int.h
@@ -0,0 +1,48 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * prefs-int.h - définitions internes pour la configuration des paramètres liés aux notes
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PREFS_INT_H
+#define _PLUGINS_PYNB_PREFS_INT_H
+
+
+#include "prefs.h"
+
+
+
+/* Composant d'édition des paramètres liés aux notes (instance) */
+struct _GtkPythonNotebookTweakPanel
+{
+ GtkBox parent; /* A laisser en premier */
+
+};
+
+/* Composant d'édition des paramètres liés aux notes (classe) */
+struct _GtkPythonNotebookTweakPanelClass
+{
+ GtkBoxClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* _PLUGINS_PYNB_PREFS_INT_H */
diff --git a/plugins/pynb/prefs.c b/plugins/pynb/prefs.c
new file mode 100644
index 0000000..9363e9b
--- /dev/null
+++ b/plugins/pynb/prefs.c
@@ -0,0 +1,143 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * prefs.c - configuration des paramètres liés aux notes
+ *
+ * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "prefs.h"
+
+
+#include <gtkext/helpers.h>
+
+
+#include "prefs-int.h"
+
+
+
+/* Procède à l'initialisation de classe des configurations. */
+static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *);
+
+/* Procède à l'initialisation des configurations de sécurité. */
+static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *);
+
+/* Supprime toutes les références externes. */
+static void gtk_python_notebook_tweak_panel_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_python_notebook_tweak_panel_finalize(GObject *);
+
+
+
+/* Indique le type du composant de configuration des notes. */
+G_DEFINE_TYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK_TYPE_BOX);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation de classe des configurations. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_python_notebook_tweak_panel_dispose;
+ object->finalize = gtk_python_notebook_tweak_panel_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-prefs.ui");
+
+ /* Stockage sécurisé */
+
+ //gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_tweak_panel_on_new_passwords_changed));
+
+ //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookTweakPanel, current_primary_passwd);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = composant GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation des configurations de sécurité. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *panel)
+{
+ gtk_widget_init_template(GTK_WIDGET(panel));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL);
+
+ G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->finalize(object);
+
+}
diff --git a/plugins/pe/section.h b/plugins/pynb/prefs.h
index 2ac21d3..6551983 100644
--- a/plugins/pe/section.h
+++ b/plugins/pynb/prefs.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * section.h - prototypes pour la gestion des sections d'un PE
+ * prefs.h - prototypes pour la configuration des paramètres liés aux notes
*
- * Copyright (C) 2010-2017 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,18 +21,21 @@
*/
-#ifndef _PLUGINS_PE_SECTION_H
-#define _PLUGINS_PE_SECTION_H
+#ifndef _PLUGINS_PYNB_PREFS_H
+#define _PLUGINS_PYNB_PREFS_H
-#include "format.h"
-#include "pe_def.h"
+#include <gtk/gtk.h>
+#include <glibext/helpers.h>
-/* Recherche une section donnée au sein de binaire par indice. */
-image_section_header *read_all_pe_sections(const GPeFormat *, vmpa2t *);
+#define GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL (gtk_python_notebook_tweak_panel_get_type())
-#endif /* _PLUGINS_PE_SECTION_H */
+DECLARE_GTYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK, PYTHON_NOTEBOOK_TWEAK_PANEL);
+
+
+
+#endif /* _PLUGINS_PYNB_PREFS_H */
diff --git a/plugins/pynb/prefs.ui b/plugins/pynb/prefs.ui
new file mode 100644
index 0000000..b8a6962
--- /dev/null
+++ b/plugins/pynb/prefs.ui
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkPythonNotebookTweakPanel" parent="GtkBox">
+
+ <property name="orientation">vertical</property>
+
+ <!-- Conservation de paramètres sécurisée -->
+ <child>
+ <object class="GtkGrid">
+ <property name="margin-start">20</property>
+ <property name="margin-end">20</property>
+ <property name="margin-top">20</property>
+ <property name="margin-bottom">20</property>
+ <property name="row-spacing">10</property>
+ <property name="column-spacing">10</property>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Notes config</property>
+ <property name="use-markup">true</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+
+ </object>
+ </child>
+
+ </template>
+</interface>