From 4ea4054f07ba5fa9be2b9a61ad0ca8f1c11afbd0 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 20 Aug 2020 22:46:09 +0200
Subject: Renamed the main file of the Python plugin.

---
 plugins/pychrysalide/Makefile.am       |    2 +-
 plugins/pychrysalide/core.c            | 1030 ++++++++++++++++++++++++++++++++
 plugins/pychrysalide/core.h            |   63 ++
 plugins/pychrysalide/core/logs.c       |    2 +-
 plugins/pychrysalide/core/processors.c |    2 +-
 plugins/pychrysalide/gui/item.c        |    2 +-
 plugins/pychrysalide/plugin.c          |    2 +-
 plugins/pychrysalide/pychrysa.c        | 1030 --------------------------------
 plugins/pychrysalide/pychrysa.h        |   63 --
 9 files changed, 1098 insertions(+), 1098 deletions(-)
 create mode 100644 plugins/pychrysalide/core.c
 create mode 100644 plugins/pychrysalide/core.h
 delete mode 100644 plugins/pychrysalide/pychrysa.c
 delete mode 100644 plugins/pychrysalide/pychrysa.h

diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am
index 5d6c3e6..530a889 100644
--- a/plugins/pychrysalide/Makefile.am
+++ b/plugins/pychrysalide/Makefile.am
@@ -8,9 +8,9 @@ pychrysalide_la_SOURCES =				\
 	access.h access.c					\
 	constants.h constants.c				\
 	constval.h constval.c				\
+	core.h core.c						\
 	helpers.h helpers.c					\
 	plugin.h plugin.c					\
