From 2e867fcae6e91594ae47528ca097952398ffcca4 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 28 Apr 2020 23:44:18 +0200
Subject: Extended the Python bindings for registers.

---
 plugins/pychrysalide/arch/processor.c     |  10 +-
 plugins/pychrysalide/arch/register.c      | 407 ++++++++++++++++++++++++++++--
 plugins/pychrysalide/glibext/Makefile.am  |   1 +
 plugins/pychrysalide/glibext/bufferline.c | 145 ++++++++++-
 plugins/pychrysalide/glibext/constants.c  | 142 +++++++++++
 plugins/pychrysalide/glibext/constants.h  |  42 +++
 plugins/pychrysalide/helpers.c            |  45 ++++
 plugins/pychrysalide/helpers.h            |  10 +
 src/glibext/gbufferline.c                 |   2 +-
 9 files changed, 775 insertions(+), 29 deletions(-)
 create mode 100644 plugins/pychrysalide/glibext/constants.c
 create mode 100644 plugins/pychrysalide/glibext/constants.h

diff --git a/plugins/pychrysalide/arch/processor.c b/plugins/pychrysalide/arch/processor.c
index 37f97a4..557e7a7 100644
--- a/plugins/pychrysalide/arch/processor.c
+++ b/plugins/pychrysalide/arch/processor.c
@@ -677,6 +677,8 @@ static GArchInstruction *py_arch_processor_disassemble_wrapper(const GArchProces
     PyObject *pyins;                        /* Instruction en objet Python */
     int ret;                                /* Bilan d'une conversion      */
 
+    result = NULL;
+
     gstate = PyGILState_Ensure();
 
     pyobj = pygobject_new(G_OBJECT(proc));
@@ -696,10 +698,7 @@ static GArchInstruction *py_arch_processor_disassemble_wrapper(const GArchProces
 
         Py_DECREF(args);
 
-        if (pyins == NULL)
-            result = NULL;
-
-        else
+        if (pyins != NULL)
         {
             ret = convert_to_arch_instruction(pyins, &result);
 
@@ -721,9 +720,6 @@ static GArchInstruction *py_arch_processor_disassemble_wrapper(const GArchProces
 
     }
 
-    else
-        result = NULL;
-
     PyGILState_Release(gstate);
 
     return result;
diff --git a/plugins/pychrysalide/arch/register.c b/plugins/pychrysalide/arch/register.c
index 7ecdb0e..84d3180 100644
--- a/plugins/pychrysalide/arch/register.c
+++ b/plugins/pychrysalide/arch/register.c
@@ -48,17 +48,35 @@ static PyObject *py_arch_register_new(PyTypeObject *, PyObject *, PyObject *);
 /* Initialise la classe des descriptions de fichier binaire. */
 static void py_arch_register_init_gclass(GArchRegisterClass *, gpointer);
 
+/* Produit une empreinte à partir d'un registre. */
+static guint py_arch_register___hash___wrapper(const GArchRegister *);
+
+/* Compare un registre avec un autre. */
+static int py_arch_register___cmp___wrapper(const GArchRegister *, const GArchRegister *);
+
+/* Traduit un registre en version humainement lisible. */
+static void py_arch_register_print_wrapper(const GArchRegister *, GBufferLine *);
+
 /* Indique si le registre correspond à ebp ou similaire. */
-static bool g_arch_register_is_base_pointer_wrapper(const GArchRegister *);
+static bool py_arch_register_is_base_pointer_wrapper(const GArchRegister *);
+
+/* Indique si le registre correspond à esp ou similaire. */
+static bool py_arch_register_is_stack_pointer_wrapper(const GArchRegister *);
 
 
 
 /* ---------------------------- PUR REGISTRE DU MATERIEL ---------------------------- */
 
 
+/* Effectue une comparaison avec un objet Python 'ArchRegister'. */
+static PyObject *py_arch_register_richcompare(PyObject *, PyObject *, int);
+
 /* Indique si le registre correspond à ebp ou similaire. */
 static PyObject *py_arch_register_is_base_pointer(PyObject *, void *);
 
+/* Indique si le registre correspond à esp ou similaire. */
+static PyObject *py_arch_register_is_stack_pointer(PyObject *, void *);
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -88,6 +106,23 @@ static PyObject *py_arch_register_new(PyTypeObject *type, PyObject *args, PyObje
     GType gtype;                            /* Nouveau type de registre    */
     bool status;                            /* Bilan d'un enregistrement   */
 
+#define ARCH_REGISTER_DOC                                               \
+    "The ArchRegister object aims to get subclassed to create"          \
+    " registers suitable for new architectures.\n"                      \
+    "\n"                                                                \
+    "Calls to the *__init__* constructor of this abstract object expect"\
+    " no particular argument.\n"                                        \
+    "\n"                                                                \
+    "The following methods have to be defined for new classes:\n"       \
+    "* pychrysalide.arch.ArchRegister.__hash__();\n"                    \
+    "* pychrysalide.arch.ArchRegister.__cmp__();\n"                     \
+    "* pychrysalide.arch.ArchRegister._print();\n"                      \
+    "* pychrysalide.arch.ArchRegister._is_base_pointer();\n"            \
+    "* pychrysalide.arch.ArchRegister._is_stack_pointer().\n"           \
+    "\n"                                                                \
+    "Chrysalide creates an internal glue to provide rich comparisons"   \
+    " for registers based on the old-style *__cmp__* function."
+
     /* Validations diverses */
 
     base = get_python_arch_register_type();
@@ -144,7 +179,182 @@ static PyObject *py_arch_register_new(PyTypeObject *type, PyObject *args, PyObje
 
 static void py_arch_register_init_gclass(GArchRegisterClass *class, gpointer unused)
 {
-    class->is_bp = g_arch_register_is_base_pointer_wrapper;
+    class->hash = py_arch_register___hash___wrapper;
+    class->compare = py_arch_register___cmp___wrapper;
+    class->print = py_arch_register_print_wrapper;
+    class->is_bp = py_arch_register_is_base_pointer_wrapper;
+    class->is_sp = py_arch_register_is_stack_pointer_wrapper;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : reg = registre visé par la procédure.                        *
+*                                                                             *
+*  Description : Produit une empreinte à partir d'un registre.                *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static guint py_arch_register___hash___wrapper(const GArchRegister *reg)
+{
+    guint result;                           /* Empreinte à retourner       */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *pyret;                        /* Bilan de consultation       */
+
+#define ARCH_REGISTER_HASH_WRAPPER PYTHON_WRAPPER_DEF           \
+(                                                               \
+    __hash__, "$self, /",                                       \
+    METH_NOARGS,                                                \
+    "Abstract method used to produce a hash of the object. The" \
+    " result must be an integer value."                         \
+)
+
+    result = 0;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(reg));
+
+    if (has_python_method(pyobj, "__hash__"))
+    {
+        pyret = run_python_method(pyobj, "__hash__", NULL);
+
+        if (pyret != NULL)
+        {
+            if (PyLong_Check(pyret))
+                result = PyLong_AsUnsignedLong(pyret);
+
+            Py_DECREF(pyret);
+
+        }
+
+    }
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a = premier opérande à consulter.                            *
+*                b = second opérande à consulter.                             *
+*                                                                             *
+*  Description : Compare un registre avec un autre.                           *
+*                                                                             *
+*  Retour      : Bilan de la comparaison.                                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int py_arch_register___cmp___wrapper(const GArchRegister *a, const GArchRegister *b)
+{
+    int result;                             /* Empreinte à retourner       */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+
+#define ARCH_REGISTER_CMP_WRAPPER PYTHON_WRAPPER_DEF            \
+(                                                               \
+    __cmp__, "$self, other, /",                                 \
+    METH_VARARGS,                                               \
+    "Abstract method used to produce a compare the register"    \
+    " with another one. This second object is always an"        \
+    " pychrysalide.arch.ArchRegister instance.\n"               \
+    "\n"                                                        \
+    " This is the old-style comparison method, but Chrysalide"  \
+    " provides a glue to automatically build a rich version of" \
+    " this function."                                           \
+)
+
+    result = 0;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(a));
+
+    if (has_python_method(pyobj, "__cmp__"))
+    {
+        args = PyTuple_New(1);
+        PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(b)));
+
+        pyret = run_python_method(pyobj, "__cmp__", args);
+
+        if (pyret != NULL)
+        {
+            if (PyLong_Check(pyret))
+                result = PyLong_AsLong(pyret);
+        }
+
+        Py_DECREF(args);
+
+        Py_XDECREF(pyret);
+
+    }
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : reg = registre visé par la procédure.                        *
+*                                                                             *
+*  Description : Traduit un registre en version humainement lisible.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_arch_register_print_wrapper(const GArchRegister *reg, GBufferLine *line)
+{
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+
+#define ARCH_REGISTER_PRINT_WRAPPER PYTHON_WRAPPER_DEF              \
+(                                                                   \
+    _print, "$self, line, /",                                       \
+    METH_VARARGS,                                                   \
+    "Abstract method used to print the register into a rendering"   \
+    " line, which is a provided pychrysalide.glibext.BufferLine"    \
+    " instance."                                                    \
+)
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(reg));
+
+    if (has_python_method(pyobj, "_print"))
+    {
+        args = PyTuple_New(1);
+        PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line)));
+
+        pyret = run_python_method(pyobj, "_print", args);
+
+        Py_DECREF(args);
+
+        Py_XDECREF(pyret);
+
+    }
+
+    PyGILState_Release(gstate);
 
 }
 
