summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/pe/core-int.h56
-rw-r--r--plugins/pe/core.c280
-rw-r--r--plugins/pe/core.h14
-rw-r--r--plugins/pychrysalide/Makefile.am22
-rw-r--r--plugins/pychrysalide/bindings.c1090
-rw-r--r--plugins/pychrysalide/bindings.h68
-rw-r--r--plugins/pychrysalide/core-int.h58
-rw-r--r--plugins/pychrysalide/core-ui-int.h56
-rw-r--r--plugins/pychrysalide/core-ui.c318
-rw-r--r--plugins/pychrysalide/core-ui.h64
-rw-r--r--plugins/pychrysalide/core.c1439
-rw-r--r--plugins/pychrysalide/core.h30
-rw-r--r--plugins/pychrysalide/helpers.c150
-rw-r--r--plugins/pychrysalide/helpers.h38
-rw-r--r--plugins/pychrysalide/plugins/Makefile.am4
-rw-r--r--plugins/pychrysalide/plugins/module.c11
-rw-r--r--plugins/pychrysalide/plugins/plugin.c958
-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
21 files changed, 3672 insertions, 1595 deletions
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 e8e6f5e..42f712d 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 l'extinction du greffon. */
+static void 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 : filename = nom du fichier à charger. *
+* *
+* 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 = module système correspondant. *
+* *
+* 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,60 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
/******************************************************************************
* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de l'extinction du greffon. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_disable(GPePlugin *plugin)
+{
+
+}
+
+
+
+#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. *
@@ -115,3 +378,6 @@ 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 2d9aeb5..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/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am
index 183a4ef..7b1a331 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,9 @@ endif
pychrysalide_la_SOURCES = \
access.h access.c \
+ bindings.h bindings.c \
constants.h constants.c \
+ core-int.h \
core.h core.c \
helpers.h helpers.c \
star.h star.c \
@@ -71,6 +73,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/bindings.c b/plugins/pychrysalide/bindings.c
new file mode 100644
index 0000000..295667f
--- /dev/null
+++ b/plugins/pychrysalide/bindings.c
@@ -0,0 +1,1090 @@
+
+/* 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"
+
+
+#ifdef PYTHON_PACKAGE
+# include <dlfcn.h>
+#endif
+#include <pygobject.h>
+#include <stdio.h>
+
+
+#include <config.h>
+#include <common/cpp.h>
+#include <plugins/pglist.h> // REMME ?
+#include <plugins/self.h> // REMME ?
+
+
+#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);
+
+/* 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 ------------------------ */
+
+
+
+/* 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 : 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;
+
+ /**
+ * 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 = do_global_init();
+
+ if (!status)
+ {
+ Py_DECREF(result);
+ result = NULL;
+ }
+
+ }
+
+ restore_original_pygobject_type(py_gobj_def);
+
+ exit:
+
+ if (result == NULL && !details->standalone)
+ /*log_pychrysalide_exception("Loading failed")*/;
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONS GLOBALES DE CHRYSALIDE */
+/* ---------------------------------------------------------------------------------- */
+
+
+
+
+/******************************************************************************
+* *
+* Paramètres : py_gobj_def = définition de type actuelle. [OUT] *
+* *
+* Description : Restore une ancienne définition de type GObject au besoin. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool do_global_init(void)
+{
+
+ return true;
+ return false;
+
+
+#if 0
+
+
+ bool result; /* Bilan à retourner */
+ 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 */
+
+ ret = Py_AtExit(PyExit_pychrysalide);
+ if (ret == -1)
+ {
+ PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
+ goto exit_and_restore;
+ }
+
+ /**
+ * 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");
+
+
+ // err msg
+
+
+ Py_DECREF(result);
+ result = NULL;
+
+ goto exit_and_restore;
+ }
+
+ self = g_plugin_module_new(info.dli_fname);
+ assert(self != NULL);
+
+ register_plugin(self);
+
+#endif
+
+
+ if (!load_core_components(ACC_GLOBAL_VARS))
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to load core components.");
+ goto exit;
+ }
+
+ init_all_plugins(false);
+
+ lock_plugin_list_for_reading();
+
+ self = get_plugin_by_name("PyChrysalide", NULL);
+ assert(self != NULL);
+
+ self_flags = g_plugin_module_get_flags(self);
+ self_flags &= ~(PSF_FAILURE | PSF_LOADED);
+ self_flags |= (status ? PSF_LOADED : PSF_FAILURE);
+
+ g_plugin_module_override_flags(self, self_flags);
+
+ unref_object(self);
+
+ unlock_plugin_list_for_reading();
+
+ load_remaning_plugins();
+
+
+
+
+
+ done:
+
+ return result;
+
+#endif
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Point de sortie pour l'initialisation de Python. *
+* *
+* Retour : ? *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void PyExit_pychrysalide(void)
+{
+ //assert(_standalone);
+
+ /*
+ extern void set_current_project(void *project);
+
+ set_current_project(NULL);
+ */
+
+#ifdef TRACK_GOBJECT_LEAKS
+ remember_gtypes_for_leaks();
+#endif
+
+ exit_all_plugins();
+
+ //unload_all_core_components(true);
+
+#ifdef TRACK_GOBJECT_LEAKS
+ dump_remaining_gtypes();
+#endif
+
+}
diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h
new file mode 100644
index 0000000..1c63956
--- /dev/null
+++ b/plugins/pychrysalide/bindings.h
@@ -0,0 +1,68 @@
+
+/* 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 <stdbool.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 ? */
+
+
+} pyinit_details_t;
+
+/* Implémente le point d'entrée pour l'initialisation de Python. */
+PyObject *init_python_pychrysalide_module(const pyinit_details_t *);
+
+
+
+
+
+bool do_global_init(void);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_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..32d3516
--- /dev/null
+++ b/plugins/pychrysalide/core-ui.c
@@ -0,0 +1,318 @@
+
+/* 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, 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;
+
+ 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 f63fd9e..e815e25 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,826 +50,568 @@
#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 */
+ iface->handle_native = (handle_native_plugins_cb)g_pychrysalide_plugin_handle_native_plugins_loaded_event;
-#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);
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon de support Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- result = PyUnicode_FromString(version);
+static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
- return result;
+ plugin->py_module = NULL;
}
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
* *
-* Description : Fournit la version du greffon pour Python. *
+* Description : Supprime toutes les références externes. *
* *
-* Retour : Numéro de version. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *plugin)
{
- 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", _chrysalide_plugin.version);
-
- result = PyUnicode_FromString(version);
-
- 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 : Détermine si l'interpréteur lancé est celui pris en compte. *
+* Description : Procède à la libération totale de la mémoire. *
* *
-* 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_finalize(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]);
+ G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->finalize(G_OBJECT(plugin));
- 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';
+/******************************************************************************
+* *
+* 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. *
+* *
+******************************************************************************/
- result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
+GPluginModule *g_pychrysalide_plugin_new(GModule *module)
+{
+ GPyChrysalidePlugin *result; /* Structure à retourner */
- icas_exit:
+ result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL);
- 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.");
+ if (!g_pychrysalide_plugin_create(result, module))
+ g_clear_object(&result);
- return result;
+ return G_PLUGIN_MODULE(result);
}
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = module système correspondant. *
* *
-* Description : Assure une pleine initialisation des objets de Python-GI. *
+* Description : Met en place un module pour un greffon de support Python. *
* *
* Retour : Bilan de l'opération. *
* *
-* Remarques : - *
+* Remarques : Le transfert de propriétée du module est total. *
* *
******************************************************************************/
-static bool install_metaclass_for_python_gobjects(void)
+bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module)
{
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.
- */
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PyChrysalide",
+ "Chrysalide bindings to Python",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("api/python/pychrysalide"),
+ NO_REQ,
+ module);
+
+ return result;
+
+}
+
+
+
- /**
- * 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
- return result;
-}
+
+
+#if 0
+
/******************************************************************************
* *
-* Paramètres : version = idenfiant de la version de GTK à stipuler. *
+* Paramètres : plugin = greffon à manipuler. *
+* action = type d'action attendue. *
+* type = type d'objet à mettre en place. *
* *
-* Description : Définit la version attendue de GTK à charger dans Python. *
+* Description : Crée une instance à partir d'un type dynamique externe. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Instance d'objet gérée par l'extension ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
-#ifdef INCLUDE_GTK_SUPPORT
-static bool set_version_for_gtk_namespace(const char *version)
+
+G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type)
{
- bool result; /* Bilan à retourner */
- PyObject *gi_mod; /* Module Python-GObject */
- PyObject *args; /* Arguments à fournir */
+ gpointer result; /* Instance à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyTypeObject *pytype; /* Classe Python concernée */
+ PyObject *instance; /* Initialisation forcée */
- result = false;
+ result = NULL;
- /**
- * 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.
- *
- */
+ gstate = PyGILState_Ensure();
- gi_mod = PyImport_ImportModule("gi");
+ pytype = pygobject_lookup_class(type);
- if (gi_mod != NULL)
+ if (pytype != NULL)
{
- args = Py_BuildValue("ss", "Gtk", version);
-
- run_python_method(gi_mod, "require_version", args);
-
- result = (PyErr_Occurred() == NULL);
+ instance = PyObject_CallObject((PyObject *)pytype, NULL);
+ assert(instance != NULL);
- Py_DECREF(args);
- Py_DECREF(gi_mod);
+ result = pygobject_get(instance);
}
+ PyGILState_Release(gstate);
+
return result;
}
+
#endif
+
+
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.*
* *
-* Description : Point de sortie pour l'initialisation de Python. *
+* Description : Présente dans le journal une exception survenue. *
* *
-* Retour : ? *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static void PyExit_pychrysalide(void)
+void log_pychrysalide_exception(const char *prefix, ...)
{
- assert(_standalone);
-
- /*
- extern void set_current_project(void *project);
+ 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 */
- set_current_project(NULL);
- */
+ assert(PyGILState_Check() == 1);
-#ifdef TRACK_GOBJECT_LEAKS
- remember_gtypes_for_leaks();
-#endif
+ if (PyErr_Occurred())
+ {
+ /* Base de la communication */
- exit_all_plugins();
+ va_start(ap, prefix);
- //unload_all_core_components(true);
+ vasprintf(&msg, prefix, ap);
-#ifdef TRACK_GOBJECT_LEAKS
- dump_remaining_gtypes();
-#endif
+ va_end(ap);
-}
+ /* Détails complémentaires */
+ PyErr_Fetch(&err_type, &err_value, &err_traceback);
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Point d'entrée pour l'initialisation de Python. *
-* *
-* Retour : ? *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
+ PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
-#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."
+ if (err_traceback == NULL)
+ {
+ err_traceback = Py_None;
+ Py_INCREF(err_traceback);
+ }
-PyMODINIT_FUNC PyInit_pychrysalide(void)
-{
- PyObject *result; /* Module Python à retourner */
- PyTypeObject *py_gobj_def; /* Définition GObject courante */
- GQuark pygobject_class_key; /* Copie d'un accès GI interne */
- bool status; /* Bilan des inclusions */
- 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 */
+ PyException_SetTraceback(err_value, err_traceback);
- static PyMethodDef py_chrysalide_methods[] = {
- PY_CHRYSALIDE_REVISION_METHOD,
- PY_CHRYSALIDE_VERSION_METHOD,
- PY_CHRYSALIDE_MOD_VERSION_METHOD,
- { NULL }
- };
+ if (err_value == NULL)
+ msg = stradd(msg, _(": no extra information is provided..."));
- static PyModuleDef py_chrysalide_module = {
+ else
+ {
+ err_string = PyObject_Str(err_value);
+ err_msg = PyUnicode_AsUTF8(err_string);
- .m_base = PyModuleDef_HEAD_INIT,
+ msg = stradd(msg, ": ");
+ msg = stradd(msg, err_msg);
- .m_name = "pychrysalide",
- .m_doc = PYCHRYSALIDE_DOC,
+ Py_DECREF(err_string);
- .m_size = -1,
+ }
- .m_methods = py_chrysalide_methods
+ /**
+ * 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);
- /**
- * 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.
- */
+ Py_XDECREF(err_traceback);
+ Py_XDECREF(err_value);
+ Py_XDECREF(err_type);
- result = get_access_to_python_module(py_chrysalide_module.m_name);
+ log_plugin_simple_message(LMT_ERROR, msg);
- if (result != NULL)
- {
- Py_INCREF(result);
- return result;
- }
+ free(msg);
- if (!is_current_abi_suitable())
- {
- /**
- * Un message d'erreur est défini par is_current_abi_suitable() en cas
- * d'interpréteur pas adapté.
- */
- 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())
- {
- PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects.");
- 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 0
- if (!import_namespace_from_gi_repository("GLib", "2.0"))
- goto exit;
-#endif
-#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);
- register_access_to_python_module(py_chrysalide_module.m_name, result);
- /**
- * 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);
- }
- 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();
- if (!status)
- {
- PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
- Py_DECREF(result);
- result = NULL;
- goto exit_and_restore;
- }
- if (_standalone)
- {
- ret = Py_AtExit(PyExit_pychrysalide);
- if (ret == -1)
- {
- PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
- Py_DECREF(result);
- result = NULL;
- goto exit_and_restore;
- }
- /**
- * 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_and_restore;
- }
- self = g_plugin_module_new(info.dli_fname);
- assert(self != NULL);
- register_plugin(self);
-#endif
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
- init_all_plugins(false);
- lock_plugin_list_for_reading();
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de l'activation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- self = get_plugin_by_name("PyChrysalide", NULL);
- assert(self != NULL);
+static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *plugin)
+{
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ int ret; /* Bilan de préparatifs */
- self_flags = g_plugin_module_get_flags(self);
- self_flags &= ~(PSF_FAILURE | PSF_LOADED);
- self_flags |= (status ? PSF_LOADED : PSF_FAILURE);
+ _standalone = false;
- g_plugin_module_override_flags(self, self_flags);
+ /* Chargement du module pour Python */
- unlock_plugin_list_for_reading();
+ ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
- load_remaning_plugins();
+ 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."));
- /**
- * 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().
- */
+ result = false;
+ goto done;
}
- exit_and_restore:
+ Py_Initialize();
- if (py_gobj_def != &PyGObject_Type)
- {
- g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def);
+ gstate = PyGILState_Ensure();
- Py_DECREF((PyObject *)py_gobj_def);
+ 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)
- exit:
+ result = (plugin->py_module != NULL);
+
+ PyGILState_Release(gstate);
- if (result == NULL && !_standalone)
- log_pychrysalide_exception("Loading failed");
+ done:
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_pychrysalide_plugin_disable(GPyChrysalidePlugin *plugin)
+{
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+
+ gstate = PyGILState_Ensure();
+
+ clear_all_accesses_to_python_modules();
+
+ Py_XDECREF(plugin->py_module);
+ plugin->py_module = NULL;
+
+ PyGILState_Release(gstate);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* ---------------------------------------------------------------------------------- */
+
+
/******************************************************************************
* *
* Paramètres : path = chemin supplémentaire pour l'espace de recherche. *
@@ -896,6 +646,85 @@ 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));
+
+ ///result->filename = strdup(filename);
+
+ /**
+ * 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);
+
+ printf(" -> REF: %p %u\n", result, G_OBJECT(result)->ref_count);
+
+ 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. *
@@ -920,7 +749,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 */
@@ -931,19 +759,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,
@@ -1007,8 +835,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)
@@ -1018,18 +845,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:
@@ -1049,116 +877,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. *
* *
@@ -1168,308 +887,48 @@ 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;
- }
+ 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/helpers.c b/plugins/pychrysalide/helpers.c
index deed584..ea2f55d 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -216,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. *
@@ -1403,6 +1450,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 ec245aa..133726a 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -47,6 +47,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 *);
@@ -179,6 +182,26 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObj
}
+/**
+ * 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 *);
@@ -279,6 +302,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..0cd6af3 100644
--- a/plugins/pychrysalide/plugins/Makefile.am
+++ b/plugins/pychrysalide/plugins/Makefile.am
@@ -3,8 +3,10 @@ noinst_LTLIBRARIES = libpychrysaplugins.la
libpychrysaplugins_la_SOURCES = \
constants.h constants.c \
- plugin.h plugin.c \
module.h module.c \
+ plugin.h plugin.c \
+ python-int.h \
+ python.h python.c \
translate.h translate.c
libpychrysaplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
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..48cc992 100644
--- a/plugins/pychrysalide/plugins/plugin.c
+++ b/plugins/pychrysalide/plugins/plugin.c
@@ -25,14 +25,12 @@
#include "plugin.h"
-#include <assert.h>
-#include <libgen.h>
#include <malloc.h>
#include <pygobject.h>
#include <string.h>
-#include <common/extstr.h>
+#include <common/compiler.h>
#include <plugins/dt.h>
#include <plugins/plugin-int.h>
#include <plugins/pglist.h>
@@ -52,85 +50,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);
/* 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 +107,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 +147,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 +207,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 +226,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 +289,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 +353,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 +399,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 +406,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 +458,15 @@ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
}
+
+
+
+
+
+
+
+
+
#if 0
#ifdef INCLUDE_GTK_SUPPORT
@@ -1382,51 +1232,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 +1276,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 +1316,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 +1352,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 +1389,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 +1604,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 +1673,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 +1683,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..a958a8d
--- /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, NULL);
+
+/* 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 */