-	pychrysa.h pychrysa.c				\
 	star.h star.c						\
 	strenum.h strenum.c					\
 	struct.h struct.c					\
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
new file mode 100644
index 0000000..fad21bf
--- /dev/null
+++ b/plugins/pychrysalide/core.c
@@ -0,0 +1,1030 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core.c - plugin permettant des extensions en Python
+ *
+ * Copyright (C) 2018-2019 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.h"
+
+
+#include <assert.h>
+#include <errno.h>
+#include <malloc.h>
+#include <pygobject.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#include <config.h>
+#include <i18n.h>
+#include <gleak.h>
+#include <common/cpp.h>
+#include <common/environment.h>
+#include <common/extstr.h>
+#include <core/core.h>
+#include <core/paths.h>
+#include <plugins/pglist.h>
+#include <plugins/self.h>
+
+
+#include "access.h"
+#include "constval.h"
+#include "helpers.h"
+#include "plugin.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 "debug/module.h"
+#include "format/module.h"
+#include "glibext/module.h"
+#include "gtkext/module.h"
+#include "gui/module.h"
+#include "mangling/module.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_LOADED));
+
+
+/* Note la nature du chargement */
+static bool _standalone = true;
+
+/* Réceptacle pour le chargement forcé */
+static PyObject *_chrysalide_module = NULL;
+
+/* Conservation des informations du thread principal */
+static PyThreadState *_main_tstate = 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);
+
+/* Définit la version attendue de GTK à charger dans Python. */
+static bool set_version_for_gtk_namespace(const char *);
+
+/* Point de sortie pour l'initialisation de Python. */
+static void PyExit_pychrysalide(void);
+
+/* Ajoute le module 'plugins' au module Python. */
+static bool add_plugin_module_to_python_module(PyObject *);
+
+/* Complète les chemins de recherches de Python. */
+static void extend_python_path(const char *);
+
+/* Charge autant de greffons composés en Python que possible. */
+static void load_python_plugins(GPluginModule *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  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", _chrysalide_plugin.version);
+
+    result = PyUnicode_FromString(version);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  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"                                    \
+    "os.write(%d, bytes(sys.abiflags, 'UTF-8'))" "\n"
+
+    result = false;
+
+    ret = pipe(fds);
+    if (ret == -1)
+    {
+        perror("pipe()");
+        return false;
+    }
+
+    snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
+
+    ret = PyRun_SimpleString(cmds);
+    if (ret != 0) goto icas_exit;
+
+    got = read(fds[0], content, sizeof(content));
+    if (got < 0)
+    {
+        perror("read()");
+        goto icas_exit;
+    }
+
+    content[got] = '\0';
+
+    result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
+
+ icas_exit:
+
+    if (!result)
+        PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
+                        "the ones of the Python library used during the module compilation.");
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : version = idenfiant de la version de GTK à stipuler.         *
+*                                                                             *
+*  Description : Définit la version attendue de GTK à charger dans Python.    *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool set_version_for_gtk_namespace(const char *version)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *gi_mod;                       /* Module Python-GObject       */
+    PyObject *args;                         /* Arguments à fournir         */
+
+    result = false;
+
+    /**
+     * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement :
+     *
+     *
+     *   PyGIWarning: Gtk was imported without specifying a version first. \
+     *   Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded.
+     *
+     */
+
+    gi_mod = PyImport_ImportModule("gi");
+
+    if (gi_mod != NULL)
+    {
+        args = Py_BuildValue("ss", "Gtk", version);
+
+        run_python_method(gi_mod, "require_version", args);
+
+        result = (PyErr_Occurred() == NULL);
+
+        Py_DECREF(args);
+        Py_DECREF(gi_mod);
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  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
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : super = module dont la définition est à compléter.           *
+*                                                                             *
+*  Description : Ajoute le module 'plugins' au module Python.                 *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool add_plugin_module_to_python_module(PyObject *super)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *module;                       /* Sous-module mis en place    */
+
+#define PYCHRYSALIDE_PLUGINS_DOC                    \
+    "Home for all plugins without another home."
+
+    static PyModuleDef py_chrysalide_deguard_module = {
+
+        .m_base = PyModuleDef_HEAD_INIT,
+
+        .m_name = "pychrysalide.plugins",
+        .m_doc = PYCHRYSALIDE_PLUGINS_DOC,
+
+        .m_size = -1,
+
+    };
+
+    result = false;
+
+    module = build_python_module(super, &py_chrysalide_deguard_module);
+
+    result = (module != NULL);
+
+    assert(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Point d'entrée pour l'initialisation de Python.              *
+*                                                                             *
+*  Retour      : ?                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+#define PYCHRYSALIDE_DOC                                                                                        \
+    "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n"                \
+    "\n"                                                                                                        \
+    "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n"       \
+    "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n"                               \
+    "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
+    "\n"                                                                                                        \
+    "In both cases, this is a good start point to have a look at already existing plugins to quickly learn "    \
+    "how the API works.\n"                                                                                      \
+    "\n"                                                                                                        \
+    "These plugins are located in the 'plugins/python' directory."
+
+PyMODINIT_FUNC PyInit_pychrysalide(void)
+{
+    PyObject *result;                       /* Module Python à retourner   */
+    bool status;                            /* Bilan des inclusions        */
+    int ret;                                /* Bilan de préparatifs        */
+    GPluginModule *self;                    /* Représentation interne      */
+    PluginStatusFlags self_flags;           /* Fanions à mettre à jour     */
+
+    static PyMethodDef py_chrysalide_methods[] = {
+        PY_CHRYSALIDE_REVISION_METHOD,
+        PY_CHRYSALIDE_VERSION_METHOD,
+        PY_CHRYSALIDE_MOD_VERSION_METHOD,
+        { NULL }
+    };
+
+    static PyModuleDef py_chrysalide_module = {
+
+        .m_base = PyModuleDef_HEAD_INIT,
+
+        .m_name = "pychrysalide",
+        .m_doc = PYCHRYSALIDE_DOC,
+
+        .m_size = -1,
+
+        .m_methods = py_chrysalide_methods
+
+    };
+
+    /**
+     * 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(py_chrysalide_module.m_name);
+
+    if (result != NULL)
+    {
+        Py_INCREF(result);
+        return result;
+    }
+
+    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 (!set_version_for_gtk_namespace("3.0"))
+        goto exit;
+
+    if (!load_all_core_components(true))
+    {
+        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);
+
+    status = true;
+
+    if (status) status = add_features_module(result);
+
+    if (status) add_plugin_module_to_python_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_core_module(result);
+    if (status) status = add_debug_module(result);
+    if (status) status = add_format_module(result);
+    if (status) status = add_glibext_module(result);
+    if (status) status = add_gtkext_module(result);
+    if (status) status = add_gui_module(result);
+    if (status) status = add_mangling_module(result);
+
+    if (status) status = ensure_python_plugin_module_is_registered();
+    if (status) status = ensure_python_py_constval_is_registered();
+    if (status) status = ensure_python_string_enum_is_registered();
+    if (status) status = ensure_python_py_struct_is_registered();
+
+    if (status) status = populate_analysis_module();
+    if (status) status = populate_arch_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();
+    if (status) status = populate_glibext_module();
+    if (status) status = populate_gtkext_module();
+    if (status) status = populate_gui_module();
+    if (status) status = populate_mangling_module();
+
+    if (!status)
+    {
+        PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
+        Py_DECREF(result);
+        result = NULL;
+        goto exit;
+    }
+
+    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;
+        }
+
+        /**
+         * 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();
+
+        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);
+
+        unlock_plugin_list_for_reading();
+
+        load_remaning_plugins();
+
+        /**
+         * 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().
+         */
+
+    }
+
+ exit:
+
+    if (result == NULL && !_standalone)
+        log_pychrysalide_exception("Loading failed");
+
+    return result;
+
+}
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : path = chemin supplémentaire pour l'espace de recherche.     *
+*                                                                             *
+*  Description : Complète les chemins de recherches de Python.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void extend_python_path(const char *path)
+{
+    PyObject *list;                         /* Liste de chemins à compléter*/
+    PyObject *new;                          /* Nouveau chemin à intégrer   */
+
+    list = PySys_GetObject("path");
+    assert(list != NULL);
+
+    new = PyUnicode_FromString(path);
+    assert(new != NULL);
+
+    PyList_Append(list, new);
+
+    Py_DECREF(new);
+
+    add_to_env_var("PYTHONPATH", path, ":");
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = instance représentant le greffon Python d'origine.  *
+*                                                                             *
+*  Description : Charge autant de greffons composés en Python que possible.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void load_python_plugins(GPluginModule *plugin)
+{
+#ifdef DISCARD_LOCAL
+    char *edir;                             /* Répertoire de base effectif */
+#endif
+    DIR *dir;                               /* Répertoire à parcourir      */
+    char *paths;                            /* Emplacements de greffons    */
+    char *save;                             /* Sauvegarde pour ré-entrance */
+    char *path;                             /* Chemin à fouiller           */
+    struct dirent *entry;                   /* Elément trouvé              */
+    char *modname;                          /* Nom du module pour Python   */
+    char *filename;                         /* Chemin d'accès reconstruit  */
+    GPluginModule *pyplugin;                /* Lien vers un grffon Python  */
+
+    /* Définition des zones d'influence */
+
+#ifndef DISCARD_LOCAL
+
+    extend_python_path(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "python");
+
+#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);
+
+    }
+
+#endif
+
+    g_plugin_module_log_variadic_message(plugin, LMT_INFO,
+                                         _("PYTHONPATH environment variable set to '%s'"),
+                                         getenv("PYTHONPATH"));
+
+    /* Chargements des extensions Python */
+
+    paths = get_env_var("PYTHONPATH");
+
+    save = NULL;   /* gcc... */
+
+    for (path = strtok_r(paths, ":", &save);
+         path != NULL; 
+         path = strtok_r(NULL, ":", &save))
+    {
+        dir = opendir(path);
+        if (dir == NULL)
+        {
+            perror("opendir");
+            continue;
+        }
+
+        g_plugin_module_log_variadic_message(plugin, LMT_INFO, 
+                                             _("Looking for Python plugins in '%s'..."),
+                                             path);
+
+        while (1)
+        {
+            errno = 0;
+
+            entry = readdir(dir);
+
+            if (entry == NULL)
+            {
+                if (errno != 0)
+                    perror("readdir");
+
+                break;
+
+            }
+
+            if (entry->d_type != DT_DIR) continue;
+            if (entry->d_name[0] == '.') continue;
+
+            modname = strdup(entry->d_name);
+            modname = stradd(modname, ".");
+            modname = stradd(modname, "__init__");
+
+            filename = strdup(path);
+            filename = stradd(filename, G_DIR_SEPARATOR_S);
+            filename = stradd(filename, entry->d_name);
+
+            pyplugin = g_python_plugin_new(modname, filename);
+
+            if (pyplugin == NULL)
+                g_plugin_module_log_variadic_message(plugin, LMT_ERROR, 
+                                                     _("No suitable Python plugin found in '%s'"),
+                                                     filename);
+            else
+            {
+                g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, 
+                                                     _("Loaded the Python plugin found in the '<b>%s</b>' directory"),
+                                                     filename);
+
+                /**
+                 * Comme le greffon n'est pas passé par la résolution des dépendances,
+                 * on simule l'effet attendu.
+                 */
+                g_object_ref(G_OBJECT(plugin));
+
+                _register_plugin(pyplugin);
+
+            }
+
+            free(filename);
+            free(modname);
+
+        }
+
+         closedir(dir);
+
+    }
+
+    free(paths);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  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           */
+    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();
+
+    PyEval_InitThreads();
+
+    PySys_SetArgv(0, (wchar_t *[]) { NULL });
+
+    _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);
+
+    _main_tstate = PyThreadState_Get();
+
+    PyEval_ReleaseLock();
+
+ 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)
+{
+    clear_all_accesses_to_python_modules();
+
+    Py_XDECREF(_chrysalide_module);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : plugin = greffon à manipuler.                                *
+*                action = type d'action attendue.                             *
+*                                                                             *
+*  Description : Accompagne la fin du chargement des modules natifs.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+G_MODULE_EXPORT void chrysalide_plugin_on_native_loaded(GPluginModule *plugin, PluginAction action)
+{
+    PyThreadState *tstate;                  /* Contexte d'environnement    */
+
+    if (!_standalone)
+    {
+        tstate = get_pychrysalide_main_tstate();
+        PyEval_RestoreThread(tstate);
+    }
+
+    load_python_plugins(plugin);
+
+    if (!_standalone)
+        PyEval_SaveThread();
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Fournit les informations du thread principal.                *
+*                                                                             *
+*  Retour      : Indications utiles à Python.                                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyThreadState *get_pychrysalide_main_tstate(void)
+{
+    PyThreadState *result;                  /* Indications à retourner     */
+
+    result = _main_tstate;
+
+    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      */
+
+    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
+         * g_python_plugin_new() 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
new file mode 100644
index 0000000..368a7c3
--- /dev/null
+++ b/plugins/pychrysalide/core.h
@@ -0,0 +1,63 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core.h - prototypes pour le plugin permettant des extensions en Python
+ *
+ * Copyright (C) 2018-2019 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_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_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 <plugins/plugin.h>
+#include <plugins/plugin-int.h>
+
+
+
+/* Point d'entrée pour l'initialisation de Python. */
+PyMODINIT_FUNC PyInit_pychrysalide(void);
+
+/* Prend acte du chargement du greffon. */
+G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+
+/* 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_native_loaded(GPluginModule *, PluginAction);
+
+/* Fournit les informations du thread principal. */
+PyThreadState *get_pychrysalide_main_tstate(void);
+
+/* Présente dans le journal une exception survenue. */
+void log_pychrysalide_exception(const char *, ...);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_CORE_H */
diff --git a/plugins/pychrysalide/core/logs.c b/plugins/pychrysalide/core/logs.c
index 4e0bc7b..6d88693 100644
--- a/plugins/pychrysalide/core/logs.c
+++ b/plugins/pychrysalide/core/logs.c
@@ -34,8 +34,8 @@
 
 #include "constants.h"
 #include "../access.h"
+#include "../core.h"
 #include "../helpers.h"
-#include "../pychrysa.h"
 
 
 
diff --git a/plugins/pychrysalide/core/processors.c b/plugins/pychrysalide/core/processors.c
index b1f85bc..fb867db 100644
--- a/plugins/pychrysalide/core/processors.c
+++ b/plugins/pychrysalide/core/processors.c
@@ -35,8 +35,8 @@
 
 
 #include "../access.h"
+#include "../core.h"
 #include "../helpers.h"
-#include "../pychrysa.h"
 #include "../arch/processor.h"
 
 
diff --git a/plugins/pychrysalide/gui/item.c b/plugins/pychrysalide/gui/item.c
index 45190e4..2e32167 100644
--- a/plugins/pychrysalide/gui/item.c
+++ b/plugins/pychrysalide/gui/item.c
@@ -33,8 +33,8 @@
 
 
 #include "../access.h"
+#include "../core.h"
 #include "../helpers.h"
-#include "../pychrysa.h"
 #include "../analysis/binary.h"
 #include "../gtkext/displaypanel.h"
 
diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c
index 77647b5..463fece 100644
--- a/plugins/pychrysalide/plugin.c
+++ b/plugins/pychrysalide/plugin.c
@@ -38,8 +38,8 @@
 
 #include "access.h"
 #include "constants.h"
+#include "core.h"
 #include "helpers.h"
-#include "pychrysa.h"
 #include "core/constants.h"
 
 
diff --git a/plugins/pychrysalide/pychrysa.c b/plugins/pychrysalide/pychrysa.c
deleted file mode 100644
index d1958ee..0000000
--- a/plugins/pychrysalide/pychrysa.c
+++ /dev/null
@@ -1,1030 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * pychrysa.c - plugin permettant des extensions en Python
- *
- * Copyright (C) 2018-2019 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 "pychrysa.h"
-
-
-#include <assert.h>
-#include <errno.h>
-#include <malloc.h>
-#include <pygobject.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <unistd.h>
-
-
-#include <config.h>
-#include <i18n.h>
-#include <gleak.h>
-#include <common/cpp.h>
-#include <common/environment.h>
-#include <common/extstr.h>
-#include <core/core.h>
-#include <core/paths.h>
-#include <plugins/pglist.h>
-#include <plugins/self.h>
-
-
-#include "access.h"
-#include "constval.h"
-#include "helpers.h"
-#include "plugin.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 "debug/module.h"
-#include "format/module.h"
-#include "glibext/module.h"
-#include "gtkext/module.h"
-#include "gui/module.h"
-#include "mangling/module.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_LOADED));
-
-
-/* Note la nature du chargement */
-static bool _standalone = true;
-
-/* Réceptacle pour le chargement forcé */
-static PyObject *_chrysalide_module = NULL;
-
-/* Conservation des informations du thread principal */
-static PyThreadState *_main_tstate = 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);
-
-/* Définit la version attendue de GTK à charger dans Python. */
-static bool set_version_for_gtk_namespace(const char *);
-
-/* Point de sortie pour l'initialisation de Python. */
-static void PyExit_pychrysalide(void);
-
-/* Ajoute le module 'plugins' au module Python. */
-static bool add_plugin_module_to_python_module(PyObject *);
-
-/* Complète les chemins de recherches de Python. */
-static void extend_python_path(const char *);
-
-/* Charge autant de greffons composés en Python que possible. */
-static void load_python_plugins(GPluginModule *);
-
-
-
-/******************************************************************************
-*                                                                             *
-*  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", _chrysalide_plugin.version);
-
-    result = PyUnicode_FromString(version);
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  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"                                    \
-    "os.write(%d, bytes(sys.abiflags, 'UTF-8'))" "\n"
-
-    result = false;
-
-    ret = pipe(fds);
-    if (ret == -1)
-    {
-        perror("pipe()");
-        return false;
-    }
-
-    snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
-
-    ret = PyRun_SimpleString(cmds);
-    if (ret != 0) goto icas_exit;
-
-    got = read(fds[0], content, sizeof(content));
-    if (got < 0)
-    {
-        perror("read()");
-        goto icas_exit;
-    }
-
-    content[got] = '\0';
-
-    result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
-
- icas_exit:
-
-    if (!result)
-        PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
-                        "the ones of the Python library used during the module compilation.");
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : version = idenfiant de la version de GTK à stipuler.         *
-*                                                                             *
-*  Description : Définit la version attendue de GTK à charger dans Python.    *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool set_version_for_gtk_namespace(const char *version)
-{
-    bool result;                            /* Bilan à retourner           */
-    PyObject *gi_mod;                       /* Module Python-GObject       */
-    PyObject *args;                         /* Arguments à fournir         */
-
-    result = false;
-
-    /**
-     * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement :
-     *
-     *
-     *   PyGIWarning: Gtk was imported without specifying a version first. \
-     *   Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded.
-     *
-     */
-
-    gi_mod = PyImport_ImportModule("gi");
-
-    if (gi_mod != NULL)
-    {
-        args = Py_BuildValue("ss", "Gtk", version);
-
-        run_python_method(gi_mod, "require_version", args);
-
-        result = (PyErr_Occurred() == NULL);
-
-        Py_DECREF(args);
-        Py_DECREF(gi_mod);
-
-    }
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  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
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : super = module dont la définition est à compléter.           *
-*                                                                             *
-*  Description : Ajoute le module 'plugins' au module Python.                 *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool add_plugin_module_to_python_module(PyObject *super)
-{
-    bool result;                            /* Bilan à retourner           */
-    PyObject *module;                       /* Sous-module mis en place    */
-
-#define PYCHRYSALIDE_PLUGINS_DOC                    \
-    "Home for all plugins without another home."
-
-    static PyModuleDef py_chrysalide_deguard_module = {
-
-        .m_base = PyModuleDef_HEAD_INIT,
-
-        .m_name = "pychrysalide.plugins",
-        .m_doc = PYCHRYSALIDE_PLUGINS_DOC,
-
-        .m_size = -1,
-
-    };
-
-    result = false;
-
-    module = build_python_module(super, &py_chrysalide_deguard_module);
-
-    result = (module != NULL);
-
-    assert(result);
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : -                                                            *
-*                                                                             *
-*  Description : Point d'entrée pour l'initialisation de Python.              *
-*                                                                             *
-*  Retour      : ?                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-#define PYCHRYSALIDE_DOC                                                                                        \
-    "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n"                \
-    "\n"                                                                                                        \
-    "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n"       \
-    "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n"                               \
-    "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
-    "\n"                                                                                                        \
-    "In both cases, this is a good start point to have a look at already existing plugins to quickly learn "    \
-    "how the API works.\n"                                                                                      \
-    "\n"                                                                                                        \
-    "These plugins are located in the 'plugins/python' directory."
-
-PyMODINIT_FUNC PyInit_pychrysalide(void)
-{
-    PyObject *result;                       /* Module Python à retourner   */
-    bool status;                            /* Bilan des inclusions        */
-    int ret;                                /* Bilan de préparatifs        */
-    GPluginModule *self;                    /* Représentation interne      */
-    PluginStatusFlags self_flags;           /* Fanions à mettre à jour     */
-
-    static PyMethodDef py_chrysalide_methods[] = {
-        PY_CHRYSALIDE_REVISION_METHOD,
-        PY_CHRYSALIDE_VERSION_METHOD,
-        PY_CHRYSALIDE_MOD_VERSION_METHOD,
-        { NULL }
-    };
-
-    static PyModuleDef py_chrysalide_module = {
-
-        .m_base = PyModuleDef_HEAD_INIT,
-
-        .m_name = "pychrysalide",
-        .m_doc = PYCHRYSALIDE_DOC,
-
-        .m_size = -1,
-
-        .m_methods = py_chrysalide_methods
-
-    };
-
-    /**
-     * 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(py_chrysalide_module.m_name);
-
-    if (result != NULL)
-    {
-        Py_INCREF(result);
-        return result;
-    }
-
-    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 (!set_version_for_gtk_namespace("3.0"))
-        goto exit;
-
-    if (!load_all_core_components(true))
-    {
-        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);
-
-    status = true;
-
-    if (status) status = add_features_module(result);
-
-    if (status) add_plugin_module_to_python_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_core_module(result);
-    if (status) status = add_debug_module(result);
-    if (status) status = add_format_module(result);
-    if (status) status = add_glibext_module(result);
-    if (status) status = add_gtkext_module(result);
-    if (status) status = add_gui_module(result);
-    if (status) status = add_mangling_module(result);
-
-    if (status) status = ensure_python_plugin_module_is_registered();
-    if (status) status = ensure_python_py_constval_is_registered();
-    if (status) status = ensure_python_string_enum_is_registered();
-    if (status) status = ensure_python_py_struct_is_registered();
-
-    if (status) status = populate_analysis_module();
-    if (status) status = populate_arch_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();
-    if (status) status = populate_glibext_module();
-    if (status) status = populate_gtkext_module();
-    if (status) status = populate_gui_module();
-    if (status) status = populate_mangling_module();
-
-    if (!status)
-    {
-        PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
-        Py_DECREF(result);
-        result = NULL;
-        goto exit;
-    }
-
-    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;
-        }
-
-        /**
-         * 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();
-
-        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);
-
-        unlock_plugin_list_for_reading();
-
-        load_remaning_plugins();
-
-        /**
-         * 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().
-         */
-
-    }
-
- exit:
-
-    if (result == NULL && !_standalone)
-        log_pychrysalide_exception("Loading failed");
-
-    return result;
-
-}
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : path = chemin supplémentaire pour l'espace de recherche.     *
-*                                                                             *
-*  Description : Complète les chemins de recherches de Python.                *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static void extend_python_path(const char *path)
-{
-    PyObject *list;                         /* Liste de chemins à compléter*/
-    PyObject *new;                          /* Nouveau chemin à intégrer   */
-
-    list = PySys_GetObject("path");
-    assert(list != NULL);
-
-    new = PyUnicode_FromString(path);
-    assert(new != NULL);
-
-    PyList_Append(list, new);
-
-    Py_DECREF(new);
-
-    add_to_env_var("PYTHONPATH", path, ":");
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : plugin = instance représentant le greffon Python d'origine.  *
-*                                                                             *
-*  Description : Charge autant de greffons composés en Python que possible.   *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static void load_python_plugins(GPluginModule *plugin)
-{
-#ifdef DISCARD_LOCAL
-    char *edir;                             /* Répertoire de base effectif */
-#endif
-    DIR *dir;                               /* Répertoire à parcourir      */
-    char *paths;                            /* Emplacements de greffons    */
-    char *save;                             /* Sauvegarde pour ré-entrance */
-    char *path;                             /* Chemin à fouiller           */
-    struct dirent *entry;                   /* Elément trouvé              */
-    char *modname;                          /* Nom du module pour Python   */
-    char *filename;                         /* Chemin d'accès reconstruit  */
-    GPluginModule *pyplugin;                /* Lien vers un grffon Python  */
-
-    /* Définition des zones d'influence */
-
-#ifndef DISCARD_LOCAL
-
-    extend_python_path(PACKAGE_SOURCE_DIR G_DIR_SEPARATOR_S "plugins" G_DIR_SEPARATOR_S "python");
-
-#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);
-
-    }
-
-#endif
-
-    g_plugin_module_log_variadic_message(plugin, LMT_INFO,
-                                         _("PYTHONPATH environment variable set to '%s'"),
-                                         getenv("PYTHONPATH"));
-
-    /* Chargements des extensions Python */
-
-    paths = get_env_var("PYTHONPATH");
-
-    save = NULL;   /* gcc... */
-
-    for (path = strtok_r(paths, ":", &save);
-         path != NULL; 
-         path = strtok_r(NULL, ":", &save))
-    {
-        dir = opendir(path);
-        if (dir == NULL)
-        {
-            perror("opendir");
-            continue;
-        }
-
-        g_plugin_module_log_variadic_message(plugin, LMT_INFO, 
-                                             _("Looking for Python plugins in '%s'..."),
-                                             path);
-
-        while (1)
-        {
-            errno = 0;
-
-            entry = readdir(dir);
-
-            if (entry == NULL)
-            {
-                if (errno != 0)
-                    perror("readdir");
-
-                break;
-
-            }
-
-            if (entry->d_type != DT_DIR) continue;
-            if (entry->d_name[0] == '.') continue;
-
-            modname = strdup(entry->d_name);
-            modname = stradd(modname, ".");
-            modname = stradd(modname, "__init__");
-
-            filename = strdup(path);
-            filename = stradd(filename, G_DIR_SEPARATOR_S);
-            filename = stradd(filename, entry->d_name);
-
-            pyplugin = g_python_plugin_new(modname, filename);
-
-            if (pyplugin == NULL)
-                g_plugin_module_log_variadic_message(plugin, LMT_ERROR, 
-                                                     _("No suitable Python plugin found in '%s'"),
-                                                     filename);
-            else
-            {
-                g_plugin_module_log_variadic_message(plugin, LMT_PROCESS, 
-                                                     _("Loaded the Python plugin found in the '<b>%s</b>' directory"),
-                                                     filename);
-
-                /**
-                 * Comme le greffon n'est pas passé par la résolution des dépendances,
-                 * on simule l'effet attendu.
-                 */
-                g_object_ref(G_OBJECT(plugin));
-
-                _register_plugin(pyplugin);
-
-            }
-
-            free(filename);
-            free(modname);
-
-        }
-
-         closedir(dir);
-
-    }
-
-    free(paths);
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  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           */
-    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();
-
-    PyEval_InitThreads();
-
-    PySys_SetArgv(0, (wchar_t *[]) { NULL });
-
-    _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);
-
-    _main_tstate = PyThreadState_Get();
-
-    PyEval_ReleaseLock();
-
- 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)
-{
-    clear_all_accesses_to_python_modules();
-
-    Py_XDECREF(_chrysalide_module);
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : plugin = greffon à manipuler.                                *
-*                action = type d'action attendue.                             *
-*                                                                             *
-*  Description : Accompagne la fin du chargement des modules natifs.          *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-G_MODULE_EXPORT void chrysalide_plugin_on_native_loaded(GPluginModule *plugin, PluginAction action)
-{
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
-
-    if (!_standalone)
-    {
-        tstate = get_pychrysalide_main_tstate();
-        PyEval_RestoreThread(tstate);
-    }
-
-    load_python_plugins(plugin);
-
-    if (!_standalone)
-        PyEval_SaveThread();
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : -                                                            *
-*                                                                             *
-*  Description : Fournit les informations du thread principal.                *
-*                                                                             *
-*  Retour      : Indications utiles à Python.                                 *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-PyThreadState *get_pychrysalide_main_tstate(void)
-{
-    PyThreadState *result;                  /* Indications à retourner     */
-
-    result = _main_tstate;
-
-    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      */
-
-    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
-         * g_python_plugin_new() 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/pychrysa.h b/plugins/pychrysalide/pychrysa.h
deleted file mode 100644
index a6d4bb1..0000000
--- a/plugins/pychrysalide/pychrysa.h
+++ /dev/null
@@ -1,63 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * pychrysa.h - prototypes pour le plugin permettant des extensions en Python
- *
- * Copyright (C) 2018-2019 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_PYCHRYSA_H
-#define _PLUGINS_PYCHRYSALIDE_PYCHRYSA_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 <plugins/plugin.h>
-#include <plugins/plugin-int.h>
-
-
-
-/* Point d'entrée pour l'initialisation de Python. */
-PyMODINIT_FUNC PyInit_pychrysalide(void);
-
-/* Prend acte du chargement du greffon. */
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
-
-/* 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_native_loaded(GPluginModule *, PluginAction);
-
-/* Fournit les informations du thread principal. */
-PyThreadState *get_pychrysalide_main_tstate(void);
-
-/* Présente dans le journal une exception survenue. */
-void log_pychrysalide_exception(const char *, ...);
-
-
-
-#endif  /* _PLUGINS_PYCHRYSALIDE_PYCHRYSA_H */
-- 
cgit v0.11.2-87-g4458