diff options
Diffstat (limited to 'plugins/pychrysalide/glibext')
34 files changed, 6106 insertions, 1088 deletions
diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am index 2ed2aa5..6d3d746 100644 --- a/plugins/pychrysalide/glibext/Makefile.am +++ b/plugins/pychrysalide/glibext/Makefile.am @@ -1,32 +1,50 @@ -noinst_LTLIBRARIES = libpychrysaglibext.la +noinst_LTLIBRARIES = libpychrysaglibext.la libpychrysaglibextui.la + +# libpychrysaglibext_la_SOURCES = \ +# binarycursor.h binarycursor.c \ +# buffercache.h buffercache.c \ +# configuration.h configuration.c \ +# linecursor.h linecursor.c + +# if BUILD_GTK_SUPPORT + +# libpychrysaglibext_la_SOURCES += \ +# bufferview.h bufferview.c \ +# loadedpanel.h loadedpanel.c \ +# named.h named.c + +# endif libpychrysaglibext_la_SOURCES = \ + comparable.h comparable.c \ constants.h constants.c \ - binarycursor.h binarycursor.c \ - binportion.h binportion.c \ - buffercache.h buffercache.c \ - bufferline.h bufferline.c \ - comparison.h comparison.c \ - configuration.h configuration.c \ - linecursor.h linecursor.c \ - linegen.h linegen.c \ + hashable.h hashable.c \ module.h module.c \ - singleton.h singleton.c - -if BUILD_GTK_SUPPORT + objhole.h objhole.c \ + portion.h portion.c \ + secstorage.h secstorage.c \ + serialize.h serialize.c \ + singleton.h singleton.c \ + storage.h storage.c \ + strbuilder.h strbuilder.c \ + work.h work.c \ + workqueue.h workqueue.c + +libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ + -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT -libpychrysaglibext_la_SOURCES += \ - bufferview.h bufferview.c \ - loadedpanel.h loadedpanel.c \ - named.h named.c -endif +libpychrysaglibextui_la_SOURCES = \ + bufferline.h bufferline.c \ + constants-ui.h constants-ui.c \ + generator.h generator.c \ + module-ui.h module-ui.c -libpychrysaglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ +libpychrysaglibextui_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT devdir = $(includedir)/chrysalide/$(subdir) -dev_HEADERS = $(libpychrysaglibext_la_SOURCES:%c=) +dev_HEADERS = $(libpychrysaglibext_la_SOURCES:%c=) $(libpychrysaglibextui_la_SOURCES:%c=) diff --git a/plugins/pychrysalide/glibext/bufferline.c b/plugins/pychrysalide/glibext/bufferline.c index 09404bc..c5b3664 100644 --- a/plugins/pychrysalide/glibext/bufferline.c +++ b/plugins/pychrysalide/glibext/bufferline.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * bufferline.c - équivalent Python du fichier "glibext/bufferline.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -26,25 +26,38 @@ #include <assert.h> -#include <malloc.h> +//#include <malloc.h> #include <pygobject.h> -#include <i18n.h> -#include <glibext/bufferline.h> -#include <plugins/dt.h> +#include <glibext/bufferline-int.h> -#include "constants.h" +//#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../arch/vmpa.h" +//#include "../arch/vmpa.h" +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(buffer_line, G_TYPE_BUFFER_LINE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_buffer_line_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ + + /* Accompagne la création d'une instance dérivée en Python. */ static PyObject *py_buffer_line_new(PyTypeObject *, PyObject *, PyObject *); +#if 0 + /* Ajoute du texte à formater dans une ligne donnée. */ static PyObject *py_buffer_line_append_text(PyObject *, PyObject *); @@ -54,29 +67,34 @@ static PyObject *py_buffer_line_get_text(PyObject *, PyObject *); /* Renseigne sur les propriétés particulières liées à une ligne. */ static PyObject *py_buffer_line_get_flags(PyObject *, void *); +#endif + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * * * -* Description : Accompagne la création d'une instance dérivée en Python. * +* Description : Initialise une instance sur la base du dérivé de GObject. * * * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* Retour : 0. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +static int py_buffer_line_init(PyObject *self, 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 */ + unsigned char col_count; /* Qté de colonnes attendues */ + int ret; /* Bilan de lecture des args. */ + GBufferLine *line; /* Ligne en version native */ #define BUFFER_LINE_DOC \ "The BufferLine object is used to display processed data: disassembled" \ @@ -84,51 +102,40 @@ static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " BufferLine()" \ + " BufferLine(col_count)" \ + "\n" \ + " Where *col_count* is a integer value providing the expected number of"\ + " rendering columns." \ "\n" \ "Such objets aim to be created from the Chrysalide core only, and" \ " then get populated on demand. Thus, these lines can be viewed as" \ " cached lines and their properties have to be set through the" \ " pychrysalide.glibext.BufferCache instance which contains them." - /* Validations diverses */ - - base = get_python_buffer_line_type(); + /* Récupération des paramètres */ - if (type == base) - goto simple_way; + ret = PyArg_ParseTuple(args, "B", &col_count); + if (!ret) return -1; - /* Mise en place d'un type dédié */ + /* Initialisation d'un objet GLib */ - first_time = (g_type_from_name(type->tp_name) == 0); + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; - 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); + /* Eléments de 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); + line = G_BUFFER_LINE(pygobject_get(self)); - exit: + if (!g_buffer_line_create(line, col_count)) + return -1; - return result; + return 0; } +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant une ligne de tampon. * @@ -256,6 +263,7 @@ static PyObject *py_buffer_line_get_flags(PyObject *self, void *closure) return result; } +#endif /****************************************************************************** @@ -273,20 +281,24 @@ static PyObject *py_buffer_line_get_flags(PyObject *self, void *closure) 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, "get_text($self, first_col, last_col, markup, /)\n--\n\nProvide the text of a buffer line." }, + */ { NULL } }; static PyGetSetDef py_buffer_line_getseters[] = { + /* { "flags", py_buffer_line_get_flags, NULL, "Current flags of the buffer line.", NULL }, + */ { NULL } }; @@ -304,7 +316,8 @@ PyTypeObject *get_python_buffer_line_type(void) .tp_methods = py_buffer_line_methods, .tp_getset = py_buffer_line_getseters, - .tp_new = py_buffer_line_new + .tp_init = py_buffer_line_init, + .tp_new = py_buffer_line_new, }; @@ -342,11 +355,13 @@ bool ensure_python_buffer_line_is_registered(void) if (!register_class_for_pygobject(dict, G_TYPE_BUFFER_LINE, type)) return false; + /* if (!define_line_segment_constants(type)) return false; if (!define_buffer_line_constants(type)) return false; + */ } diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c new file mode 100644 index 0000000..e4982d7 --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.c @@ -0,0 +1,482 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - équivalent Python du fichier "glibext/comparable.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "comparable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/comparable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *); + +/* Réalise une comparaison étendue entre objets. */ +static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet le statut d'une comparaison effectuée par le parent. */ +static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *); + +/* Effectue une comparaison avec un objet 'ComparableObject'. */ +static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused) +{ +#define COMPARABLE_OBJECT_DOC \ + "The ComparableObject class provides an interface to compare" \ + " objects.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, ComparableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.ComparableObject._compare().\n" + + iface->compare = py_comparable_object_compare_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _compare, "$self, other", \ + METH_VARARGS, \ + "Abstract method allowing to compare two objects implementing" \ + " the interface. This method is used to handle rich comparisons"\ + " automatically.\n" \ + "\n" \ + "The result has to be an integer lesser than, equal to, or" \ + " greater than zero if *self* is found, respectively, to be" \ + " lesser than, to match, or to be greater than *other*.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); + + pyret = run_python_method(pyobj, "_compare", args); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet le statut d'une comparaison effectuée par le parent.* +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GComparableObject *other; /* Second objet à comparer */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *object; /* Mécanismes natifs */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + int status; /* Bilan d'une comparaison */ + +#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \ +( \ + parent_compare, "$sel, otherf", \ + METH_VARARGS, py_comparable_object, \ + "Provide the comparison status defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a signed integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.ComparableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._compare() function has to be" \ + " used instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other); + if (!ret) return NULL; + + object = G_COMPARABLE_OBJECT(pygobject_get(self)); + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface")); + + result = NULL; + + } + else + { + status = parent_iface->compare(object, other); + + result = PyLong_FromLong(status); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premier object Python à consulter. * +* b = second object Python à consulter. * +* op = type de comparaison menée. * +* * +* Description : Effectue une comparaison avec un objet 'ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op) +{ + PyObject *result; /* Bilan à retourner */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *obj_a; /* Instance à manipuler #1 */ + GComparableObject *obj_b; /* Instance à manipuler #2 */ + int status; /* Bilan d'une comparaison */ + + ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type()); + if (!ret) + { + result = Py_NotImplemented; + goto cmp_done; + } + + obj_a = G_COMPARABLE_OBJECT(pygobject_get(a)); + obj_b = G_COMPARABLE_OBJECT(pygobject_get(b)); + + status = g_comparable_object_compare(obj_a, obj_b); + + switch (op) + { + case Py_LT: + result = (status < 0 ? Py_True : Py_False); + break; + + case Py_LE: + result = (status <= 0 ? Py_True : Py_False); + break; + + case Py_EQ: + result = (status == 0 ? Py_True : Py_False); + break; + + case Py_NE: + result = (status != 0 ? Py_True : Py_False); + break; + + case Py_GT: + result = (status > 0 ? Py_True : Py_False); + break; + + case Py_GE: + result = (status >= 0 ? Py_True : Py_False); + break; + + default: + assert(false); + result = Py_NotImplemented; + break; + + } + + cmp_done: + + Py_INCREF(result); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_comparable_object_type(void) +{ + static PyMethodDef py_comparable_object_methods[] = { + COMPARABLE_OBJECT_COMPARE_WRAPPER, + COMPARABLE_OBJECT_PARENT_COMPARE_METHOD, + { NULL } + }; + + static PyGetSetDef py_comparable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_comparable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ComparableObject", + .tp_basicsize = sizeof(PyObject), + + /** + * Une valeur de .tp_richcompare non nulle écarte la définition du + * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type + * dans les préparatifs de la fonction Python inherit_slots(). + * + * Ces préparatifs se poursuivent avec type_ready_set_hash(), + * qui initialise .tp_hash avec PyObject_HashNotImplemented(), + * qui n'est donc pas un comportement par défaut. + * + * Côté PyGObject, la fonction pygobject_inherit_slots() y voit + * une implémentation de .tp_hash personnalisée, ce qui bloque + * la défintion d'autres personnalisations, comme celle de + * l'interface HashableObject. + * + * La valeur nominale nulle est ainsi écartée au préalable ici. + */ + .tp_hash = (hashfunc)_Py_HashPointer, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = COMPARABLE_OBJECT_DOC, + + .tp_richcompare = py_comparable_object_richcompare, + + .tp_methods = py_comparable_object_methods, + .tp_getset = py_comparable_object_getseters + + }; + + return &py_comparable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_comparable_object_is_registered(void) +{ + PyTypeObject *type; /* Type 'ComparableObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_comparable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'objet comparable. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_comparable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable object"); + break; + + case 1: + *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h new file mode 100644 index 0000000..d4c6ecf --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_comparable_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */ +bool ensure_python_comparable_object_is_registered(void); + +/* Tente de convertir en interface d'objet comparable. */ +int convert_to_comparable_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */ diff --git a/plugins/pychrysalide/glibext/constants-ui.c b/plugins/pychrysalide/glibext/constants-ui.c new file mode 100644 index 0000000..4101600 --- /dev/null +++ b/plugins/pychrysalide/glibext/constants-ui.c @@ -0,0 +1,131 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants-ui.c - ajout des constantes pour les extensions graphique à la GLib + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "constants-ui.h" + + +#include <glibext/bufferline.h> + + +#include "../helpers.h" + + + +/****************************************************************************** +* * +* Paramètres : type = type dont le dictionnaire est à compléter. * +* * +* Description : Définit les constantes relatives aux lignes de tampon. * +* * +* Retour : true en cas de succès de l'opération, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool define_buffer_line_constants(PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + PyObject *values; /* Groupe de valeurs à établir */ + + values = PyDict_New(); + + result = add_const_to_group(values, "NONE", BLF_NONE); + if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE); + if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL); + if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT); + if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK); + if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER); + if (result) result = add_const_to_group(values, "ALL", BLF_ALL); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, true, "BufferLineFlags", values, + "Optional flags linked to a rendering line."); + + 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 BufferLineFlags. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_buffer_line_flags(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + unsigned long value; /* Valeur transcrite */ + + 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 BufferLineFlags"); + break; + + case 1: + value = PyLong_AsUnsignedLong(arg); + + if ((value & BLF_ALL) != value) + { + PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags"); + result = 0; + } + + else + *((BufferLineFlags *)dst) = value; + + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/constants-ui.h b/plugins/pychrysalide/glibext/constants-ui.h new file mode 100644 index 0000000..6c7bc6e --- /dev/null +++ b/plugins/pychrysalide/glibext/constants-ui.h @@ -0,0 +1,41 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * constants-ui.h - prototypes pour l'ajout des constantes pour les extensions graphique à la GLib + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Définit les constantes relatives aux lignes de tampon. */ +bool define_buffer_line_constants(PyTypeObject *); + +/* Tente de convertir en constante BufferLineFlags. */ +int convert_to_buffer_line_flags(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_UI_H */ diff --git a/plugins/pychrysalide/glibext/constants.c b/plugins/pychrysalide/glibext/constants.c index 169ffa2..f733cf6 100644 --- a/plugins/pychrysalide/glibext/constants.c +++ b/plugins/pychrysalide/glibext/constants.c @@ -25,16 +25,17 @@ #include "constants.h" +#include <glibext/portion.h> + +/* #include <i18n.h> -#include <glibext/bufferline.h> #include <glibext/comparison.h> #include <glibext/configuration.h> #include <glibext/linesegment.h> -#include <glibext/gbinportion.h> #ifdef INCLUDE_GTK_SUPPORT # include <glibext/gloadedpanel.h> #endif - +*/ #include "../helpers.h" @@ -58,6 +59,7 @@ bool define_binary_portion_constants(PyTypeObject *type) PyObject *strdict; /* Groupe de chaînes constantes*/ PyObject *values; /* Groupe de valeurs à établir */ + /* result = create_string_constants_group_to_type(type, "BinaryPortionCode", "Selector names for the CSS rendering.", &strdict); @@ -69,6 +71,7 @@ bool define_binary_portion_constants(PyTypeObject *type) if (!result) goto exit; + */ values = PyDict_New(); @@ -150,105 +153,7 @@ int convert_to_portion_access_rights(PyObject *arg, void *dst) } -/****************************************************************************** -* * -* Paramètres : type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes relatives aux lignes de tampon. * -* * -* Retour : true en cas de succès de l'opération, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool define_buffer_line_constants(PyTypeObject *type) -{ - bool result; /* Bilan à retourner */ - PyObject *values; /* Groupe de valeurs à établir */ - - values = PyDict_New(); - - result = add_const_to_group(values, "NONE", BLF_NONE); - if (result) result = add_const_to_group(values, "HAS_CODE", BLF_HAS_CODE); - if (result) result = add_const_to_group(values, "IS_LABEL", BLF_IS_LABEL); - if (result) result = add_const_to_group(values, "ENTRYPOINT", BLF_ENTRYPOINT); - if (result) result = add_const_to_group(values, "BOOKMARK", BLF_BOOKMARK); - if (result) result = add_const_to_group(values, "WIDTH_MANAGER", BLF_WIDTH_MANAGER); - if (result) result = add_const_to_group(values, "ALL", BLF_ALL); - - if (!result) - { - Py_DECREF(values); - goto exit; - } - - result = attach_constants_group_to_type(type, true, "BufferLineFlags", values, - "Optional flags linked to a rendering line."); - - 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 BufferLineFlags. * -* * -* Retour : Bilan de l'opération, voire indications supplémentaires. * -* * -* Remarques : - * -* * -******************************************************************************/ - -int convert_to_buffer_line_flags(PyObject *arg, void *dst) -{ - int result; /* Bilan à retourner */ - unsigned long value; /* Valeur transcrite */ - - 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 BufferLineFlags"); - break; - - case 1: - value = PyLong_AsUnsignedLong(arg); - - if ((value & BLF_ALL) != value) - { - PyErr_SetString(PyExc_TypeError, "invalid value for BufferLineFlags"); - result = 0; - } - - else - *((BufferLineFlags *)dst) = value; - - break; - - default: - assert(false); - break; - - } - - return result; - -} - - +#if 0 /****************************************************************************** * * * Paramètres : type = type dont le dictionnaire est à compléter. * @@ -617,3 +522,4 @@ int convert_to_scroll_position_tweak(PyObject *arg, void *dst) #endif +#endif diff --git a/plugins/pychrysalide/glibext/constants.h b/plugins/pychrysalide/glibext/constants.h index 4a4f6da..c695aa9 100644 --- a/plugins/pychrysalide/glibext/constants.h +++ b/plugins/pychrysalide/glibext/constants.h @@ -37,11 +37,7 @@ bool define_binary_portion_constants(PyTypeObject *); /* Tente de convertir en constante PortionAccessRights. */ int convert_to_portion_access_rights(PyObject *, void *); -/* Définit les constantes relatives aux lignes de tampon. */ -bool define_buffer_line_constants(PyTypeObject *); - -/* Tente de convertir en constante BufferLineFlags. */ -int convert_to_buffer_line_flags(PyObject *, void *); +#if 0 /* Définit les constantes relatives aux modes de comparaison. */ bool define_comparable_item_constants(PyTypeObject *); @@ -68,6 +64,8 @@ int convert_to_scroll_position_tweak(PyObject *, void *); #endif +#endif + #endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_CONSTANTS_H */ diff --git a/plugins/pychrysalide/glibext/linegen.c b/plugins/pychrysalide/glibext/generator.c index d7e96fd..3a9a8ab 100644 --- a/plugins/pychrysalide/glibext/linegen.c +++ b/plugins/pychrysalide/glibext/generator.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * linegen.c - équivalent Python du fichier "glibext/linegen.h" + * generator.c - équivalent Python du fichier "glibext/generator.h" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,21 +22,21 @@ */ -#include "linegen.h" +#include "generator.h" #include <pygobject.h> -#include <glibext/linegen-int.h> +#include <glibext/generator-int.h> #include "bufferline.h" -#include "constants.h" -#include "linecursor.h" +#include "constants-ui.h" +//#include "linecursor.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" +//#include "../analysis/content.h" @@ -44,42 +44,55 @@ /* Procède à l'initialisation de l'interface de génération. */ -static void py_line_generator_interface_init(GLineGeneratorIface *, gpointer *); +static void py_token_generator_interface_init(GTokenGeneratorInterface *, gpointer *); /* Indique le nombre de ligne prêtes à être générées. */ -static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *); +static size_t py_token_generator_count_lines_wrapper(const GTokenGenerator *); + +/* Renseigne sur les propriétés liées à un générateur. */ +static BufferLineFlags py_token_generator_get_flags_wrapper(const GTokenGenerator *, size_t, size_t); + +/*Description : Etablit dans une ligne de rendu le contenu représenté. */ +static void py_token_generator_populate_line_wrappper(const GTokenGenerator *, size_t, size_t, GBufferLine *, void *); + + +#if 0 + /* Retrouve l'emplacement correspondant à une position donnée. */ -static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *, gint, size_t, size_t, GLineCursor **); +static void py_token_generator_compute_cursor_wrapper(const GTokenGenerator *, gint, size_t, size_t, GLineCursor **); /* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *, size_t, size_t, const GLineCursor *); +static int py_token_generator_contain_cursor_wrapper(const GTokenGenerator *, size_t, size_t, const GLineCursor *); + + +#endif -/* Renseigne sur les propriétés liées à un générateur. */ -static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *, size_t, size_t); -/* Imprime dans une ligne de rendu le contenu représenté. */ -static void py_line_generator_print_wrapper(GLineGenerator *, GBufferLine *, size_t, size_t, const GBinContent *); +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ -/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ +/* Renseigne sur les propriétés liées à un générateur. */ +static PyObject *py_token_generator_get_flags(PyObject *, PyObject *); + +/* Etablit dans une ligne de rendu le contenu représenté. */ +static PyObject *py_token_generator_populate_line(PyObject *self, PyObject *args); + +#if 0 /* Retrouve l'emplacement correspondant à une position donnée. */ -static PyObject *py_line_generator_compute_cursor(PyObject *, PyObject *); +static PyObject *py_token_generator_compute_cursor(PyObject *, PyObject *); /* Détermine si le conteneur s'inscrit dans une plage donnée. */ -static PyObject *py_line_generator_contain_cursor(PyObject *, PyObject *); +static PyObject *py_token_generator_contain_cursor(PyObject *, PyObject *); -/* Renseigne sur les propriétés liées à un générateur. */ -static PyObject *py_line_generator_get_flags(PyObject *, PyObject *); +#endif -/* Imprime dans une ligne de rendu le contenu représenté. */ -static PyObject *py_line_generator_print(PyObject *, PyObject *); /* Indique le nombre de ligne prêtes à être générées. */ -static PyObject *py_line_generator_get_lines_count(PyObject *, void *); +static PyObject *py_token_generator_get_lines_count(PyObject *, void *); @@ -101,31 +114,33 @@ static PyObject *py_line_generator_get_lines_count(PyObject *, void *); * * ******************************************************************************/ -static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointer *unused) +static void py_token_generator_interface_init(GTokenGeneratorInterface *iface, gpointer *unused) { -#define LINE_GENERATOR_DOC \ - "LineGenerator gives an interface to all objects which aim to produce" \ +#define TOKEN_GENERATOR_DOC \ + "TokenGenerator gives an interface to all objects which aim to produce" \ " content for rendering lines. Such lines can be exported to graphical" \ " interfaces or text files.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, LineGenerator):\n" \ + " class NewImplem(GObject.Object, TokenGenerator):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ - "* pychrysalide.glibext.LineGenerator._count_lines();\n" \ - "* pychrysalide.glibext.LineGenerator._compute_cursor();\n" \ - "* pychrysalide.glibext.LineGenerator._contain_cursor();\n" \ - "* pychrysalide.glibext.LineGenerator._get_flags();\n" \ - "* pychrysalide.glibext.LineGenerator._print();\n" \ - - iface->count = py_line_generator_count_lines_wrapper; - iface->compute = py_line_generator_compute_cursor_wrapper; - iface->contain = py_line_generator_contain_cursor_wrapper; - iface->get_flags = py_line_generator_get_flags_wrapper; - iface->print = py_line_generator_print_wrapper; + "* pychrysalide.glibext.TokenGenerator._count_lines();\n" \ + "* pychrysalide.glibext.TokenGenerator._get_flags();\n" \ + "* pychrysalide.glibext.TokenGenerator._populate_line();\n" \ + "* pychrysalide.glibext.TokenGenerator._compute_cursor();\n" \ + "* pychrysalide.glibext.TokenGenerator._contain_cursor().\n" + + iface->count = py_token_generator_count_lines_wrapper; + iface->get_flags = py_token_generator_get_flags_wrapper; + iface->populate = py_token_generator_populate_line_wrappper; +#if 0 + iface->compute = py_token_generator_compute_cursor_wrapper; + iface->contain = py_token_generator_contain_cursor_wrapper; +#endif } @@ -142,7 +157,7 @@ static void py_line_generator_interface_init(GLineGeneratorIface *iface, gpointe * * ******************************************************************************/ -static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *generator) +static size_t py_token_generator_count_lines_wrapper(const GTokenGenerator *generator) { size_t result; /* Décompte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ @@ -150,7 +165,7 @@ static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *genera PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_COUNT_LINES_WRAPPER PYTHON_WRAPPER_DEF \ ( \ _count_lines, "$self, /", \ METH_NOARGS, \ @@ -193,61 +208,61 @@ static size_t py_line_generator_count_lines_wrapper(const GLineGenerator *genera /****************************************************************************** * * * Paramètres : generator = générateur à consulter. * -* x = position géographique sur la ligne concernée. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Renseigne sur les propriétés liées à un générateur. * * * -* Retour : Emplacement constitué. * +* Retour : Propriétés particulières associées. * * * * Remarques : - * * * ******************************************************************************/ -static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor) +static BufferLineFlags py_token_generator_get_flags_wrapper(const GTokenGenerator *generator, size_t index, size_t repeat) { + BufferLineFlags result; /* Fanions à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _compute_cursor, "$self, x, index, repeat, /", \ + _get_flags, "$self, index, repeat, /", \ METH_VARARGS, \ - "Abstract method used to create a new cursor for a given" \ - " location inside displayed lines.\n" \ + "Abstract method used to provide flags for a given rendering" \ + " line.\n" \ "\n" \ - "The position on the horizontal axis, the line index and the" \ - " number of repetitions (only relevant if the generator" \ - " produces several lines) give indications about the active" \ - " position.\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position.\n" \ "\n" \ - "The result has to be a pychrysalide.glibext.LineCursor" \ - " instance." \ + "The result has to be a" \ + " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n" \ ) + result = BLF_NONE; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_compute_cursor")) + if (has_python_method(pyobj, "_get_flags")) { - args = PyTuple_New(3); - PyTuple_SetItem(args, 0, PyLong_FromSize_t(x)); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); - pyret = run_python_method(pyobj, "_compute_cursor", args); + pyret = run_python_method(pyobj, "_get_flags", args); if (pyret != NULL) { - ret = convert_to_line_cursor(pyret, cursor); + ret = convert_to_buffer_line_flags(pyret, &result); if (ret != 1) - *cursor = NULL; + result = BLF_NONE; Py_DECREF(pyret); @@ -261,75 +276,66 @@ static void py_line_generator_compute_cursor_wrapper(const GLineGenerator *gener PyGILState_Release(gstate); + return result; + } /****************************************************************************** * * -* Paramètres : generator = générateur à consulter. * +* Paramètres : generator = générateur à utiliser pour l'impression. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * -* cursor = emplacement à analyser. * +* line = ligne de rendu à compléter. * +* data = éventuelle donnée complémentaire fournie. * * * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* Description : Etablit dans une ligne de rendu le contenu représenté. * * * -* Retour : Bilan de la détermination, utilisable en comparaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor) +static void py_token_generator_populate_line_wrappper(const GTokenGenerator *generator, size_t index, size_t repeat, GBufferLine *line, void *data) { - int result; /* Bilan d'analyse à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_POPULATE_LINE_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _contain_cursor, "$self, index, repeat, cursor, /", \ + _populate_line, "$self, index, repeat, line, /", \ METH_VARARGS, \ - "Abstract method used to check the position of a cursor in" \ - " relation to rendering lines.\n" \ + "Abstract method used to generate content into a rendering" \ + " line.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ + "This rendering output is pointed by the *line* argument, which"\ + " is a provided pychrysalide.glibext.BufferLine instance. The" \ + " line *index* and the number of repetitions (only relevant" \ " if the generator produces several lines) give indications" \ - " about the active position. The cursor is a" \ - " pychrysalide.glibext.LineCursor instance.\n" \ + " about the current rendering position.\n" \ "\n" \ - "The result has to be an integer less than, equal to, or" \ - " greater than zero if the cursor is, respectively, before," \ - " inside or after the area covered by the generator." \ + "If set, the content is a pychrysalide.analysis.BinContent" \ + " instance providing access to the processed binary data." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_contain_cursor")) + if (has_python_method(pyobj, "_populate_line")) { args = PyTuple_New(3); PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); - PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor))); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(line))); + //PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content))); - pyret = run_python_method(pyobj, "_contain_cursor", args); - - if (pyret != NULL) - { - ret = PyLong_Check(pyret); + pyret = run_python_method(pyobj, "_populate_line", args); - if (ret) - result = PyLong_AsLong(pyret); - - Py_DECREF(pyret); - - } + Py_XDECREF(pyret); Py_DECREF(args); @@ -339,69 +345,70 @@ static int py_line_generator_contain_cursor_wrapper(const GLineGenerator *genera PyGILState_Release(gstate); - return result; - } +#if 0 + + /****************************************************************************** * * * Paramètres : generator = générateur à consulter. * +* x = position géographique sur la ligne concernée. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Retrouve l'emplacement correspondant à une position donnée. * * * -* Retour : Propriétés particulières associées. * +* Retour : Emplacement constitué. * * * * Remarques : - * * * ******************************************************************************/ -static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator *generator, size_t index, size_t repeat) +static void py_token_generator_compute_cursor_wrapper(const GTokenGenerator *generator, gint x, size_t index, size_t repeat, GLineCursor **cursor) { - BufferLineFlags result; /* Fanions à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_GET_FLAGS_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_COMPUTE_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _get_flags, "$self, index, repeat, /", \ + _compute_cursor, "$self, x, index, repeat, /", \ METH_VARARGS, \ - "Abstract method used to provide flags for a given rendering" \ - " line.\n" \ + "Abstract method used to create a new cursor for a given" \ + " location inside displayed lines.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position.\n" \ + "The position on the horizontal axis, the line index and the" \ + " number of repetitions (only relevant if the generator" \ + " produces several lines) give indications about the active" \ + " position.\n" \ "\n" \ - "The result has to be a" \ - " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n" \ + "The result has to be a pychrysalide.glibext.LineCursor" \ + " instance." \ ) - result = BLF_NONE; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_get_flags")) + if (has_python_method(pyobj, "_compute_cursor")) { - args = PyTuple_New(2); - PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(x)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); - pyret = run_python_method(pyobj, "_get_flags", args); + pyret = run_python_method(pyobj, "_compute_cursor", args); if (pyret != NULL) { - ret = convert_to_buffer_line_flags(pyret, &result); + ret = convert_to_line_cursor(pyret, cursor); if (ret != 1) - result = BLF_NONE; + *cursor = NULL; Py_DECREF(pyret); @@ -415,65 +422,75 @@ static BufferLineFlags py_line_generator_get_flags_wrapper(const GLineGenerator PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * -* Paramètres : generator = générateur à utiliser pour l'impression. * -* line = ligne de rendu à compléter. * +* Paramètres : generator = générateur à consulter. * * index = indice de cette même ligne dans le tampon global.* * repeat = indice d'utilisations successives du générateur. * -* content = éventuel contenu binaire brut à imprimer. * +* cursor = emplacement à analyser. * * * -* Description : Imprime dans une ligne de rendu le contenu représenté. * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * * * -* Retour : - * +* Retour : Bilan de la détermination, utilisable en comparaisons. * * * * Remarques : - * * * ******************************************************************************/ -static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content) +static int py_token_generator_contain_cursor_wrapper(const GTokenGenerator *generator, size_t index, size_t repeat, const GLineCursor *cursor) { + int result; /* Bilan d'analyse à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ + int ret; /* Bilan d'une conversion */ -#define LINE_GENERATOR_PRINT_WRAPPER PYTHON_WRAPPER_DEF \ +#define TOKEN_GENERATOR_CONTAIN_CURSOR_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _print, "$self, line, index, repeat, content, /", \ + _contain_cursor, "$self, index, repeat, cursor, /", \ METH_VARARGS, \ - "Abstract method used to generate content into a rendering" \ - " line, which is a provided pychrysalide.glibext.BufferLine" \ - " instance.\n" \ + "Abstract method used to check the position of a cursor in" \ + " relation to rendering lines.\n" \ "\n" \ "The line index and the number of repetitions (only relevant" \ " if the generator produces several lines) give indications" \ - " about the current rendering position.\n" \ + " about the active position. The cursor is a" \ + " pychrysalide.glibext.LineCursor instance.\n" \ "\n" \ - "If set, the content is a pychrysalide.analysis.BinContent" \ - " instance providing access to the processed binary data." \ + "The result has to be an integer less than, equal to, or" \ + " greater than zero if the cursor is, respectively, before," \ + " inside or after the area covered by the generator." \ ) + result = 0; + gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(generator)); - if (has_python_method(pyobj, "_print")) + if (has_python_method(pyobj, "_contain_cursor")) { - args = PyTuple_New(4); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(line))); - PyTuple_SetItem(args, 1, PyLong_FromSize_t(index)); - PyTuple_SetItem(args, 2, PyLong_FromSize_t(repeat)); - PyTuple_SetItem(args, 3, pygobject_new(G_OBJECT(content))); + args = PyTuple_New(3); + PyTuple_SetItem(args, 0, PyLong_FromSize_t(index)); + PyTuple_SetItem(args, 1, PyLong_FromSize_t(repeat)); + PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(cursor))); - pyret = run_python_method(pyobj, "_print", args); + pyret = run_python_method(pyobj, "_contain_cursor", args); - Py_XDECREF(pyret); + if (pyret != NULL) + { + ret = PyLong_Check(pyret); + + if (ret) + result = PyLong_AsLong(pyret); + + Py_DECREF(pyret); + + } Py_DECREF(args); @@ -483,12 +500,16 @@ static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLi PyGILState_Release(gstate); + return result; + } +#endif + /* ---------------------------------------------------------------------------------- */ -/* CONNEXION AVEC L'API DE PYTHON */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ /* ---------------------------------------------------------------------------------- */ @@ -497,57 +518,45 @@ static void py_line_generator_print_wrapper(GLineGenerator *generator, GBufferLi * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Retrouve l'emplacement correspondant à une position donnée. * +* Description : Renseigne sur les propriétés liées à un générateur. * * * -* Retour : Emplacement constitué. * +* Retour : Propriétés particulières associées. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args) +static PyObject *py_token_generator_get_flags(PyObject *self, PyObject *args) { PyObject *result; /* Propriétés à retourner */ - int x; /* Position horizontale */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - GLineCursor *cursor; /* Curseur nouveau obtenu */ + GTokenGenerator *generator; /* Version native */ + BufferLineFlags flags; /* Propriétés courantes */ -#define LINE_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF \ -( \ - compute_cursor, "$self, x, index, repeat, /", \ - METH_VARARGS, py_line_generator, \ - "Create a a new cursor for a given location inside displayed" \ - " lines.\n" \ - "\n" \ - "The position on the horizontal axis, the line index and the" \ - " number of repetitions (only relevant if the generator" \ - " produces several lines) give indications about the active" \ - " position.\n" \ - "\n" \ - "The result has to be a pychrysalide.glibext.LineCursor" \ - " instance." \ +#define TOKEN_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF \ +( \ + get_flags, "$self, index, repeat, /", \ + METH_VARARGS, py_token_generator, \ + "Get the flags of a given position from the generator.\n" \ + "\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position.\n" \ + "\n" \ + "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags" \ + " value." \ ) - ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat); + ret = PyArg_ParseTuple(args, "nn", &index, &repeat); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - cursor = g_line_generator_compute_cursor(generator, x, index, repeat); + flags = g_token_generator_get_flags(generator, index, repeat); - if (cursor != NULL) - { - result = pygobject_new(G_OBJECT(cursor)); - g_object_unref(G_OBJECT(cursor)); - } - else - { - result = Py_None; - Py_INCREF(result); - } + result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags); return result; @@ -559,99 +568,108 @@ static PyObject *py_line_generator_compute_cursor(PyObject *self, PyObject *args * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * +* Description : Etablit dans une ligne de rendu le contenu représenté. * * * -* Retour : Bilan de la détermination, utilisable en comparaisons. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_contain_cursor(PyObject *self, PyObject *args) +static PyObject *py_token_generator_populate_line(PyObject *self, PyObject *args) { - PyObject *result; /* Propriétés à retourner */ + GBufferLine *line; /* Ligne de rendu à compléter */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ - GLineCursor *cursor; /* Curseur à venir situer */ + GTokenGenerator *generator; /* Version native */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - int status; /* Bilan d'une analyse */ -#define LINE_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF \ +#define TOKEN_GENERATOR_POPULATE_LINE_METHOD PYTHON_METHOD_DEF \ ( \ - contain_cursor, "$self, index, repeat, cursor, /", \ - METH_VARARGS, py_line_generator, \ - "Check the position of a cursor in relation to rendering" \ - " lines.\n" \ + populate_line, "$self, index, repeat, line, /", \ + METH_VARARGS, py_token_generator, \ + "Produce output into a rendering line with optional content.\n" \ "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position. The cursor is a" \ - " pychrysalide.glibext.LineCursor instance.\n" \ + "The provided *line* is a pychrysalide.glibext.BufferLine" \ + " instance. The *index* and the number of repetitions (only" \ + " relevant if the generator produces several lines) give" \ + " indications about the current rendering position.\n" \ "\n" \ - "The result has to be an integer less than, equal to, or" \ - " greater than zero if the cursor is, respectively, before," \ - " inside or after the area covered by the generator." \ + "If set, the content is a pychrysalide.analysis.BinContent" \ + " instance providing access to the processed binary data." \ ) - ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor); + ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_buffer_line, &line); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - status = g_line_generator_contain_cursor(generator, index, repeat, cursor); + g_token_generator_populate_line(generator, index, repeat, line, NULL); - result = PyLong_FromLong(status); - - return result; + Py_RETURN_NONE; } + +#if 0 + /****************************************************************************** * * * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Renseigne sur les propriétés liées à un générateur. * +* Description : Retrouve l'emplacement correspondant à une position donnée. * * * -* Retour : Propriétés particulières associées. * +* Retour : Emplacement constitué. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args) +static PyObject *py_token_generator_compute_cursor(PyObject *self, PyObject *args) { PyObject *result; /* Propriétés à retourner */ + int x; /* Position horizontale */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ int ret; /* Bilan de lecture des args. */ - GLineGenerator *generator; /* Version native */ - BufferLineFlags flags; /* Propriétés courantes */ + GTokenGenerator *generator; /* Version native */ + GLineCursor *cursor; /* Curseur nouveau obtenu */ -#define LINE_GENERATOR_GET_FLAGS_METHOD PYTHON_METHOD_DEF \ -( \ - get_flags, "$self, index, repeat, /", \ - METH_VARARGS, py_line_generator, \ - "Get the flags of a given position from the generator.\n" \ - "\n" \ - "The line index and the number of repetitions (only relevant" \ - " if the generator produces several lines) give indications" \ - " about the active position.\n" \ - "\n" \ - "The result is a pychrysalide.glibext.BufferLine.BufferLineFlags" \ - " value." \ +#define TOKEN_GENERATOR_COMPUTE_CURSOR_METHOD PYTHON_METHOD_DEF \ +( \ + compute_cursor, "$self, x, index, repeat, /", \ + METH_VARARGS, py_token_generator, \ + "Create a a new cursor for a given location inside displayed" \ + " lines.\n" \ + "\n" \ + "The position on the horizontal axis, the line index and the" \ + " number of repetitions (only relevant if the generator" \ + " produces several lines) give indications about the active" \ + " position.\n" \ + "\n" \ + "The result has to be a pychrysalide.glibext.LineCursor" \ + " instance." \ ) - ret = PyArg_ParseTuple(args, "nn", &index, &repeat); + ret = PyArg_ParseTuple(args, "inn", &x, &index, &repeat); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - flags = g_line_generator_get_flags(generator, index, repeat); + cursor = g_token_generator_compute_cursor(generator, x, index, repeat); - result = cast_with_constants_group_from_type(get_python_buffer_line_type(), "BufferLineFlags", flags); + if (cursor != NULL) + { + result = pygobject_new(G_OBJECT(cursor)); + g_object_unref(G_OBJECT(cursor)); + } + else + { + result = Py_None; + Py_INCREF(result); + } return result; @@ -663,50 +681,56 @@ static PyObject *py_line_generator_get_flags(PyObject *self, PyObject *args) * Paramètres : self = classe représentant un générateur à manipuler. * * args = arguments fournis à l'appel. * * * -* Description : Imprime dans une ligne de rendu le contenu représenté. * +* Description : Détermine si le conteneur s'inscrit dans une plage donnée. * * * -* Retour : - * +* Retour : Bilan de la détermination, utilisable en comparaisons. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_line_generator_print(PyObject *self, PyObject *args) +static PyObject *py_token_generator_contain_cursor(PyObject *self, PyObject *args) { - GBufferLine *line; /* Ligne de rendu à compléter */ + PyObject *result; /* Propriétés à retourner */ size_t index; /* Indice dans le tampon */ size_t repeat; /* Utilisations successives */ - GBinContent *content; /* Contenu binaire associé */ - GLineGenerator *generator; /* Version native */ + GLineCursor *cursor; /* Curseur à venir situer */ int ret; /* Bilan de lecture des args. */ + GTokenGenerator *generator; /* Version native */ + int status; /* Bilan d'une analyse */ -#define LINE_GENERATOR_PRINT_METHOD PYTHON_METHOD_DEF \ +#define TOKEN_GENERATOR_CONTAIN_CURSOR_METHOD PYTHON_METHOD_DEF \ ( \ - print, "$self, line, index, repeat, content, /", \ - METH_VARARGS, py_line_generator, \ - "Produce output into a rendering line with optional content.\n" \ + contain_cursor, "$self, index, repeat, cursor, /", \ + METH_VARARGS, py_token_generator, \ + "Check the position of a cursor in relation to rendering" \ + " lines.\n" \ "\n" \ - "The provided line is a pychrysalide.glibext.BufferLine" \ - " instance. The index and the number of repetitions (only" \ - " relevant if the generator produces several lines) give" \ - " indications about the current rendering position.\n" \ + "The line index and the number of repetitions (only relevant" \ + " if the generator produces several lines) give indications" \ + " about the active position. The cursor is a" \ + " pychrysalide.glibext.LineCursor instance.\n" \ "\n" \ - "If set, the content is a pychrysalide.analysis.BinContent" \ - " instance providing access to the processed binary data." \ + "The result has to be an integer less than, equal to, or" \ + " greater than zero if the cursor is, respectively, before," \ + " inside or after the area covered by the generator." \ ) - ret = PyArg_ParseTuple(args, "O&nnO&", convert_to_buffer_line, &line, &index, - &repeat, convert_to_binary_content, &content); + ret = PyArg_ParseTuple(args, "nnO&", &index, &repeat, convert_to_line_cursor, &cursor); if (!ret) return NULL; - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - g_line_generator_print(generator, line, index, repeat, content); + status = g_token_generator_contain_cursor(generator, index, repeat, cursor); - Py_RETURN_NONE; + result = PyLong_FromLong(status); + + return result; } +#endif + /****************************************************************************** * * @@ -721,24 +745,24 @@ static PyObject *py_line_generator_print(PyObject *self, PyObject *args) * * ******************************************************************************/ -static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure) +static PyObject *py_token_generator_get_lines_count(PyObject *self, void *closure) { PyObject *result; /* Décompte à retourner */ - GLineGenerator *generator; /* Version native */ + GTokenGenerator *generator; /* Version native */ size_t count; /* Nombre de lignes présentes */ -#define LINE_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL \ +#define TOKEN_GENERATOR_LINES_COUNT_ATTRIB PYTHON_GET_DEF_FULL \ ( \ - lines_count, py_line_generator, \ + lines_count, py_token_generator, \ "Quantity of lines produced by the generator.\n" \ "\n" \ "This number may vary between calls, if a width has changed" \ " for instance." \ ) - generator = G_LINE_GENERATOR(pygobject_get(self)); + generator = G_TOKEN_GENERATOR(pygobject_get(self)); - count = g_line_generator_count_lines(generator); + count = g_token_generator_count_lines(generator); result = PyLong_FromSize_t(count); @@ -759,43 +783,47 @@ static PyObject *py_line_generator_get_lines_count(PyObject *self, void *closure * * ******************************************************************************/ -PyTypeObject *get_python_line_generator_type(void) +PyTypeObject *get_python_token_generator_type(void) { - static PyMethodDef py_line_generator_methods[] = { - LINE_GENERATOR_COUNT_LINES_WRAPPER, - LINE_GENERATOR_COMPUTE_CURSOR_WRAPPER, - LINE_GENERATOR_CONTAIN_CURSOR_WRAPPER, - LINE_GENERATOR_GET_FLAGS_WRAPPER, - LINE_GENERATOR_PRINT_WRAPPER, - LINE_GENERATOR_COMPUTE_CURSOR_METHOD, - LINE_GENERATOR_CONTAIN_CURSOR_METHOD, - LINE_GENERATOR_GET_FLAGS_METHOD, - LINE_GENERATOR_PRINT_METHOD, + static PyMethodDef py_token_generator_methods[] = { + TOKEN_GENERATOR_COUNT_LINES_WRAPPER, + TOKEN_GENERATOR_GET_FLAGS_WRAPPER, + TOKEN_GENERATOR_POPULATE_LINE_WRAPPER, + /* + TOKEN_GENERATOR_COMPUTE_CURSOR_WRAPPER, + TOKEN_GENERATOR_CONTAIN_CURSOR_WRAPPER, + */ + TOKEN_GENERATOR_GET_FLAGS_METHOD, + TOKEN_GENERATOR_POPULATE_LINE_METHOD, + /* + TOKEN_GENERATOR_COMPUTE_CURSOR_METHOD, + TOKEN_GENERATOR_CONTAIN_CURSOR_METHOD, + */ { NULL } }; - static PyGetSetDef py_line_generator_getseters[] = { - LINE_GENERATOR_LINES_COUNT_ATTRIB, + static PyGetSetDef py_token_generator_getseters[] = { + TOKEN_GENERATOR_LINES_COUNT_ATTRIB, { NULL } }; - static PyTypeObject py_line_generator_type = { + static PyTypeObject py_token_generator_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.LineGenerator", + .tp_name = "pychrysalide.glibext.TokenGenerator", .tp_basicsize = sizeof(PyObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = LINE_GENERATOR_DOC, + .tp_doc = TOKEN_GENERATOR_DOC, - .tp_methods = py_line_generator_methods, - .tp_getset = py_line_generator_getseters, + .tp_methods = py_token_generator_methods, + .tp_getset = py_token_generator_getseters, }; - return &py_line_generator_type; + return &py_token_generator_type; } @@ -804,7 +832,7 @@ PyTypeObject *get_python_line_generator_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'.* +* Description : Prend en charge l'objet 'pychrysalide.....TokenGenerator'. * * * * Retour : Bilan de l'opération. * * * @@ -812,21 +840,21 @@ PyTypeObject *get_python_line_generator_type(void) * * ******************************************************************************/ -bool ensure_python_line_generator_is_registered(void) +bool ensure_python_token_generator_is_registered(void) { - PyTypeObject *type; /* Type Python 'LineGenerator' */ + PyTypeObject *type; /* Type Python 'TokenGenerator'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ static GInterfaceInfo info = { /* Paramètres d'inscription */ - .interface_init = (GInterfaceInitFunc)py_line_generator_interface_init, + .interface_init = (GInterfaceInitFunc)py_token_generator_interface_init, .interface_finalize = NULL, .interface_data = NULL, }; - type = get_python_line_generator_type(); + type = get_python_token_generator_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -834,7 +862,7 @@ bool ensure_python_line_generator_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_LINE_GENERATOR, type, &info)) + if (!register_interface_for_pygobject(dict, G_TYPE_TOKEN_GENERATOR, type, &info)) return false; } @@ -857,11 +885,11 @@ bool ensure_python_line_generator_is_registered(void) * * ******************************************************************************/ -int convert_to_line_generator(PyObject *arg, void *dst) +int convert_to_token_generator(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_line_generator_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_token_generator_type()); switch (result) { @@ -871,11 +899,11 @@ int convert_to_line_generator(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to line generator"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to token generator"); break; case 1: - *((GLineGenerator **)dst) = G_LINE_GENERATOR(pygobject_get(arg)); + *((GTokenGenerator **)dst) = G_TOKEN_GENERATOR(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/linegen.h b/plugins/pychrysalide/glibext/generator.h index bfad885..b2672a8 100644 --- a/plugins/pychrysalide/glibext/linegen.h +++ b/plugins/pychrysalide/glibext/generator.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * linegen.h - prototypes pour l'équivalent Python du fichier "glibext/linegen.h" + * generator.h - prototypes pour l'équivalent Python du fichier "glibext/generator.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2018-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_line_generator_type(void); +PyTypeObject *get_python_token_generator_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.LineGenerator'. */ -bool ensure_python_line_generator_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.TokenGenerator'. */ +bool ensure_python_token_generator_is_registered(void); /* Tente de convertir en générateur de lignes. */ -int convert_to_line_generator(PyObject *, void *); +int convert_to_token_generator(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_LINEGEN_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_GENERATOR_H */ diff --git a/plugins/pychrysalide/glibext/hashable.c b/plugins/pychrysalide/glibext/hashable.c new file mode 100644 index 0000000..c870d55 --- /dev/null +++ b/plugins/pychrysalide/glibext/hashable.c @@ -0,0 +1,398 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.c - équivalent Python du fichier "glibext/hashable.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "hashable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/hashable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint py_hashable_object_hash_wrapper(const GHashableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet l'empreinte d'un objet calculée par son parent. */ +static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static Py_hash_t py_hashable_object_hash(PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused) +{ +#define HASHABLE_OBJECT_DOC \ + "The HashableObject class defines a interface ensuring that a" \ + " customized hashing method is available for an object.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, HashableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.HashableObject._hash().\n" + + iface->hash = py_hashable_object_hash_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static guint py_hashable_object_hash_wrapper(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _hash, "$self", \ + METH_NOARGS, \ + "Abstract method computing a hash from an object which is used" \ + " as the default implementation of the __hash__() method.\n" \ + "\n" \ + "The result has to be an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + pyret = run_python_method(pyobj, "_hash", NULL); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsUnsignedLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet l'empreinte d'un objet calculée par son parent. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + guint hash; /* Valeur d'empreitne */ + +#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \ +( \ + parent_hash, "$self", \ + METH_NOARGS, py_hashable_object, \ + "Provide the hash value defined by the interface implementation"\ + " from the object native parent.\n" \ + "\n" \ + "The result is an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.HashableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._hash() function has to be used" \ + " instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface")); + + result = NULL; + + } + else + { + hash = parent_iface->hash(object); + + result = PyLong_FromUnsignedLong(hash); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static Py_hash_t py_hashable_object_hash(PyObject *self) +{ + Py_hash_t result; /* Empreinte à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + result = g_hashable_object_hash(object); + + UPDATE_RESULT_IF_RAISED_EXCEPTION(-1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_hashable_object_type(void) +{ + static PyMethodDef py_hashable_object_methods[] = { + HASHABLE_OBJECT_HASH_WRAPPER, + HASHABLE_OBJECT_PARENT_HASH_METHOD, + { NULL } + }; + + static PyGetSetDef py_hashable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_hashable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.HashableObject", + .tp_basicsize = sizeof(PyObject), + + .tp_hash = py_hashable_object_hash, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = HASHABLE_OBJECT_DOC, + + .tp_methods = py_hashable_object_methods, + .tp_getset = py_hashable_object_getseters, + + }; + + return &py_hashable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_hashable_object_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'HashableObject'*/ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_hashable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'objet réductible. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_hashable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object"); + break; + + case 1: + *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h index 79f7092..8583118 100644 --- a/plugins/pychrysalide/glibext/comparison.h +++ b/plugins/pychrysalide/glibext/hashable.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h" + * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_comparable_item_type(void); +PyTypeObject *get_python_hashable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */ -bool ensure_python_comparable_item_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */ +bool ensure_python_hashable_object_is_registered(void); -/* Tente de convertir en élément comparable. */ -int convert_to_comparable_item(PyObject *, void *); +/* Tente de convertir en interface d'objet réductible. */ +int convert_to_hashable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */ diff --git a/plugins/pychrysalide/glibext/module-ui.c b/plugins/pychrysalide/glibext/module-ui.c new file mode 100644 index 0000000..8fa6d0e --- /dev/null +++ b/plugins/pychrysalide/glibext/module-ui.c @@ -0,0 +1,62 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.c - intégration du répertoire glibext (forme graphique) en tant que module + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "module-ui.h" + + +#include <assert.h> + + +#include "bufferline.h" +#include "generator.h" + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Intègre les objets du module 'glibext' (mode UI). * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool populate_glibext_module_ui(void) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (result) result = ensure_python_token_generator_is_registered(); + + if (result) result = ensure_python_buffer_line_is_registered(); + + assert(result); + + return result; + +} diff --git a/plugins/pychrysalide/glibext/module-ui.h b/plugins/pychrysalide/glibext/module-ui.h new file mode 100644 index 0000000..d91a2fa --- /dev/null +++ b/plugins/pychrysalide/glibext/module-ui.h @@ -0,0 +1,38 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * module.h - prototypes pour l'intégration du répertoire glibext (forme graphique) en tant que module + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H + + +#include <Python.h> +#include <stdbool.h> + + +/* Intègre les objets du module 'glibext' (mode UI). */ +bool populate_glibext_module_ui(void); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_MODULE_UI_H */ diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c index 3e4307a..bbe357d 100644 --- a/plugins/pychrysalide/glibext/module.c +++ b/plugins/pychrysalide/glibext/module.c @@ -28,18 +28,28 @@ #include <assert.h> +/* #include "binarycursor.h" -#include "binportion.h" #include "buffercache.h" #include "bufferline.h" #include "bufferview.h" -#include "comparison.h" #include "configuration.h" #include "linecursor.h" #include "linegen.h" #include "loadedpanel.h" #include "named.h" +*/ +#include "comparable.h" +#include "hashable.h" +#include "objhole.h" +#include "portion.h" +#include "secstorage.h" +#include "serialize.h" #include "singleton.h" +#include "storage.h" +#include "strbuilder.h" +#include "work.h" +#include "workqueue.h" #include "../helpers.h" @@ -106,16 +116,28 @@ bool populate_glibext_module(void) result = true; + if (result) result = ensure_python_comparable_object_is_registered(); + if (result) result = ensure_python_hashable_object_is_registered(); + if (result) result = ensure_python_serializable_object_is_registered(); if (result) result = ensure_python_singleton_candidate_is_registered(); + if (result) result = ensure_python_string_builder_is_registered(); - if (result) result = ensure_python_binary_cursor_is_registered(); + if (result) result = ensure_python_thick_object_is_registered(); if (result) result = ensure_python_binary_portion_is_registered(); + if (result) result = ensure_python_generic_work_is_registered(); + if (result) result = ensure_python_object_storage_is_registered(); + if (result) result = ensure_python_secret_storage_is_registered(); + if (result) result = ensure_python_singleton_factory_is_registered(); + if (result) result = ensure_python_work_queue_is_registered(); + + /* + + if (result) result = ensure_python_binary_cursor_is_registered(); if (result) result = ensure_python_buffer_cache_is_registered(); if (result) result = ensure_python_buffer_line_is_registered(); #ifdef INCLUDE_GTK_SUPPORT if (result) result = ensure_python_buffer_view_is_registered(); #endif - if (result) result = ensure_python_comparable_item_is_registered(); if (result) result = ensure_python_config_param_is_registered(); if (result) result = ensure_python_config_param_iterator_is_registered(); if (result) result = ensure_python_generic_config_is_registered(); @@ -125,7 +147,7 @@ bool populate_glibext_module(void) if (result) result = ensure_python_loaded_panel_is_registered(); if (result) result = ensure_python_named_widget_is_registered(); #endif - if (result) result = ensure_python_singleton_factory_is_registered(); + */ assert(result); diff --git a/plugins/pychrysalide/glibext/objhole.c b/plugins/pychrysalide/glibext/objhole.c new file mode 100644 index 0000000..6bea5d1 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.c @@ -0,0 +1,429 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - équivalent Python du fichier "glibext/objhole.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "objhole.h" + + +#include <pygobject.h> + + +#include <i18n.h> +#include <glibext/objhole-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(thick_object, G_TYPE_THICK_OBJECT); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_thick_object_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------ LIAISON DE FONCTIONNALITES AVEC L'API PYTHON ------------------ */ + + +/* Pose un verrou à l'aide du bit dédié de GObject. */ +static PyObject *py_thick_object_lock(PyObject *, PyObject *); + +/* Retire un verrou via le bit dédié de GObject. */ +static PyObject *py_thick_object_unlock(PyObject *, PyObject *); + +/* Indique le nombre de bits accaparés par la GLib. */ +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *, void *); + +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +static PyObject *py_thick_object_get_extra(PyObject *, void *); + +/* Définit la valeur courante de la zone de stockage d'un objet. */ +static int py_thick_object_set_extra(PyObject *, PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define THICK_OBJECT_DOC \ + "The ThickObject class is an alternative version of the GObject class." \ + " It provides an access to a 32-bit wide memory hole inside the native" \ + " GObject structure in order to exploit the area as various values" \ + " storage.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " ThickObject()" + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* LIAISON DE FONCTIONNALITES AVEC L'API PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = instance représentant une extension d'objet. * +* args = arguments fournis à l'appel, non utilisé ici. * +* * +* Description : Pose un verrou à l'aide du bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + +#define THICK_OBJECT_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "$self", \ + METH_NOARGS, py_thick_object, \ + "Lock the object using the internal GLib bit.\n" \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_lock(obj); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = instance représentant une extension d'objet. * +* args = arguments fournis à l'appel, non utilisé ici. * +* * +* Description : Retire un verrou via le bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + +#define THICK_OBJECT_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "$self", \ + METH_NOARGS, py_thick_object, \ + "Unlock the object using the internal GLib bit.\n" \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_unlock(obj); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique le nombre de bits accaparés par la GLib. * +* * +* Retour : Nombre de bits, à priori inférieur à 32. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + +#define THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + _GOBJECT_RESERVED_EXTRA_BITS, py_thick_object, \ + "Quantity of lower bits used by the GLib inside the memory hole.\n" \ + "\n" \ + "The returned value should be less then 32 and is provided for" \ + " pychrysalide.glibext.ThickObject subclass implementation." \ +) + + result = PyLong_FromUnsignedLong(GOBJECT_RESERVED_EXTRA_BITS); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_thick_object_get_extra(PyObject *self, void *closure) +{ + PyObject *result; /* Résultat à retourner */ + GThickObject *obj; /* Version GLib de l'instance */ + guint extra; /* Valeur à transmettre */ + +#define THICK_OBJECT_EXTRA_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + extra, py_thick_object, \ + "Data stored inside the structure memory hole of all GObject" \ + " native instances.\n" \ + "\n" \ + "Less than 32 bits are available for storing arbitrary values." \ +) + + obj = G_THICK_OBJECT(pygobject_get(self)); + + extra = g_thick_object_get_extra(obj); + + result = PyLong_FromUnsignedLong(extra); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* value = valeur fournie à intégrer ou prendre en compte. * +* closure = non utilisé ici. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_thick_object_set_extra(PyObject *self, PyObject *value, void *closure) +{ + unsigned long extra; /* Valeur à inscrire */ + GThickObject *obj; /* Version GLib de l'instance */ + + if (!PyLong_Check(value)) + { + PyErr_SetString(PyExc_TypeError, _("Invalid integer value")); + return -1; + } + + extra = PyLong_AsUnsignedLong(value); + + obj = G_THICK_OBJECT(pygobject_get(self)); + + g_thick_object_set_extra(obj, extra); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_thick_object_type(void) +{ + static PyMethodDef py_thick_object_methods[] = { + THICK_OBJECT_LOCK_METHOD, + THICK_OBJECT_UNLOCK_METHOD, + { NULL } + }; + + static PyGetSetDef py_thick_object_getseters[] = { + THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB, + THICK_OBJECT_EXTRA_ATTRIB, + { NULL } + }; + + static PyTypeObject py_thick_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ThickObject", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = THICK_OBJECT_DOC, + + .tp_methods = py_thick_object_methods, + .tp_getset = py_thick_object_getseters, + + .tp_init = py_thick_object_init, + .tp_new = py_thick_object_new, + + }; + + return &py_thick_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_thick_object_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ThickObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_thick_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_THICK_OBJECT, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en objet dense. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_thick_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_thick_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to thick object"); + break; + + case 1: + *((GThickObject **)dst) = G_THICK_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/objhole.h b/plugins/pychrysalide/glibext/objhole.h new file mode 100644 index 0000000..b541b70 --- /dev/null +++ b/plugins/pychrysalide/glibext/objhole.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - prototypes pour l'équivalent Python du fichier "glibext/objhole.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_thick_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. */ +bool ensure_python_thick_object_is_registered(void); + +/* Tente de convertir en objet dense. */ +int convert_to_thick_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_OBJHOLE_H */ diff --git a/plugins/pychrysalide/glibext/binportion.c b/plugins/pychrysalide/glibext/portion.c index 70eb314..d95308c 100644 --- a/plugins/pychrysalide/glibext/binportion.c +++ b/plugins/pychrysalide/glibext/portion.c @@ -1,6 +1,6 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * binportion.c - équivalent Python du fichier "glibext/gbinportion.c" + * portion.c - équivalent Python du fichier "glibext/portion.c" * * Copyright (C) 2019-2020 Cyrille Bagard * @@ -22,15 +22,14 @@ */ -#include "binportion.h" +#include "portion.h" #include <pygobject.h> #include <i18n.h> -#include <glibext/gbinportion-int.h> -#include <plugins/dt.h> +#include <glibext/portion-int.h> #include "constants.h" @@ -40,13 +39,13 @@ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_bin_portion_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(binary_portion, G_TYPE_BINARY_PORTION); + /* Initialise une instance sur la base du dérivé de GObject. */ -static int py_bin_portion_init(PyObject *, PyObject *, PyObject *); +static int py_binary_portion_init(PyObject *, PyObject *, PyObject *); -/* Effectue une comparaison avec un objet Python 'BinPortion'. */ +/* Effectue une comparaison avec un objet Python BinaryPortion. */ static PyObject *py_binary_portion_richcompare(PyObject *, PyObject *, int); /* Assure qu'une portion ne dépasse pas une position donnée. */ @@ -77,66 +76,6 @@ static int py_binary_portion_set_rights(PyObject *, PyObject *, void *); /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_bin_portion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_binary_portion_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_BIN_PORTION, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -149,33 +88,30 @@ static PyObject *py_bin_portion_new(PyTypeObject *type, PyObject *args, PyObject * * ******************************************************************************/ -static int py_bin_portion_init(PyObject *self, PyObject *args, PyObject *kwds) +static int py_binary_portion_init(PyObject *self, PyObject *args, PyObject *kwds) { - const char *code; /* Identifiant de couleur */ vmpa2t *addr; /* Emplacement de portion */ unsigned long long size; /* Taille de la portion */ int ret; /* Bilan de lecture des args. */ - GBinPortion *portion; /* Portion à manipuler */ + GBinaryPortion *portion; /* Portion à manipuler */ - static char *kwlist[] = { "code", "addr", "size", NULL }; + static char *kwlist[] = { "addr", "size", NULL }; #define BINARY_PORTION_DOC \ - "The BinPortion object handles parts of binaries usually formally" \ + "The BinaryPortion object handles parts of binaries usually formally" \ " identified in binary formats, like program segments or sections for ELF" \ " files for example.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ - " BinPortion(code, addr, size)" \ + " BinaryPortion(addr, size)" \ "\n" \ - "Where code is the CSS class style for the rendering color to use, addr is" \ - " the starting point of the portion in memory, as a pychrysalide.arch.vmpa" \ - " value, and size is the size of the portion." \ + "Where *addr* is the starting point of the portion in memory, as a" \ + " pychrysalide.arch.vmpa value, and *size* is the size of the portion." /* Récupération des paramètres */ - ret = PyArg_ParseTupleAndKeywords(args, kwds, "sO&K", kwlist, - &code, convert_any_to_vmpa, &addr, &size); + ret = PyArg_ParseTupleAndKeywords(args, kwds, "O&K", kwlist, convert_any_to_vmpa, &addr, &size); if (!ret) return -1; /* Initialisation d'un objet GLib */ @@ -189,11 +125,13 @@ static int py_bin_portion_init(PyObject *self, PyObject *args, PyObject *kwds) /* Eléments de base */ - portion = G_BIN_PORTION(pygobject_get(self)); - - portion->code = strdup(code); + portion = G_BINARY_PORTION(pygobject_get(self)); - init_mrange(&portion->range, addr, size); + if (!g_binary_portion_create(portion, addr, size)) + { + clean_vmpa_arg(addr); + return -1; + } clean_vmpa_arg(addr); @@ -208,7 +146,7 @@ static int py_bin_portion_init(PyObject *self, PyObject *args, PyObject *kwds) * b = second object Python à consulter. * * op = type de comparaison menée. * * * -* Description : Effectue une comparaison avec un objet Python 'BinPortion'. * +* Description : Effectue une comparaison avec un objet Python BinaryPortion. * * * * Retour : Bilan de l'opération. * * * @@ -220,8 +158,8 @@ static PyObject *py_binary_portion_richcompare(PyObject *a, PyObject *b, int op) { PyObject *result; /* Bilan à retourner */ int ret; /* Bilan de lecture des args. */ - const GBinPortion *portion_a; /* Premier élément à traiter */ - const GBinPortion *portion_b; /* Second élément à traiter */ + const GBinaryPortion *portion_a; /* Premier élément à traiter */ + const GBinaryPortion *portion_b; /* Second élément à traiter */ int status; /* Résultat d'une comparaison */ ret = PyObject_IsInstance(b, (PyObject *)get_python_binary_portion_type()); @@ -231,8 +169,8 @@ static PyObject *py_binary_portion_richcompare(PyObject *a, PyObject *b, int op) goto cmp_done; } - portion_a = G_BIN_PORTION(pygobject_get(a)); - portion_b = G_BIN_PORTION(pygobject_get(b)); + portion_a = G_BINARY_PORTION(pygobject_get(a)); + portion_b = G_BINARY_PORTION(pygobject_get(b)); status = g_binary_portion_compare(&portion_a, &portion_b); @@ -265,16 +203,16 @@ static PyObject *py_binary_portion_limit_range(PyObject *self, PyObject *args) PyObject *result; /* Trouvailles à retourner */ unsigned long long max; /* Taille maximale à accorder */ int ret; /* Bilan de lecture des args. */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ bool status; /* Bilan de la modification */ -#define BINARY_SYMBOL_LIMIT_RANGE_METHOD PYTHON_METHOD_DEF \ +#define BINARY_PORTION_LIMIT_RANGE_METHOD PYTHON_METHOD_DEF \ ( \ limit_range, "$self, max, /", \ METH_VARARGS, py_binary_portion, \ "Ensure the portion range does not cross a boundary size.\n" \ "\n" \ - "An integer value is expected as the maximum size of the" \ + "An *max* integer value is expected as the maximum size of the" \ " portion.\n" \ "\n" \ "A boolean value indicating the success of the operation is" \ @@ -284,7 +222,7 @@ static PyObject *py_binary_portion_limit_range(PyObject *self, PyObject *args) ret = PyArg_ParseTuple(args, "K", &max); if (!ret) return NULL; - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); status = g_binary_portion_limit_range(portion, max); @@ -311,24 +249,27 @@ static PyObject *py_binary_portion_limit_range(PyObject *self, PyObject *args) static PyObject *py_binary_portion_include(PyObject *self, PyObject *args) { - GBinPortion *sub; /* Sous-portion à inclure */ + GBinaryPortion *sub; /* Sous-portion à inclure */ int ret; /* Bilan de lecture des args. */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ -#define BINARY_SYMBOL_INCLUDE_METHOD PYTHON_METHOD_DEF \ -( \ - include, "$self, sub, /", \ - METH_VARARGS, py_binary_portion, \ - "Include another binary portion as a child item.\n" \ - "\n" \ - "The sub portion has to be a pychrysalide.glibext.BinPortion" \ - " instance." \ +#define BINARY_PORTION_INCLUDE_METHOD PYTHON_METHOD_DEF \ +( \ + include, "$self, sub, /", \ + METH_VARARGS, py_binary_portion, \ + "Include another binary portion as a child item.\n" \ + "\n" \ + "The provided *sub* portion has to be a" \ + " pychrysalide.glibext.BinaryPortion instance." \ + "\n" \ + "The returned value is a boolean value providing the" \ + " status of the operation." \ ) ret = PyArg_ParseTuple(args, "O&", convert_to_binary_portion, &sub); if (!ret) return NULL; - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); g_object_ref(G_OBJECT(sub)); g_binary_portion_include(portion, sub); @@ -354,7 +295,7 @@ static PyObject *py_binary_portion_include(PyObject *self, PyObject *args) static PyObject *py_binary_portion_get_desc(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ const char *desc; /* Description récupérée */ #define BINARY_PORTION_DESC_ATTRIB PYTHON_GETSET_DEF_FULL \ @@ -364,7 +305,7 @@ static PyObject *py_binary_portion_get_desc(PyObject *self, void *closure) " simple string." \ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); desc = g_binary_portion_get_desc(portion); @@ -391,7 +332,7 @@ static PyObject *py_binary_portion_get_desc(PyObject *self, void *closure) static int py_binary_portion_set_desc(PyObject *self, PyObject *value, void *closure) { - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ const char *desc; /* Description à définir */ if (!PyUnicode_Check(value)) @@ -400,7 +341,7 @@ static int py_binary_portion_set_desc(PyObject *self, PyObject *value, void *clo return -1; } - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); desc = PyUnicode_DATA(value); @@ -427,7 +368,7 @@ static int py_binary_portion_set_desc(PyObject *self, PyObject *value, void *clo static PyObject *py_binary_portion_get_range(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ const mrange_t *range; /* Espace de couverture */ #define BINARY_PORTION_RANGE_ATTRIB PYTHON_GET_DEF_FULL \ @@ -438,7 +379,7 @@ static PyObject *py_binary_portion_get_range(PyObject *self, void *closure) "This property is a pychrysalide.arch.mrange instance." \ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); range = g_binary_portion_get_range(portion); @@ -465,7 +406,7 @@ static PyObject *py_binary_portion_get_range(PyObject *self, void *closure) static PyObject *py_binary_portion_get_continuation(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ bool status; /* Bilan d'une consultation */ #define BINARY_PORTION_CONTINUATION_ATTRIB PYTHON_GETSET_DEF_FULL \ @@ -478,7 +419,7 @@ static PyObject *py_binary_portion_get_continuation(PyObject *self, void *closur " several parts when included in the portion tree." \ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); status = g_binary_portion_is_continuation(portion); @@ -506,7 +447,7 @@ static PyObject *py_binary_portion_get_continuation(PyObject *self, void *closur static int py_binary_portion_set_continuation(PyObject *self, PyObject *value, void *closure) { - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ bool status; /* Valeur à manipuler */ if (!PyBool_Check(value)) @@ -515,7 +456,7 @@ static int py_binary_portion_set_continuation(PyObject *self, PyObject *value, v return -1; } - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); status = (value == Py_True); @@ -542,17 +483,17 @@ static int py_binary_portion_set_continuation(PyObject *self, PyObject *value, v static PyObject *py_binary_portion_get_rights(PyObject *self, void *closure) { PyObject *result; /* Résultat à retourner */ - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ PortionAccessRights rights; /* Bilan d'une consultation */ #define BINARY_PORTION_RIGHTS_ATTRIB PYTHON_GETSET_DEF_FULL \ ( \ rights, py_binary_portion, \ "Access rights declared for the binary portion, as a" \ - " pychrysalide.glibext.BinPortion.PortionAccessRights value." \ + " pychrysalide.glibext.BinaryPortion.PortionAccessRights value."\ ) - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); rights = g_binary_portion_get_rights(portion); @@ -579,13 +520,13 @@ static PyObject *py_binary_portion_get_rights(PyObject *self, void *closure) static int py_binary_portion_set_rights(PyObject *self, PyObject *value, void *closure) { - GBinPortion *portion; /* Version GLib du type */ + GBinaryPortion *portion; /* Version GLib du type */ PortionAccessRights rights; /* Valeur à manipuler */ if (convert_to_portion_access_rights(value, &rights) != 1) return -1; - portion = G_BIN_PORTION(pygobject_get(self)); + portion = G_BINARY_PORTION(pygobject_get(self)); g_binary_portion_set_rights(portion, rights); @@ -609,8 +550,8 @@ static int py_binary_portion_set_rights(PyObject *self, PyObject *value, void *c PyTypeObject *get_python_binary_portion_type(void) { static PyMethodDef py_binary_portion_methods[] = { - BINARY_SYMBOL_LIMIT_RANGE_METHOD, - BINARY_SYMBOL_INCLUDE_METHOD, + BINARY_PORTION_LIMIT_RANGE_METHOD, + BINARY_PORTION_INCLUDE_METHOD, { NULL } }; @@ -626,7 +567,7 @@ PyTypeObject *get_python_binary_portion_type(void) PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.BinPortion", + .tp_name = "pychrysalide.glibext.BinaryPortion", .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, @@ -638,8 +579,8 @@ PyTypeObject *get_python_binary_portion_type(void) .tp_methods = py_binary_portion_methods, .tp_getset = py_binary_portion_getseters, - .tp_init = py_bin_portion_init, - .tp_new = py_bin_portion_new, + .tp_init = py_binary_portion_init, + .tp_new = py_binary_portion_new, }; @@ -652,7 +593,7 @@ PyTypeObject *get_python_binary_portion_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.glibext.BinPortion'. * +* Description : Prend en charge l'objet 'pychrysalide.glibext.BinaryPortion'.* * * * Retour : Bilan de l'opération. * * * @@ -662,7 +603,7 @@ PyTypeObject *get_python_binary_portion_type(void) bool ensure_python_binary_portion_is_registered(void) { - PyTypeObject *type; /* Type Python 'BinPortion' */ + PyTypeObject *type; /* Type Python 'BinaryPortion' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ @@ -674,7 +615,7 @@ bool ensure_python_binary_portion_is_registered(void) dict = PyModule_GetDict(module); - if (!register_class_for_pygobject(dict, G_TYPE_BIN_PORTION, type)) + if (!register_class_for_pygobject(dict, G_TYPE_BINARY_PORTION, type)) return false; if (!define_binary_portion_constants(type)) @@ -718,7 +659,7 @@ int convert_to_binary_portion(PyObject *arg, void *dst) break; case 1: - *((GBinPortion **)dst) = G_BIN_PORTION(pygobject_get(arg)); + *((GBinaryPortion **)dst) = G_BINARY_PORTION(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/binportion.h b/plugins/pychrysalide/glibext/portion.h index b27c8ea..a417c01 100644 --- a/plugins/pychrysalide/glibext/binportion.h +++ b/plugins/pychrysalide/glibext/portion.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * binportion.h - prototypes pour l'équivalent Python du fichier "glibext/gbinportion.h" + * portion.h - prototypes pour l'équivalent Python du fichier "glibext/portion.h" * - * Copyright (C) 2019 Cyrille Bagard + * Copyright (C) 2019-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_BINPORTION_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_BINPORTION_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_PORTION_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_PORTION_H #include <Python.h> @@ -34,7 +34,7 @@ /* Fournit un accès à une définition de type à diffuser. */ PyTypeObject *get_python_binary_portion_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.BinPortion'. */ +/* Prend en charge l'objet 'pychrysalide.glibext.BinaryPortion'. */ bool ensure_python_binary_portion_is_registered(void); /* Tente de convertir en portion de binaire. */ @@ -42,4 +42,4 @@ int convert_to_binary_portion(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_BINPORTION_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_PORTION_H */ diff --git a/plugins/pychrysalide/glibext/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c new file mode 100644 index 0000000..5935d29 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.c @@ -0,0 +1,625 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - équivalent Python du fichier "glibext/secstorage.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/secstorage-int.h> + + +#include "../access.h" +#include "../convert.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_secret_storage_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Définit un mot de passe pour protéger une clef maître. */ +static PyObject *py_secret_storage_set_password(PyObject *, PyObject *); + +/* Déverrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_unlock(PyObject *, PyObject *); + +/* Verrouille la clef de chiffrement maître. */ +static PyObject *py_secret_storage_lock(PyObject *, PyObject *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +static PyObject *py_secret_storage_has_key(PyObject *, void *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +static PyObject *py_secret_storage_is_locked(PyObject *, void *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + GSettings *settings; /* Configuration à considérer */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage natif à manipuler */ + +#define SECRET_STORAGE_DOC \ + "SecretStorage acts as guardian for secrets using ecryption," \ + " mainly for sensitive information stored as configuration" \ + " parameters.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " SecretStorage(settings=None)" \ + "\n" \ + "The *settings* arguement may point to a GSettings instance." \ + " This optional argument is mainly used for testing purpose;" \ + " the main configuration settings are used by default." + + settings = NULL; + + ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings_or_none, &settings); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + if (!g_secret_storage_create(storage, settings)) + return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \ +( \ + set_password, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Create a master key used for protecting secrets. This key is" \ + " itself protected by the provided password.\n" \ + "\n" \ + "The supplied *password* has to be a string.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed,"\ + " *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_set_password(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *passwd; /* Mot de passe associé */ + int ret; /* Bilan de lecture des args. */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + +#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \ +( \ + unlock, "/, password=''", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt in memory the master key used for protecting secrets.\n" \ + "\n" \ + "The supplied *password* is the primary password used to protect" \ + " this key.\n" \ + "\n" \ + "The result is a boolean status: *True* if the operation successed" \ + " or if the master key is already unlocked, *False* otherwise." \ +) + + passwd = ""; + + ret = PyArg_ParseTuple(args, "|s", &passwd); + if (!ret) return NULL; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_unlock(storage, passwd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + +#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \ +( \ + lock, "", \ + METH_NOARGS, py_secret_storage, \ + "Clear from memory the master key used for protecting secrets." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + g_secret_storage_lock(storage); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + encrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Encrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either encrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_encrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* args = arguments fournis à l'appel. * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args) +{ + PyObject *result; /* Conversion à retourner */ + const char *data_in; /* Données d'entrée à chiffrer */ + Py_ssize_t size_in; /* Quantité de ces données */ + int ret; /* Bilan de lecture des args. */ + sized_binary_t in; /* Données à chiffer */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de situation */ + sized_binary_t out; /* Données chiffrées */ + +#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \ +( \ + decrypt_data, "data", \ + METH_VARARGS, py_secret_storage, \ + "Decrypt data using an unlocked the master key.\n" \ + "\n" \ + "The *data* arguement points to bytes to process." \ + "\n" \ + "The result is either decrypted *data* as bytes in case of success,"\ + " or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in); + if (!ret) return NULL; + + in.static_data = data_in; + in.size = size_in; + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_decrypt_data(storage, &in, &out); + + if (status) + { + result = PyBytes_FromStringAndSize(out.static_data, out.size); + exit_sized_binary(&out); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_has_key(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \ +( \ + key, py_secret_storage, \ + "Indicate if a master key used for protecting secrets seems to have"\ + " been defined. Without any unlocking attempt, the test only relies"\ + " on the length of saved master data.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " seems to exist, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_has_key(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure) +{ + PyObject *result; /* Instance Python à retourner */ + GSecretStorage *storage; /* Stockage sécurisé visé */ + bool status; /* Bilan de consultation */ + +#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + locked, py_secret_storage, \ + "Indicate if the master key used for protecting secrets is" \ + " currently decrypted in memory.\n" \ + "\n" \ + "The *settings* arguement must point to a GSettings intance; the" \ + " main configuration settings are used by default.\n" \ + "\n" \ + "The returned status is a boolean status: *True* if the master key" \ + " is unlocked and ready for use, *False* otherwise." \ +) + + storage = G_SECRET_STORAGE(pygobject_get(self)); + + status = g_secret_storage_is_locked(storage); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_secret_storage_type(void) +{ + static PyMethodDef py_secret_storage_methods[] = { + SECRET_STORAGE_SET_PASSWORD_METHOD, + SECRET_STORAGE_UNLOCK_METHOD, + SECRET_STORAGE_LOCK_METHOD, + SECRET_STORAGE_ENCRYPT_DATA_METHOD, + SECRET_STORAGE_DECRYPT_DATA_METHOD, + { NULL } + }; + + static PyGetSetDef py_secret_storage_getseters[] = { + PY_SECRET_STORAGE_HAS_KEY_ATTRIB, + SECRET_STORAGE_IS_LOCKED_ATTRIB, + { NULL } + }; + + static PyTypeObject py_secret_storage_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.SecretStorage", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = SECRET_STORAGE_DOC, + + .tp_methods = py_secret_storage_methods, + .tp_getset = py_secret_storage_getseters, + + .tp_init = py_secret_storage_init, + .tp_new = py_secret_storage_new, + + }; + + return &py_secret_storage_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_secret_storage_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'SecretStorage' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_secret_storage_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en gardien des secrets avec stockage. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_secret_storage(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to secret storage"); + break; + + case 1: + *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h new file mode 100644 index 0000000..68726c3 --- /dev/null +++ b/plugins/pychrysalide/glibext/secstorage.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_secret_storage_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */ +bool ensure_python_secret_storage_is_registered(void); + +/* Tente de convertir en gardien des secrets avec stockage. */ +int convert_to_secret_storage(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */ diff --git a/plugins/pychrysalide/glibext/serialize.c b/plugins/pychrysalide/glibext/serialize.c new file mode 100644 index 0000000..61f359f --- /dev/null +++ b/plugins/pychrysalide/glibext/serialize.c @@ -0,0 +1,478 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * serialize.c - équivalent Python du fichier "glibext/serialize.h" + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "serialize.h" + + +#include <pygobject.h> + + +#include <glibext/serialize-int.h> + + +#include "storage.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de génération. */ +static void py_serializable_object_interface_init(GSerializableObjectInterface *, gpointer *); + +/* Charge un objet depuis un flux de données. */ +static bool py_serializable_object_load_wrapper(GSerializableObject *, GObjectStorage *, int); + +/* Sauvegarde un objet dans un flux de données. */ +static bool py_serializable_object_store_wrapper(const GSerializableObject *, GObjectStorage *, int); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Charge un objet depuis un flux de données. */ +static bool py_serializable_object_load(PyObject *, PyObject *); + +/* Sauvegarde un objet dans un flux de données. */ +static bool py_serializable_object_store(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de génération. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_serializable_object_interface_init(GSerializableObjectInterface *iface, gpointer *unused) +{ + +#define SERIALIZABLE_OBJECT_DOC \ + "SerializableObject defines an interface used to store and load" \ + " objects to and from a data buffer.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, SerializableObject):\n" \ + " ...\n" \ + "\n" \ + "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SerializableObject._load();\n" \ + "* pychrysalide.glibext.SerializableObject._store();\n" + + iface->load = py_serializable_object_load_wrapper; + iface->store = py_serializable_object_store_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instruction d'assemblage à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * +* * +* Description : Charge un objet depuis un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_serializable_object_load_wrapper(GSerializableObject *object, GObjectStorage *storage, int fd) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define SERIALIZABLE_OBJECT_LOAD_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _load, "$self, storage, fd, /", \ + METH_VARARGS, \ + "Abstract method used to load an object definition from a data stream.\n" \ + "\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " provided as a file descriptor which as to be kept open after" \ + " processing.\n" \ + "\n" \ + "The result is a boolean indicating the status of the operation." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + if (has_python_method(pyobj, "_load")) + { + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(storage))); + PyTuple_SetItem(args, 1, PyLong_FromLong(fd)); + + pyret = run_python_method(pyobj, "_load", args); + + result = (pyret == Py_True ? true : false); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instruction d'assemblage à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * +* * +* Description : Sauvegarde un objet dans un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_serializable_object_store_wrapper(const GSerializableObject *object, GObjectStorage *storage, int fd) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define SERIALIZABLE_OBJECT_STORE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _store, "$self, storage, fd, /", \ + METH_VARARGS, \ + "Abstract method used to store an object definition into a data stream.\n" \ + "\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " provided as a file descriptor which as to be kept open after" \ + " processing.\n" \ + "\n" \ + "The result is a boolean indicating the status of the operation." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + if (has_python_method(pyobj, "_store")) + { + args = PyTuple_New(2); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(storage))); + PyTuple_SetItem(args, 1, PyLong_FromLong(fd)); + + pyret = run_python_method(pyobj, "_store", args); + + result = (pyret == Py_True ? true : false); + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant un générateur à manipuler. * +* args = arguments fournis à l'appel. * +* * +* Description : Charge un objet depuis un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_serializable_object_load(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GObjectStorage *storage; /* Conservateur à manipuler */ + int fd; /* Flux ouvert (en lecture) */ + int ret; /* Bilan de lecture des args. */ + GSerializableObject *object; /* Version native */ + bool status; /* Bilan de l'opération */ + +#define SERIALIZABLE_OBJECT_LOAD_METHOD PYTHON_METHOD_DEF \ +( \ + load, "$self, storage, fd, /", \ + METH_VARARGS, py_serializable_object, \ + "Load an object definition from a data stream.\n" \ + "\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " used as a file descriptor for writing data\n" \ + "\n" \ + "The result is a boolean indicating the status of the operation." \ +) + + ret = PyArg_ParseTuple(args, "O&i", convert_to_object_storage, &storage, &fd); + if (!ret) return NULL; + + object = G_SERIALIZABLE_OBJECT(pygobject_get(self)); + + status = g_serializable_object_load(object, storage, fd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant un générateur à manipuler. * +* args = arguments fournis à l'appel. * +* * +* Description : Sauvegarde un objet dans un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_serializable_object_store(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GObjectStorage *storage; /* Conservateur à manipuler */ + int fd; /* Flux ouvert (en lecture) */ + int ret; /* Bilan de lecture des args. */ + GSerializableObject *object; /* Version native */ + bool status; /* Bilan de l'opération */ + +#define SERIALIZABLE_OBJECT_STORE_METHOD PYTHON_METHOD_DEF \ +( \ + store, "$self, storage, fd, /", \ + METH_VARARGS, py_serializable_object, \ + "Store an object definition into a data stream.\n" \ + "\n" \ + "The *storage* is a pychrysalide.glibext.ObjectStorage instance" \ + " provided to store inner objects. The *fd* argument is an integer value" \ + " used as a file descriptor for writing data\n" \ + "\n" \ + "The result is a boolean indicating the status of the operation." \ +) + + ret = PyArg_ParseTuple(args, "O&i", convert_to_object_storage, &storage, &fd); + if (!ret) return NULL; + + object = G_SERIALIZABLE_OBJECT(pygobject_get(self)); + + status = g_serializable_object_store(object, storage, fd); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_serializable_object_type(void) +{ + static PyMethodDef py_serializable_object_methods[] = { + SERIALIZABLE_OBJECT_LOAD_WRAPPER, + SERIALIZABLE_OBJECT_STORE_WRAPPER, + SERIALIZABLE_OBJECT_LOAD_METHOD, + SERIALIZABLE_OBJECT_STORE_METHOD, + { NULL } + }; + + static PyGetSetDef py_serializable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_serializable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.SerializableObject", + .tp_basicsize = sizeof(PyObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = SERIALIZABLE_OBJECT_DOC, + + .tp_methods = py_serializable_object_methods, + .tp_getset = py_serializable_object_getseters, + + }; + + return &py_serializable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide....SerializableObject'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_serializable_object_is_registered(void) +{ + PyTypeObject *type; /* Type 'SerializableObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_serializable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_serializable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_SERIALIZABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en objet adapté à une mise en cache. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_serializable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_serializable_object_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to serializable object"); + break; + + case 1: + *((GSerializableObject **)dst) = G_SERIALIZABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/serialize.h b/plugins/pychrysalide/glibext/serialize.h new file mode 100644 index 0000000..90688ba --- /dev/null +++ b/plugins/pychrysalide/glibext/serialize.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * serialize.h - prototypes pour l'équivalent Python du fichier "glibext/serialize.h" + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_serializable_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.SerializableObject'. */ +bool ensure_python_serializable_object_is_registered(void); + +/* Tente de convertir en objet adapté à une mise en cache. */ +int convert_to_serializable_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SERIALIZE_H */ diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c index 8491473..8712506 100644 --- a/plugins/pychrysalide/glibext/singleton.c +++ b/plugins/pychrysalide/glibext/singleton.c @@ -30,7 +30,6 @@ #include <glibext/singleton-int.h> -#include <plugins/dt.h> #include "../access.h" @@ -42,7 +41,7 @@ /* Procède à l'initialisation de l'interface de candidature. */ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *); +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *, gpointer *); /* Fournit une liste de candidats embarqués par un candidat. */ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *); @@ -50,37 +49,27 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper /* Met à jour une liste de candidats embarqués par un candidat. */ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *); - /* Marque un candidat comme figé. */ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *); +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *); /* Indique si le candidat est figé. */ -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *); - -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *); - -/* Fournit une liste de candidats embarqués par un candidat. */ -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *); +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *); /* Indique si le candidat est figé. */ -static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *); +static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *); + +/* Crée une copie modifiable d'un object unique. */ +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *); -/* Effectue une comparaison avec un objet 'SingletonCandidate'. */ -static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int); +/* Crée une copie modifiable d'un object unique. */ +static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Accompagne la création d'une instance dérivée en Python. */ -static PyObject *py_singleton_factory_new(PyTypeObject *, PyObject *, PyObject *); +CREATE_DYN_CONSTRUCTOR(singleton_factory, G_TYPE_SINGLETON_FACTORY); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_singleton_factory_init(PyObject *, PyObject *, PyObject *); @@ -108,37 +97,44 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *); * * ******************************************************************************/ -static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused) +static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *iface, gpointer *unused) { #define SINGLETON_CANDIDATE_DOC \ "The SingletonCandidate class is a required interface for objects" \ " aiming at becoming singleton instances. All shared singletons are" \ " registered within a pychrysalide.glibext.SingletonFactory object.\n" \ "\n" \ + "Implementations of the pychrysalide.glibext.HashableObject and" \ + " pychrysalide.glibext.ComparableObject interfaces are required for" \ + " types implementing the SingletonCandidate interface.\n" \ + "\n" \ "The main implemantations come with types derived from" \ - " pychrysalide.analysis.DataType.\n" \ + " pychrysalide.analysis.DataType (with possible recursivity) or from" \ + " pychrysalide.arch.ArchOperand.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, SingletonCandidate):\n" \ + " class NewImplem(GObject.Object, HashableObject, ComparableObject," \ + " SingletonCandidate):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._dup().\n" \ + "\n" \ + "The following methods may bbe defined for new implementations if" \ + " inner SingletonCandidate objets are carried:\n" \ "* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \ - "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\ - "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \ - "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \ - "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \ - "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n" + "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n" iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper; iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper; - iface->hash = py_singleton_candidate___hash__wrapper; - iface->is_equal = py_singleton_candidate___eq__wrapper; + iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper; + iface->is_ro = py_singleton_candidate_is_read_only_wrapper; - iface->set_ro = py_singleton_candidate_set_ro_wrapper; - iface->is_ro = py_singleton_candidate_is_ro_wrapper; + iface->dup = py_singleton_candidate_dup_wrapper; } @@ -313,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate) +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object.\n" \ - "\n" \ - "The result must be an integer value up to 64 bits." \ - "\n" \ - "Inner instances which are listed through the" \ - " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \ - " method do not need to get processed here as they are handled" \ - " automatically by the interface core." \ +#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _mark_as_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to seal the object as unmodifiable.\n" \ + "\n" \ + "No result is expected." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); + pyret = run_python_method(pyobj, "_mark_as_read_only", NULL); - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLongMask(pyret); - - Py_DECREF(pyret); - - } - - } + Py_XDECREF(pyret); Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other) +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __eq__, "$self, other, /", \ - METH_NOARGS, \ - "Abstract method used to provide the *__eq__* method for" \ - " rich comparison.\n" \ - "\n" \ - "The expected result is a boolean value." \ +#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _is_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to provide the state of the object: are" \ + " its properties frozen (*True*) or can it be modified" \ + " (*False*)?\n" \ + "\n" \ + "The result has to be a boolean status.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a boolean." \ ) - result = 0; + result = false; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__eq__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); - - pyret = run_python_method(pyobj, "__eq__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); + pyret = run_python_method(pyobj, "_is_read_only", NULL); - Py_DECREF(pyret); - - } + if (pyret != NULL) + { + if (PyBool_Check(pyret)) + result = (pyret == Py_True); - Py_DECREF(args); + else + PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value")); } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -442,80 +413,38 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate * * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate) +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate) { + GSingletonCandidate *result; /* Instance à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ + PyObject *state; /* Validation du mode */ -#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \ +#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \ ( \ - _set_read_only, "$self, /", \ + _dup, "$self", \ METH_NOARGS, \ - "Abstract method used to mark the content of a singleton" \ - " candidate as read-only.\n" \ + "Abstract method used to create a copy of the object. This" \ + " has to be able to get modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ "\n" \ - "The read-only state is mandatory once the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton." \ -) - - gstate = PyGILState_Ensure(); - - pyobj = pygobject_new(G_OBJECT(candidate)); - - if (has_python_method(pyobj, "_set_read_only")) - { - pyret = run_python_method(pyobj, "_set_read_only", NULL); - - Py_XDECREF(pyret); - - } - - Py_DECREF(pyobj); - - PyGILState_Release(gstate); - -} - - -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Indique si le candidat est figé. * -* * -* Retour : true si le contenu du candidat ne peut plus être modifié. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyobj; /* Objet Python concerné */ - PyObject *pyret; /* Bilan de consultation */ - -#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - _is_read_only, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to retrieve the status of the data" \ - " contained by a singleton candidate.\n" \ + "The result has to be a new intance of type(self).\n" \ + "\n" \ + "A *TypeError* exception is raised if the type of the" \ + " return value is different from the type of self.\n" \ "\n" \ - "The retured value is *True* if the candidate is" \ - " registered inside a pychrysalide.glibext.SingletonFactory"\ - " instance as official singleton, *False* otherwise." \ + "A *ValueError* exception is raised of the return object" \ + " is in read-only mode." \ ) result = false; @@ -524,16 +453,32 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "_is_read_only")) + pyret = run_python_method(pyobj, "_dup", NULL); + + if (pyret != NULL) { - pyret = run_python_method(pyobj, "_is_read_only", NULL); + if (Py_TYPE(pyret) != Py_TYPE(pyobj)) + PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type")); - result = (pyret == Py_True); + else + { + state = py_singleton_candidate_is_read_only(pyret, NULL); - Py_XDECREF(pyret); + if (state != NULL) + { + if (state != Py_False) + PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode")); + + Py_DECREF(state); + + } + + } } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -545,97 +490,51 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand /****************************************************************************** * * -* Paramètres : self = objet dont l'instance se veut unique. * +* Paramètres : self = objet manipulé ici. * * args = adresse non utilisée ici. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) +static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args) { PyObject *result; /* Emplacement à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - guint hash; /* Valeur d'empreitne */ + GSingletonCandidate *copy; /* Copie des mécanismes natifs */ -#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \ -( \ - hash, "$self", \ - METH_NOARGS, py_singleton_candidate, \ - "Compute the hash value of the singleton candidate.\n" \ - "\n" \ - "The method relies on the interface core to include in the" \ - " process the optional embedded instances which may become" \ - " singletons.\n" \ - "\n" \ - "The result is an integer value.\n" \ - "\n" \ - "Even if the Python *hash()* method, relying on the" \ - " pychrysalide.glibext.SingletonCandidate.__hash__()" \ - " implementation, provides values up to 64 bits, the final" \ - " hashes processed by the native GLib hash methods are" \ - " limited to 32 bits values." \ +#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \ +( \ + dup, "$self", \ + METH_NOARGS, py_singleton_candidate, \ + "Create a copy of the object. This has to be able to get" \ + " modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ + "\n" \ + "The result has to be a new intance of type(self)." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - hash = g_singleton_candidate_hash(candidate); + copy = g_singleton_candidate_dup(candidate); - result = PyLong_FromUnsignedLong(hash); + if (copy == NULL) + result = NULL; - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * -* * -* Description : Fournit une liste de candidats embarqués par un candidat. * -* * -* Retour : Liste de candidats internes, vide si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure) -{ - PyObject *result; /* Valeur à retourner */ - GSingletonCandidate *candidate; /* Mécanismes natifs */ - size_t count; /* Quantité d'objets internes */ - GSingletonCandidate **instances; /* Liste des embarqués */ - size_t i; /* Boucle de parcours */ - -#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - inner_instances, py_singleton_candidate, \ - "List of optional internal singleton candidate instances.\n" \ - "\n" \ - "The result has to be a tuple containing zero or more" \ - " pychrysalide.glibext.SingletonCandidate instances." \ -) - - candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - - instances = g_singleton_candidate_list_inner_instances(candidate, &count); + else + { + result = pygobject_new(G_OBJECT(candidate)); - result = PyTuple_New(count); + unref_object(copy); - for (i = 0; i < count; i++) - { - PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i]))); - g_object_unref(G_OBJECT(instances[i])); } - if (instances != NULL) - free(instances); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -649,87 +548,33 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void * * * Description : Indique si le candidat est figé. * * * -* Retour : true si le contenu du candidat ne peut plus être modifié. * +* Retour : True si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure) +static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure) { PyObject *result; /* Valeur à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - bool status; /* Etat de l'élément consulté */ + bool state; /* Etat de l'objet courant */ -#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - read_only, py_singleton_candidate, \ - "State of the singleton candidate content.\n" \ - "\n" \ - "The result is a boolean: *True* if the object is registered" \ - " as singleton, *False* otherwise.\n" \ - "\n" \ - "Once a singleton, the object must not change its content as" \ - " it is a shared instance." \ +#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + read_only, py_singleton_candidate, \ + "Boolean state of the object: *True* if all its properties are" \ + " frozen, *False* if the object can be modified." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - status = g_singleton_candidate_is_read_only(candidate); + state = g_singleton_candidate_is_read_only(candidate); - result = status ? Py_True : Py_False; + result = state ? Py_True : Py_False; Py_INCREF(result); - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * -* * -* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op) -{ - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GSingletonCandidate *cand_a; /* Premier élément à traiter */ - GSingletonCandidate *cand_b; /* Second élément à traiter */ - gboolean status; /* Résultat d'une comparaison */ - - if (op != Py_EQ) - { - result = Py_NotImplemented; - goto cmp_done; - } - - ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a)); - cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b)); - - status = g_singleton_candidate_is_equal(cand_a, cand_b); - - result = (status ? Py_True : Py_False); - - cmp_done: - - Py_INCREF(result); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -753,16 +598,14 @@ PyTypeObject *get_python_singleton_candidate_type(void) static PyMethodDef py_singleton_candidate_methods[] = { SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER, SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER, - SINGLETON_CANDIDATE_HASH_WRAPPER, - SINGLETON_CANDIDATE_EQ_WRAPPER, - SINGLETON_CANDIDATE_SET_RO_WRAPPER, - SINGLETON_CANDIDATE_IS_RO_WRAPPER, - SINGLETON_CANDIDATE_HASH_METHOD, + SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_DUP_WRAPPER, + SINGLETON_CANDIDATE_DUP_METHOD, { NULL } }; static PyGetSetDef py_singleton_candidate_getseters[] = { - SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB, SINGLETON_CANDIDATE_READ_ONLY_ATTRIB, { NULL } }; @@ -778,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void) .tp_doc = SINGLETON_CANDIDATE_DOC, - .tp_richcompare = py_singleton_candidate_richcompare, - .tp_methods = py_singleton_candidate_methods, .tp_getset = py_singleton_candidate_getseters @@ -804,7 +645,7 @@ PyTypeObject *get_python_singleton_candidate_type(void) bool ensure_python_singleton_candidate_is_registered(void) { - PyTypeObject *type; /* Type Python 'BinContent' */ + PyTypeObject *type; /* Type 'SingletonCandidate' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ @@ -887,66 +728,6 @@ int convert_to_singleton_candidate(PyObject *arg, void *dst) /****************************************************************************** * * -* Paramètres : type = type du nouvel objet à mettre en place. * -* args = éventuelle liste d'arguments. * -* kwds = éventuel dictionnaire de valeurs mises à disposition. * -* * -* Description : Accompagne la création d'une instance dérivée en Python. * -* * -* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static PyObject *py_singleton_factory_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *result; /* Objet à retourner */ - PyTypeObject *base; /* Type de base à dériver */ - bool first_time; /* Evite les multiples passages*/ - GType gtype; /* Nouveau type de processeur */ - bool status; /* Bilan d'un enregistrement */ - - /* Validations diverses */ - - base = get_python_singleton_factory_type(); - - if (type == base) - goto simple_way; - - /* Mise en place d'un type dédié */ - - first_time = (g_type_from_name(type->tp_name) == 0); - - gtype = build_dynamic_type(G_TYPE_SINGLETON_FACTORY, type->tp_name, NULL, NULL, NULL); - - if (first_time) - { - status = register_class_for_dynamic_pygobject(gtype, type); - - if (!status) - { - result = NULL; - goto exit; - } - - } - - /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ - - simple_way: - - result = PyType_GenericNew(type, args, kwds); - - exit: - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : self = objet à initialiser (théoriquement). * * args = arguments fournis à l'appel. * * kwds = arguments de type key=val fournis. * @@ -973,11 +754,6 @@ static int py_singleton_factory_init(PyObject *self, PyObject *args, PyObject *k "\n" \ "The first processed instance defines the type handled by the factory." - /* Récupération des paramètres */ - - ret = PyArg_ParseTuple(args, ""); - if (!ret) return -1; - /* Initialisation d'un objet GLib */ ret = forward_pygobjet_init(self); diff --git a/plugins/pychrysalide/glibext/storage.c b/plugins/pychrysalide/glibext/storage.c new file mode 100644 index 0000000..f2962bf --- /dev/null +++ b/plugins/pychrysalide/glibext/storage.c @@ -0,0 +1,572 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage.c - équivalent Python du fichier "glibext/storage.c" + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "storage.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/storage-int.h> + + +#include "serialize.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(object_storage, G_TYPE_OBJECT_STORAGE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_object_storage_init(PyObject *, PyObject *, PyObject *); + + + +/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */ + + +/* Charge le support d'une conservation d'objets en place. */ +static PyObject *py_object_storage_load(PyObject *, PyObject *); + +/* Sauvegarde le support d'une conservation d'objets en place. */ +static PyObject *py_object_storage_store(PyObject *, PyObject *); + +/* Charge un objet à partir de données rassemblées. */ +static PyObject *py_object_storage_load_object(PyObject *, PyObject *); + +/* Charge un objet interne à partir de données rassemblées. */ +static PyObject *py_object_storage_unpack_object(PyObject *, PyObject *); + +/* Sauvegarde un object sous forme de données rassemblées. */ +static PyObject *py_object_storage_store_object(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_object_storage_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + const char *type; /* Type global de conservation */ + unsigned char version; /* Version de ce type */ + const char *uid; /* Identifiant de distinction */ + int ret; /* Bilan de lecture des args. */ + GObjectStorage *storage; /* Mécanismes natifs */ + +#define OBJECT_STORAGE_DOC \ + "The ObjectStorage object manages the generic storage of GLib" \ + " objects through serialization.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " ObjectStorage(type, version uid)" \ + "\n" \ + "Where *type* is a short string describing the storage kind," \ + " *version* provides a version control for this type and *uid* is" \ + " an arbitrary unique identifier used for creating temporary files." + + /* Récupération des paramètres */ + + ret = PyArg_ParseTuple(args, "sbs", &type, &version, &uid); + if (!ret) return -1; + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + /* Eléments de base */ + + storage = G_OBJECT_STORAGE(pygobject_get(self)); + + if (!g_object_storage_create(storage, type, version, uid)) + return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* TAMPON POUR CODE DESASSEMBLE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Charge le support d'une conservation d'objets en place. * +* * +* Retour : Gestionnaire de conservations construit ou None si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_object_storage_load(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + const char *filename; /* Fichier de source à traiter */ + int ret; /* Bilan de lecture des args. */ + GObjectStorage *storage; /* Mécanismes natifs */ + +#define OBJECT_STORAGE_LOAD_METHOD PYTHON_METHOD_DEF \ +( \ + load, "filename, /", \ + METH_STATIC | METH_VARARGS, py_object_storage, \ + "Construct a new storage from a filename.\n" \ + "\n" \ + "The *filename* argument points to the source file to" \ + " read.\n" \ + "\n" \ + "The result is a new pychrysalide.glibext.ObjectStorage" \ + " object on success, *None* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "s", &filename); + if (!ret) return NULL; + + storage = g_object_storage_load(filename); + + if (storage == NULL) + { + result = Py_None; + Py_INCREF(result); + } + else + { + result = pygobject_new(G_OBJECT(storage)); + unref_object(storage); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Sauvegarde le support d'une conservation d'objets en place. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_object_storage_store(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + const char *filename; /* Fichier de destination */ + int ret; /* Bilan de lecture des args. */ + GObjectStorage *storage; /* Mécanismes natifs */ + bool status; /* Bilan de l'opération */ + +#define OBJECT_STORAGE_STORE_METHOD PYTHON_METHOD_DEF \ +( \ + store, "$self, filename, /", \ + METH_VARARGS, py_object_storage, \ + "Save a storage into a file.\n" \ + "\n" \ + "The *filename* argument points to the destination" \ + " file to write.\n" \ + "\n" \ + "The result is *True* on success, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "s", &filename); + if (!ret) return NULL; + + storage = G_OBJECT_STORAGE(pygobject_get(self)); + + status = g_object_storage_store(storage, filename); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Charge un objet à partir de données rassemblées. * +* * +* Retour : Objet restauré en mémoire ou None en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_object_storage_load_object(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + const char *name; /* Désignation de groupe */ + unsigned long long pos; /* Emplacement des données */ + int ret; /* Bilan de lecture des args. */ + GObjectStorage *storage; /* Mécanismes natifs */ + GSerializableObject *object; /* Objet reconstruit ou NULL */ + +#define OBJECT_STORAGE_LOAD_OBJECT_METHOD PYTHON_METHOD_DEF \ +( \ + load_object, "$self, name, pos, /", \ + METH_VARARGS, py_object_storage, \ + "Load an object from serialized data.\n" \ + "\n" \ + "The *name* is a string label for the group of target objects and" \ + " *pos* is an offset into the data stream indicating the start of" \ + " the data to unserialize.\n" \ + "\n" \ + "The result is a pychrysalide.analysis.storage.SerializableObject" \ + " instancet in case of success, or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "sK", &name, &pos); + if (!ret) return NULL; + + storage = G_OBJECT_STORAGE(pygobject_get(self)); + + object = g_object_storage_load_object(storage, name, pos); + + if (object != NULL) + result = pygobject_new(G_OBJECT(object)); + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Charge un objet interne à partir de données rassemblées. * +* * +* Retour : Objet restauré en mémoire ou None en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_object_storage_unpack_object(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + int fd; /* Flux de fonnées courant */ + const char *name; /* Désignation de groupe */ + int ret; /* Bilan de lecture des args. */ + GObjectStorage *storage; /* Mécanismes natifs */ + GSerializableObject *object; /* Objet reconstruit ou NULL */ + +#define OBJECT_STORAGE_UNPACK_OBJECT_METHOD PYTHON_METHOD_DEF \ +( \ + unpack_object, "$self, fd, name, /", \ + METH_VARARGS, py_object_storage, \ + "Load an object from a reference to serialized data.\n" \ + "\n" \ + "The *fd* argument is a file descriptor pointing to the data" \ + " stream for a current object being restored. A reference to" \ + " another object belonging to a group pointed by the string *name*" \ + " should be available at the current read position for this data" \ + " stream.\n" \ + "\n" \ + "The result is a pychrysalide.analysis.storage.SerializableObject" \ + " instancet in case of success, or *None* in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "is", &fd, &name); + if (!ret) return NULL; + + storage = G_OBJECT_STORAGE(pygobject_get(self)); + + object = g_object_storage_unpack_object(storage, fd, name); + + if (object != NULL) + result = pygobject_new(G_OBJECT(object)); + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Sauvegarde un object sous forme de données rassemblées. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_object_storage_store_object(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + const char *name; /* Désignation de groupe */ + GSerializableObject *object; /* Objet à traiter */ + int ret; /* Bilan de lecture des args. */ + GObjectStorage *storage; /* Mécanismes natifs */ + off64_t pos; /* Emplacement d'enregistrement*/ + bool status; /* Bilan de l'opération */ + +#define OBJECT_STORAGE_STORE_OBJECT_METHOD PYTHON_METHOD_DEF \ +( \ + store_object, "$self, name, object, /", \ + METH_VARARGS, py_object_storage, \ + "Save an object as serialized data.\n" \ + "\n" \ + "The *name* is a string label for the group of target objects" \ + " and the processed *object* has to be a" \ + " pychrysalide.analysis.storage.SerializableObject instance.\n" \ + "\n" \ + "The result is the position of the data for stored object," \ + " provided as an integer offset, in case of success or *None*" \ + " in case of failure." \ +) + + ret = PyArg_ParseTuple(args, "sO&", &name, convert_to_serializable_object, &object); + if (!ret) return NULL; + + storage = G_OBJECT_STORAGE(pygobject_get(self)); + + status = g_object_storage_store_object(storage, name, object, &pos); + + if (status) + result = PyLong_FromUnsignedLongLong((unsigned long long)pos); + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_object_storage_type(void) +{ + static PyMethodDef py_object_storage_methods[] = { + OBJECT_STORAGE_LOAD_METHOD, + OBJECT_STORAGE_STORE_METHOD, + OBJECT_STORAGE_LOAD_OBJECT_METHOD, + OBJECT_STORAGE_UNPACK_OBJECT_METHOD, + OBJECT_STORAGE_STORE_OBJECT_METHOD, + { NULL } + }; + + static PyGetSetDef py_object_storage_getseters[] = { + { NULL } + }; + + static PyTypeObject py_object_storage_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ObjectStorage", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = OBJECT_STORAGE_DOC, + + .tp_methods = py_object_storage_methods, + .tp_getset = py_object_storage_getseters, + + .tp_init = py_object_storage_init, + .tp_new = py_object_storage_new + + }; + + return &py_object_storage_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.ObjectStorage'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_object_storage_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'ObjectStorage' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_object_storage_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_OBJECT_STORAGE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en conservateur d'objets. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_object_storage(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_object_storage_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to object storage"); + break; + + case 1: + *((GObjectStorage **)dst) = G_OBJECT_STORAGE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + 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 conservateur d'objets ou NULL. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_object_storage_or_none(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + if (arg == Py_None) + { + *((GTypeMemory **)dst) = NULL; + result = 1; + } + + else + result = convert_to_object_storage(arg, dst); + + return result; + +} diff --git a/plugins/pychrysalide/glibext/storage.h b/plugins/pychrysalide/glibext/storage.h new file mode 100644 index 0000000..681f99a --- /dev/null +++ b/plugins/pychrysalide/glibext/storage.h @@ -0,0 +1,48 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage.h - prototypes pour l'équivalent Python du fichier "glibext/storage.h" + * + * Copyright (C) 2020-2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_object_storage_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ObjectStorage'. */ +bool ensure_python_object_storage_is_registered(void); + +/* Tente de convertir en conservateur d'objets. */ +int convert_to_object_storage(PyObject *, void *); + +/* Tente de convertir en conservateur d'objets ou NULL. */ +int convert_to_object_storage_or_none(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STORAGE_H */ diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c new file mode 100644 index 0000000..a6de0f0 --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.c @@ -0,0 +1,542 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "strbuilder.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/strbuilder-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet la description d'un objet définie par son parent. */ +static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *); + +/* Exporte une chaîne de caractères à partir d'un objet. */ +static PyObject *py_string_builder_to_string(PyObject *, PyObject *); + +/* Fournit une représentation de l'objet exportable. */ +static PyObject *py_string_builder_str(PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpointer *unused) +{ +#define STRING_BUILDER_DOC \ + "The StringBuilder class defines a interface for native objects aiming" \ + " at providing a string representation of themselves.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, StringBuilder):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.StringBuilder._to_string().\n" + + iface->to_string = py_string_builder_to_string_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + const char *utf8; /* Chaîne UTF-8 portée */ + Py_ssize_t size; /* Taille de ces données */ + +#define STRING_BUILDER_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _to_string, "$self, /, flags=0", \ + METH_VARARGS, \ + "Abstract method providing a string representation for the" \ + " object which is used as the default implementation of the" \ + " __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result has to be a string." \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a string." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(builder)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags)); + + pyret = run_python_method(pyobj, "_to_string", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + { + utf8 = PyUnicode_AsUTF8AndSize(pyret, &size); + + if (utf8 != NULL) + { + assert(size >= 0); + + add_to_sized_binary(out, utf8, size); + result = true; + + } + + } + + if (!result) + PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet la description d'un objet définie par son parent. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + GStringBuilderInterface *iface; /* Interface utilisée */ + GStringBuilderInterface *parent_iface; /* Interface parente */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + parent_to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.StringBuilder" \ + " interface." \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._to_string() function has to be" \ + " used instead.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface")); + + result = NULL; + + } + else + { + init_sized_binary(&out); + + status = parent_iface->to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Emplacement à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation for the object which is used" \ + " as the default implementation of the __repr__() method.\n" \ + "\n" \ + "The optional *flags* argument define hints for the operation" \ + " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Fournit une représentation de l'objet exportable. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_str(PyObject *self) +{ + PyObject *result; /* Emplacement à retourner */ + GStringBuilder *builder; /* Mécanismes natifs */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + + builder = G_STRING_BUILDER(pygobject_get(self)); + + init_sized_binary(&out); + + status = g_string_builder_to_string(builder, 0, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_string_builder_type(void) +{ + static PyMethodDef py_string_builder_methods[] = { + STRING_BUILDER_TO_STRING_WRAPPER, + STRING_BUILDER_PARENT_TO_STRING_METHOD, + STRING_BUILDER_TO_STRING_METHOD, + { NULL } + }; + + static PyGetSetDef py_string_builder_getseters[] = { + { NULL } + }; + + static PyTypeObject py_string_builder_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.StringBuilder", + .tp_basicsize = sizeof(PyObject), + + .tp_str = py_string_builder_str, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = STRING_BUILDER_DOC, + + .tp_methods = py_string_builder_methods, + .tp_getset = py_string_builder_getseters + + }; + + return &py_string_builder_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_string_builder_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'StringBuilder' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_string_builder_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_string_builder_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_STRING_BUILDER, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en interface d'exportation en chaîne. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_string_builder(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_string_builder_type()); + + switch (result) + { + case -1: + /* L'exception est déjà fixée par Python */ + result = 0; + break; + + case 0: + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to string builder"); + break; + + case 1: + *((GStringBuilder **)dst) = G_STRING_BUILDER(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h new file mode 100644 index 0000000..1881cae --- /dev/null +++ b/plugins/pychrysalide/glibext/strbuilder.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h" + * + * Copyright (C) 2025 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_string_builder_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'. */ +bool ensure_python_string_builder_is_registered(void); + +/* Tente de convertir en interface d'exportation en chaîne. */ +int convert_to_string_builder(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H */ diff --git a/plugins/pychrysalide/glibext/tpmem.c b/plugins/pychrysalide/glibext/tpmem.c new file mode 100644 index 0000000..ae07008 --- /dev/null +++ b/plugins/pychrysalide/glibext/tpmem.c @@ -0,0 +1,508 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem.c - équivalent Python du fichier "analysis/storage/tpmem.c" + * + * 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 "tpmem.h" + + +#include <pygobject.h> + + +#include <analysis/storage/tpmem.h> +#include <plugins/dt.h> + + +#include "../../access.h" +#include "../../helpers.h" +#include "../../common/packed.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Accompagne la création d'une instance dérivée en Python. */ +static PyObject *py_type_memory_new(PyTypeObject *, PyObject *, PyObject *); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_type_memory_init(PyObject *, PyObject *, PyObject *); + + + +/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */ + + +/* Apprend tous les types mémorisés dans un tampon. */ +static PyObject *py_type_memory_load_types(PyObject *, PyObject *); + +/* Crée une nouvelle instance d'objet à partir de son type. */ +static PyObject *py_type_memory_create_object(PyObject *, PyObject *); + +/* Sauvegarde le type d'un objet instancié. */ +static PyObject *py_type_memory_store_object_gtype(PyObject *, PyObject *); + +/* Enregistre tous les types mémorisés dans un tampon. */ +static PyObject *py_type_memory_store_types(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* 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_type_memory_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *result; /* Objet à retourner */ + PyTypeObject *base; /* Type de base à dériver */ + bool first_time; /* Evite les multiples passages*/ + GType gtype; /* Nouveau type de processeur */ + bool status; /* Bilan d'un enregistrement */ + + /* Validations diverses */ + + base = get_python_type_memory_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_TYPE_MEMORY, type->tp_name, NULL, NULL, NULL); + + if (first_time) + { + status = register_class_for_dynamic_pygobject(gtype, type); + + if (!status) + { + result = NULL; + goto exit; + } + + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + simple_way: + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_type_memory_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define TYPE_MEMORY_DOC \ + "The TypeMemory remembers all the types of objects involved in" \ + " a serialization process.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " TypeMemory()" \ + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* TAMPON POUR CODE DESASSEMBLE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Apprend tous les types mémorisés dans un tampon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_type_memory_load_types(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + packed_buffer_t *pbuf; /* Tampon à consulter */ + int ret; /* Bilan de lecture des args. */ + GTypeMemory *tpmem; /* Mémorisation native */ + bool status; /* Bilan de l'opération */ + +#define TYPE_MEMORY_LOAD_TYPES_METHOD PYTHON_METHOD_DEF \ +( \ + load_types, "$self, pbuf", \ + METH_VARARGS, py_type_memory, \ + "Read types from a buffer.\n" \ + "\n" \ + "This operation is usually handled internally by the" \ + " Chrysalide's core.\n" \ + "\n" \ + "The *pbuf* parameter is a pychrysalide.common.PackedBuffer"\ + " instance providing buffered data to read." \ + "\n" \ + "The result is a boolean value indicating the status of" \ + " the operation: True for success, False for failure." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + if (!ret) return NULL; + + tpmem = G_TYPE_MEMORY(pygobject_get(self)); + + status = g_type_memory_load_types(tpmem, pbuf); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Crée une nouvelle instance d'objet à partir de son type. * +* * +* Retour : Instance issue de l'opération ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_type_memory_create_object(PyObject *self, PyObject *args) +{ + PyObject *result; /* Instance à retourner */ + packed_buffer_t *pbuf; /* Tampon à consulter */ + int ret; /* Bilan de lecture des args. */ + GTypeMemory *tpmem; /* Mémorisation native */ + GObject *obj; /* Instance retournée */ + +#define TYPE_MEMORY_CREATE_OBJECT_METHOD PYTHON_METHOD_DEF \ +( \ + create_object, "$self, pbuf", \ + METH_VARARGS, py_type_memory, \ + "Create a new GLib object from serialized data.\n" \ + "\n" \ + "The *pbuf* parameter is a pychrysalide.common.PackedBuffer" \ + " instance providing buffered data to read." \ + "\n" \ + "The result is a Python object linked to a native GLib" \ + " object instance." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + if (!ret) return NULL; + + tpmem = G_TYPE_MEMORY(pygobject_get(self)); + + obj = g_type_memory_create_object(tpmem, pbuf); + + result = pygobject_new(obj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Sauvegarde le type d'un objet instancié. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_type_memory_store_object_gtype(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GObject *obj; /* Instance à traiter */ + packed_buffer_t *pbuf; /* Tampon à consulter */ + int ret; /* Bilan de lecture des args. */ + GTypeMemory *tpmem; /* Mémorisation native */ + bool status; /* Bilan de l'opération */ + +#define TYPE_MEMORY_STORE_OBJECT_GTYPE_METHOD PYTHON_METHOD_DEF \ +( \ + store_object_gtype, "$self, obj, pbuf", \ + METH_VARARGS, py_type_memory, \ + "Create a new GLib object from serialized data.\n" \ + "\n" \ + "The *obj* parameter is the Python version of the GObject" \ + " whose type is to process and the *pbuf* parameter is a" \ + " pychrysalide.common.PackedBuffer instance providing buffered" \ + " data to extend." \ + "\n" \ + "The result is a boolean value indicating the status of the" \ + " operation: True for success, False for failure." \ +) + + ret = PyArg_ParseTuple(args, "O!O&", PyGObject_Type, &obj, convert_to_packed_buffer, &pbuf); + if (!ret) return NULL; + + tpmem = G_TYPE_MEMORY(pygobject_get(self)); + + status = g_type_memory_store_object_gtype(tpmem, obj, pbuf); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = classe représentant une mémorisation de types. * +* args = arguments fournis à l'appel. * +* * +* Description : Enregistre tous les types mémorisés dans un tampon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_type_memory_store_types(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + packed_buffer_t *pbuf; /* Tampon à consulter */ + int ret; /* Bilan de lecture des args. */ + GTypeMemory *tpmem; /* Mémorisation native */ + bool status; /* Bilan de l'opération */ + +#define TYPE_MEMORY_STORE_TYPES_METHOD PYTHON_METHOD_DEF \ +( \ + store_types, "$self, pbuf", \ + METH_VARARGS, py_type_memory, \ + "Write types into a buffer.\n" \ + "\n" \ + "This operation is usually handled internally by the" \ + " Chrysalide's core.\n" \ + "\n" \ + "The *pbuf* parameter is a pychrysalide.common.PackedBuffer"\ + " instance providing buffered data to read." \ + "\n" \ + "The result is a boolean value indicating the status of" \ + " the operation: True for success, False for failure." \ +) + + ret = PyArg_ParseTuple(args, "O&", convert_to_packed_buffer, &pbuf); + if (!ret) return NULL; + + tpmem = G_TYPE_MEMORY(pygobject_get(self)); + + status = g_type_memory_store_types(tpmem, pbuf); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_type_memory_type(void) +{ + static PyMethodDef py_type_memory_methods[] = { + TYPE_MEMORY_LOAD_TYPES_METHOD, + TYPE_MEMORY_CREATE_OBJECT_METHOD, + TYPE_MEMORY_STORE_OBJECT_GTYPE_METHOD, + TYPE_MEMORY_STORE_TYPES_METHOD, + { NULL } + }; + + static PyGetSetDef py_type_memory_getseters[] = { + { NULL } + }; + + static PyTypeObject py_type_memory_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.analysis.storage.TypeMemory", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT, + + .tp_doc = TYPE_MEMORY_DOC, + + .tp_methods = py_type_memory_methods, + .tp_getset = py_type_memory_getseters, + + .tp_init = py_type_memory_init, + .tp_new = py_type_memory_new + + }; + + return &py_type_memory_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.analysis...TypeMemory'.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_type_memory_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'BufferCache' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_type_memory_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.analysis.storage"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_TYPE_MEMORY, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en mémorisation de types. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_type_memory(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_type_memory_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 buffer cache"); + break; + + case 1: + *((GTypeMemory **)dst) = G_TYPE_MEMORY(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/tpmem.h b/plugins/pychrysalide/glibext/tpmem.h new file mode 100644 index 0000000..1085632 --- /dev/null +++ b/plugins/pychrysalide/glibext/tpmem.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem.h - prototypes pour l'équivalent Python du fichier "analysis/storage/tpmem.h" + * + * 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_ANALYSIS_STORAGE_TPMEM_H +#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_TPMEM_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_type_memory_type(void); + +/* Prend en charge l'objet 'pychrysalide.analysis.storage.TypeMemory'. */ +bool ensure_python_type_memory_is_registered(void); + +/* Tente de convertir en mémorisation de types. */ +int convert_to_type_memory(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_STORAGE_TPMEM_H */ diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/work.c index 548f700..e6791e3 100644 --- a/plugins/pychrysalide/glibext/comparison.c +++ b/plugins/pychrysalide/glibext/work.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - équivalent Python du fichier "glibext/comparison.h" + * work.c - prototypes pour l'équivalent Python du fichier "glibext/work.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,38 +22,42 @@ */ -#include "comparison.h" +#include "work.h" +#include <assert.h> #include <pygobject.h> -#include <glibext/comparison-int.h> +#include <glibext/work-int.h> -#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Procède à l'initialisation de l'interface de comparaison. */ -static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *); +/* Initialise la classe des travaux programmés. */ +static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *); -/* Réalise une comparaison entre objets selon un critère précis. */ -static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_generic_work_init(PyObject *, PyObject *, PyObject *); + +/* Mène l'opération programmée. */ +static void py_generic_work_run_wrapper(GGenericWork *); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Effectue une comparaison avec un objet 'ComparableItem'. */ -static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); +/* Mène l'opération programmée. */ +static PyObject *py_generic_work_process(PyObject *, PyObject *); @@ -64,88 +68,111 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); /****************************************************************************** * * -* Paramètres : iface = interface GLib à initialiser. * -* unused = adresse non utilisée ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * -* Description : Procède à l'initialisation de l'interface de comparaison. * +* Description : Initialise la classe des travaux programmés. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused) +static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass) { + PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper); + + return 0; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_generic_work_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ -#define COMPARABLE_ITEM_DOC \ - "ComparableItem provides an interface to compare objects.\n" \ - "\n" \ - "A typical class declaration for a new implementation looks like:\n" \ - "\n" \ - " class NewImplem(GObject.Object, ComparableItem):\n" \ - " ...\n" \ - "\n" +#define GENERIC_WORK_DOC \ + "GenericWork defines a basic work aimed to get processed in a" \ + " thread set up by a pychrysalide.glibext.WorkQueue object.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " GenericWork()" \ + "\n" \ + "The following method has to be defined for new classes:\n" \ + "* pychrysalide.glibext.GenericWork._run();\n" \ + "\n" \ + "The following signal may be emitted:\n" \ + "* *work-completed* : when the work has been processed." - iface->cmp_rich = py_comparable_item_compare_rich; + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; } /****************************************************************************** * * -* Paramètres : item = premier objet à cnsulter pour une comparaison. * -* other = second objet à cnsulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * +* Paramètres : work = travail à effectuer. * * * -* Description : Réalise une comparaison entre objets selon un critère précis.* +* Description : Mène l'opération programmée. * * * -* Retour : true si la comparaison a pu être effectuée, false sinon. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) +static void py_generic_work_run_wrapper(GGenericWork *work) { - bool result; /* Etat à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyitem; /* Objet Python concerné #1 */ - PyObject *pyother; /* Objet Python concerné #2 */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ - result = false; +#define GENERIC_WORK_RUN_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _run, "$self", \ + METH_NOARGS, \ + "Abstract method used to process the work.\n" \ + "\n" \ + "No result is expected from this function." \ +) gstate = PyGILState_Ensure(); - pyitem = pygobject_new(G_OBJECT(item)); - pyother = pygobject_new(G_OBJECT(other)); + pyobj = pygobject_new(G_OBJECT(work)); - pyret = PyObject_RichCompare(pyitem, pyother, op); - - if (pyret != NULL) + if (has_python_method(pyobj, "_run")) { - ret = PyBool_Check(pyret); - - if (ret) - { - *status = (pyret == Py_True); - result = true; - } + pyret = run_python_method(pyobj, "_run", NULL); - Py_DECREF(pyret); + Py_XDECREF(pyret); } - Py_DECREF(pyother); - Py_DECREF(pyitem); + Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } @@ -157,46 +184,39 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * * * -* Description : Effectue une comparaison avec un objet 'ComparableItem'. * +* Description : Mène l'opération programmée. * * * -* Retour : Bilan de l'opération. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op) +static PyObject *py_generic_work_process(PyObject *self, PyObject *args) { PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GComparableItem *item_a; /* Instance à manipuler #1 */ - GComparableItem *item_b; /* Instance à manipuler #2 */ - bool valid; /* Indication de validité */ - bool status; /* Résultat d'une comparaison */ + GGenericWork *work; /* Version native */ - ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - item_a = G_COMPARABLE_ITEM(pygobject_get(a)); - item_b = G_COMPARABLE_ITEM(pygobject_get(b)); +#define GENERIC_WORK_PROCESS_METHOD PYTHON_METHOD_DEF \ +( \ + process, "$self", \ + METH_NOARGS, py_generic_work, \ + "Process the work.\n" \ + "\n" \ + "The function does not return anything.\n" \ + "\n" \ + "The *work-completed* signal is emitted when the" \ + " function terminates." \ +) - valid = g_comparable_item_compare_rich(item_a, item_b, op, &status); + work = G_GENERIC_WORK(pygobject_get(self)); - if (valid) - result = status ? Py_True : Py_False; - else - result = Py_NotImplemented; - - cmp_done: + g_generic_work_process(work); + result = Py_None; Py_INCREF(result); return result; @@ -216,35 +236,38 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op * * ******************************************************************************/ -PyTypeObject *get_python_comparable_item_type(void) +PyTypeObject *get_python_generic_work_type(void) { - static PyMethodDef py_comparable_item_methods[] = { + static PyMethodDef py_generic_work_methods[] = { + GENERIC_WORK_RUN_WRAPPER, + GENERIC_WORK_PROCESS_METHOD, { NULL } }; - static PyGetSetDef py_comparable_item_getseters[] = { + static PyGetSetDef py_generic_work_getseters[] = { { NULL } }; - static PyTypeObject py_comparable_item_type = { + static PyTypeObject py_generic_work_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.ComparableItem", - .tp_basicsize = sizeof(PyObject), + .tp_name = "pychrysalide.glibext.GenericWork", + .tp_basicsize = sizeof(PyGObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = COMPARABLE_ITEM_DOC, + .tp_doc = GENERIC_WORK_DOC, - .tp_richcompare = py_comparable_item_richcompare, + .tp_methods = py_generic_work_methods, + .tp_getset = py_generic_work_getseters, - .tp_methods = py_comparable_item_methods, - .tp_getset = py_comparable_item_getseters, + .tp_init = py_generic_work_init, + .tp_new = py_generic_work_new, }; - return &py_comparable_item_type; + return &py_generic_work_type; } @@ -253,7 +276,7 @@ PyTypeObject *get_python_comparable_item_type(void) * * * Paramètres : module = module dont la définition est à compléter. * * * -* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. * +* Description : Prend en charge l'objet 'pychrysalide.glibext.GenericWork'. * * * * Retour : Bilan de l'opération. * * * @@ -261,21 +284,13 @@ PyTypeObject *get_python_comparable_item_type(void) * * ******************************************************************************/ -bool ensure_python_comparable_item_is_registered(void) +bool ensure_python_generic_work_is_registered(void) { - PyTypeObject *type; /* Type Python 'ComparableItem' */ + PyTypeObject *type; /* Type Python 'GenericWork' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ - static GInterfaceInfo info = { /* Paramètres d'inscription */ - - .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init, - .interface_finalize = NULL, - .interface_data = NULL, - - }; - - type = get_python_comparable_item_type(); + type = get_python_generic_work_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -283,10 +298,9 @@ bool ensure_python_comparable_item_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info)) - return false; + pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass); - if (!define_comparable_item_constants(type)) + if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type)) return false; } @@ -301,7 +315,7 @@ bool ensure_python_comparable_item_is_registered(void) * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * -* Description : Tente de convertir en élément comparable. * +* Description : Tente de convertir en travail générique. * * * * Retour : Bilan de l'opération, voire indications supplémentaires. * * * @@ -309,11 +323,11 @@ bool ensure_python_comparable_item_is_registered(void) * * ******************************************************************************/ -int convert_to_comparable_item(PyObject *arg, void *dst) +int convert_to_generic_work(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_generic_work_type()); switch (result) { @@ -323,11 +337,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to generic work"); break; case 1: - *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg)); + *((GGenericWork **)dst) = G_GENERIC_WORK(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/work.h b/plugins/pychrysalide/glibext/work.h new file mode 100644 index 0000000..de9a936 --- /dev/null +++ b/plugins/pychrysalide/glibext/work.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * work.h - prototypes pour l'équivalent Python du fichier "glibext/work.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_generic_work_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.GenericWork'. */ +bool ensure_python_generic_work_is_registered(void); + +/* Tente de convertir en travail générique. */ +int convert_to_generic_work(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORK_H */ diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c new file mode 100644 index 0000000..ca6c73c --- /dev/null +++ b/plugins/pychrysalide/glibext/workqueue.c @@ -0,0 +1,586 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * workqueue.c - prototypes pour l'équivalent Python du fichier "glibext/workqueue.c" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "workqueue.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/workqueue-int.h> + + +#include "work.h" +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +CREATE_DYN_CONSTRUCTOR(work_queue, G_TYPE_WORK_QUEUE); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_work_queue_init(PyObject *, PyObject *, PyObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Constitue un nouveau groupe de travail. */ +static PyObject *py_work_queue_define_group(PyObject *, PyObject *); + +/* Dissout un groupe de travail existant. */ +static PyObject *py_work_queue_delete_group(PyObject *, PyObject *); + +/* Place une nouvelle tâche en attente. */ +static PyObject *py_work_queue_schedule(PyObject *, PyObject *); + +/* Détermine si un groupe est vide de toute programmation. */ +static PyObject *py_work_queue_is_empty(PyObject *, PyObject *); + +/* Attend que toutes les tâches d'un groupe soient traitées. */ +static PyObject *py_work_queue_wait_for_completion(PyObject *, PyObject *); + +/* Force un réveil d'une attente en cours pour la confirmer. */ +static PyObject *py_work_queue_wake_up_waiters(PyObject *, PyObject *); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define WORK_QUEUE_DOC \ + "WorkQueue creates threads in order to process" \ + " pychrysalide.glibext.Work instances.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " WorkQueue()" + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Constitue un nouveau groupe de travail. * +* * +* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_define_group(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned int count; /* Nombre de threads à activer */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + wgroup_id_t id; /* Identifiant de groupe créé */ + +#define WORK_QUEUE_DEFINE_GROUP_METHOD PYTHON_METHOD_DEF \ +( \ + define_group, "$self, /, count=0", \ + METH_VARARGS, py_work_queue, \ + "Create a new work group for a work queue.\n" \ + "\n" \ + "The *count* argument states the number of threads which will" \ + " be activated for the processing. If the provided value is" \ + " zero, then a default value based on the current hardware is" \ + " selected.\n" \ + "\n" \ + "The result is a work group identifier." \ +) + + count = 0; + + ret = PyArg_ParseTuple(args, "|I", &count); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + id = g_work_queue_define_group(queue, count); + + result = PyLong_FromUnsignedLong(id); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Dissout un groupe de travail existant. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_delete_group(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + +#define WORK_QUEUE_DELETE_GROUP_METHOD PYTHON_METHOD_DEF \ +( \ + delete_group, "$self, id", \ + METH_VARARGS, py_work_queue, \ + "Destroy a work group previously registered inside a queue.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \ +) + + ret = PyArg_ParseTuple(args, "K", &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + g_work_queue_delete_group(queue, id); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Place une nouvelle tâche en attente. * +* * +* Retour : Bilan, qui correspond à l'existence du groupe ciblé. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_schedule(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GGenericWork *work; /* Tâche à programmer */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + bool status; /* Bilan de programmation */ + +#define WORK_QUEUE_SCHEDULE_METHOD PYTHON_METHOD_DEF \ +( \ + schedule, "$self, work, id", \ + METH_VARARGS, py_work_queue, \ + "Schedule a work into the queue processing.\n" \ + "\n" \ + "The *work* argument has to point to a" \ + " pychrysalide.glibext.GenericWork instance, and the *id*" \ + " argument refers to a work group identifier which should have" \ + " been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group().\n"\ + "\n" \ + "The result is a boolean value: *True* in case of succes (does" \ + " the provided identifier exist?), *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "O&K", convert_to_generic_work, &work, &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + status = g_work_queue_schedule(queue, work, id); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Détermine si un groupe est vide de toute programmation. * +* * +* Retour : Etat du groupe de travail. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_is_empty(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + bool status; /* Bilan de consultation */ + +#define WORK_QUEUE_IS_EMPTY_METHOD PYTHON_METHOD_DEF \ +( \ + is_empty, "$self, id", \ + METH_VARARGS, py_work_queue, \ + "Signal if there is some remaining work to get processed by" \ + " the queue or not.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group().\n"\ + "\n" \ + "The result is a boolean value: *True* if the group does not" \ + " contain any works or does not exist, *False* otherwise." \ +) + + ret = PyArg_ParseTuple(args, "K", &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + status = g_work_queue_is_empty(queue, id); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_wait_for_completion(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long rel; /* Durée d'attente */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + bool status; /* Bilan de l'attente menée */ + + /** + * Python appelle cette fonction, lorsqu'elle est sollicitée dans des scripts, + * via la fonction PyObject_Vectorcall(), qui obtient la structure de suivi + * du thread courant avec _PyThreadState_GET(). + * + * Un commentaire associé à cette dernière fonction précise : + * + * The caller must hold the GIL. + * + * Comme le verrou GIL est posé par Python au moment de l'exécution de + * py_work_queue_wait_for_completion(), aucune attente bloquante ne peut + * alors être engagée ici : elle bloquerait la tâche _run() d'un travail + * réalisé en Python(), comme son déférencement via unref_ojbect() qui + * implique une exécution de la fonction pyg_toggle_notify() appelant + * PyGILState_Ensure() dans python3-gi. + * + * Aussi une attente bornée dans le temps est la seule option possible + * ici. + */ + +#define WORK_QUEUE_WAIT_FOR_COMPLETION_METHOD PYTHON_METHOD_DEF \ +( \ + wait_for_completion, "$self, id, /, rel=100000", \ + METH_VARARGS, py_work_queue, \ + "Wait until all the works from a given group have been" \ + " processed.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \ + " The *rel* range provide a relative time to wait (100ms by" \ + " default).\n" \ + "\n" \ + "The returned value is a boolean status: *False* on a timeout," \ + " *True* otherwise." \ + "\n" \ + "_Important note:_\n" \ + "As this function is called by Python with its Global" \ + " Interpreter Lock (GIL) held, an endless wait is impossible" \ + " because it would keep the GIL unreleased whereas others" \ + " threads (including those which may end the wait) may need to" \ + " acquire it.\n" \ + "\n" \ + "So an unoptimized timed wait is performed here instead, and" \ + " the returned status has to be checked by the caller in order" \ + " to schedule another wait if needed." \ +) + + rel = 100 * G_TIME_SPAN_MILLISECOND; + + ret = PyArg_ParseTuple(args, "K|K", &id, &rel); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + status = g_work_queue_wait_timed_for_completion(queue, id, rel); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = élément d'appel à consulter. * +* args = arguments fournis pour l'opération. * +* * +* Description : Force un réveil d'une attente en cours pour la confirmer. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_work_queue_wake_up_waiters(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + unsigned long long id; /* Identifiant de groupe ciblé */ + int ret; /* Bilan de lecture des args. */ + GWorkQueue *queue; /* Version native */ + +#define WORK_QUEUE_WAKE_UP_WAITERS_METHOD PYTHON_METHOD_DEF \ +( \ + wake_up_waiters, "$self, id", \ + METH_VARARGS, py_work_queue, \ + "Force a wake up of all threads waiting for processing a given" \ + " queue work group.\n" \ + "\n" \ + "The *id* argument refers to a work group identifier. It" \ + " should have been provided by a previous call to" \ + " pychrysalide.glibext.WorkQueue.g_work_queue_define_group()." \ +) + + ret = PyArg_ParseTuple(args, "K", &id); + if (!ret) return NULL; + + queue = G_WORK_QUEUE(pygobject_get(self)); + + g_work_queue_wake_up_waiters(queue, id); + + result = Py_None; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_work_queue_type(void) +{ + static PyMethodDef py_work_queue_methods[] = { + WORK_QUEUE_DEFINE_GROUP_METHOD, + WORK_QUEUE_DELETE_GROUP_METHOD, + WORK_QUEUE_SCHEDULE_METHOD, + WORK_QUEUE_IS_EMPTY_METHOD, + WORK_QUEUE_WAIT_FOR_COMPLETION_METHOD, + WORK_QUEUE_WAKE_UP_WAITERS_METHOD, + { NULL } + }; + + static PyGetSetDef py_work_queue_getseters[] = { + { NULL } + }; + + static PyTypeObject py_work_queue_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.WorkQueue", + .tp_basicsize = sizeof(PyGObject), + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = WORK_QUEUE_DOC, + + .tp_methods = py_work_queue_methods, + .tp_getset = py_work_queue_getseters, + + .tp_init = py_work_queue_init, + .tp_new = py_work_queue_new, + + }; + + return &py_work_queue_type; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module dont la définition est à compléter. * +* * +* Description : Prend en charge l'objet 'pychrysalide.glibext.WorkQueue'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_work_queue_is_registered(void) +{ + PyTypeObject *type; /* Type Python 'WorkQueue' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + type = get_python_work_queue_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_class_for_pygobject(dict, G_TYPE_WORK_QUEUE, type)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* Paramètres : arg = argument quelconque à tenter de convertir. * +* dst = destination des valeurs récupérées en cas de succès. * +* * +* Description : Tente de convertir en gestionnaire de travaux différés. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_work_queue(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_work_queue_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 work queue"); + break; + + case 1: + *((GWorkQueue **)dst) = G_WORK_QUEUE(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/workqueue.h b/plugins/pychrysalide/glibext/workqueue.h new file mode 100644 index 0000000..3fe426b --- /dev/null +++ b/plugins/pychrysalide/glibext/workqueue.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * workqueue.h - prototypes pour l'équivalent Python du fichier "glibext/workqueue.h" + * + * Copyright (C) 2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_work_queue_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.WorkQueue'. */ +bool ensure_python_work_queue_is_registered(void); + +/* Tente de convertir en gestionnaire de travaux différés. */ +int convert_to_work_queue(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_WORKQUEUE_H */ |