@@ -161,21 +371,96 @@ static void py_arch_register_init_gclass(GArchRegisterClass *class, gpointer unu
 *                                                                             *
 ******************************************************************************/
 
-static bool g_arch_register_is_base_pointer_wrapper(const GArchRegister *reg)
+static bool py_arch_register_is_base_pointer_wrapper(const GArchRegister *reg)
+{
+    bool result;                            /* Bilan à renvoyer            */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *pyret;                        /* Bilan de consultation       */
+
+#define ARCH_REGISTER_IS_BASE_POINTER_WRAPPER PYTHON_WRAPPER_DEF    \
+(                                                                   \
+    _is_base_pointer, "$self, /",                                   \
+    METH_NOARGS,                                                    \
+    "Abstract method used to tell if the register is handling a"    \
+    " base pointer. The result must be a boolean value."            \
+)
+
+    result = false;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(reg));
+
+    if (has_python_method(pyobj, "_is_base_pointer"))
+    {
+        pyret = run_python_method(pyobj, "_is_base_pointer", NULL);
+
+        if (pyret != NULL)
+        {
+            result = (pyret == Py_True);
+
+            Py_DECREF(pyret);
+
+        }
+
+    }
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : reg = registre visé par la procédure.                        *
+*                                                                             *
+*  Description : Indique si le registre correspond à esp ou similaire.        *
+*                                                                             *
+*  Retour      : true si la correspondance est avérée, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool py_arch_register_is_stack_pointer_wrapper(const GArchRegister *reg)
 {
     bool result;                            /* Bilan à renvoyer            */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
     PyObject *pyobj;                        /* Objet Python concerné       */
     PyObject *pyret;                        /* Bilan de consultation       */
 
+#define ARCH_REGISTER_IS_STACK_POINTER_WRAPPER PYTHON_WRAPPER_DEF   \
+(                                                                   \
+    _is_stack_pointer, "$self, /",                                  \
+    METH_NOARGS,                                                    \
+    "Abstract method used to tell if the register is handling a"    \
+    " stack pointer. The result must be a boolean value."           \
+)
+
+    result = false;
+
+    gstate = PyGILState_Ensure();
+
     pyobj = pygobject_new(G_OBJECT(reg));
 
-    assert(has_python_method(pyobj, "_is_base_pointer"));
+    if (has_python_method(pyobj, "_is_stack_pointer"))
+    {
+        pyret = run_python_method(pyobj, "_is_stack_pointer", NULL);
+
+        if (pyret != NULL)
+        {
+            result = (pyret == Py_True);
 
-    pyret = run_python_method(pyobj, "_is_base_pointer", NULL);
+            Py_DECREF(pyret);
 
-    result = (pyret == Py_True);
+        }
+
+    }
 
-    Py_XDECREF(pyret);
+    PyGILState_Release(gstate);
 
     return result;
 
@@ -190,6 +475,51 @@ static bool g_arch_register_is_base_pointer_wrapper(const GArchRegister *reg)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : a  = premier object Python à consulter.                      *
+*                b  = second object Python à consulter.                       *
+*                op = type de comparaison menée.                              *
+*                                                                             *
+*  Description : Effectue une comparaison avec un objet Python 'ArchRegister'.*
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_arch_register_richcompare(PyObject *a, PyObject *b, int op)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    int ret;                                /* Bilan de lecture des args.  */
+    const GArchRegister *reg_a;             /* Premier élément à traiter   */
+    const GArchRegister *reg_b;             /* Second élément à traiter    */
+    int status;                             /* Résultat d'une comparaison  */
+
+    ret = PyObject_IsInstance(b, (PyObject *)get_python_arch_register_type());
+    if (!ret)
+    {
+        result = Py_NotImplemented;
+        goto cmp_done;
+    }
+
+    reg_a = G_ARCH_REGISTER(pygobject_get(a));
+    reg_b = G_ARCH_REGISTER(pygobject_get(b));
+
+    status = py_arch_register___cmp___wrapper(reg_a, reg_b);
+
+    result = status_to_rich_cmp_state(status, op);
+
+ cmp_done:
+
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self    = objet Python concerné par l'appel.                 *
 *                closure = non utilisé ici.                                   *
 *                                                                             *
@@ -207,6 +537,12 @@ static PyObject *py_arch_register_is_base_pointer(PyObject *self, void *closure)
     GArchRegister *reg;                     /* Registre visé               */
     bool status;                            /* Bilan de consultation       */
 
+#define ARCH_REGISTER_IS_BASE_POINTER_ATTRIB PYTHON_IS_DEF_FULL     \
+(                                                                   \
+    base_pointer, py_arch_register,                                 \
+    "Tell if the register is a base pointer or not."                \
+)
+
     reg = G_ARCH_REGISTER(pygobject_get(self));
 
     status = g_arch_register_is_base_pointer(reg);
@@ -221,6 +557,43 @@ static PyObject *py_arch_register_is_base_pointer(PyObject *self, void *closure)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
+*  Description : Indique si le registre correspond à esp ou similaire.        *
+*                                                                             *
+*  Retour      : True si la correspondance est avérée, False sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_arch_register_is_stack_pointer(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Instance Python à retourner */
+    GArchRegister *reg;                     /* Registre visé               */
+    bool status;                            /* Bilan de consultation       */
+
+#define ARCH_REGISTER_IS_STACK_POINTER_ATTRIB PYTHON_IS_DEF_FULL    \
+(                                                                   \
+    stack_pointer, py_arch_register,                                \
+    "Tell if the register is a stack pointer or not."               \
+)
+
+    reg = G_ARCH_REGISTER(pygobject_get(self));
+
+    status = g_arch_register_is_stack_pointer(reg);
+
+    result = status ? Py_True : Py_False;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Fournit un accès à une définition de type à diffuser.        *
@@ -234,19 +607,17 @@ static PyObject *py_arch_register_is_base_pointer(PyObject *self, void *closure)
 PyTypeObject *get_python_arch_register_type(void)
 {
     static PyMethodDef py_arch_register_methods[] = {
-        {
-            "_is_base_pointer", not_yet_implemented_method,
-            METH_NOARGS,
-            "_is_base_pointer($self, /)\n--\n\nDefine an implementation to provide the relative member value."
-        },
+        ARCH_REGISTER_HASH_WRAPPER,
+        ARCH_REGISTER_CMP_WRAPPER,
+        ARCH_REGISTER_PRINT_WRAPPER,
+        ARCH_REGISTER_IS_BASE_POINTER_WRAPPER,
+        ARCH_REGISTER_IS_STACK_POINTER_WRAPPER,
         { NULL }
     };
 
     static PyGetSetDef py_arch_register_getseters[] = {
-        {
-            "is_base_pointer", py_arch_register_is_base_pointer, NULL,
-            "Tell if the register is a base pointer or not.", NULL
-        },
+        ARCH_REGISTER_IS_BASE_POINTER_ATTRIB,
+        ARCH_REGISTER_IS_STACK_POINTER_ATTRIB,
         { NULL }
     };
 
@@ -259,7 +630,9 @@ PyTypeObject *get_python_arch_register_type(void)
 
         .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IS_ABSTRACT | Py_TPFLAGS_BASETYPE,
 
-        .tp_doc         = "PyChrysalide register for a given architecture.",
+        .tp_doc         = ARCH_REGISTER_DOC,
+
+        .tp_richcompare = py_arch_register_richcompare,
 
         .tp_methods     = py_arch_register_methods,
         .tp_getset      = py_arch_register_getseters,
diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am
index 28d27f6..c5fee67 100644
--- a/plugins/pychrysalide/glibext/Makefile.am
+++ b/plugins/pychrysalide/glibext/Makefile.am
@@ -2,6 +2,7 @@
 noinst_LTLIBRARIES = libpychrysaglibext.la
 
 libpychrysaglibext_la_SOURCES =			\
+	constants.h constants.c				\
 	binarycursor.h binarycursor.c		\
 	binportion.h binportion.c			\
 	buffercache.h buffercache.c			\
diff --git a/plugins/pychrysalide/glibext/bufferline.c b/plugins/pychrysalide/glibext/bufferline.c
index d6b391a..47b2bc4 100644
--- a/plugins/pychrysalide/glibext/bufferline.c
+++ b/plugins/pychrysalide/glibext/bufferline.c
@@ -31,17 +31,23 @@
 
 
 #include <i18n.h>
-
-
 #include <glibext/gbufferline.h>
+#include <plugins/dt.h>
 
 
+#include "constants.h"
 #include "../access.h"
 #include "../helpers.h"
 #include "../arch/vmpa.h"
 
 
 
+/* Accompagne la création d'une instance dérivée en Python. */
+static PyObject *py_buffer_line_new(PyTypeObject *, PyObject *, PyObject *);
+
+/* Ajoute du texte à formater dans une ligne donnée. */
+static PyObject *py_buffer_line_append_text(PyObject *, PyObject *);
+
 /* Reconstruit et fournit le texte présent sur une ligne tampon. */
 static PyObject *py_buffer_line_get_text(PyObject *, PyObject *);
 
@@ -64,6 +70,131 @@ static bool py_buffer_line_define_constants(PyTypeObject *);
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : type = type du nouvel objet à mettre en place.               *
+*                args = éventuelle liste d'arguments.                         *
+*                kwds = éventuel dictionnaire de valeurs mises à disposition. *
+*                                                                             *
+*  Description : Accompagne la création d'une instance dérivée en Python.     *
+*                                                                             *
+*  Retour      : Nouvel objet Python mis en place ou NULL en cas d'échec.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *result;                       /* Objet à retourner           */
+    PyTypeObject *base;                     /* Type de base à dériver      */
+    bool first_time;                        /* Evite les multiples passages*/
+    GType gtype;                            /* Nouveau type de processeur  */
+    bool status;                            /* Bilan d'un enregistrement   */
+
+#define BUFFER_LINE_DOC                                                     \
+    "The BufferLine object is used to display processed data: disassembled" \
+    " instruction, binary content in hexadecimal form, aso.\n"              \
+    "\n"                                                                    \
+    "Instances can be created using the following constructor:\n"           \
+    "\n"                                                                    \
+    "    BufferLine()"                                                      \
+    "\n"                                                                    \
+    "Such objets aim to be created from the Chrysalide core only, and"      \
+    " then get populated by plugins on demand."
+
+    /* Validations diverses */
+
+    base = get_python_buffer_line_type();
+
+    if (type == base)
+        goto simple_way;
+
+    /* Mise en place d'un type dédié */
+
+    first_time = (g_type_from_name(type->tp_name) == 0);
+
+    gtype = build_dynamic_type(G_TYPE_BUFFER_LINE, type->tp_name, NULL, NULL, NULL);
+
+    if (first_time)
+    {
+        status = register_class_for_dynamic_pygobject(gtype, type, base);
+
+        if (!status)
+        {
+            result = NULL;
+            goto exit;
+        }
+
+    }
+
+    /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
+
+ simple_way:
+
+    result = PyType_GenericNew(type, args, kwds);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = classe représentant une ligne de tampon.              *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Ajoute du texte à formater dans une ligne donnée.            *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_line_append_text(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    unsigned long column;                   /* Indice de colonne           */
+    const char *text;                       /* Texte à ajouter             */
+    RenderingTagType type;                  /* Type de rendu attendu       */
+    GObject *creator;                       /* Eventuel créateur à associer*/
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferLine *line;                      /* Version native              */
+
+#define BUFFER_LINE_APPEND_TEXT_METHOD PYTHON_METHOD_DEF            \
+(                                                                   \
+    append_text, "$self, column, text, tag, /, creator=None",       \
+    METH_VARARGS, py_buffer_line,                                   \
+    "Append some text to a line at a given column index. The"       \
+    " expected rendering for this text is defined by the tag, which"\
+    " must be a pychrysalide.glibext.BufferLine.RenderingTagType"   \
+    " value."                                                       \
+    "\n"                                                            \
+    "An optional GObject instance may be provided as origin of the" \
+    " creation."                                                    \
+)
+
+    creator = NULL;
+
+    ret = PyArg_ParseTuple(args, "ksO&|O&", &column, &text,
+                           convert_to_rendering_tag_type, &type, convert_to_gobject, &creator);
+    if (!ret) return NULL;
+
+    line = G_BUFFER_LINE(pygobject_get(self));
+
+    g_buffer_line_append_text(line, column, text, strlen(text), type, creator);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = classe représentant une ligne de tampon.              *
 *                args = arguments fournis à l'appel.                          *
 *                                                                             *
@@ -292,6 +423,7 @@ static bool py_buffer_line_define_constants(PyTypeObject *obj_type)
 PyTypeObject *get_python_buffer_line_type(void)
 {
     static PyMethodDef py_buffer_line_methods[] = {
+        BUFFER_LINE_APPEND_TEXT_METHOD,
         {
             "get_text", py_buffer_line_get_text,
             METH_VARARGS,
@@ -329,13 +461,15 @@ PyTypeObject *get_python_buffer_line_type(void)
         .tp_name        = "pychrysalide.glibext.BufferLine",
         .tp_basicsize   = sizeof(PyGObject),
 
-        .tp_flags       = Py_TPFLAGS_DEFAULT,
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
 
-        .tp_doc         = "PyChrysalide buffer line",
+        .tp_doc         = BUFFER_LINE_DOC,
 
         .tp_methods     = py_buffer_line_methods,
         .tp_getset      = py_buffer_line_getseters,
 
+        .tp_new         = py_buffer_line_new
+
     };
 
     return &py_buffer_line_type;
@@ -372,6 +506,9 @@ bool ensure_python_buffer_line_is_registered(void)
         if (!register_class_for_pygobject(dict, G_TYPE_BUFFER_LINE, type, &PyGObject_Type))
             return false;
 
+        if (!define_line_segment_constants(type))
+            return false;
+
         if (!py_buffer_line_define_constants(type))
             return false;
 
diff --git a/plugins/pychrysalide/glibext/constants.c b/plugins/pychrysalide/glibext/constants.c
new file mode 100644
index 0000000..d18e051
--- /dev/null
+++ b/plugins/pychrysalide/glibext/constants.c
@@ -0,0 +1,142 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * constants.c - ajout des constantes de base pour les extensions à la GLib
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "constants.h"
+
+
+#include <glibext/linesegment.h>
+
+
+#include "../helpers.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type dont le dictionnaire est à compléter.            *
+*                                                                             *
+*  Description : Définit les constantes relatives aux segments de ligne.      *
+*                                                                             *
+*  Retour      : true en cas de succès de l'opération, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool define_line_segment_constants(PyTypeObject *type)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *values;                       /* Groupe de valeurs à établir */
+
+    values = PyDict_New();
+
+    result = add_const_to_group(values, "NONE", RTT_NONE);
+    if (result) result = add_const_to_group(values, "RAW", RTT_RAW);
+    if (result) result = add_const_to_group(values, "RAW_FULL", RTT_RAW_FULL);
+    if (result) result = add_const_to_group(values, "RAW_NULL", RTT_RAW_NULL);
+    if (result) result = add_const_to_group(values, "PRINTABLE", RTT_PRINTABLE);
+    if (result) result = add_const_to_group(values, "NOT_PRINTABLE", RTT_NOT_PRINTABLE);
+    if (result) result = add_const_to_group(values, "COMMENT", RTT_COMMENT);
+    if (result) result = add_const_to_group(values, "INDICATION", RTT_INDICATION);
+    if (result) result = add_const_to_group(values, "PHYS_ADDR_PAD", RTT_PHYS_ADDR_PAD);
+    if (result) result = add_const_to_group(values, "PHYS_ADDR", RTT_PHYS_ADDR);
+    if (result) result = add_const_to_group(values, "VIRT_ADDR_PAD", RTT_VIRT_ADDR_PAD);
+    if (result) result = add_const_to_group(values, "VIRT_ADDR", RTT_VIRT_ADDR);
+    if (result) result = add_const_to_group(values, "RAW_CODE", RTT_RAW_CODE);
+    if (result) result = add_const_to_group(values, "RAW_CODE_NULL", RTT_RAW_CODE_NULL);
+    if (result) result = add_const_to_group(values, "LABEL", RTT_LABEL);
+    if (result) result = add_const_to_group(values, "INSTRUCTION", RTT_INSTRUCTION);
+    if (result) result = add_const_to_group(values, "IMMEDIATE", RTT_IMMEDIATE);
+    if (result) result = add_const_to_group(values, "REGISTER", RTT_REGISTER);
+    if (result) result = add_const_to_group(values, "PUNCT", RTT_PUNCT);
+    if (result) result = add_const_to_group(values, "HOOK", RTT_HOOK);
+    if (result) result = add_const_to_group(values, "SIGNS", RTT_SIGNS);
+    if (result) result = add_const_to_group(values, "LTGT", RTT_LTGT);
+    if (result) result = add_const_to_group(values, "SECTION", RTT_SECTION);
+    if (result) result = add_const_to_group(values, "SEGMENT", RTT_SEGMENT);
+    if (result) result = add_const_to_group(values, "STRING", RTT_STRING);
+    if (result) result = add_const_to_group(values, "VAR_NAME", RTT_VAR_NAME);
+    if (result) result = add_const_to_group(values, "KEY_WORD", RTT_KEY_WORD);
+    if (result) result = add_const_to_group(values, "ERROR", RTT_ERROR);
+    if (result) result = add_const_to_group(values, "COUNT", RTT_COUNT);
+
+    if (!result)
+    {
+        Py_DECREF(values);
+        goto exit;
+    }
+
+    result = attach_constants_group_to_type(type, false, "RenderingTagType", values,
+                                            "Kinds of text rendering.");
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en constante RenderingTagType.            *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_rendering_tag_type(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to RenderingTagType");
+            break;
+
+        case 1:
+            *((RenderingTagType *)dst) = PyLong_AsUnsignedLong(arg);
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/constants.h b/plugins/pychrysalide/glibext/constants.h
new file mode 100644
index 0000000..472507b
--- /dev/null
+++ b/plugins/pychrysalide/glibext/constants.h
@@ -0,0 +1,42 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * constants.h - prototypes pour l'ajout des constantes de base pour les extensions à la GLib
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit les constantes relatives aux segments de ligne. */
+bool define_line_segment_constants(PyTypeObject *);
+
+/* Tente de convertir en constante RenderingTagType. */
+int convert_to_rendering_tag_type(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_H */
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 59fd323..47285f0 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -1016,6 +1016,51 @@ int forward_pygobjet_init(PyObject *self)
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en instance GObject.                      *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_gobject(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type);
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GObject instance");
+            break;
+
+        case 1:
+            *((GObject **)dst) = G_OBJECT(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                         TRANSFERT DES VALEURS CONSTANTES                           */
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index fe13f84..8ed9d9a 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -90,6 +90,13 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
         #name "(" args ")\n--\n\n" doc                  \
     }
 
+#define PYTHON_WRAPPER_DEF(name, args, flags, doc)      \
+    {                                                   \
+        #name, (PyCFunction)not_yet_implemented_method, \
+        flags,                                          \
+        #name "(" args ")\n--\n\n" doc                  \
+    }
+
 #define PYTHON_GETSET_DEF(name, get, set, doc, closure) \
     {                                                   \
         name, get, set,                                 \
@@ -163,6 +170,9 @@ bool register_class_for_dynamic_pygobject(GType, PyTypeObject *, PyTypeObject *)
 /* Fait suivre à la partie GObject une initialisation nouvelle. */
 int forward_pygobjet_init(PyObject *);
 
+/* Tente de convertir en instance GObject. */
+int convert_to_gobject(PyObject *, void *);
+
 
 
 /* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */
diff --git a/src/glibext/gbufferline.c b/src/glibext/gbufferline.c
index b7c6a4a..9b0a5a9 100644
--- a/src/glibext/gbufferline.c
+++ b/src/glibext/gbufferline.c
@@ -540,7 +540,7 @@ GObject *g_buffer_line_find_first_segment_creator(const GBufferLine *line, Buffe
 *                                                                             *
 *  Description : Ajoute du texte à formater dans une ligne donnée.            *
 *                                                                             *
-*  Retour      : Portion de texte mis en place, voire NULL.                   *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
-- 
cgit v0.11.2-87-g4458