summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac26
-rw-r--r--data/images/locked-symbolic.svg62
-rw-r--r--data/images/nolock-symbolic.svg70
-rw-r--r--data/images/unlocked-symbolic.svg62
-rw-r--r--plugins/Makefile.am4
-rw-r--r--plugins/dalvik/context.h2
-rw-r--r--plugins/pe/Makefile.am19
-rw-r--r--plugins/pe/core-int.h56
-rw-r--r--plugins/pe/core.c288
-rw-r--r--plugins/pe/core.h14
-rw-r--r--plugins/pe/format-int.c (renamed from plugins/pe/pe-int.c)28
-rw-r--r--plugins/pe/format-int.h (renamed from plugins/pe/pe-int.h)37
-rw-r--r--plugins/pe/format.c646
-rw-r--r--plugins/pe/format.h34
-rw-r--r--plugins/pe/pe-def.h (renamed from plugins/pe/pe_def.h)55
-rw-r--r--plugins/pe/python/Makefile.am8
-rw-r--r--plugins/pe/python/constants.c5
-rw-r--r--plugins/pe/python/constants.h2
-rw-r--r--plugins/pe/python/format.c205
-rw-r--r--plugins/pe/python/module.c6
-rw-r--r--plugins/pe/python/translate.c290
-rw-r--r--plugins/pe/python/translate.h10
-rw-r--r--plugins/pe/section.c73
-rw-r--r--plugins/pychrysalide/Makefile.am23
-rw-r--r--plugins/pychrysalide/analysis/content.c42
-rw-r--r--plugins/pychrysalide/arch/module.c3
-rw-r--r--plugins/pychrysalide/bindings.c1466
-rw-r--r--plugins/pychrysalide/bindings.h73
-rw-r--r--plugins/pychrysalide/constants.c1
-rw-r--r--plugins/pychrysalide/convert.c89
-rw-r--r--plugins/pychrysalide/convert.h (renamed from plugins/pychrysalide/plugins/translate.h)17
-rw-r--r--plugins/pychrysalide/core-int.h58
-rw-r--r--plugins/pychrysalide/core-ui-int.h56
-rw-r--r--plugins/pychrysalide/core-ui.c319
-rw-r--r--plugins/pychrysalide/core-ui.h64
-rw-r--r--plugins/pychrysalide/core.c1269
-rw-r--r--plugins/pychrysalide/core.h30
-rw-r--r--plugins/pychrysalide/format/executable.c22
-rw-r--r--plugins/pychrysalide/format/known.c22
-rw-r--r--plugins/pychrysalide/format/program.c62
-rw-r--r--plugins/pychrysalide/glibext/Makefile.am10
-rw-r--r--plugins/pychrysalide/glibext/comparable.c482
-rw-r--r--plugins/pychrysalide/glibext/comparable.h45
-rw-r--r--plugins/pychrysalide/glibext/hashable.c (renamed from plugins/pychrysalide/glibext/comparison.c)251
-rw-r--r--plugins/pychrysalide/glibext/hashable.h (renamed from plugins/pychrysalide/glibext/comparison.h)20
-rw-r--r--plugins/pychrysalide/glibext/module.c19
-rw-r--r--plugins/pychrysalide/glibext/objhole.c329
-rw-r--r--plugins/pychrysalide/glibext/objhole.h45
-rw-r--r--plugins/pychrysalide/glibext/secstorage.c624
-rw-r--r--plugins/pychrysalide/glibext/secstorage.h45
-rw-r--r--plugins/pychrysalide/glibext/singleton.c510
-rw-r--r--plugins/pychrysalide/glibext/strbuilder.c542
-rw-r--r--plugins/pychrysalide/glibext/strbuilder.h45
-rw-r--r--plugins/pychrysalide/glibext/work.c18
-rw-r--r--plugins/pychrysalide/glibext/workqueue.c6
-rw-r--r--plugins/pychrysalide/helpers.c292
-rw-r--r--plugins/pychrysalide/helpers.h108
-rw-r--r--plugins/pychrysalide/plugins/Makefile.am6
-rw-r--r--plugins/pychrysalide/plugins/constants.c147
-rw-r--r--plugins/pychrysalide/plugins/module.c11
-rw-r--r--plugins/pychrysalide/plugins/plugin.c963
-rw-r--r--plugins/pychrysalide/plugins/plugin.h7
-rw-r--r--plugins/pychrysalide/plugins/python-int.h58
-rw-r--r--plugins/pychrysalide/plugins/python.c489
-rw-r--r--plugins/pychrysalide/plugins/python.h57
-rw-r--r--plugins/pychrysalide/plugins/translate.c110
-rw-r--r--plugins/pynb/Makefile.am77
-rw-r--r--plugins/pynb/core-ui-int.h56
-rw-r--r--plugins/pynb/core-ui.c363
-rw-r--r--plugins/pynb/core-ui.h43
-rw-r--r--plugins/pynb/data/images/pynb-symbolic.svg144
-rw-r--r--plugins/pynb/gresource.xml11
-rw-r--r--plugins/pynb/panel-int.h56
-rw-r--r--plugins/pynb/panel.c210
-rw-r--r--plugins/pynb/panel.h47
-rw-r--r--plugins/pynb/panel.ui21
-rw-r--r--plugins/pynb/params-int.h50
-rw-r--r--plugins/pynb/params.c171
-rw-r--r--plugins/pynb/params.h41
-rw-r--r--plugins/pynb/params.ui52
-rw-r--r--plugins/pynb/prefs-int.h48
-rw-r--r--plugins/pynb/prefs.c143
-rw-r--r--plugins/pynb/prefs.h41
-rw-r--r--plugins/pynb/prefs.ui38
-rw-r--r--src/arch/archbase.h6
-rw-r--r--src/arch/vmpa.h1
-rw-r--r--src/common/Makefile.am15
-rw-r--r--src/common/compiler.h16
-rw-r--r--src/common/cpp.h8
-rw-r--r--src/common/datatypes.h26
-rw-r--r--src/common/entropy.h3
-rw-r--r--src/common/szbin.h10
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/core.c4
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/global.c46
-rw-r--r--src/core/global.h7
-rw-r--r--src/format/program-int.h4
-rw-r--r--src/format/program.c6
-rw-r--r--src/format/program.h2
-rw-r--r--src/glibext/Makefile.am16
-rw-r--r--src/glibext/comparable-int.h47
-rw-r--r--src/glibext/comparable.c110
-rw-r--r--src/glibext/comparable.h46
-rw-r--r--src/glibext/comparison-int.h58
-rw-r--r--src/glibext/comparison.c199
-rw-r--r--src/glibext/comparison.h80
-rw-r--r--src/glibext/hashable-int.h47
-rw-r--r--src/glibext/hashable.c82
-rw-r--r--src/glibext/hashable.h42
-rw-r--r--src/glibext/objhole-int.h166
-rw-r--r--src/glibext/objhole.c191
-rw-r--r--src/glibext/objhole.h114
-rw-r--r--src/glibext/secstorage-int.h60
-rw-r--r--src/glibext/secstorage.c950
-rw-r--r--src/glibext/secstorage.h74
-rw-r--r--src/glibext/singleton-int.h47
-rw-r--r--src/glibext/singleton.c341
-rw-r--r--src/glibext/singleton.h53
-rw-r--r--src/glibext/strbuilder-int.h47
-rw-r--r--src/glibext/strbuilder.c89
-rw-r--r--src/glibext/strbuilder.h46
-rw-r--r--src/gtkext/Makefile.am18
-rw-r--r--src/gtkext/bufferview.c6
-rw-r--r--src/gtkext/gresource.xml7
-rw-r--r--src/gtkext/hexview.c6
-rw-r--r--src/gtkext/launcher-int.h56
-rw-r--r--src/gtkext/launcher.c211
-rw-r--r--src/gtkext/launcher.h45
-rw-r--r--src/gtkext/launcher.ui76
-rw-r--r--src/gtkext/statusstack-int.h4
-rw-r--r--src/gtkext/statusstack.c56
-rw-r--r--src/gtkext/statusstack.ui15
-rw-r--r--src/gtkext/tweak-int.h65
-rw-r--r--src/gtkext/tweak.c319
-rw-r--r--src/gtkext/tweak.h90
-rw-r--r--src/gtkext/tweak.ui46
-rw-r--r--src/gui/Makefile.am2
-rw-r--r--src/gui/core/core.c6
-rw-r--r--src/gui/core/panels.c285
-rw-r--r--src/gui/core/panels.h55
-rw-r--r--src/gui/dialogs/Makefile.am22
-rw-r--r--src/gui/dialogs/about-int.h4
-rw-r--r--src/gui/dialogs/about.c4
-rw-r--r--src/gui/dialogs/about.ui2
-rw-r--r--src/gui/dialogs/gresource.xml8
-rw-r--r--src/gui/dialogs/preferences-int.h62
-rw-r--r--src/gui/dialogs/preferences.c421
-rw-r--r--src/gui/dialogs/preferences.h16
-rw-r--r--src/gui/dialogs/preferences.ui247
-rw-r--r--src/gui/dialogs/prefs/Makefile.am31
-rw-r--r--src/gui/dialogs/prefs/gresource.xml6
-rw-r--r--src/gui/dialogs/prefs/security-int.h64
-rw-r--r--src/gui/dialogs/prefs/security.c400
-rw-r--r--src/gui/dialogs/prefs/security.h41
-rw-r--r--src/gui/dialogs/prefs/security.ui170
-rw-r--r--src/gui/panel-int.h71
-rw-r--r--src/gui/panel.c1207
-rw-r--r--src/gui/panel.h148
-rw-r--r--src/gui/panels/Makefile.am5
-rw-r--r--src/gui/panels/binary-int.h23
-rw-r--r--src/gui/panels/binary-launch.ui65
-rw-r--r--src/gui/panels/binary-params-int.h50
-rw-r--r--src/gui/panels/binary-params.c178
-rw-r--r--src/gui/panels/binary-params.h (renamed from plugins/pe/section.h)21
-rw-r--r--src/gui/panels/binary-params.ui (renamed from src/gui/panels/binary-props.ui)11
-rw-r--r--src/gui/panels/binary.c346
-rw-r--r--src/gui/panels/binary.h17
-rw-r--r--src/gui/panels/gresource.xml3
-rw-r--r--src/gui/panels/welcome-int.h28
-rw-r--r--src/gui/panels/welcome.c379
-rw-r--r--src/gui/panels/welcome.h21
-rw-r--r--src/gui/style.css9
-rw-r--r--src/gui/window.c41
-rw-r--r--src/gui/window.ui1
-rw-r--r--src/plugins/Makefile.am19
-rw-r--r--src/plugins/dt.c562
-rw-r--r--src/plugins/dt.h50
-rw-r--r--src/plugins/manager-int.h54
-rw-r--r--src/plugins/manager.c113
-rw-r--r--src/plugins/manager.h61
-rw-r--r--src/plugins/native-int.h73
-rw-r--r--src/plugins/native.c295
-rw-r--r--src/plugins/native.h (renamed from plugins/pychrysalide/plugins/constants.h)27
-rw-r--r--src/plugins/pglist.c435
-rw-r--r--src/plugins/pglist.h98
-rw-r--r--src/plugins/plugin-def.h16
-rw-r--r--src/plugins/plugin-int.h88
-rw-r--r--src/plugins/plugin.c962
-rw-r--r--src/plugins/plugin.h41
-rw-r--r--src/plugins/self.h99
-rw-r--r--src/plugins/tweakable-int.h50
-rw-r--r--src/plugins/tweakable.c98
-rw-r--r--src/plugins/tweakable.h62
-rw-r--r--src/schemas/re.chrysalide.framework.gschema.xml38
-rw-r--r--tests/arch/operands/immediate.py (renamed from tests/arch/immediate.py)0
-rw-r--r--tests/constval.py44
-rw-r--r--tests/format/program.py9
-rw-r--r--tests/glibext/comparable.py132
-rw-r--r--tests/glibext/configuration.py106
-rw-r--r--tests/glibext/hashable.py190
-rw-r--r--tests/glibext/objhole.py31
-rw-r--r--tests/glibext/re.chrysalide.tests.secstorage.gschema.xml15
-rw-r--r--tests/glibext/secstorage.py153
-rw-r--r--tests/glibext/singleton.py166
-rw-r--r--tests/glibext/strbuilder.py114
-rw-r--r--tests/plugins/plugin.py219
-rw-r--r--tests/plugins/python.py27
-rw-r--r--tools/maint/exportall.inc8
-rw-r--r--tools/maint/libpath.inc10
210 files changed, 16872 insertions, 8621 deletions
diff --git a/configure.ac b/configure.ac
index 23e4e9b..300c970 100644
--- a/configure.ac
+++ b/configure.ac
@@ -241,6 +241,10 @@ AC_ARG_ENABLE([magic-support],
AS_HELP_STRING([--disable-magic-support], [disable magic number recognition [default=no]]),
[], [enable_magic_support=yes])
+AC_ARG_ENABLE([jsonglib-support],
+ AS_HELP_STRING([--disable-jsonglib-support], [disable jsonglib support [default=no]]),
+ [], [enable_jsonglib_support=yes])
+
AC_ARG_ENABLE([python-bindings],
AS_HELP_STRING([--disable-python-bindings], [disable Python bindings [default=no]]),
[], [enable_python_bindings=yes])
@@ -593,6 +597,14 @@ AC_SUBST(LIBHS_LIBS)
#--- Checks for json-glib-1.0
+AM_CONDITIONAL([BUILD_JSONGLIB_SUPPORT], [test "x$enable_jsonglib_support" = "xyes"])
+
+if test "x$BUILD_JSONGLIB_SUPPORT_TRUE" = "x"; then
+ # json-glib support is available and enabled
+ CPPFLAGS="$CPPFLAGS -DINCLUDE_JSONGLIB_SUPPORT"
+fi
+
+
PKG_CHECK_MODULES(LIBJSONGLIB,json-glib-1.0 >= 1.6.6,[libjsonglib_found=yes],[libjsonglib_found=no])
if test "$libjsonglib_found" = "yes"; then
@@ -601,8 +613,14 @@ else
libjsonglib_version='-'
fi
-AC_SUBST(LIBJSONGLIB_CFLAGS)
-AC_SUBST(LIBJSONGLIB_LIBS)
+if test "x$enable_json_glib_support" = "xyes"; then
+
+ AC_SUBST(LIBJSONGLIB_CFLAGS)
+ AC_SUBST(LIBJSONGLIB_LIBS)
+
+ true # empty if/then body not allowed
+
+fi
#--- Checks for Python
@@ -784,6 +802,7 @@ AC_CONFIG_FILES([Makefile
plugins/pychrysalide/gui/panels/Makefile
plugins/pychrysalide/mangling/Makefile
plugins/pychrysalide/plugins/Makefile
+ plugins/pynb/Makefile
plugins/python/Makefile
plugins/python/abackup/Makefile
plugins/python/apkfiles/Makefile
@@ -839,6 +858,7 @@ AC_CONFIG_FILES([Makefile
src/gui/Makefile
src/gui/core/Makefile
src/gui/dialogs/Makefile
+ src/gui/dialogs/prefs/Makefile
src/gui/menus/Makefile
src/gui/panels/Makefile
src/gui/tb/Makefile
@@ -891,7 +911,7 @@ echo The client URL library....................... : $libcurl_version
echo The YAML support library..................... : $libyaml_version
echo The magic number recognition library......... : $libmagic_version
echo The high-performance matching library........ : $libhs_version
-echo The JSON Parser for GLib library............. : $libjsonglib_version
+echo The JSON Parser for the GLib library......... : $libjsonglib_version
echo
echo Available Python programming language........ : $python3_version
diff --git a/data/images/locked-symbolic.svg b/data/images/locked-symbolic.svg
new file mode 100644
index 0000000..1b24147
--- /dev/null
+++ b/data/images/locked-symbolic.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="300"
+ height="300"
+ viewBox="0 0 79.375 79.374998"
+ version="1.1"
+ id="svg341"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ sodipodi:docname="locked-symbolic.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview343"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="px"
+ showgrid="true"
+ inkscape:zoom="2.3450501"
+ inkscape:cx="87.418175"
+ inkscape:cy="159.05844"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ showguides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid462"
+ originx="0"
+ originy="0" />
+ </sodipodi:namedview>
+ <defs
+ id="defs338" />
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ id="path1942"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 39.687499 1.3229166 A 13.229166 13.229166 0 0 0 26.458333 14.552083 A 13.229166 13.229166 0 0 0 31.75 25.124564 L 31.75 14.552083 A 7.9375 7.9375 0 0 1 39.687499 6.6145832 A 7.9375 7.9375 0 0 1 47.624999 14.552083 L 47.624999 25.135416 A 13.229166 13.229166 0 0 0 52.916666 14.552083 A 13.229166 13.229166 0 0 0 39.687499 1.3229166 z " />
+ <path
+ id="rect1950"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 26.458333 14.552083 L 26.458333 39.687499 L 31.75 39.687499 L 31.75 14.552083 L 26.458333 14.552083 z M 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 14.552083 L 47.624999 14.552083 z " />
+ <path
+ id="rect2099"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 18.520833 35.71875 C 14.123462 35.71875 10.583333 39.258879 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 74.511953 14.123462 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251537 78.052082 68.791666 74.511953 68.791666 70.114582 L 68.791666 43.656249 C 68.791666 39.258879 65.251537 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 37.703125 52.916666 L 41.671874 52.916666 C 42.40477 52.916666 42.994791 53.506688 42.994791 54.239583 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 z " />
+ </g>
+</svg>
diff --git a/data/images/nolock-symbolic.svg b/data/images/nolock-symbolic.svg
new file mode 100644
index 0000000..ed59858
--- /dev/null
+++ b/data/images/nolock-symbolic.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="300"
+ height="300"
+ viewBox="0 0 79.375 79.374998"
+ version="1.1"
+ id="svg341"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ sodipodi:docname="nolock-symbolic.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview343"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="px"
+ showgrid="true"
+ inkscape:zoom="2.3450501"
+ inkscape:cx="84.006734"
+ inkscape:cy="159.05844"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ showguides="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid462"
+ originx="0"
+ originy="0" />
+ </sodipodi:namedview>
+ <defs
+ id="defs338" />
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ id="path1942"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 39.687499 1.3229166 A 13.229166 13.229166 0 0 0 26.458333 14.552083 A 13.229166 13.229166 0 0 0 31.75 25.124564 L 31.75 14.552083 A 7.9375 7.9375 0 0 1 39.687499 6.6145832 A 7.9375 7.9375 0 0 1 47.624999 14.552083 L 47.624999 25.135416 A 13.229166 13.229166 0 0 0 52.916666 14.552083 A 13.229166 13.229166 0 0 0 39.687499 1.3229166 z " />
+ <path
+ id="rect1950"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 26.458333 14.552083 L 26.458333 39.687499 L 31.75 39.687499 L 31.75 14.552083 L 26.458333 14.552083 z M 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 14.552083 L 47.624999 14.552083 z " />
+ <path
+ id="rect2099"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 18.520833 35.71875 C 14.123466 35.71875 10.583333 39.258884 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 70.465451 10.608349 70.810037 10.652063 71.148628 L 36.380208 57.337585 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 L 41.671874 52.916666 C 42.259126 52.916666 42.753343 53.295986 42.927095 53.823587 L 68.065612 40.329321 C 66.811534 37.603321 64.061787 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 68.791666 47.447232 L 42.994791 61.294449 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 64.845137 L 14.176912 76.763789 C 15.423753 77.577929 16.914362 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251533 78.052082 68.791666 74.511949 68.791666 70.114582 L 68.791666 47.447232 z " />
+ <rect
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ id="rect3055"
+ width="79.375"
+ height="6.614583"
+ x="-30.998156"
+ y="61.111198"
+ transform="rotate(-28.226475)" />
+ </g>
+</svg>
diff --git a/data/images/unlocked-symbolic.svg b/data/images/unlocked-symbolic.svg
new file mode 100644
index 0000000..da7b6ac
--- /dev/null
+++ b/data/images/unlocked-symbolic.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="300"
+ height="300"
+ viewBox="0 0 79.375 79.374998"
+ version="1.1"
+ id="svg341"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ sodipodi:docname="unlocked-symbolic.svg"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview343"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="px"
+ showgrid="true"
+ inkscape:zoom="2.3450501"
+ inkscape:cx="87.418175"
+ inkscape:cy="159.05844"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ showguides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid462"
+ originx="0"
+ originy="0" />
+ </sodipodi:namedview>
+ <defs
+ id="defs338" />
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ id="path1942"
+ style="display:none;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 39.687499,1.3229166 A 13.229166,13.229166 0 0 0 26.458333,14.552083 V 39.687499 H 31.75 V 25.124564 14.552083 a 7.9375,7.9375 0 0 1 7.937499,-7.9374998 7.9375,7.9375 0 0 1 7.9375,7.9374998 v 10.583333 14.552083 h 5.291667 V 14.552083 A 13.229166,13.229166 0 0 0 39.687499,1.3229166 Z" />
+ <path
+ id="rect2099"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 18.520833 35.71875 C 14.123462 35.71875 10.583333 39.258879 10.583333 43.656249 L 10.583333 70.114582 C 10.583333 74.511953 14.123462 78.052082 18.520833 78.052082 L 60.854166 78.052082 C 65.251537 78.052082 68.791666 74.511953 68.791666 70.114582 L 68.791666 43.656249 C 68.791666 39.258879 65.251537 35.71875 60.854166 35.71875 L 18.520833 35.71875 z M 37.703125 52.916666 L 41.671874 52.916666 C 42.40477 52.916666 42.994791 53.506688 42.994791 54.239583 L 42.994791 70.114582 C 42.994791 70.847478 42.40477 71.437499 41.671874 71.437499 L 37.703125 71.437499 C 36.970229 71.437499 36.380208 70.847478 36.380208 70.114582 L 36.380208 54.239583 C 36.380208 53.506688 36.970229 52.916666 37.703125 52.916666 z " />
+ <path
+ id="path2191"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 60.854166 1.3229166 A 13.229166 13.229166 0 0 0 47.624999 14.552083 L 47.624999 39.687499 L 52.916666 39.687499 L 52.916666 25.124564 L 52.916666 14.552083 A 7.9375 7.9375 0 0 1 60.854166 6.6145832 A 7.9375 7.9375 0 0 1 68.791666 14.552083 L 68.791666 25.135416 L 68.791666 25.24497 C 69.221772 25.173377 69.663543 25.135416 70.114582 25.135416 L 74.083332 25.135416 L 74.083332 14.552083 A 13.229166 13.229166 0 0 0 60.854166 1.3229166 z " />
+ </g>
+</svg>
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 9e177fa..529f4fe 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -13,7 +13,9 @@ endif
# androhelpers
SUBDIRS = \
- $(PYTHON3_SUBDIRS)
+ $(PYTHON3_SUBDIRS) \
+ pe \
+ pynb
# arm \
# bootimg \
diff --git a/plugins/dalvik/context.h b/plugins/dalvik/context.h
index f09cfa6..1954b1e 100644
--- a/plugins/dalvik/context.h
+++ b/plugins/dalvik/context.h
@@ -58,7 +58,7 @@ GType g_dalvik_context_get_type(void);
GDalvikContext *g_dalvik_context_new(void);
/* Mémorise une zone comme étant des données de branchements. */
-bool g_dalvik_context_register_switch_data(GDalvikContext *ctx, const vmpa2t *start, phys_t length);
+bool g_dalvik_context_register_switch_data(GDalvikContext *, const vmpa2t *, phys_t);
/* Mémorise une zone comme étant des données d'un tableau. */
bool g_dalvik_context_register_array_data(GDalvikContext *, const vmpa2t *, uint16_t, phys_t);
diff --git a/plugins/pe/Makefile.am b/plugins/pe/Makefile.am
index e9cd482..ddc4ee4 100644
--- a/plugins/pe/Makefile.am
+++ b/plugins/pe/Makefile.am
@@ -39,22 +39,25 @@ endif
libpe_la_SOURCES = \
core.h core.c \
- pe-int.h pe-int.c \
+ format-int.h format-int.c \
format.h format.c \
- pe_def.h \
- rich.h rich.c \
- routine.h routine.c \
- section.h section.c \
- symbols.h symbols.c
+ pe-def.h
+
+
+# rich.h rich.c \
+# routine.h routine.c \
+# symbols.h symbols.c
+
+
libpe_la_LIBADD = \
$(PYTHON3_LIBADD)
-libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src
+libpe_la_CFLAGS = $(TOOLKIT_CFLAGS) -I$(top_srcdir)/src
libpe_la_LDFLAGS = \
-avoid-version \
- -L$(top_srcdir)/src/.libs -lchrysacore \
+ -L$(top_srcdir)/src/.libs -lchrysacore4 \
$(RUN_PATH) $(PYTHON3_LDFLAGS)
diff --git a/plugins/pe/core-int.h b/plugins/pe/core-int.h
new file mode 100644
index 0000000..1d1b817
--- /dev/null
+++ b/plugins/pe/core-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-int.h - prototypes internes pour l'intégration du support du format PE
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PE_CORE_INT_H
+#define _PLUGINS_PE_CORE_INT_H
+
+
+#include "core.h"
+
+
+#include <plugins/native-int.h>
+
+
+
+/* Greffon natif pour le support du format PE (instance) */
+struct _GPePlugin
+{
+ GNativePlugin parent; /* A laisser en premier */
+
+};
+
+
+/* Greffon natif pour le support du format PE (classe) */
+struct _GPePluginClass
+{
+ GNativePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un greffon de support PE. */
+bool g_pe_plugin_create(GPePlugin *, GModule *);
+
+
+
+#endif /* _PLUGINS_PE_CORE_INT_H */
diff --git a/plugins/pe/core.c b/plugins/pe/core.c
index ddbacf5..b752735 100644
--- a/plugins/pe/core.c
+++ b/plugins/pe/core.c
@@ -24,35 +24,244 @@
#include "core.h"
-#include <core/global.h>
+//#include <core/global.h>
#include <plugins/self.h>
+#include "core-int.h"
#include "format.h"
#ifdef INCLUDE_PYTHON3_BINDINGS
# include "python/module.h"
#endif
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des greffons de support PE. */
+static void g_pe_plugin_class_init(GPePluginClass *);
+
+/* Procède à l'initialisation de l'interface de gestion. */
+//static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *);
+
+/* Initialise une instance de greffon de support PE. */
+static void g_pe_plugin_init(GPePlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_pe_plugin_dispose(GPePlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_pe_plugin_finalize(GPePlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_pe_plugin_enable(GPePlugin *);
+
+/* Prend acte de la désactivation du greffon. */
+static bool g_pe_plugin_disable(GPePlugin *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon de liaison Python */
+G_DEFINE_TYPE_WITH_CODE(GPePlugin, g_pe_plugin, G_TYPE_NATIVE_PLUGIN,
+ /*G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pe_plugin_plugin_manager_interface_init)*/);
+
+
+NATIVE_PLUGIN_ENTRYPOINT(g_pe_plugin_new);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons de support PE. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_class_init(GPePluginClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_pe_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_pe_plugin_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_pe_plugin_enable;
+ plugin->disable = (pg_management_fc)g_pe_plugin_disable;
+
+}
+
+#if 0
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface de gestion. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface)
+{
+ iface->handle_native = (handle_native_plugins_cb)g_pe_plugin_handle_native_plugins_loaded_event;
+
+}
+
+#endif
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon de support PE. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_init(GPePlugin *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_dispose(GPePlugin *plugin)
+{
+ G_OBJECT_CLASS(g_pe_plugin_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_finalize(GPePlugin *plugin)
+{
+ G_OBJECT_CLASS(g_pe_plugin_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = extension vue du système. *
+* *
+* Description : Crée un module pour un greffon de support PE. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+GPluginModule *g_pe_plugin_new(GModule *module)
+{
+ GPePlugin *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_PE_PLUGIN, NULL);
+
+ if (!g_pe_plugin_create(result, module))
+ g_clear_object(&result);
+
+ return G_PLUGIN_MODULE(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = extension vue du système. *
+* *
+* Description : Met en place un module pour un greffon de support PE. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_pe_plugin_create(GPePlugin *plugin, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
#ifdef INCLUDE_PYTHON3_BINDINGS
-# define PG_REQ RL("PyChrysalide")
+# define PG_REQ REQ_LIST("PyChrysalide")
#else
# define PG_REQ NO_REQ
#endif
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PeFmt",
+ "PE format support",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("doc/formats"),
+ PG_REQ,
+ module);
+ return result;
-DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support",
- PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/formats"),
- PG_REQ, AL(PGA_PLUGIN_INIT, PGA_CONTENT_RESOLVER));
+}
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
/******************************************************************************
* *
* Paramètres : plugin = greffon à manipuler. *
* *
-* Description : Prend acte du chargement du greffon. *
+* Description : Prend acte de l'activation du greffon. *
* *
* Retour : - *
* *
@@ -60,7 +269,7 @@ DEFINE_CHRYSALIDE_PLUGIN("Pe", "PE format support",
* *
******************************************************************************/
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
+static bool g_pe_plugin_enable(GPePlugin *plugin)
{
bool result; /* Bilan à retourner */
@@ -77,6 +286,65 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
/******************************************************************************
* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de la désactivation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_pe_plugin_disable(GPePlugin *plugin)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ return result;
+
+}
+
+
+
+#if 0
+
+/* ---------------------------------------------------------------------------------- */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = interface à manipuler. *
+* *
+* Description : Accompagne la fin du chargement des modules natifs. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pe_plugin_handle_native_plugins_loaded_event(GPePlugin *plugin)
+{
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+
+ gstate = PyGILState_Ensure();
+
+ load_python_plugins(G_PLUGIN_MODULE(plugin));
+
+ PyGILState_Release(gstate);
+
+}
+
+
+
+/******************************************************************************
+* *
* Paramètres : plugin = greffon à manipuler. *
* action = type d'action attendue. *
* content = contenu binaire à traiter. *
@@ -90,7 +358,7 @@ G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
* Remarques : - *
* *
******************************************************************************/
-
+#if 0
G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *plugin, PluginAction action, GBinContent *content, wgroup_id_t wid, GtkStatusStack *status)
{
bool test; /* Bilan des accès mémoire */
@@ -114,3 +382,7 @@ G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule
}
}
+#endif
+
+
+#endif
diff --git a/plugins/pe/core.h b/plugins/pe/core.h
index 4497cf5..5c0696f 100644
--- a/plugins/pe/core.h
+++ b/plugins/pe/core.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.h - prototypes pour l'intégration du support du format PE
*
- * Copyright (C) 2017-2018 Cyrille Bagard
+ * Copyright (C) 2017-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,16 +25,18 @@
#define _PLUGINS_PE_CORE_H
+#include <glibext/helpers.h>
#include <plugins/plugin.h>
-#include <plugins/plugin-int.h>
-/* Prend acte du chargement du greffon. */
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+#define G_TYPE_PE_PLUGIN (g_pe_plugin_get_type())
-/* Procède à une opération liée à un contenu binaire. */
-G_MODULE_EXPORT void chrysalide_plugin_handle_binary_content(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
+DECLARE_GTYPE(GPePlugin, g_pe_plugin, G, PE_PLUGIN);
+
+
+/* Crée un module pour un greffon de support PE. */
+GPluginModule *g_pe_plugin_new(GModule *);
diff --git a/plugins/pe/pe-int.c b/plugins/pe/format-int.c
index 4104ce1..2d1e6c8 100644
--- a/plugins/pe/pe-int.c
+++ b/plugins/pe/format-int.c
@@ -1,6 +1,6 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe-int.c - structures internes du format Portable Executable
+ * format-int.c - structures internes du format Portable Executable
*
* Copyright (C) 2010-2017 Cyrille Bagard
*
@@ -21,14 +21,14 @@
*/
-#include "pe-int.h"
+#include "format-int.h"
#include <malloc.h>
#include <string.h>
#include <i18n.h>
-#include <common/endianness.h>
+#include <common/datatypes.h>
#include <core/logs.h>
@@ -46,7 +46,7 @@
* *
******************************************************************************/
-bool read_dos_image_header(const GPeFormat *format, image_dos_header *header)
+bool read_dos_image_header(const GPeFormat *format, image_dos_header_t *header)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -104,7 +104,7 @@ bool read_dos_image_header(const GPeFormat *format, image_dos_header *header)
* *
******************************************************************************/
-bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header *header)
+bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header_t *header)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -140,13 +140,13 @@ bool read_pe_file_header(const GPeFormat *format, vmpa2t *pos, image_file_header
* *
******************************************************************************/
-bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header *header)
+bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optional_header_t *header)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
- image_optional_header_32 *hdr32; /* Version 32 bits */
- image_optional_header_64 *hdr64; /* Version 64 bits */
- image_data_directory *directories; /* Répertoires à charger */
+ image_optional_header_32_t *hdr32; /* Version 32 bits */
+ image_optional_header_64_t *hdr64; /* Version 64 bits */
+ image_data_directory_t *directories; /* Répertoires à charger */
uint32_t *number_of_rva_and_sizes; /* Quantité de ces répertoires */
uint32_t i; /* Boucle de parcours */
@@ -278,7 +278,7 @@ bool read_pe_optional_header(const GPeFormat *format, vmpa2t *pos, image_optiona
* *
******************************************************************************/
-bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t *next)
+bool read_pe_nt_header(const GPeFormat *format, image_nt_headers_t *header, vmpa2t *next)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -318,7 +318,7 @@ bool read_pe_nt_header(const GPeFormat *format, image_nt_headers *header, vmpa2t
* *
******************************************************************************/
-bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header *section)
+bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_section_header_t *section)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -331,7 +331,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se
for (i = 0; i < IMAGE_SIZEOF_SHORT_NAME && result; i++)
result = g_binary_content_read_u8(content, pos, (uint8_t *)&section->name[i]);
- if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->misc.physical_address);
+ if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->physical_address);
if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->virtual_address);
if (result) result = g_binary_content_read_u32(content, pos, SRE_LITTLE, &section->size_of_raw_data);
@@ -361,7 +361,7 @@ bool read_pe_image_section_header(const GPeFormat *format, vmpa2t *pos, image_se
* *
******************************************************************************/
-bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory *dir)
+bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_export_directory_t *dir)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
@@ -401,7 +401,7 @@ bool read_pe_image_export_directory(const GPeFormat *format, vmpa2t *pos, image_
* *
******************************************************************************/
-bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor *desc)
+bool read_pe_image_import_descriptor(const GPeFormat *format, vmpa2t *pos, image_import_descriptor_t *desc)
{
bool result; /* Bilan à retourner */
const GBinContent *content; /* Contenu binaire à lire */
diff --git a/plugins/pe/pe-int.h b/plugins/pe/format-int.h
index ebcf3f0..0b5ad4b 100644
--- a/plugins/pe/pe-int.h
+++ b/plugins/pe/format-int.h
@@ -1,6 +1,6 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe-int.h - prototypes pour les structures internes du format Portable Executable
+ * format-int.h - prototypes pour les structures internes du format Portable Executable
*
* Copyright (C) 2009-2017 Cyrille Bagard
*
@@ -21,14 +21,13 @@
*/
-#ifndef _PLUGINS_PE_PE_INT_H
-#define _PLUGINS_PE_PE_INT_H
+#ifndef _PLUGINS_PE_FORMAT_INT_H
+#define _PLUGINS_PE_FORMAT_INT_H
#include <format/executable-int.h>
-#include "pe_def.h"
#include "format.h"
@@ -36,13 +35,14 @@
/* Format d'exécutable PE (instance) */
struct _GPeFormat
{
- GExeFormat parent; /* A laisser en premier */
+ GExecutableFormat parent; /* A laisser en premier */
- image_dos_header dos_header; /* En-tête DOS */
+ image_dos_header_t dos_header; /* En-tête DOS */
mrange_t rich_header; /* En-tête enrichi */
- image_nt_headers nt_headers; /* En-tête Windows */
+ image_nt_headers_t nt_headers; /* En-tête Windows */
- image_section_header *sections; /* Liste des sections */
+ vmpa2t sections_start; /* Début de la zone de sections*/
+ image_section_header_t *sections; /* Liste des sections */
bool loaded; /* Détection partielle menée */
@@ -51,32 +51,35 @@ struct _GPeFormat
/* Format d'exécutable PE (classe) */
struct _GPeFormatClass
{
- GExeFormatClass parent; /* A laisser en premier */
+ GExecutableFormatClass parent; /* A laisser en premier */
};
+/* Met en place une nouvelle instance de format PE. */
+bool g_pe_format_create(GPeFormat *, GBinContent *);
+
/* Procède à la lecture d'un en-tête de programme DOS. */
-bool read_dos_image_header(const GPeFormat *, image_dos_header *);
+bool read_dos_image_header(const GPeFormat *, image_dos_header_t *);
/* Procède à la lecture d'un en-tête de programme PE (1). */
-bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header *);
+bool read_pe_file_header(const GPeFormat *, vmpa2t *, image_file_header_t *);
/* Procède à la lecture d'un en-tête de programme PE (2). */
-bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header *);
+bool read_pe_optional_header(const GPeFormat *, vmpa2t *, image_optional_header_t *);
/* Procède à la lecture d'un en-tête de programme PE. */
-bool read_pe_nt_header(const GPeFormat *, image_nt_headers *, vmpa2t *);
+bool read_pe_nt_header(const GPeFormat *, image_nt_headers_t *, vmpa2t *);
/* Procède à la lecture d'un en-tête de section PE. */
-bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header *);
+bool read_pe_image_section_header(const GPeFormat *, vmpa2t *, image_section_header_t *);
/* Procède à la lecture d'un répertoire d'exportations. */
-bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory *);
+bool read_pe_image_export_directory(const GPeFormat *, vmpa2t *, image_export_directory_t *);
/* Procède à la lecture d'un répertoire de programme PE. */
-bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor *);
+bool read_pe_image_import_descriptor(const GPeFormat *, vmpa2t *, image_import_descriptor_t *);
-#endif /* _PLUGINS_PE_PE_INT_H */
+#endif /* _PLUGINS_PE_FORMAT_INT_H */
diff --git a/plugins/pe/format.c b/plugins/pe/format.c
index 3839063..11bc116 100644
--- a/plugins/pe/format.c
+++ b/plugins/pe/format.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe.c - support du format Portable Executable
+ * format.c - support du format Portable Executable
*
- * Copyright (C) 2010-2017 Cyrille Bagard
+ * Copyright (C) 2010-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,15 +25,18 @@
#include <assert.h>
+#include <string.h>
-#include "pe-int.h"
-#include "rich.h"
-#include "section.h"
-#include "symbols.h"
+#include "format-int.h"
+//#include "rich.h"
+//#include "symbols.h"
+/* ------------------------- DEFINITION D'UN NOUVEAU FORMAT ------------------------- */
+
+
/* Initialise la classe des formats d'exécutables ELF. */
static void g_pe_format_class_init(GPeFormatClass *);
@@ -46,6 +49,11 @@ static void g_pe_format_dispose(GPeFormat *);
/* Procède à la libération totale de la mémoire. */
static void g_pe_format_finalize(GPeFormat *);
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
/* Indique la désignation interne du format. */
static char *g_pe_format_get_key(const GPeFormat *);
@@ -53,39 +61,28 @@ static char *g_pe_format_get_key(const GPeFormat *);
static char *g_pe_format_get_description(const GPeFormat *);
/* Assure l'interprétation d'un format en différé. */
-static bool g_pe_format_analyze(GPeFormat *, wgroup_id_t, GtkStatusStack *);
+static bool g_pe_format_analyze(GPeFormat *);
/* Informe quant au boutisme utilisé. */
static SourceEndian g_pe_format_get_endianness(const GPeFormat *);
/* Indique le type d'architecture visée par le format. */
-static const char *g_pe_format_get_target_machine(const GPeFormat *);
+static char *g_pe_format_get_target_machine(const GPeFormat *);
-/* Fournit l'adresse principale associée à un format Elf. */
+/* Fournit l'adresse principale associée à un format. */
static bool g_pe_format_get_main_address(GPeFormat *, vmpa2t *);
-
-#if 0
-
/* Etend la définition des portions au sein d'un binaire. */
-static void g_pe_format_refine_portions(GPeFormat *);
-
-#endif
-
-
-
-/* Fournit l'emplacement correspondant à une adresse virtuelle. */
-bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *, virt_t, vmpa2t *);
-
-
-#if 0
+static bool g_pe_format_refine_portions(GPeFormat *);
/* Fournit l'emplacement d'une section donnée. */
-static bool g_pe_format_get_section_range_by_name(const GPeFormat *, const char *, mrange_t *);
-#endif
+static bool g_pe_format_find_section_range_by_name(const GPeFormat *, const char *, mrange_t *);
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UN NOUVEAU FORMAT */
+/* ---------------------------------------------------------------------------------- */
/******************************************************************************
@@ -116,7 +113,7 @@ bool check_pe_format(const GBinContent *content)
if (result)
{
- G_KNOWN_FORMAT(&format)->content = (GBinContent *)content;
+ ((GKnownFormat *)&format)->content = (GBinContent *)content;
result = read_dos_image_header(&format, &format.dos_header);
}
@@ -137,7 +134,7 @@ bool check_pe_format(const GBinContent *content)
/* Indique le type défini pour un format d'exécutable ELF. */
-G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXE_FORMAT);
+G_DEFINE_TYPE(GPeFormat, g_pe_format, G_TYPE_EXECUTABLE_FORMAT);
/******************************************************************************
@@ -156,8 +153,8 @@ static void g_pe_format_class_init(GPeFormatClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GKnownFormatClass *known; /* Version de format connu */
- GBinFormatClass *fmt; /* Version en format basique */
- GExeFormatClass *exe; /* Version en exécutable */
+ GProgramFormatClass *prgm; /* Version en format basique */
+ GExecutableFormatClass *exe; /* Version en exécutable */
object = G_OBJECT_CLASS(klass);
@@ -170,20 +167,19 @@ static void g_pe_format_class_init(GPeFormatClass *klass)
known->get_desc = (known_get_desc_fc)g_pe_format_get_description;
known->analyze = (known_analyze_fc)g_pe_format_analyze;
- fmt = G_BIN_FORMAT_CLASS(klass);
+ prgm = G_PROGRAM_FORMAT_CLASS(klass);
- fmt->get_endian = (format_get_endian_fc)g_pe_format_get_endianness;
+ prgm->get_endian = (program_get_endian_fc)g_pe_format_get_endianness;
+ prgm->find_range_by_name = (find_range_by_name_fc)g_pe_format_find_section_range_by_name;
- exe = G_EXE_FORMAT_CLASS(klass);
+ exe = G_EXECUTABLE_FORMAT_CLASS(klass);
exe->get_machine = (get_target_machine_fc)g_pe_format_get_target_machine;
exe->get_main_addr = (get_main_addr_fc)g_pe_format_get_main_address;
- //exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions;
+ exe->refine_portions = (refine_portions_fc)g_pe_format_refine_portions;
- //exe->translate_phys = (translate_phys_fc)g_exe_format_translate_offset_into_vmpa_using_portions;
- exe->translate_virt = (translate_virt_fc)g_pe_format_translate_address_into_vmpa_using_portions;
-
- //exe->get_range_by_name = (get_range_by_name_fc)g_pe_format_get_section_range_by_name;
+ exe->translate_phys = g_executable_format_translate_offset_into_vmpa_with_portions;
+ exe->translate_virt = g_executable_format_translate_address_into_vmpa_with_portions;
}
@@ -262,39 +258,41 @@ static void g_pe_format_finalize(GPeFormat *format)
* *
******************************************************************************/
-GExeFormat *g_pe_format_new(GBinContent *content)
+GPeFormat *g_pe_format_new(GBinContent *content)
{
GPeFormat *result; /* Structure à retourner */
- if (!check_pe_format(content))
- return NULL;
-
result = g_object_new(G_TYPE_PE_FORMAT, NULL);
- g_known_format_set_content(G_KNOWN_FORMAT(result), content);
+ if (!g_pe_format_create(result, content))
+ g_clear_object(&result);
- return G_EXE_FORMAT(result);
+ return result;
}
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
+* Paramètres : format = description du format connu à consulter. *
+* content = contenu binaire à parcourir. *
* *
-* Description : Indique la désignation interne du format. *
+* Description : Met en place une nouvelle instance de format PE. *
* *
-* Retour : Désignation du format. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *g_pe_format_get_key(const GPeFormat *format)
+bool g_pe_format_create(GPeFormat *format, GBinContent *content)
{
- char *result; /* Désignation à retourner */
+ bool result; /* Bilan à retourner */
- result = strdup("pe");
+ result = true;//check_pe_format(content);
+
+ if (result)
+ result = g_executable_format_create(G_EXECUTABLE_FORMAT(format), content);
return result;
@@ -303,21 +301,21 @@ static char *g_pe_format_get_key(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
+* Paramètres : format = format en place à consulter. *
* *
-* Description : Fournit une description humaine du format. *
+* Description : Présente l'en-tête MS-DOS du format chargé. *
* *
-* Retour : Description du format. *
+* Retour : Pointeur vers la description principale. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *g_pe_format_get_description(const GPeFormat *format)
+const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *format)
{
- char *result; /* Désignation à retourner */
+ const image_dos_header_t *result; /* Informations à retourner */
- result = strdup("Portable Executable");
+ result = &format->dos_header;
return result;
@@ -326,44 +324,21 @@ static char *g_pe_format_get_description(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = format chargé dont l'analyse est lancée. *
-* gid = groupe de travail dédié. *
-* status = barre de statut à tenir informée. *
+* Paramètres : format = format en place à consulter. *
* *
-* Description : Assure l'interprétation d'un format en différé. *
+* Description : Présente l'en-tête NT du format chargé. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Pointeur vers la description principale. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusStack *status)
+const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *format)
{
- bool result; /* Bilan à retourner */
- GExeFormat *exe; /* Autre version du format */
- vmpa2t section_start; /* Zone de départ des sections */
-
- exe = G_EXE_FORMAT(format);
-
- result = read_dos_image_header(format, &format->dos_header);
- if (!result) goto error;
-
- result = read_pe_nt_header(format, &format->nt_headers, &section_start);
- if (!result) goto error;
+ const image_nt_headers_t *result; /* Informations à retourner */
- format->sections = read_all_pe_sections(format, &section_start);
- if (format->sections == NULL) goto error;
-
- extract_pe_rich_header(format);
-
- result = load_pe_symbols(format, gid, status);
- if (!result) goto error;
-
- result = g_executable_format_complete_loading(exe, gid, status);
- if (!result) goto error;
-
- error:
+ result = &format->nt_headers;
return result;
@@ -372,76 +347,73 @@ static bool g_pe_format_analyze(GPeFormat *format, wgroup_id_t gid, GtkStatusSta
/******************************************************************************
* *
-* Paramètres : format = informations chargées à consulter. *
+* Paramètres : format = format en place à consulter. *
* *
-* Description : Informe quant au boutisme utilisé. *
+* Description : Indique si le format PE est en 32 bits ou en 64 bits. *
* *
-* Retour : Indicateur de boutisme. *
+* Retour : true si le format est en 32 bits, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static SourceEndian g_pe_format_get_endianness(const GPeFormat *format)
+bool g_pe_format_get_is_32b(const GPeFormat *format)
{
- SourceEndian result; /* Boutisme à retourner */
+ bool result; /* Nature à retourner */
- /**
- * Sauf exception, le boutisme est généralement petit.
- *
- * Cf. https://reverseengineering.stackexchange.com/a/17923
- * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness
- */
+ assert(format->loaded);
- result = SRE_LITTLE;
+ switch (format->nt_headers.optional_header.header_32.magic)
+ {
+ case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ result = true;
+ break;
+ case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ result = false;
+ break;
+ default:
+ result = true;
+ assert(false);
+ break;
+ }
return result;
+
}
/******************************************************************************
* *
-* Paramètres : format = informations chargées à consulter. *
+* Paramètres : format = format en place à consulter. *
+* count = taille (fixe) du tableau renvoyé. [OUT] *
* *
-* Description : Indique le type d'architecture visée par le format. *
+* Description : Offre un raccourci vers les répertoires du format PE. *
* *
-* Retour : Identifiant de l'architecture ciblée par le format. *
+* Retour : Pointeur vers le tableau des répertoires. *
* *
* Remarques : - *
* *
******************************************************************************/
-static const char *g_pe_format_get_target_machine(const GPeFormat *format)
+const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *format, size_t *count)
{
- const char *result; /* Identifiant à retourner */
+ const image_data_directory_t *result; /* Liste à retourner */
- switch (format->nt_headers.file_header.machine)
+ if (g_pe_format_get_is_32b(format))
{
- case IMAGE_FILE_MACHINE_I386:
- result = "i386";
- break;
+ result = format->nt_headers.optional_header.header_32.data_directory;
- case IMAGE_FILE_MACHINE_R3000:
- case IMAGE_FILE_MACHINE_R4000:
- case IMAGE_FILE_MACHINE_R10000:
- case IMAGE_FILE_MACHINE_WCEMIPSV2:
- case IMAGE_FILE_MACHINE_MIPS16:
- case IMAGE_FILE_MACHINE_MIPSFPU:
- case IMAGE_FILE_MACHINE_MIPSFPU16:
- result = "mips";
- break;
+ if (count != NULL)
+ *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes;
- case IMAGE_FILE_MACHINE_ARM:
- case IMAGE_FILE_MACHINE_THUMB:
- case IMAGE_FILE_MACHINE_ARMNT:
- result = "armv7";
- break;
+ }
+ else
+ {
+ result = format->nt_headers.optional_header.header_64.data_directory;
- case IMAGE_FILE_MACHINE_UNKNOWN:
- default:
- result = NULL;
- break;
+ if (count != NULL)
+ *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes;
}
@@ -452,140 +424,174 @@ static const char *g_pe_format_get_target_machine(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
-* addr = adresse principale trouvée si possible. [OUT] *
+* Paramètres : format = format en place à consulter. *
+* index = indice du répertoire visé. *
* *
-* Description : Fournit l'adresse principale associée à un format Elf. *
+* Description : Extrait le contenu d'un répertoire du format PE. *
* *
-* Retour : Bilan des recherches. *
+* Retour : Pointeur vers un contenu chargé ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr)
+void *g_pe_format_get_directory(const GPeFormat *format, size_t index)
{
- bool result; /* Bilan à retourner */
- GBinSymbol *symbol; /* Point d'entrée trouvé */
- GBinFormat *base; /* Version d'instance parente */
- const mrange_t *range; /* Emplacement de ce point */
-
- result = false;
- symbol = NULL;
+ void *result; /* Données à retourner */
+ size_t max; /* Quantité de répertoires */
+ const image_data_directory_t *dir; /* Localisation du répertoire */
+ vmpa2t pos; /* Tête de lecture */
+ bool status; /* Bilan d'un traitement */
+ image_export_directory_t *export; /* Répertoire de type 0 */
+ image_import_descriptor_t *imports; /* Répertoire de type 1 */
+ size_t imported_count; /* Quantité de DLL requises */
- base = G_BIN_FORMAT(format);
+ result = NULL;
- if (g_binary_format_find_symbol_by_label(base, "main", &symbol))
- goto done;
+ dir = g_pe_format_get_directories(format, &max);
- if (g_binary_format_find_symbol_by_label(base, "_start", &symbol))
- goto done;
+ if (index >= max)
+ goto exit;
- if (g_binary_format_find_symbol_by_label(base, "entry_point", &symbol))
- goto done;
+ dir += index;
- done:
+ status = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format),
+ dir->virtual_address, &pos);
+ if (!status) goto exit;
- if (symbol != NULL)
+ switch (index)
{
- result = true;
+ case IMAGE_DIRECTORY_ENTRY_EXPORT:
+
+ export = malloc(sizeof(image_export_directory_t));
+
+ status = read_pe_image_export_directory(format, &pos, export);
+
+ if (!status)
+ {
+ free(export);
+ goto exit;
+ }
- range = g_binary_symbol_get_range(symbol);
+ result = export;
+ break;
- copy_vmpa(addr, get_mrange_addr(range));
+ case IMAGE_DIRECTORY_ENTRY_IMPORT:
- g_object_unref(G_OBJECT(symbol));
+ imports = NULL;
+ imported_count = 0;
+
+ do
+ {
+ imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor_t));
+
+ status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1));
+
+ if (!status)
+ {
+ free(imports);
+ goto exit;
+ }
+
+ }
+ while (imports[imported_count - 1].original_first_thunk != 0);
+
+ result = imports;
+ break;
}
+ exit:
+
return result;
}
-#if 0
-
/******************************************************************************
* *
-* Paramètres : format = informations chargées à consulter. *
+* Paramètres : format = format en place à consulter. *
+* count = taille (fixe) du tableau renvoyé. [OUT] *
* *
-* Description : Etend la définition des portions au sein d'un binaire. *
+* Description : Offre un raccourci vers les sections du format PE. *
* *
-* Retour : - *
+* Retour : Pointeur vers la liste des sections. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void g_pe_format_refine_portions(GPeFormat *format)
+const image_section_header_t *g_pe_format_get_sections(const GPeFormat *format, size_t *count)
{
+ const image_section_header_t *result; /* Liste à retourner */
+
+ if (count != NULL)
+ *count = format->nt_headers.file_header.number_of_sections;
+
+ result = format->sections;
+
+ return result;
}
-#endif
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
/******************************************************************************
* *
* Paramètres : format = description de l'exécutable à consulter. *
-* addr = adresse virtuelle à retrouver. *
-* pos = position correspondante. [OUT] *
* *
-* Description : Fournit l'emplacement correspondant à une adresse virtuelle. *
+* Description : Indique la désignation interne du format. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Désignation du format. *
* *
* Remarques : - *
* *
******************************************************************************/
-bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, virt_t addr, vmpa2t *pos)
+static char *g_pe_format_get_key(const GPeFormat *format)
{
- bool result; /* Bilan à retourner */
- uint16_t i; /* Boucle de parcours */
- const image_section_header *section; /* Section à consulter */
- phys_t diff; /* Décallage à appliquer */
-
- result = false;
-
- for (i = 0; i < format->nt_headers.file_header.number_of_sections && !result; i++)
- {
- section = &format->sections[i];
+ char *result; /* Désignation à retourner */
- if (addr < section->virtual_address)
- continue;
+ result = strdup("pe");
- if (addr >= (section->virtual_address + section->size_of_raw_data))
- continue;
+ return result;
- diff = addr - section->virtual_address;
+}
- init_vmpa(pos, section->pointer_to_raw_data + diff, addr);
- result = true;
+/******************************************************************************
+* *
+* Paramètres : format = description de l'exécutable à consulter. *
+* *
+* Description : Fournit une description humaine du format. *
+* *
+* Retour : Description du format. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- }
+static char *g_pe_format_get_description(const GPeFormat *format)
+{
+ char *result; /* Désignation à retourner */
- //printf(" // trans // %x -> %x (valid? %d)\n", (unsigned int)addr, (unsigned int)pos->physical, result);
+ result = strdup("Portable Executable");
return result;
}
-
-
-#if 0
-
-
/******************************************************************************
* *
-* Paramètres : format = description de l'exécutable à consulter. *
-* name = nom de la section recherchée. *
-* range = emplacement en mémoire à renseigner. [OUT] *
+* Paramètres : format = format chargé dont l'analyse est lancée. *
* *
-* Description : Fournit l'emplacement d'une section donnée. *
+* Description : Assure l'interprétation d'un format en différé. *
* *
* Retour : Bilan de l'opération. *
* *
@@ -593,51 +599,82 @@ bool g_pe_format_translate_address_into_vmpa_using_portions(GPeFormat *format, v
* *
******************************************************************************/
-static bool g_pe_format_get_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range)
+static bool g_pe_format_analyze(GPeFormat *format)
{
bool result; /* Bilan à retourner */
- phys_t offset; /* Position physique de section*/
- phys_t size; /* Taille de la section trouvée*/
- virt_t address; /* Adresse virtuelle de section*/
- vmpa2t tmp; /* Adresse à initialiser */
+ vmpa2t start; /* Zone de départ des sections */
+ uint16_t count; /* Quantité de sections */
+ uint16_t i; /* Boucle de parcours */
- result = find_elf_section_content_by_name(format, name, &offset, &size, &address);
+ result = read_dos_image_header(format, &format->dos_header);
+ if (!result) goto error;
- if (result)
+ result = read_pe_nt_header(format, &format->nt_headers, &start);
+ if (!result) goto error;
+
+ /* Chargement des définitions des sections déclarées */
+
+ copy_vmpa(&format->sections_start, &start);
+
+ count = format->nt_headers.file_header.number_of_sections;
+
+ format->sections = malloc(count * sizeof(image_section_header_t));
+
+ for (i = 0; i < count; i++)
{
- init_vmpa(&tmp, offset, address);
- init_mrange(range, &tmp, size);
+ result = read_pe_image_section_header(format, &start, &format->sections[i]);
+ if (!result) goto error;
}
- return result;
+ /* Passage de relais */
-}
-#endif
+ if (result)
+ result = G_KNOWN_FORMAT_CLASS(g_pe_format_parent_class)->analyze(G_KNOWN_FORMAT(format));
+
+
+
+
+#if 0
+ extract_pe_rich_header(format);
+ result = load_pe_symbols(format, gid, status);
+ if (!result) goto error;
+
+#endif
+ error:
+ return result;
+}
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
+* Paramètres : format = informations chargées à consulter. *
* *
-* Description : Présente l'en-tête MS-DOS du format chargé. *
+* Description : Informe quant au boutisme utilisé. *
* *
-* Retour : Pointeur vers la description principale. *
+* Retour : Indicateur de boutisme. *
* *
* Remarques : - *
* *
******************************************************************************/
-const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format)
+static SourceEndian g_pe_format_get_endianness(const GPeFormat *format)
{
- const image_dos_header *result; /* Informations à retourner */
+ SourceEndian result; /* Boutisme à retourner */
- result = &format->dos_header;
+ /**
+ * Sauf exception, le boutisme est généralement petit.
+ *
+ * Cf. https://reverseengineering.stackexchange.com/a/17923
+ * https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-160#endianness
+ */
+
+ result = SRE_LITTLE;
return result;
@@ -646,21 +683,43 @@ const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
+* Paramètres : format = description de l'exécutable à consulter. *
+* name = nom de la section recherchée. *
+* range = emplacement en mémoire à renseigner. [OUT] *
* *
-* Description : Présente l'en-tête NT du format chargé. *
+* Description : Fournit l'emplacement d'une section donnée. *
* *
-* Retour : Pointeur vers la description principale. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format)
+static bool g_pe_format_find_section_range_by_name(const GPeFormat *format, const char *name, mrange_t *range)
{
- const image_nt_headers *result; /* Informations à retourner */
+ bool result; /* Bilan à retourner */
+ uint16_t i; /* Boucle de parcours */
+ image_section_header_t *section; /* Section à traiter */
+ char tmp[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */
+ vmpa2t addr; /* Emplacement dans le binaire */
- result = &format->nt_headers;
+ result = false;
+
+ for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++)
+ {
+ memcpy(tmp, section->name, IMAGE_SIZEOF_SHORT_NAME);
+ tmp[IMAGE_SIZEOF_SHORT_NAME] = '\0';
+
+ if (strcmp(name, tmp) != 0)
+ continue;
+
+ init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address);
+ init_mrange(range, &addr, section->size_of_raw_data);
+
+ result = true;
+ break;
+
+ }
return result;
@@ -669,75 +728,78 @@ const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *format)
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
+* Paramètres : format = informations chargées à consulter. *
* *
-* Description : Indique si le format PE est en 32 bits ou en 64 bits. *
+* Description : Indique le type d'architecture visée par le format. *
* *
-* Retour : true si le format est en 32 bits, false sinon. *
+* Retour : Identifiant de l'architecture ciblée par le format. *
* *
* Remarques : - *
* *
******************************************************************************/
-bool g_pe_format_get_is_32b(const GPeFormat *format)
+static char *g_pe_format_get_target_machine(const GPeFormat *format)
{
- bool result; /* Nature à retourner */
-
- assert(format->loaded);
+ char *result; /* Identifiant à retourner */
- switch (format->nt_headers.optional_header.header_32.magic)
+ switch (format->nt_headers.file_header.machine)
{
- case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
- result = true;
+ case IMAGE_FILE_MACHINE_I386:
+ result = strdup("i386");
break;
- case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
- result = false;
+
+ case IMAGE_FILE_MACHINE_R3000:
+ case IMAGE_FILE_MACHINE_R4000:
+ case IMAGE_FILE_MACHINE_R10000:
+ case IMAGE_FILE_MACHINE_WCEMIPSV2:
+ case IMAGE_FILE_MACHINE_MIPS16:
+ case IMAGE_FILE_MACHINE_MIPSFPU:
+ case IMAGE_FILE_MACHINE_MIPSFPU16:
+ result = strdup("mips");
break;
+
+ case IMAGE_FILE_MACHINE_ARM:
+ case IMAGE_FILE_MACHINE_THUMB:
+ case IMAGE_FILE_MACHINE_ARMNT:
+ result = strdup("armv7");
+ break;
+
+ case IMAGE_FILE_MACHINE_UNKNOWN:
default:
- result = true;
- assert(false);
+ result = NULL;
break;
+
}
return result;
-
}
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
-* count = taille (fixe) du tableau renvoyé. [OUT] *
+* Paramètres : format = description de l'exécutable à consulter. *
+* addr = adresse principale trouvée si possible. [OUT] *
* *
-* Description : Offre un raccourci vers les répertoires du format PE. *
+* Description : Fournit l'adresse principale associée à un format. *
* *
-* Retour : Pointeur vers le tableau des répertoires. *
+* Retour : Bilan des recherches. *
* *
* Remarques : - *
* *
******************************************************************************/
-const image_data_directory *g_pe_format_get_directories(const GPeFormat *format, size_t *count)
+static bool g_pe_format_get_main_address(GPeFormat *format, vmpa2t *addr)
{
- const image_data_directory *result; /* Liste à retourner */
+ bool result; /* Bilan à retourner */
+ virt_t ep; /* Point d'entrée */
if (g_pe_format_get_is_32b(format))
- {
- result = format->nt_headers.optional_header.header_32.data_directory;
-
- if (count != NULL)
- *count = format->nt_headers.optional_header.header_32.number_of_rva_and_sizes;
-
- }
+ ep = format->nt_headers.optional_header.header_32.address_of_entry_point;
else
- {
- result = format->nt_headers.optional_header.header_64.data_directory;
-
- if (count != NULL)
- *count = format->nt_headers.optional_header.header_64.number_of_rva_and_sizes;
+ ep = format->nt_headers.optional_header.header_64.address_of_entry_point;
- }
+ result = g_executable_format_translate_address_into_vmpa(G_EXECUTABLE_FORMAT(format), ep, addr);
return result;
@@ -746,83 +808,75 @@ const image_data_directory *g_pe_format_get_directories(const GPeFormat *format,
/******************************************************************************
* *
-* Paramètres : format = format en place à consulter. *
-* index = indice du répertoire visé. *
+* Paramètres : format = informations chargées à consulter. *
* *
-* Description : Extrait le contenu d'un répertoire du format PE. *
+* Description : Etend la définition des portions au sein d'un binaire. *
* *
-* Retour : Pointeur vers un contenu chargé ou NULL. *
+* Retour : Bilan des définitions de portions. *
* *
* Remarques : - *
* *
******************************************************************************/
-void *g_pe_format_get_directory(const GPeFormat *format, size_t index)
+static bool g_pe_format_refine_portions(GPeFormat *format)
{
- void *result; /* Données à retourner */
- size_t max; /* Quantité de répertoires */
- const image_data_directory *dir; /* Localisation du répertoire */
- vmpa2t pos; /* Tête de lecture */
- bool status; /* Bilan d'un traitement */
- image_export_directory *export; /* Répertoire de type 0 */
- image_import_descriptor *imports; /* Répertoire de type 1 */
- size_t imported_count; /* Quantité de DLL requises */
+ bool result; /* Bilan à retourner */
+ vmpa2t origin; /* Origine d'une définition */
+ uint16_t i; /* Boucle de parcours */
+ image_section_header_t *section; /* Section à traiter */
+ vmpa2t addr; /* Emplacement dans le binaire */
+ GBinaryPortion *portion; /* Nouvelle portion de binaire */
+ char name[IMAGE_SIZEOF_SHORT_NAME + 1]; /* Nom de la section */
+ PortionAccessRights rights; /* Droits d'accès à la section */
- result = NULL;
+ result = true;
- dir = g_pe_format_get_directories(format, &max);
+ copy_vmpa(&origin, &format->sections_start);
- if (index >= max)
- goto exit;
+ for (i = 0, section = format->sections; i < format->nt_headers.file_header.number_of_sections; i++, section++)
+ {
+ if (section->pointer_to_raw_data == 0)
+ continue;
- dir += index;
+ /* Emplacement */
- status = g_exe_format_translate_address_into_vmpa(G_EXE_FORMAT(format), dir->virtual_address, &pos);
- if (!status) goto exit;
+ init_vmpa(&addr, section->pointer_to_raw_data, section->virtual_address);
- switch (index)
- {
- case IMAGE_DIRECTORY_ENTRY_EXPORT:
+ portion = g_binary_portion_new(&addr, section->size_of_raw_data);
- export = malloc(sizeof(image_export_directory));
+ /* Nom */
- status = read_pe_image_export_directory(format, &pos, export);
+ memcpy(name, section->name, IMAGE_SIZEOF_SHORT_NAME);
+ name[IMAGE_SIZEOF_SHORT_NAME] = '\0';
- if (!status)
- {
- free(export);
- goto exit;
- }
+ g_binary_portion_set_desc(portion, name);
- result = export;
- break;
+ /* Droits d'accès */
- case IMAGE_DIRECTORY_ENTRY_IMPORT:
+ rights = PAC_NONE;
- imports = NULL;
- imported_count = 0;
+ if (section->characteristics & IMAGE_SCN_MEM_EXECUTE)
+ rights |= PAC_EXEC;
- do
- {
- imports = realloc(imports, ++imported_count * sizeof(image_import_descriptor));
+ if (section->characteristics & IMAGE_SCN_MEM_READ)
+ rights |= PAC_READ;
- status = read_pe_image_import_descriptor(format, &pos, imports + (imported_count - 1));
+ if (section->characteristics & IMAGE_SCN_MEM_WRITE)
+ rights |= PAC_WRITE;
- if (!status)
- {
- free(imports);
- goto exit;
- }
+ g_binary_portion_set_rights(portion, rights);
- }
- while (imports[imported_count - 1].original_first_thunk != 0);
+ /* Inclusion finale */
- result = imports;
- break;
+ result = g_executable_format_include_portion(G_EXECUTABLE_FORMAT(format), portion, &origin);
- }
+ unref_object(portion);
- exit:
+ if (!result) break;
+
+ advance_vmpa(&origin, sizeof(image_section_header_t));
+
+ }
return result;
diff --git a/plugins/pe/format.h b/plugins/pe/format.h
index a48e04d..829f4de 100644
--- a/plugins/pe/format.h
+++ b/plugins/pe/format.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * pe.h - prototypes pour le support du format Portable Executable
+ * format.h - prototypes pour le support du format Portable Executable
*
- * Copyright (C) 2010-2017 Cyrille Bagard
+ * Copyright (C) 2010-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,31 +25,20 @@
#define _PLUGINS_PE_FORMAT_H
-#include <glib-object.h>
#include <stdbool.h>
#include <analysis/content.h>
-#include <format/executable.h>
+#include <glibext/helpers.h>
-#include "pe_def.h"
+#include "pe-def.h"
-#define G_TYPE_PE_FORMAT g_pe_format_get_type()
-#define G_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PE_FORMAT, GPeFormat))
-#define G_IS_PE_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PE_FORMAT))
-#define G_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PE_FORMAT, GPeFormatClass))
-#define G_IS_PE_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PE_FORMAT))
-#define G_PE_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PE_FORMAT, GPeFormatClass))
+#define G_TYPE_PE_FORMAT (g_pe_format_get_type())
-
-/* Format d'exécutable PE (instance) */
-typedef struct _GPeFormat GPeFormat;
-
-/* Format d'exécutable PE (classe) */
-typedef struct _GPeFormatClass GPeFormatClass;
+DECLARE_GTYPE(GPeFormat, g_pe_format, G, PE_FORMAT);
/* Valide un contenu comme étant un format PE. */
@@ -59,23 +48,26 @@ bool check_pe_format(const GBinContent *);
GType g_pe_format_get_type(void);
/* Prend en charge un nouveau format PE. */
-GExeFormat *g_pe_format_new(GBinContent *);
+GPeFormat *g_pe_format_new(GBinContent *);
/* Présente l'en-tête MS-DOS du format chargé. */
-const image_dos_header *g_pe_format_get_dos_header(const GPeFormat *);
+const image_dos_header_t *g_pe_format_get_dos_header(const GPeFormat *);
/* Présente l'en-tête NT du format chargé. */
-const image_nt_headers *g_pe_format_get_nt_headers(const GPeFormat *);
+const image_nt_headers_t *g_pe_format_get_nt_headers(const GPeFormat *);
/* Indique si le format PE est en 32 bits ou en 64 bits. */
bool g_pe_format_get_is_32b(const GPeFormat *);
/* Offre un raccourci vers les répertoires du format PE. */
-const image_data_directory *g_pe_format_get_directories(const GPeFormat *, size_t *);
+const image_data_directory_t *g_pe_format_get_directories(const GPeFormat *, size_t *);
/* Extrait le contenu d'un répertoire du format PE. */
void *g_pe_format_get_directory(const GPeFormat *, size_t);
+/* Offre un raccourci vers les sections du format PE. */
+const image_section_header_t *g_pe_format_get_sections(const GPeFormat *, size_t *);
+
#endif /* _PLUGINS_PE_FORMAT_H */
diff --git a/plugins/pe/pe_def.h b/plugins/pe/pe-def.h
index 62b4607..4812897 100644
--- a/plugins/pe/pe_def.h
+++ b/plugins/pe/pe-def.h
@@ -31,6 +31,7 @@
/**
* Références :
*
+ * - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
* - https://fr.wikipedia.org/wiki/Portable_Executable#En-tête_MZ_sous_MS-DOS
* - https://www.nirsoft.net/kernel_struct/vista/IMAGE_DOS_HEADER.html
*
@@ -42,7 +43,7 @@
/* En-tête DOS */
-typedef struct _image_dos_header
+typedef struct _image_dos_header_t
{
uint16_t e_magic; /* Numéro magique */
uint16_t e_cblp; /* Octets de la dernière page */
@@ -64,7 +65,7 @@ typedef struct _image_dos_header
uint16_t e_res2[10]; /* Mots réservés */
uint32_t e_lfanew; /* Décalage de bon en-tête */
-} image_dos_header;
+} image_dos_header_t;
/* Archtectures supportées */
@@ -123,7 +124,7 @@ typedef struct _image_dos_header
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 /* Octets inv. ; obsolète */
/* Première en-tête du "vrai" format */
-typedef struct _image_file_header
+typedef struct _image_file_header_t
{
uint16_t machine; /* Type de machine visée */
uint16_t number_of_sections; /* Nombre de sections */
@@ -133,7 +134,7 @@ typedef struct _image_file_header
uint16_t size_of_optional_header; /* Taille de l'en-tête n°2 */
uint16_t characteristics; /* Propriétés de l'image */
-} image_file_header;
+} image_file_header_t;
@@ -148,12 +149,12 @@ typedef struct _image_file_header
*/
/* Zone de données Windows */
-typedef struct _image_data_directory
+typedef struct _image_data_directory_t
{
uint32_t virtual_address; /* Adresse de la table */
uint32_t size; /* Taille de la table */
-} image_data_directory;
+} image_data_directory_t;
// Directory Entries
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
@@ -182,7 +183,7 @@ typedef struct _image_data_directory
/* Seconde en-tête, optionnelle */
-typedef struct _image_optional_header_32
+typedef struct _image_optional_header_32_t
{
uint16_t magic; /* Type de binaire manipulé */
uint8_t major_linker_version; /* Version majeure du linker */
@@ -214,11 +215,11 @@ typedef struct _image_optional_header_32
uint32_t size_of_heap_commit; /* Taille de tas au démarrage */
uint32_t loader_flags; /* Champ obslète */
uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */
- image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
-} image_optional_header_32;
+} image_optional_header_32_t;
-typedef struct _image_optional_header_64
+typedef struct _image_optional_header_64_t
{
uint16_t magic; /* Type de binaire manipulé */
@@ -250,16 +251,16 @@ typedef struct _image_optional_header_64
uint64_t size_of_heap_commit; /* Taille de tas au démarrage */
uint32_t loader_flags; /* Champ obslète */
uint32_t number_of_rva_and_sizes; /* Nombre d'entrées suivantes */
- image_data_directory data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ image_data_directory_t data_directory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
-} image_optional_header_64;
+} image_optional_header_64_t;
-typedef union _image_optional_header
+typedef union _image_optional_header_t
{
- image_optional_header_32 header_32; /* Version 32 bits */
- image_optional_header_64 header_64; /* Version 64 bits */
+ image_optional_header_32_t header_32; /* Version 32 bits */
+ image_optional_header_64_t header_64; /* Version 64 bits */
-} image_optional_header;
+} image_optional_header_t;
@@ -302,13 +303,13 @@ typedef union _image_optional_header
/* Résumé global */
-typedef struct _image_nt_headers
+typedef struct _image_nt_headers_t
{
uint32_t signature; /* Numéro magique */
- image_file_header file_header; /* En-tête n°1 */
- image_optional_header optional_header; /* En-tête n°2 */
+ image_file_header_t file_header; /* En-tête n°1 */
+ image_optional_header_t optional_header;/* En-tête n°2 */
-} image_nt_headers;
+} image_nt_headers_t;
@@ -322,7 +323,7 @@ typedef struct _image_nt_headers
#define IMAGE_SIZEOF_SHORT_NAME 8
/* Description d'une section */
-typedef struct _image_section_header
+typedef struct _image_section_header_t
{
char name[IMAGE_SIZEOF_SHORT_NAME]; /* Nom de la section */
@@ -331,7 +332,7 @@ typedef struct _image_section_header
uint32_t physical_address; /* Adresse physique */
uint32_t virtual_size; /* Taille en mémoire */
- } misc;
+ };
uint32_t virtual_address; /* Adresse en mémoire */
uint32_t size_of_raw_data; /* Taille de données définies */
@@ -342,7 +343,7 @@ typedef struct _image_section_header
uint16_t number_of_line_numbers; /* Quantité de numéros de ligne*/
uint32_t characteristics; /* Caractéristiques */
-} image_section_header;
+} image_section_header_t;
/* Détails des caractéristiques d'une image (champ 'characteristics') */
#define IMAGE_SCN_UNKNOWN_0 0x00000000 /* Réservé */
@@ -400,7 +401,7 @@ typedef struct _image_section_header
*/
/* Répertoire des importations */
-typedef struct _image_export_directory
+typedef struct _image_export_directory_t
{
uint32_t characteristics; /* Zéro !? */
uint32_t time_date_stamp; /* Date de création du fichier */
@@ -414,7 +415,7 @@ typedef struct _image_export_directory
uint32_t address_of_names; /* Liste de RVA de noms */
uint32_t address_of_name_ordinals; /* Liste de RVA d'ordinaux */
-} image_export_directory;
+} image_export_directory_t;
/**
@@ -427,7 +428,7 @@ typedef struct _image_export_directory
*/
/* Point de départ de la chaîne des importations */
-typedef struct _image_import_descriptor
+typedef struct _image_import_descriptor_t
{
uint32_t original_first_thunk;
uint32_t time_date_stamp;
@@ -435,7 +436,7 @@ typedef struct _image_import_descriptor
uint32_t module_name;
uint32_t first_thunk;
-} image_import_descriptor;
+} image_import_descriptor_t;
diff --git a/plugins/pe/python/Makefile.am b/plugins/pe/python/Makefile.am
index 5949821..4a70769 100644
--- a/plugins/pe/python/Makefile.am
+++ b/plugins/pe/python/Makefile.am
@@ -1,14 +1,16 @@
noinst_LTLIBRARIES = libpepython.la
+# libpepython_la_SOURCES = \
+# constants.h constants.c \
+# routine.h routine.c
+
libpepython_la_SOURCES = \
- constants.h constants.c \
format.h format.c \
module.h module.c \
- routine.h routine.c \
translate.h translate.c
-libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+libpepython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
-I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
diff --git a/plugins/pe/python/constants.c b/plugins/pe/python/constants.c
index 9b4942d..bb10a7e 100644
--- a/plugins/pe/python/constants.c
+++ b/plugins/pe/python/constants.c
@@ -29,7 +29,7 @@
#include "../pe_def.h"
-#include "../routine.h"
+//#include "../routine.h"
@@ -101,7 +101,7 @@ bool define_python_pe_format_constants(PyTypeObject *type)
* Remarques : - *
* *
******************************************************************************/
-
+#if 0
bool define_python_pe_exported_routine_constants(PyTypeObject *type)
{
bool result; /* Bilan à retourner */
@@ -141,3 +141,4 @@ bool define_python_pe_exported_routine_constants(PyTypeObject *type)
return result;
}
+#endif
diff --git a/plugins/pe/python/constants.h b/plugins/pe/python/constants.h
index 25b0adb..fe4293c 100644
--- a/plugins/pe/python/constants.h
+++ b/plugins/pe/python/constants.h
@@ -35,7 +35,7 @@
bool define_python_pe_format_constants(PyTypeObject *);
/* Définit les constantes pour les routines du format PE. */
-bool define_python_pe_exported_routine_constants(PyTypeObject *);
+//bool define_python_pe_exported_routine_constants(PyTypeObject *);
diff --git a/plugins/pe/python/format.c b/plugins/pe/python/format.c
index 4bbb99a..71d3305 100644
--- a/plugins/pe/python/format.c
+++ b/plugins/pe/python/format.c
@@ -28,25 +28,22 @@
#include <pygobject.h>
-#include <format/known.h>
-#include <plugins/dt.h>
#include <plugins/pychrysalide/helpers.h>
#include <plugins/pychrysalide/analysis/content.h>
#include <plugins/pychrysalide/format/executable.h>
-#include "constants.h"
+//#include "constants.h"
#include "translate.h"
-#include "../format.h"
-#include "../rich.h"
+#include "../format-int.h"
+//#include "../rich.h"
/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
-/* Accompagne la création d'une instance dérivée en Python. */
-static PyObject *py_pe_format_new(PyTypeObject *, PyObject *, PyObject *);
+CREATE_DYN_CONSTRUCTOR(pe_format, G_TYPE_PE_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_pe_format_init(PyObject *, PyObject *, PyObject *);
@@ -59,9 +56,15 @@ static int py_pe_format_init(PyObject *, PyObject *, PyObject *);
/* Présente l'en-tête MS-DOS du format chargé. */
static PyObject *py_pe_format_get_dos_header(PyObject *, void *);
+/* Présente l'en-tête NT du format chargé. */
+static PyObject *py_pe_format_get_nt_headers(PyObject *, void *);
+
/* Offre un raccourci vers les répertoires du format PE. */
static PyObject *py_pe_format_get_directories(PyObject *, void *);
+/* Offre un raccourci vers les sections du format PE. */
+static PyObject *py_pe_format_get_sections(PyObject *, void *);
+
/* Présente l'en-tête enrichi du format chargé. */
static PyObject *py_pe_format_get_rich_header(PyObject *, void *);
@@ -80,66 +83,6 @@ static PyObject *py_pe_format_get_comp_ids(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : type = type du nouvel objet à mettre en place. *
-* args = éventuelle liste d'arguments. *
-* kwds = éventuel dictionnaire de valeurs mises à disposition. *
-* *
-* Description : Accompagne la création d'une instance dérivée en Python. *
-* *
-* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_pe_format_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
- PyObject *result; /* Objet à retourner */
- PyTypeObject *base; /* Type de base à dériver */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
-
- /* Validations diverses */
-
- base = get_python_pe_format_type();
-
- if (type == base)
- goto simple_way;
-
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(G_TYPE_PE_FORMAT, type->tp_name, NULL, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
- /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
-
- simple_way:
-
- result = PyType_GenericNew(type, args, kwds);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : self = objet à initialiser (théoriquement). *
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
@@ -183,7 +126,8 @@ static int py_pe_format_init(PyObject *self, PyObject *args, PyObject *kwds)
format = G_PE_FORMAT(pygobject_get(self));
- g_known_format_set_content(G_KNOWN_FORMAT(format), content);
+ if (!g_pe_format_create(format, content))
+ return -1;
return 0;
@@ -223,7 +167,6 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure)
"\n" \
"The provided information is composed of the following" \
" properties :\n" \
- "\n" \
"* e_magic;\n" \
"* e_cblp;\n" \
"* e_cp;\n" \
@@ -259,6 +202,52 @@ static PyObject *py_pe_format_get_dos_header(PyObject *self, void *closure)
* Paramètres : self = format en place à consulter. *
* closure = non utilisé ici. *
* *
+* Description : Présente l'en-tête NT du format chargé. *
+* *
+* Retour : Structure Python créée pour l'occasion. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_pe_format_get_nt_headers(PyObject *self, void *closure)
+{
+ PyObject *result; /* Trouvaille à retourner */
+ GPeFormat *format; /* Version GLib du format */
+
+#define PE_FORMAT_NT_HEADERS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ nt_headers, py_pe_format, \
+ "NT headers of the file format.\n" \
+ "\n" \
+ "This property is a pychrysalide.StructObject instance." \
+ "\n" \
+ "The provided information is composed of the following" \
+ " properties :\n" \
+ "* signature;\n" \
+ "* file_header;\n" \
+ "* optional_header.\n" \
+ "\n" \
+ "The last two fields are pychrysalide.StructObject" \
+ " which contain more fields. These fields can be" \
+ " enumerated with the keys() method (for instance:" \
+ " *mype.nt_headers.file_header.keys()*).\n" \
+)
+
+ format = G_PE_FORMAT(pygobject_get(self));
+
+ result = translate_pe_nt_headers_to_python(format, g_pe_format_get_nt_headers(format));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = format en place à consulter. *
+* closure = non utilisé ici. *
+* *
* Description : Offre un raccourci vers les répertoires du format PE. *
* *
* Retour : Structure Python créée pour l'occasion. *
@@ -272,7 +261,7 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure)
PyObject *result; /* Trouvaille à retourner */
GPeFormat *format; /* Version GLib du format */
size_t count; /* Quantité de répertoires */
- const image_data_directory *directories; /* Répertoires à exporter */
+ const image_data_directory_t *directories; /* Répertoires à exporter */
size_t i; /* Boucle de parcours */
PyObject *item; /* Elément de tableau */
int ret; /* Bilan d'une mise en place */
@@ -323,6 +312,76 @@ static PyObject *py_pe_format_get_directories(PyObject *self, void *closure)
* Paramètres : self = format en place à consulter. *
* closure = non utilisé ici. *
* *
+* Description : Offre un raccourci vers les sections du format PE. *
+* *
+* Retour : Structure Python créée pour l'occasion. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_pe_format_get_sections(PyObject *self, void *closure)
+{
+ PyObject *result; /* Trouvaille à retourner */
+ GPeFormat *format; /* Version GLib du format */
+ size_t count; /* Quantité de répertoires */
+ const image_section_header_t *sections; /* Sections à exporter */
+ size_t i; /* Boucle de parcours */
+ PyObject *item; /* Elément de tableau */
+ int ret; /* Bilan d'une mise en place */
+
+#define PE_FORMAT_SECTIONS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ sections, py_pe_format, \
+ "Shortcut to the definitions of all PE format sections.\n" \
+ "\n" \
+ "This property is a pychrysalide.StructObject instance.\n" \
+ "\n" \
+ "Each returned item is composed of the following properties :\n"\
+ "\n" \
+ "* name;\n" \
+ "* misc.virtual_size;\n" \
+ "* virtual_address;\n" \
+ "* size_of_raw_data;\n" \
+ "* pointer_to_raw_data;\n" \
+ "* pointer_to_relocations;\n" \
+ "* pointer_to_line_numbers;\n" \
+ "* number_of_relocations;\n" \
+ "* number_of_line_numbers;\n" \
+ "* characteristics." \
+)
+
+ format = G_PE_FORMAT(pygobject_get(self));
+
+ sections = g_pe_format_get_sections(format, &count);
+
+ result = PyTuple_New(count);
+
+ for (i = 0; i < count; i++)
+ {
+ item = translate_pe_section_header_to_python(format, sections + i);
+
+ ret = PyTuple_SetItem(result, i, item);
+
+ if (ret != 0)
+ {
+ Py_DECREF(result);
+ result = NULL;
+ break;
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = format en place à consulter. *
+* closure = non utilisé ici. *
+* *
* Description : Présente l'en-tête enrichi du format chargé. *
* *
* Retour : Tableau de valeurs brutes d'information. *
@@ -487,7 +546,9 @@ PyTypeObject *get_python_pe_format_type(void)
static PyGetSetDef py_pe_format_getseters[] = {
PE_FORMAT_DOS_HEADER_ATTRIB,
+ PE_FORMAT_NT_HEADERS_ATTRIB,
PE_FORMAT_DIRECTORIES_ATTRIB,
+ PE_FORMAT_SECTIONS_ATTRIB,
PE_FORMAT_RICH_HEADER_ATTRIB,
PE_FORMAT_RICH_HEADER_CHECKSUM_ATTRIB,
PE_FORMAT_COMP_IDS_ATTRIB,
@@ -545,8 +606,8 @@ bool register_python_pe_format(PyObject *module)
if (!register_class_for_pygobject(dict, G_TYPE_PE_FORMAT, type))
return false;
- if (!define_python_pe_format_constants(type))
- return false;
+ //if (!define_python_pe_format_constants(type))
+ // return false;
return true;
diff --git a/plugins/pe/python/module.c b/plugins/pe/python/module.c
index 93b1337..ce0c8d7 100644
--- a/plugins/pe/python/module.c
+++ b/plugins/pe/python/module.c
@@ -33,7 +33,7 @@
#include "format.h"
-#include "routine.h"
+//#include "routine.h"
@@ -83,8 +83,8 @@ bool add_format_pe_module_to_python_module(void)
result = (module != NULL);
if (result) result = register_python_pe_format(module);
- if (result) result = register_python_pe_exported_routine(module);
- if (result) result = register_python_pe_imported_routine(module);
+ //if (result) result = register_python_pe_exported_routine(module);
+ //if (result) result = register_python_pe_imported_routine(module);
assert(result);
diff --git a/plugins/pe/python/translate.c b/plugins/pe/python/translate.c
index c01a337..1b4b3ce 100644
--- a/plugins/pe/python/translate.c
+++ b/plugins/pe/python/translate.c
@@ -45,7 +45,7 @@
* *
******************************************************************************/
-PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header *header)
+PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_header_t *header)
{
PyObject *result; /* Construction à retourner */
PyTypeObject *base; /* Modèle d'objet à créer */
@@ -64,6 +64,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
{ \
attrib = PyLong_FromUnsignedLongLong(header->e_ ## _f); \
ret = PyDict_SetItemString(result, "e_" #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
@@ -79,8 +80,13 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
ret = PyTuple_SetItem(attrib, i, item); \
if (ret != 0) break; \
} \
- if (ret != 0) goto failed; \
+ if (i < _n) \
+ { \
+ Py_DECREF(attrib); \
+ goto failed; \
+ } \
ret = PyDict_SetItemString(result, "e_" #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
@@ -119,6 +125,215 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
/******************************************************************************
* *
* Paramètres : format = format PE chargé sur lequel s'appuyer. *
+* header = en-tête NT à décrire en Python. *
+* *
+* Description : Traduit un en-tête PE en Python. *
+* *
+* Retour : Structure mise en place ou NULL en cas d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *translate_pe_nt_headers_to_python(GPeFormat *format, const image_nt_headers_t *header)
+{
+ PyObject *result; /* Construction à retourner */
+ PyTypeObject *base; /* Modèle d'objet à créer */
+ PyObject *attrib; /* Attribut à constituer */
+ int ret; /* Bilan d'une mise en place */
+ PyObject *sub; /* Sous-construction #1 */
+ bool is_32b; /* Format en version 32 bits ? */
+ const image_data_directory_t *directories; /* Répertoires à charger */
+ uint32_t number_of_rva_and_sizes; /* Quantité de ces répertoires */
+ uint32_t i; /* Boucle de parcours */
+ PyObject *dirs; /* Répertoires de données */
+ PyObject *subsub; /* Sous-construction #2 */
+
+ base = get_python_py_struct_type();
+
+ result = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(result != NULL);
+
+#define TRANSLATE_IMAGE_NT_HEADERS_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->_f); \
+ ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) goto failed; \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_NT_HEADERS_FIELD(signature);
+
+ /* Partie file_header */
+
+ sub = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(sub != NULL);
+
+#define TRANSLATE_IMAGE_FILE_HEADER_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->file_header._f); \
+ ret = PyDict_SetItemString(sub, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) \
+ { \
+ Py_DECREF(sub); \
+ goto failed; \
+ } \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(machine);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_sections);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(time_date_stamp);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(pointer_to_symbol_table);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(number_of_symbols);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(size_of_optional_header);
+ TRANSLATE_IMAGE_FILE_HEADER_FIELD(characteristics);
+
+ ret = PyDict_SetItemString(result, "file_header", sub);
+ Py_DECREF(sub);
+ if (ret != 0) goto failed;
+
+ /* Partie optional_header */
+
+ sub = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(sub != NULL);
+
+ is_32b = g_pe_format_get_is_32b(format);
+
+#define TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(_f) \
+ do \
+ { \
+ if (is_32b) \
+ attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \
+ else \
+ attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_64._f); \
+ ret = PyDict_SetItemString(sub, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) \
+ { \
+ Py_DECREF(sub); \
+ goto failed; \
+ } \
+ } \
+ while (0);
+
+#define TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->optional_header.header_32._f); \
+ ret = PyDict_SetItemString(sub, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) \
+ { \
+ Py_DECREF(sub); \
+ goto failed; \
+ } \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(magic);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_linker_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_linker_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_code);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_initialized_data);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_uninitialized_data);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(address_of_entry_point);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(base_of_code);
+ if (is_32b) TRANSLATE_IMAGE_OPTIONAL_HEADER_32B_FIELD(base_of_data);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(image_base);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(section_alignment);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(file_alignment);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_operating_system_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_operating_system_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_image_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_image_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(major_subsystem_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(minor_subsystem_version);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(win32_version_value);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_image);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_headers);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(checksum);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(subsystem);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(dll_characteristics);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_reserve);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_stack_commit);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_reserve);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(size_of_heap_commit);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(loader_flags);
+ TRANSLATE_IMAGE_OPTIONAL_HEADER_FIELD(number_of_rva_and_sizes);
+
+ ret = PyDict_SetItemString(result, "optional_header", sub);
+ Py_DECREF(sub);
+ if (ret != 0) goto failed;
+
+ /* Répertoires de données */
+
+ if (is_32b)
+ {
+ directories = header->optional_header.header_32.data_directory;
+ number_of_rva_and_sizes = header->optional_header.header_32.number_of_rva_and_sizes;
+ }
+ else
+ {
+ directories = header->optional_header.header_64.data_directory;
+ number_of_rva_and_sizes = header->optional_header.header_64.number_of_rva_and_sizes;
+ }
+
+ dirs = PyTuple_New(number_of_rva_and_sizes);
+
+ for (i = 0; i < number_of_rva_and_sizes; i++)
+ {
+ subsub = translate_pe_image_data_directory_to_python(format, directories + i);
+ if (subsub == NULL) break;
+
+ ret = PyTuple_SetItem(dirs, i, subsub);
+ if (ret != 0) break;
+
+ }
+
+ if (i < number_of_rva_and_sizes)
+ goto failed_with_dirs;
+
+ /**
+ * La fonction PyTuple_SetItem() comporte le prologue suivant :
+ *
+ * if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
+ * Py_XDECREF(newitem);
+ * PyErr_BadInternalCall();
+ * return -1;
+ * }
+ *
+ * Comme l'appel à PyDict_SetItemString() incrémente le compte de référence
+ * de dirs, il convient de le réaliser après la consitution de la liste.
+ */
+
+ ret = PyDict_SetItemString(sub, "directories", dirs);
+ if (ret != 0) goto failed_with_dirs;
+
+ Py_DECREF(dirs);
+
+ return result;
+
+ failed_with_dirs:
+
+ Py_DECREF(dirs);
+
+ failed:
+
+ Py_DECREF(result);
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = format PE chargé sur lequel s'appuyer. *
* dir = répertoire PE à décrire en Python. *
* *
* Description : Traduit un répertoire PE en Python. *
@@ -129,7 +344,7 @@ PyObject *translate_pe_dos_header_to_python(GPeFormat *format, const image_dos_h
* *
******************************************************************************/
-PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory *dir)
+PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const image_data_directory_t *dir)
{
PyObject *result; /* Construction à retourner */
PyTypeObject *base; /* Modèle d'objet à créer */
@@ -146,6 +361,7 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i
{ \
attrib = PyLong_FromUnsignedLongLong(dir->_f); \
ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
@@ -167,6 +383,73 @@ PyObject *translate_pe_image_data_directory_to_python(GPeFormat *format, const i
/******************************************************************************
* *
* Paramètres : format = format PE chargé sur lequel s'appuyer. *
+* header = en-tête de section à décrire en Python. *
+* *
+* Description : Traduit une section PE en Python. *
+* *
+* Retour : Structure mise en place ou NULL en cas d'erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *translate_pe_section_header_to_python(GPeFormat *format, const image_section_header_t *header)
+{
+ PyObject *result; /* Construction à retourner */
+ PyTypeObject *base; /* Modèle d'objet à créer */
+ PyObject *attrib; /* Attribut à constituer */
+ int ret; /* Bilan d'une mise en place */
+
+ base = get_python_py_struct_type();
+
+ result = PyObject_CallFunction((PyObject *)base, NULL);
+ assert(result != NULL);
+
+ /* Nom de la section */
+
+ attrib = PyBytes_FromStringAndSize(header->name, IMAGE_SIZEOF_SHORT_NAME);
+
+ ret = PyDict_SetItemString(result, "name", attrib);
+ Py_DECREF(attrib);
+
+ if (ret != 0) goto failed;
+
+ /* Eléments classiques */
+
+#define TRANSLATE_IMAGE_SECTION_HEADER_FIELD(_f) \
+ do \
+ { \
+ attrib = PyLong_FromUnsignedLongLong(header->_f); \
+ ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
+ if (ret != 0) goto failed; \
+ } \
+ while (0);
+
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_size);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(virtual_address);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(size_of_raw_data);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_raw_data);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_relocations);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(pointer_to_line_numbers);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_relocations);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(number_of_line_numbers);
+ TRANSLATE_IMAGE_SECTION_HEADER_FIELD(characteristics);
+
+ return result;
+
+ failed:
+
+ Py_DECREF(result);
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : format = format PE chargé sur lequel s'appuyer. *
* id = ensemble d'informations à décrire en Python. *
* *
* Description : Traduit une série d'informations enrichies en Python. *
@@ -194,6 +477,7 @@ PyObject *translate_pe_comp_id_to_python(GPeFormat *format, const comp_id_t *id)
{ \
attrib = PyLong_FromUnsignedLongLong(id->_f); \
ret = PyDict_SetItemString(result, #_f, attrib); \
+ Py_DECREF(attrib); \
if (ret != 0) goto failed; \
} \
while (0);
diff --git a/plugins/pe/python/translate.h b/plugins/pe/python/translate.h
index dbde6c8..e12b4ae 100644
--- a/plugins/pe/python/translate.h
+++ b/plugins/pe/python/translate.h
@@ -35,10 +35,16 @@
/* Traduit un en-tête MS-DOS en Python. */
-PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header *);
+PyObject *translate_pe_dos_header_to_python(GPeFormat *, const image_dos_header_t *);
+
+/* Traduit un en-tête PE en Python. */
+PyObject *translate_pe_nt_headers_to_python(GPeFormat *, const image_nt_headers_t *);
/* Traduit un répertoire PE en Python. */
-PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory *);
+PyObject *translate_pe_image_data_directory_to_python(GPeFormat *, const image_data_directory_t *);
+
+/* Traduit une section PE en Python. */
+PyObject *translate_pe_section_header_to_python(GPeFormat *, const image_section_header_t *);
/* Traduit une série d'informations enrichies en Python. */
PyObject *translate_pe_comp_id_to_python(GPeFormat *, const comp_id_t *);
diff --git a/plugins/pe/section.c b/plugins/pe/section.c
deleted file mode 100644
index 732b35c..0000000
--- a/plugins/pe/section.c
+++ /dev/null
@@ -1,73 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * section.h - prototypes pour la gestion des sections d'un PE
- *
- * Copyright (C) 2010-2021 Cyrille Bagard
- *
- * This file is part of Chrysalide.
- *
- * Chrysalide is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chrysalide is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "section.h"
-
-
-#include <malloc.h>
-
-
-#include "pe-int.h"
-
-
-
-/******************************************************************************
-* *
-* Paramètres : format = description de l'exécutable à consulter. *
-* pos = tête de lecture positionnée. [OUT] *
-* *
-* Description : Recherche une section donnée au sein de binaire par indice. *
-* *
-* Retour : Liste de sections reconstituées ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-image_section_header *read_all_pe_sections(const GPeFormat *format, vmpa2t *pos)
-{
- image_section_header *result; /* Liste à retourner */
- uint16_t count; /* Quantité de sections */
- uint16_t i; /* Boucle de parcours */
- bool status; /* Bilan d'une lecture */
-
- count = format->nt_headers.file_header.number_of_sections;
-
- result = malloc(count * sizeof(image_section_header));
-
- for (i = 0; i < count; i++)
- {
- status = read_pe_image_section_header(format, pos, &result[i]);
-
- if (!status)
- {
- free(result);
- result = NULL;
- break;
- }
-
- }
-
- return result;
-
-}
diff --git a/plugins/pychrysalide/Makefile.am b/plugins/pychrysalide/Makefile.am
index 183a4ef..d1bf457 100644
--- a/plugins/pychrysalide/Makefile.am
+++ b/plugins/pychrysalide/Makefile.am
@@ -1,7 +1,7 @@
DEFAULT_INCLUDES = -I$(top_builddir) -idirafter.
-lib_LTLIBRARIES = pychrysalide.la
+lib_LTLIBRARIES = pychrysalide.la pychrysalideui.la
libdir = $(pluginslibdir)
@@ -32,7 +32,10 @@ endif
pychrysalide_la_SOURCES = \
access.h access.c \
+ bindings.h bindings.c \
constants.h constants.c \
+ convert.h convert.c \
+ core-int.h \
core.h core.c \
helpers.h helpers.c \
star.h star.c \
@@ -71,6 +74,24 @@ pychrysalide_la_LDFLAGS = \
$(RUN_PATH)
+EXTRA_pychrysalideui_la_DEPENDENCIES = pychrysalide.la
+
+pychrysalideui_la_SOURCES = \
+ core-ui-int.h \
+ core-ui.h core-ui.c
+
+pychrysalideui_la_LIBADD =
+
+# -ldl: dladdr(), dlerror()
+pychrysalideui_la_LDFLAGS = \
+ -module -avoid-version -ldl \
+ $(LIBPYTHON_INTERPRETER_LIBS) \
+ $(LIBPYGOBJECT_LIBS) \
+ -L.libs -l:pychrysalide.so \
+ -L$(top_srcdir)/src/.libs -lchrysacoreui\
+ $(RUN_PATH)
+
+
devdir = $(includedir)/chrysalide/$(subdir)
dev_HEADERS = $(pychrysalide_la_SOURCES:%c=)
diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c
index dd9c1c1..c271139 100644
--- a/plugins/pychrysalide/analysis/content.c
+++ b/plugins/pychrysalide/analysis/content.c
@@ -50,9 +50,9 @@
/* Initialise la classe générique des contenus de binaire. */
-static void py_binary_content_init_gclass(GBinContentClass *, gpointer);
+static int py_binary_content_init_gclass(GBinContentClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT, py_binary_content_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_binary_content_init(PyObject *, PyObject *, PyObject *);
@@ -163,37 +163,39 @@ static PyObject *py_binary_content_get_data(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe générique des contenus de binaire. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_binary_content_init_gclass(GBinContentClass *class, gpointer unused)
+static int py_binary_content_init_gclass(GBinContentClass *gclass, PyTypeObject *pyclass)
{
- class->describe = py_binary_content_describe_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->describe, py_binary_content_describe_wrapper);
+
+ PY_CLASS_SET_WRAPPER(gclass->compute_checksum, py_binary_content_compute_checksum_wrapper);
- class->compute_checksum = py_binary_content_compute_checksum_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->compute_size, py_binary_content_compute_size_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->compute_start_pos, py_binary_content_compute_start_pos_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->compute_end_pos, py_binary_content_compute_end_pos_wrapper);
- class->compute_size = py_binary_content_compute_size_wrapper;
- class->compute_start_pos = py_binary_content_compute_start_pos_wrapper;
- class->compute_end_pos = py_binary_content_compute_end_pos_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->seek, py_binary_content_seek_wrapper);
- class->seek = py_binary_content_seek_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->read_raw, py_binary_content_read_raw_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u8, py_binary_content_read_u8_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u16, py_binary_content_read_u16_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u32, py_binary_content_read_u32_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_u64, py_binary_content_read_u64_wrapper);
- class->read_raw = py_binary_content_read_raw_wrapper;
- class->read_u8 = py_binary_content_read_u8_wrapper;
- class->read_u16 = py_binary_content_read_u16_wrapper;
- class->read_u32 = py_binary_content_read_u32_wrapper;
- class->read_u64 = py_binary_content_read_u64_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->read_uleb128, py_binary_content_read_uleb128_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->read_leb128, py_binary_content_read_leb128_wrapper);
- class->read_uleb128 = py_binary_content_read_uleb128_wrapper;
- class->read_leb128 = py_binary_content_read_leb128_wrapper;
+ return 0;
}
@@ -2248,6 +2250,8 @@ bool ensure_python_binary_content_is_registered(void)
dict = PyModule_GetDict(module);
+ pyg_register_class_init(G_TYPE_BIN_CONTENT, (PyGClassInitFunc)py_binary_content_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_BIN_CONTENT, type))
return false;
diff --git a/plugins/pychrysalide/arch/module.c b/plugins/pychrysalide/arch/module.c
index bbdaf76..0127348 100644
--- a/plugins/pychrysalide/arch/module.c
+++ b/plugins/pychrysalide/arch/module.c
@@ -28,9 +28,6 @@
#include <assert.h>
-#include <arch/archbase.h>
-
-
/*
#include "context.h"
#include "instriter.h"
diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c
new file mode 100644
index 0000000..7e87e27
--- /dev/null
+++ b/plugins/pychrysalide/bindings.c
@@ -0,0 +1,1466 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bindings.c - éléments d'un socle commun aux fonctionnalités graphiques et non graphiques
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "bindings.h"
+
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <pygobject.h>
+#include <stddef.h>
+#include <stdio.h>
+
+
+#include <common/cpp.h>
+#include <common/extstr.h>
+#include <core/core.h>
+#include <plugins/pglist.h>
+#include <plugins/self.h>
+
+
+#include "access.h"
+#include "constants.h"
+#include "helpers.h"
+#include "star.h"
+//#include "strenum.h"
+#include "struct.h"
+#include "analysis/module.h"
+#include "arch/module.h"
+#include "common/module.h"
+#include "core/module.h"
+#include "glibext/module.h"
+/* #include "debug/module.h" */
+#include "format/module.h"
+/* #ifdef INCLUDE_GTK_SUPPORT */
+/* # include "gtkext/module.h" */
+/* # include "gui/module.h" */
+/* #endif */
+/* #include "mangling/module.h" */
+#include "plugins/module.h"
+
+
+
+
+
+/* ------------------------ FONCTIONNALITES POUR CODE PYTHON ------------------------ */
+
+
+#define PYCHRYSALIDE_NAME "pychrysalide"
+
+#define PYCHRYSALIDE_DOC \
+ "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \
+ "\n" \
+ "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \
+ "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \
+ "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
+ "\n" \
+ "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \
+ "how the API works.\n" \
+ "\n" \
+ "These plugins are located in the 'plugins/python' directory.\n" \
+ "\n" \
+ "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup."
+
+
+/* Fournit la révision du programme global. */
+static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
+
+/* Fournit la version du programme global. */
+static PyObject *py_chrysalide_version(PyObject *, PyObject *);
+
+/* Fournit la version du greffon pour Python. */
+static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
+
+
+
+/* ------------------------ FONCTIONNALITES DE MISE EN PLACE ------------------------ */
+
+
+/* Détermine si l'interpréteur lancé est celui pris en compte. */
+static bool is_current_abi_suitable(void);
+
+/* Assure une pleine initialisation des objets de Python-GI. */
+static bool install_metaclass_for_python_gobjects(void);
+
+/* Met en place un environnement pour l'extension Python. */
+static bool setup_python_context(void);
+
+/* Intègre les éventuelles fonctions natives des interfaces. */
+static void inherit_interface_slots(PyObject *);
+
+/**
+ * Conservation d'anciens pointeurs remplacés
+ */
+static initproc __old_gobject_meta_base_init = NULL;
+static initproc __old_gobject_meta_init = NULL;
+
+/**
+ * La fonction unhook_pygobject_behaviour(), inversant les opérations de la fonction
+ * unhook_pygobject_behaviour() manipulerait volontiers les fonctions PyImport_ImportModule()
+ * et PyObject_GetAttrString().
+ *
+ * Cependant, les appels à ces dernières depuis la clôture organisée par la fonction
+ * PyExit_pychrysalide() provoque l'erreur suivante :
+ *
+ * Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
+ *
+ * Les accès nécessaires sont donc conservés ici.
+ */
+static PyTypeObject *__gobject_meta_base = NULL;
+static PyTypeObject *__gobject_meta = NULL;
+
+/* Interceptionne une initialisation pour types gi._gi.GObject. */
+static int hook_gobject_meta_base_init(PyObject *, PyObject *, PyObject *);
+
+/* Interceptionne une initialisation pour types GObject.Object. */
+static int hook_gobject_meta_init(PyObject *, PyObject *, PyObject *);
+
+/* Modifie légèrement le comportement des GObjects en Python. */
+static bool hook_pygobject_behaviour(void);
+
+/* Restaure le comportement d'origine des GObjects en Python. */
+static void unhook_pygobject_behaviour(void);
+
+/* Assure la définition d'un type GObject pour Python adapté. */
+static void ensure_native_pygobject_type(PyTypeObject **);
+
+/* Fournit la référence à un éventuel module déjà en place. */
+static PyObject *get_existing_modules(void);
+
+/* Définit les différents modules du support Python. */
+static PyObject *create_basic_modules(void);
+
+/* Inscrit les défintions des objets Python de Chrysalide. */
+static bool populate_python_modules(const pyinit_details_t *);
+
+/* Restore une ancienne définition de type GObject au besoin. */
+static void restore_original_pygobject_type(PyTypeObject *);
+
+
+/* ------------------------ FONCTIONS GLOBALES DE CHRYSALIDE ------------------------ */
+
+
+/* Assure le plein chargement dans un interpréteur Python. */
+static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *);
+
+/* Point de sortie pour l'initialisation de Python. */
+static void PyExit_pychrysalide(void);
+
+
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONNALITES POUR CODE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la révision du programme global. *
+* *
+* Retour : Numéro de révision. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+
+#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \
+( \
+ revision, "/", \
+ METH_NOARGS, py_chrysalide, \
+ "Provide the revision number of Chrysalide.\n" \
+ "\n" \
+ "The returned value is provided as a string, for instance: 'r1665'." \
+)
+
+ result = PyUnicode_FromString("r" XSTR(REVISION));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la version du programme global. *
+* *
+* Retour : Numéro de version. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_version(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ int major; /* Numéro de version majeur */
+ int minor; /* Numéro de version mineur */
+ int revision; /* Numéro de révision */
+ char version[16]; /* Conservation temporaire */
+
+#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \
+( \
+ version, "/", \
+ METH_NOARGS, py_chrysalide, \
+ "Provide the version number of Chrysalide.\n" \
+ "\n" \
+ "The returned value is provided as a string, for instance: '1.6.65'." \
+)
+
+ major = REVISION / 1000;
+ minor = (REVISION - (major * 1000)) / 100;
+ revision = REVISION % 100;
+
+ snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
+
+ result = PyUnicode_FromString(version);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = NULL car méthode statique. *
+* args = non utilisé ici. *
+* *
+* Description : Fournit la version du greffon pour Python. *
+* *
+* Retour : Numéro de version. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ char version[16]; /* Conservation temporaire */
+
+#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \
+( \
+ mod_version, "/", \
+ METH_NOARGS, py_chrysalide, \
+ "Provide the version number of Chrysalide module for Python.\n" \
+ "\n" \
+ "The returned value is provided as a string, for instance: '0.1.0'." \
+)
+
+ snprintf(version, sizeof(version), "%s", "x.x.x");// FIXME _chrysalide_plugin.version);
+
+ result = PyUnicode_FromString(version);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONNALITES DE MISE EN PLACE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Détermine si l'interpréteur lancé est celui pris en compte. *
+* *
+* Retour : true si l'exécution peut se poursuivre, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool is_current_abi_suitable(void)
+{
+ bool result;
+ int fds[2];
+ int ret;
+ char cmds[128];
+ char content[64];
+ ssize_t got;
+
+#define GRAB_ABI_FLAGS_IN_PYTHON \
+ "import sys" "\n" \
+ "import os" "\n" \
+ "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \
+ "os.write(%d, data)" "\n"
+
+ result = false;
+
+ ret = pipe(fds);
+ if (ret == -1)
+ {
+ perror("pipe()");
+ goto exit;
+ }
+
+ snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
+
+ ret = PyRun_SimpleString(cmds);
+ if (ret != 0) goto exit_with_pipe;
+
+ got = read(fds[0], content, sizeof(content));
+ if (got < 0)
+ {
+ perror("read()");
+ goto exit_with_pipe;
+ }
+
+ content[got] = '\0';
+
+ result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
+
+ exit_with_pipe:
+
+ close(fds[0]);
+ close(fds[1]);
+
+ exit:
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
+ "the ones of the Python library used during the module compilation.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Assure une pleine initialisation des objets de Python-GI. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool install_metaclass_for_python_gobjects(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *gi_types_mod; /* Module Python-GObject */
+
+ /**
+ * Les extensions Python sont chargées à partir de la fonction load_python_plugins(),
+ * qui fait appel à create_python_plugin(). Une instance y est construite via un
+ * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad
+ * dans le fichier __init__.py présent dans chaque module d'extension.
+ *
+ * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique
+ * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction
+ * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c.
+ * Le code de cette dernière comprend notamment la portion suivante :
+ *
+ * [...]
+ * Py_SET_TYPE(type, PyGObject_MetaType);
+ * [...]
+ * if (PyType_Ready(type) < 0) {
+ * g_warning ("couldn't make the type `%s' ready", type->tp_name);
+ * return;
+ * }
+ * [...]
+ *
+ * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c
+ * et commence par :
+ *
+ * int PyType_Ready(PyTypeObject *type)
+ * {
+ * if (type->tp_flags & Py_TPFLAGS_READY) {
+ * assert(_PyType_CheckConsistency(type));
+ * return 0;
+ * }
+ * [...]
+ * }
+ *
+ * La vérification de cohérencce commence par analyser le type et son propre
+ * type :
+ *
+ * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c :
+ *
+ * int _PyType_CheckConsistency(PyTypeObject *type)
+ * {
+ * [...]
+ * CHECK(!_PyObject_IsFreed((PyObject *)type));
+ * [...]
+ * }
+ *
+ * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c :
+ *
+ * int _PyObject_IsFreed(PyObject *op)
+ * {
+ * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
+ * return 1;
+ * }
+ *
+ * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL.
+ *
+ * Or le type du type est écrasé dans la fonction pygobject_register_class()
+ * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est
+ * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c :
+ *
+ * static PyObject *
+ * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass)
+ * {
+ * Py_INCREF(metaclass);
+ * PyGObject_MetaType = metaclass;
+ * Py_INCREF(metaclass);
+ *
+ * Py_SET_TYPE(&PyGObject_Type, metaclass);
+ *
+ * Py_INCREF(Py_None);
+ * return Py_None;
+ * }
+ *
+ * Afin de valider la vérification de _PyType_CheckConsistency() pour les
+ * modules externes qui entraînent un enregistrement tout en portant le drapeau
+ * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut
+ * initialiser au besoin la variable PyGObject_MetaType.
+ *
+ * Une ligne suffit donc à enregistrer le type intermédiaire :
+ *
+ * from _gi import types
+ *
+ * On simule ici une déclaration similaire si nécessaire, selon la valeur
+ * portée par PyGObject_Type.ob_base.ob_base.ob_type.tp_name :
+ * - "type" (PyType_Type) : état initial ;
+ * - "_GObjectMetaBase" : état revu.
+ */
+
+ /**
+ * PyGObject_Type.ob_base.ob_base.ob_type != &PyType_Type ?
+ */
+ result = (PyType_CheckExact(&PyGObject_Type) == 0);
+
+ if (!result)
+ {
+ gi_types_mod = PyImport_ImportModule("gi.types");
+
+ result = (PyErr_Occurred() == NULL);
+
+ if (result)
+ result = (PyType_CheckExact(&PyGObject_Type) == 0);
+
+ Py_XDECREF(gi_types_mod);
+
+ }
+
+#ifndef NDEBUG
+ if (result)
+ assert(strcmp(PyGObject_Type.ob_base.ob_base.ob_type->tp_name, "_GObjectMetaBase") == 0);
+#endif
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "unable to install metaclass for Python GObjects.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Met en place un environnement pour l'extension Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool setup_python_context(void)
+{
+ bool result; /* Bilan à retourner */
+
+ result = false;
+
+ /**
+ * Un message d'erreur doit être défini en cas d'échec de l'initialisation,
+ * via un appel à PyErr_SetString().
+ */
+
+ if (!is_current_abi_suitable())
+ goto exit;
+
+ if (pygobject_init(-1, -1, -1) == NULL)
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
+ goto exit;
+ }
+
+ if (!install_metaclass_for_python_gobjects())
+ goto exit;
+
+ result = true;
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : cls = classe instanciée pour la construction d'un objet. *
+* *
+* Description : Intègre les éventuelles fonctions natives des interfaces. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void inherit_interface_slots(PyObject *cls)
+{
+ GType gtype; /* Type GObject lié au Python */
+ GType *ifaces; /* Interfaces implémentées */
+ guint ifaces_count; /* Nombre de ces interfaces */
+ guint i; /* Boucle de parcours */
+ PyTypeObject *iface_type; /* Type Python pour interface */
+ size_t k; /* Boucle de parcours */
+ size_t offset; /* Position dans une structure */
+ void *src_slot; /* Eventuelle fonction idéale */
+ void *dst_slot; /* Eventuelle fonction en place*/
+
+ static size_t slot_offsets[] = { /* Emplacements à actualiser */
+ //offsetof(PyTypeObject, tp_str),
+ offsetof(PyTypeObject, tp_hash),
+ offsetof(PyTypeObject, tp_richcompare),
+ };
+
+ /**
+ * Cette fonction reprend les principes de la fonction d'importation de
+ * PyGObject pygobject_inherit_slots().
+ *
+ * Cependant, cette dernière n'est appelée que depuis les fonctions :
+ * - pygobject_register_class() (send C -> Python), qui peut écraser des
+ * slots existants ;
+ * - pygobject_new_with_interfaces() / pygobject_lookup_class(), qui ne
+ * remplace pas les fonctions par défaut déjà en place.
+ *
+ * Pour mémoire, les types créés dynamiquement depuis des scripts (sens
+ * Python -> C) passent par la fonction _wrap_pyg_type_register().
+ */
+
+ gtype = pyg_type_from_object(cls);
+ assert(gtype != G_TYPE_INVALID);
+
+ ifaces = g_type_interfaces(gtype, &ifaces_count);
+
+ for (i = 0; i < ifaces_count; i++)
+ {
+ iface_type = pygobject_lookup_class(ifaces[i]);
+
+#define PYTYPE_SLOT(tp, off) \
+ *(void **)(void *)(((char *)tp) + off)
+
+ for (k = 0; k < ARRAY_SIZE(slot_offsets); k++)
+ {
+ offset = slot_offsets[k];
+
+ src_slot = PYTYPE_SLOT(iface_type, offset);
+
+ if (src_slot == NULL)
+ continue;
+
+ if (src_slot == PYTYPE_SLOT(&PyBaseObject_Type, offset)
+ || src_slot == PYTYPE_SLOT(&PyGObject_Type, offset))
+ continue;
+
+ dst_slot = PYTYPE_SLOT(cls, offset);
+
+ if (src_slot == dst_slot)
+ continue;
+
+ if (dst_slot != NULL)
+ {
+ if (dst_slot != PYTYPE_SLOT(&PyBaseObject_Type, offset)
+ && dst_slot != PYTYPE_SLOT(&PyGObject_Type, offset))
+ continue;
+ }
+
+ /**
+ * Usage du *(void **)(void *)
+ */
+ PYTYPE_SLOT(cls, offset) = src_slot;
+
+ }
+
+ }
+
+ g_free(ifaces);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Interceptionne une initialisation pour types gi._gi.GObject. *
+* *
+* Retour : Bilan de l'initialisation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int hook_gobject_meta_base_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int result; /* Bilan à retourner */
+
+ /**
+ * Le type de self (self->ob_type->tp_name) est ici _GObjectMetaBase.
+ */
+
+ result = __old_gobject_meta_base_init(self, args, kwds);
+
+ if (result == 0)
+ inherit_interface_slots(self);
+
+ return result;
+
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Interceptionne une initialisation pour types GObject.Object. *
+* *
+* Retour : Bilan de l'initialisation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int hook_gobject_meta_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int result; /* Bilan à retourner */
+
+ /**
+ * Le type de self (self->ob_type->tp_name) est ici GObjectMeta.
+ */
+
+ result = __old_gobject_meta_init(self, args, kwds);
+
+ if (result == 0)
+ inherit_interface_slots(self);
+
+ return result;
+
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Modifie légèrement le comportement des GObjects en Python. *
+* *
+* Retour : Bilan de l'initialisation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool hook_pygobject_behaviour(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *gi_types_mod; /* Module Python-GObject */
+
+ result = false;
+
+ /**
+ * Validation des accès.
+ *
+ * Les références prises sur les attributs sont restituées dans
+ * unhook_pygobject_behaviour().
+ */
+
+ gi_types_mod = PyImport_ImportModule("gi.types");
+ if (gi_types_mod == NULL) goto exit;
+
+ __gobject_meta_base = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "_GObjectMetaBase");
+ assert(__gobject_meta_base != NULL);
+ if (__gobject_meta_base == NULL) goto exit_with_mod;
+
+ __gobject_meta = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "GObjectMeta");
+ assert(__gobject_meta != NULL);
+ if (__gobject_meta == NULL) goto exit_with_mod;
+
+ /**
+ * Modification des comportements.
+ */
+
+ __old_gobject_meta_base_init = __gobject_meta_base->tp_init;
+
+ __gobject_meta_base->tp_init = hook_gobject_meta_base_init;
+
+ __old_gobject_meta_init = __gobject_meta->tp_init;
+
+ __gobject_meta->tp_init = hook_gobject_meta_init;
+
+ result = true;
+
+ exit_with_mod:
+
+ Py_DECREF(gi_types_mod);
+
+ exit:
+
+ if (!result)
+ PyErr_SetString(PyExc_SystemError, "unable to hook the GObject behaviour in Python.");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Restaure le comportement d'origine des GObjects en Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void unhook_pygobject_behaviour(void)
+{
+ /**
+ * Le déclenchement de la fonction PyExit_pychrysalide() appelante est
+ * programmé depuis init_python_pychrysalide_module(), appelée si
+ * hook_pygobject_behaviour() a opéré avec réussite.
+ */
+ assert(__gobject_meta_base != NULL);
+ assert(__gobject_meta != NULL);
+
+ __gobject_meta_base->tp_init = __old_gobject_meta_base_init;
+
+ Py_XDECREF(__gobject_meta_base);
+
+ __gobject_meta->tp_init = __old_gobject_meta_init;
+
+ Py_XDECREF(__gobject_meta);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : namespace = module particulier à charger à partir de gi. *
+* version = idenfiant de la version à stipuler. *
+* *
+* Description : Charge un module GI dans Python avec une version attendue. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool import_namespace_from_gi_repository(const char *namespace, const char *version)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *module; /* Module Python-GObject */
+ PyObject *args; /* Arguments à fournir */
+ int ret; /* Bilan d'une mise en place */
+
+ result = false;
+
+ /* Sélection d'une version */
+
+ module = PyImport_ImportModule("gi");
+
+ if (module != NULL)
+ {
+ args = Py_BuildValue("ss", namespace, version);
+
+ run_python_method(module, "require_version", args);
+
+ result = (PyErr_Occurred() == NULL);
+
+ Py_DECREF(args);
+ Py_DECREF(module);
+
+ }
+
+ /* Importation du module visé */
+
+ if (result)
+ {
+ args = PyTuple_New(1);
+
+ ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace));
+ if (ret != 0)
+ {
+ result = false;
+ goto args_error;
+ }
+
+ module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args);
+
+ result = (module != NULL);
+
+ Py_XDECREF(module);
+
+ args_error:
+
+ Py_DECREF(args);
+
+ }
+
+ // TODO : message d'erreur ?
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : py_gobj_def = définition de type actuelle. [OUT] *
+* *
+* Description : Assure la définition d'un type GObject pour Python adapté. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void ensure_native_pygobject_type(PyTypeObject **py_gobj_def)
+{
+ GQuark pygobject_class_key; /* Copie d'un accès GI interne */
+
+ /**
+ * Les appels suivants procèdent à l'enregistrement de différents éléments
+ * dans l'espace de noms Python pour Chrysalide.
+ *
+ * Une majeure partie de ces éléments est constituée d'objets dérivés de
+ * GObject. Ce type d'objet (G_TYPE_OBJECT) est représenté par deux types
+ * en Python :
+ *
+ * - gi._gi.GObject, mis en place lors de l'importation du module gi.
+ *
+ * Ce dernier lance automatiquement l'importation du module natif gi._gi,
+ * lequel, via son initialisation dans la fonction PyInit__gi()
+ * (cf. ./gi/gimodule.c) lance un appel à pyi_object_register_types()
+ * qui procède à l'enregistrement du type "GObject" porté par la structure
+ * PyGObject_Type en correspondance au type GLib G_TYPE_OBJECT (cf. appel
+ * à pygobject_register_class() dans gi/pygobject-object.c).
+ *
+ * - gi.repository.GObject.Object, qui vient surclasser le type précédent
+ * lors d'un appel d'initialisation : from gi.repository import GObject.
+ *
+ * Cette seconde définition est destinée à apporter une représentation
+ * de l'introspection GObject de plus haut niveau pour l'utilisateur par
+ * rapport à celle de bas niveau gi._gi.
+ *
+ * Il demeure que la seconde définition est entièrement implémentée en Python
+ * et porte ainsi le fanion Py_TPFLAGS_HEAPTYPE, imposant cette même dernière
+ * propriétée à tous les objets qui en dérivent.
+ *
+ * Les définitions de Chrysalide sont cependant toutes statiques et donc
+ * incompatibles avec une définition gi.repository.GObject.Object, comme le
+ * pointent les validations opérées par PyType_Ready().
+ *
+ * Une solution consiste ici à restaurer au besoin la définition gi._gi.GObject
+ * brute, effectuer les enregistrements de Chrysalide sur cette base, et
+ * remettre en place la définition éventuellement remplacée ensuite.
+ */
+
+ *py_gobj_def = pygobject_lookup_class(G_TYPE_OBJECT);
+
+ if (*py_gobj_def != &PyGObject_Type)
+ {
+ Py_INCREF((PyObject *)*py_gobj_def);
+
+ /* Définition récupérée de pyi_object_register_types() */
+ pygobject_class_key = g_quark_from_static_string("PyGObject::class");
+
+ g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, &PyGObject_Type);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit la référence à un éventuel module déjà en place. *
+* *
+* Retour : Pointeur vers le module mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *get_existing_modules(void)
+{
+ PyObject *result; /* Module Python à retourner */
+
+ /**
+ * Vérification préalable : dans le cas où on est embarqué directement dans
+ * un interpréteur Python, le module se charge et termine par charger à leur
+ * tour les différentes extensions trouvées, via load_remaning_plugins() puis
+ * chrysalide_plugin_on_native_loaded().
+ *
+ * Lesquelles vont très probablement charger le module pychrysalide.
+ *
+ * Comme le chargement de ce dernier n'est alors pas encore terminé,
+ * Python va relancer cette procédure, et register_access_to_python_module()
+ * va détecter un doublon.
+ */
+
+ result = get_access_to_python_module(PYCHRYSALIDE_NAME);
+
+ Py_XINCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Définit les différents modules du support Python. *
+* *
+* Retour : Pointeur vers le module mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *create_basic_modules(void)
+{
+ PyObject *result; /* Module Python à retourner */
+ bool status; /* Bilan des inclusions */
+
+ static PyMethodDef py_chrysalide_methods[] = {
+ PY_CHRYSALIDE_REVISION_METHOD,
+ PY_CHRYSALIDE_VERSION_METHOD,
+ PY_CHRYSALIDE_MOD_VERSION_METHOD,
+ { NULL }
+ };
+
+ static PyModuleDef py_chrysalide_module = {
+
+ .m_base = PyModuleDef_HEAD_INIT,
+
+ .m_name = PYCHRYSALIDE_NAME,
+ .m_doc = PYCHRYSALIDE_DOC,
+
+ .m_size = -1,
+
+ .m_methods = py_chrysalide_methods
+
+ };
+
+ result = PyModule_Create(&py_chrysalide_module);
+
+ register_access_to_python_module(py_chrysalide_module.m_name, result);
+
+ status = true;
+
+ /**
+ * Réceptacle pour objets et constantes : à laisser en premier ajout donc.
+ */
+ if (status) status = add_features_module(result);
+
+ if (status) status = define_data_types_constants(result);
+
+ if (status) status = add_analysis_module(result);
+ if (status) status = add_arch_module(result);
+ if (status) status = add_common_module(result);
+ if (status) status = add_glibext_module(result);
+ if (status) status = add_core_module(result);
+ /*
+ if (status) status = add_debug_module(result);
+ */
+ if (status) status = add_format_module(result);
+ /*
+#ifdef INCLUDE_GTK_SUPPORT
+ if (status) status = add_gtkext_module(result);
+ if (status) status = add_gui_module(result);
+#endif
+ if (status) status = add_mangling_module(result);
+ */
+ if (status) status = add_plugins_module(result);
+
+ if (!status)
+ {
+ Py_DECREF(result);
+ result = NULL;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : details = précisions de chargement complémentaires. *
+* *
+* Description : Inscrit les défintions des objets Python de Chrysalide. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool populate_python_modules(const pyinit_details_t *details)
+{
+ bool result; /* Bilan à retourner */
+
+ /**
+ * Les chargements de types supplémentaires, apportés par la version UI, ne
+ * peuvent être forcés depuis les mises en places des versions non-UI via
+ * les classiques appels ensure_xxx().
+ *
+ * L'assurance d'un chargement préalable est ainsi réalisée ici, via
+ * un chargement préliminaire, si besoin est.
+ */
+
+ if (details->populate_extra)
+ result = details->populate_extra();
+ else
+ result = true;
+
+ /*
+ if (result) result = ensure_python_string_enum_is_registered();
+ */
+ if (result) result = ensure_python_py_struct_is_registered();
+
+ if (result) result = populate_analysis_module();
+ if (result) result = populate_arch_module();
+ if (result) result = populate_glibext_module();
+ if (result) result = populate_common_module();
+ if (result) result = populate_core_module();
+ /*
+ if (result) result = populate_debug_module();
+ */
+ if (result) result = populate_format_module();
+ /*
+#ifdef INCLUDE_GTK_SUPPORT
+ if (result) result = populate_gtkext_module();
+ if (result) result = populate_gui_module();
+#endif
+ if (result) result = populate_mangling_module();
+ */
+ if (result) result = populate_plugins_module();
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : py_gobj_def = définition de type actuelle. [OUT] *
+* *
+* Description : Restore une ancienne définition de type GObject au besoin. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void restore_original_pygobject_type(PyTypeObject *py_gobj_def)
+{
+ GQuark pygobject_class_key; /* Copie d'un accès GI interne */
+
+ if (py_gobj_def != &PyGObject_Type)
+ {
+ /* Définition récupérée de pyi_object_register_types() */
+ pygobject_class_key = g_quark_from_static_string("PyGObject::class");
+
+ g_type_set_qdata(G_TYPE_OBJECT, pygobject_class_key, py_gobj_def);
+
+ Py_DECREF((PyObject *)py_gobj_def);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : details = précisions de chargement complémentaires. *
+* *
+* Description : Implémente le point d'entrée pour l'initialisation de Python.*
+* *
+* Retour : Module mis en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *init_python_pychrysalide_module(const pyinit_details_t *details)
+{
+ PyObject *result; /* Module Python à retourner */
+ PyTypeObject *py_gobj_def; /* Définition GObject courante */
+ bool status; /* Bilan des inclusions */
+
+ result = get_existing_modules();
+
+ if (result != NULL)
+ return result;
+
+ if (!setup_python_context())
+ goto exit;
+
+ if (!hook_pygobject_behaviour())
+ goto exit;
+
+ /**
+ * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil,
+ * à savoir des types convertis de façon incomplète. Par exemple, pour une
+ * structure GChecksum, le type à l'exécution est :
+ *
+ * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>]
+ *
+ * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>]
+ *
+ * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec
+ * la version debug de Python3 (version de python3-gi : 3.42.2-3) :
+ *
+ * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module()
+ *
+ * Code de reproduction dans un interpréteur classique :
+ *
+ * import gi
+ * gi.require_version('GLib', '2.0')
+ * from gi.repository import GLib
+ *
+ */
+
+ if (!import_namespace_from_gi_repository("GLib", "2.0"))
+ goto exit;
+
+ /* Mise en place des fonctionnalités offertes */
+
+ ensure_native_pygobject_type(&py_gobj_def);
+
+ result = create_basic_modules();
+
+ if (result == NULL)
+ PyErr_SetString(PyExc_SystemError, "failed to create all PyChrysalide modules.");
+
+ else
+ {
+ status = populate_python_modules(details);
+
+ if (!status)
+ PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
+
+ else if (details->standalone)
+ status = init_python_interpreter_for_standalone_mode(details);
+
+ if (!status)
+ {
+ Py_DECREF(result);
+ result = NULL;
+ }
+
+ }
+
+ restore_original_pygobject_type(py_gobj_def);
+
+ exit:
+
+ if (result == NULL && !details->standalone)
+ log_pychrysalide_exception("Python bindings loading failed");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.*
+* *
+* Description : Présente dans le journal une exception survenue. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void log_pychrysalide_exception(const char *prefix, ...)
+{
+ va_list ap; /* Compléments argumentaires */
+ char *msg; /* Message complet à imprimer */
+ PyObject *err_type; /* Type d'erreur Python */
+ PyObject *err_value; /* Instance Python d'erreur */
+ PyObject *err_traceback; /* Trace Python associée */
+ PyObject *err_string; /* Description Python d'erreur */
+ const char *err_msg; /* Représentation humaine */
+
+ assert(PyGILState_Check() == 1);
+
+ if (PyErr_Occurred())
+ {
+ /* Base de la communication */
+
+ va_start(ap, prefix);
+
+ vasprintf(&msg, prefix, ap);
+
+ va_end(ap);
+
+ /* Détails complémentaires */
+
+ PyErr_Fetch(&err_type, &err_value, &err_traceback);
+
+ PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
+
+ if (err_traceback == NULL)
+ {
+ err_traceback = Py_None;
+ Py_INCREF(err_traceback);
+ }
+
+ PyException_SetTraceback(err_value, err_traceback);
+
+ if (err_value == NULL)
+ msg = stradd(msg, _(": no extra information is provided..."));
+
+ else
+ {
+ err_string = PyObject_Str(err_value);
+ err_msg = PyUnicode_AsUTF8(err_string);
+
+ msg = stradd(msg, ": ");
+ msg = stradd(msg, err_msg);
+
+ Py_DECREF(err_string);
+
+ }
+
+ /**
+ * Bien que la documentation précise que la fonction PyErr_Fetch()
+ * transfère la propritété des éléments retournés, la pratique
+ * montre que le programme plante à la terminaison en cas d'exception.
+ *
+ * C'est par exemple le cas quand un greffon Python ne peut se lancer
+ * correctement ; l'exception est alors levée à partir de la fonction
+ * create_python_plugin() et le plantage intervient en sortie d'exécution,
+ * au moment de la libération de l'extension Python :
+ *
+ * ==14939== Jump to the invalid address stated on the next line
+ * ==14939== at 0x1A8FCBC9: ???
+ * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
+ * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
+ * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153)
+ * ==14939== by 0x10AD19: main (main.c:440)
+ * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
+ *
+ * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
+ * appel à PyErr_PrintEx(0) ne change rien.
+ *
+ * La seule différence de l'instruction set_sys_last_vars réside en quelques
+ * lignes dans le code de l'interpréteur Python :
+ *
+ * if (set_sys_last_vars) {
+ * _PySys_SetObjectId(&PyId_last_type, exception);
+ * _PySys_SetObjectId(&PyId_last_value, v);
+ * _PySys_SetObjectId(&PyId_last_traceback, tb);
+ * }
+ *
+ * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
+ * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
+ * à ces éléments.
+ *
+ * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
+ */
+
+ PySys_SetObject("last_type", err_type);
+ PySys_SetObject("last_value", err_value);
+ PySys_SetObject("last_traceback", err_traceback);
+
+ Py_XDECREF(err_traceback);
+ Py_XDECREF(err_value);
+ Py_XDECREF(err_type);
+
+ log_plugin_simple_message(LMT_EXT_ERROR, msg);
+
+ free(msg);
+
+ }
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* FONCTIONS GLOBALES DE CHRYSALIDE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : details = précisions de chargement complémentaires. *
+* *
+* Description : Assure le plein chargement dans un interpréteur Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *details)
+{
+ bool result; /* Bilan à retourner */
+ int ret; /* Bilan de préparatifs */
+ Dl_info info; /* Informations dynamiques */
+ GModule *module; /* Structure de chargement GLib*/
+ GPluginModule *self; /* Représentation interne */
+
+ result = false;
+
+ ret = Py_AtExit(PyExit_pychrysalide);
+ if (ret == -1)
+ {
+ PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
+ goto exit;
+ }
+
+ if (!load_core_components(ACC_ALL_COMPONENTS))
+ {
+ PyErr_SetString(PyExc_SystemError, "unable to load core components.");
+ goto exit;
+ }
+
+ /**
+ * Le module chargé par Python n'apparaît pas dans la liste des greffons de
+ * Chrysalide et ne peut donc pas être référencé comme dépendance par d'autres
+ * extensions.
+ *
+ * Par ailleurs, lors de la recherche d'autres greffons via l'appel à la
+ * fonction init_all_plugins() ci-après, il faut que le nom du greffon soit
+ * déjà réservé pour faire échouer le second chargement du greffon courant
+ * lors du parcours des répertoires conservant les fichiers d'extensions.
+ */
+
+ ret = dladdr(__FUNCTION__, &info);
+ if (ret == 0)
+ {
+ LOG_ERROR_DL_N("dladdr");
+
+ PyErr_SetString(PyExc_SystemError, "failed to force bindings registration.");
+ goto exit;
+
+ }
+
+ module = g_module_open(info.dli_fname, G_MODULE_BIND_LAZY);
+ assert(module != NULL);
+
+ self = details->create_self(module);
+
+ /* A ce stade, le greffon a été chargé correctement */
+ g_plugin_module_override_flags(self, PSF_LOADED);
+
+ register_plugin(self);
+
+ unref_object(self);
+
+ /**
+ * Intégration des fonctionnalités portées par d'autres greffons.
+ */
+
+ result = true;
+
+ init_all_plugins(true);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Point de sortie pour l'initialisation de Python. *
+* *
+* Retour : ? *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void PyExit_pychrysalide(void)
+{
+ unhook_pygobject_behaviour();
+
+ exit_all_plugins();
+
+ unload_core_components(ACC_ALL_COMPONENTS);
+
+}
diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h
new file mode 100644
index 0000000..1758747
--- /dev/null
+++ b/plugins/pychrysalide/bindings.h
@@ -0,0 +1,73 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bindings.h - prototypes pour les éléments d'un socle commun aux fonctionnalités graphiques et non graphiques
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_BINDINGS_H
+#define _PLUGINS_PYCHRYSALIDE_BINDINGS_H
+
+
+/**
+ * Note:
+ * Since Python may define some pre-processor definitions which affect the standard headers
+ * on some systems, you must include Python.h before any standard headers are included.
+ *
+ * cf. https://docs.python.org/3.4/c-api/intro.html
+ */
+#include <Python.h>
+
+
+#include <gmodule.h>
+#include <stdbool.h>
+
+
+#include <plugins/plugin.h>
+
+
+
+/* Charge un module GI dans Python avec une version attendue. */
+bool import_namespace_from_gi_repository(const char *, const char *);
+
+/* Raffinements pour la mise en place du module Python */
+typedef struct _pyinit_details_t
+{
+ bool standalone; /* Chargement depuis Python ? */
+
+ bool (* populate_extra) (void); /* Ajout de types ? */
+
+ /**
+ * Prototype de la fonction de création, à garder synchronisé avec
+ * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h).
+ */
+ GPluginModule * (* create_self) (GModule *);
+
+} pyinit_details_t;
+
+/* Implémente le point d'entrée pour l'initialisation de Python. */
+PyObject *init_python_pychrysalide_module(const pyinit_details_t *);
+
+/* Présente dans le journal une exception survenue. */
+void log_pychrysalide_exception(const char *, ...);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_H */
diff --git a/plugins/pychrysalide/constants.c b/plugins/pychrysalide/constants.c
index 8142284..34caa12 100644
--- a/plugins/pychrysalide/constants.c
+++ b/plugins/pychrysalide/constants.c
@@ -25,7 +25,6 @@
#include "constants.h"
-#include <arch/archbase.h>
#include <common/datatypes.h>
diff --git a/plugins/pychrysalide/convert.c b/plugins/pychrysalide/convert.c
new file mode 100644
index 0000000..c67c8ba
--- /dev/null
+++ b/plugins/pychrysalide/convert.c
@@ -0,0 +1,89 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * convert.c - conversion d'arguments en éléments usuels externes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "convert.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+#ifndef NDEBUG
+# include <stdbool.h>
+#endif
+#include <gio/gio.h>
+
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en instance GSettings. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_gsettings(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+ GType type; /* Type obtenu ou 0 */
+
+ result = PyObject_IsInstance(arg, (PyObject *)&PyGObject_Type);
+
+ if (result == 1)
+ {
+ type = pyg_type_from_object(arg);
+
+ if (type != G_TYPE_SETTINGS)
+ result = 0;
+
+ }
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to GSetting instance");
+ break;
+
+ case 1:
+ *((GSettings **)dst) = G_SETTINGS(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/plugins/translate.h b/plugins/pychrysalide/convert.h
index facffd2..7bdf7da 100644
--- a/plugins/pychrysalide/plugins/translate.h
+++ b/plugins/pychrysalide/convert.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * translate.h - prototypes pour la conversion de structures liées aux greffons en objets Python
+ * convert.h - prototypes pour la conversion d'arguments en éléments usuels externes
*
- * Copyright (C) 2021 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,20 +22,17 @@
*/
-#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H
-#define _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H
+#ifndef _PLUGINS_PYCHRYSALIDE_CONVERT_H
+#define _PLUGINS_PYCHRYSALIDE_CONVERT_H
#include <Python.h>
-#include <plugins/plugin-def.h>
+/* Tente de convertir en instance GSettings. */
+int convert_to_gsettings(PyObject *, void *);
-/* Traduit une description d'interface de greffon. */
-PyObject *translate_plugin_interface_to_python(const plugin_interface *);
-
-
-#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_TRANSLATE_H */
+#endif /* _PLUGINS_PYCHRYSALIDE_CONVERT_H */
diff --git a/plugins/pychrysalide/core-int.h b/plugins/pychrysalide/core-int.h
new file mode 100644
index 0000000..2b8fcc8
--- /dev/null
+++ b/plugins/pychrysalide/core-int.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-int.h - prototypes internes pour le plugin permettant des extensions en Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_CORE_INT_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_INT_H
+
+
+#include "core.h"
+
+
+#include <plugins/native-int.h>
+
+
+
+/* Greffon natif pour la liaison Python de Chrysalide (instance) */
+struct _GPyChrysalidePlugin
+{
+ GNativePlugin parent; /* A laisser en premier */
+
+ PyObject *py_module; /* Réceptacle de chargement */
+
+};
+
+
+/* Greffon natif pour la liaison Python de Chrysalide (classe) */
+struct _GPyChrysalidePluginClass
+{
+ GNativePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un greffon de support Python. */
+bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *, GModule *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_CORE_INT_H */
diff --git a/plugins/pychrysalide/core-ui-int.h b/plugins/pychrysalide/core-ui-int.h
new file mode 100644
index 0000000..7cecc13
--- /dev/null
+++ b/plugins/pychrysalide/core-ui-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui-int.h - prototypes internes pour le plugin permettant des extensions UI en Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H
+
+
+#include "core-ui.h"
+
+
+#include "core-int.h"
+
+
+
+/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (instance) */
+struct _GPyChrysalidePluginUI
+{
+ GPyChrysalidePlugin parent; /* A laisser en premier */
+
+};
+
+
+/* Greffon natif pour la liaison Python de Chrysalide sous forme graphique (classe) */
+struct _GPyChrysalidePluginUIClass
+{
+ GPyChrysalidePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un greffon de support Python. */
+bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *, GModule *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_INT_H */
diff --git a/plugins/pychrysalide/core-ui.c b/plugins/pychrysalide/core-ui.c
new file mode 100644
index 0000000..1b332b7
--- /dev/null
+++ b/plugins/pychrysalide/core-ui.c
@@ -0,0 +1,319 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.c - plugin permettant des extensions UI en Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "core-ui.h"
+
+
+#include <stdbool.h>
+
+
+#include <i18n.h>
+#include <plugins/self.h>
+
+
+#include "bindings.h"
+#include "core-ui-int.h"
+
+
+
+/* Note la nature du chargement */
+static bool _standalone = true;
+
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des greffons de support Python. */
+static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *);
+
+/* Initialise une instance de greffon de support Python. */
+static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *);
+
+/* Supprime toutes les références externes. */
+static void g_pychrysalide_plugin_ui_dispose(GPyChrysalidePluginUI *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_pychrysalide_plugin_ui_finalize(GPyChrysalidePluginUI *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_pychrysalide_plugin_ui_enable(GPyChrysalidePluginUI *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon de liaison Python */
+G_DEFINE_TYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G_TYPE_PYCHRYSALIDE_PLUGIN);
+
+
+NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_ui_new);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons de support Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_class_init(GPyChrysalidePluginUIClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_pychrysalide_plugin_ui_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_pychrysalide_plugin_ui_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_pychrysalide_plugin_ui_enable;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon de support Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_init(GPyChrysalidePluginUI *plugin)
+{
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_dispose(GPyChrysalidePluginUI *plugin)
+{
+ G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_pychrysalide_plugin_ui_finalize(GPyChrysalidePluginUI *plugin)
+{
+ G_OBJECT_CLASS(g_pychrysalide_plugin_ui_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = nom du fichier à charger. *
+* *
+* Description : Crée un module pour un greffon de support Python. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+GPluginModule *g_pychrysalide_plugin_ui_new(GModule *module)
+{
+ GPyChrysalidePluginUI *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN_UI, NULL);
+
+ if (!g_pychrysalide_plugin_ui_create(result, module))
+ g_clear_object(&result);
+
+ return G_PLUGIN_MODULE(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = module système correspondant. *
+* *
+* Description : Met en place un module pour un greffon de support Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_pychrysalide_plugin_ui_create(GPyChrysalidePluginUI *plugin, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_pychrysalide_plugin_create(G_PYCHRYSALIDE_PLUGIN(plugin), module);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de l'activation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_pychrysalide_plugin_ui_enable(GPyChrysalidePluginUI *plugin)
+{
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ int ret; /* Bilan de préparatifs */
+
+ _standalone = false;
+
+ /* Chargement du module pour Python */
+
+ ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalideui);
+
+ if (ret == -1)
+ {
+ g_plugin_module_log_simple_message(G_PLUGIN_MODULE(plugin),
+ LMT_ERROR,
+ _("Can not extend the existing table of Python built-in modules."));
+
+ result = false;
+ goto done;
+
+ }
+
+ Py_Initialize();
+
+ gstate = PyGILState_Ensure();
+
+ G_PYCHRYSALIDE_PLUGIN(plugin)->py_module = PyImport_ImportModule("pychrysalide");
+
+ /**
+ * Pour mémoire, une situation concrête conduisant à un échec :
+ * le paquet python3-gi-dbg n'est pas installé alors que le
+ * programme est compilé en mode débogage.
+ *
+ * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize()
+ * le laisse rien filtrer...
+ *
+ * En mode autonome, le shell Python remonte bien l'erreur par contre.
+ */
+
+ // TODO : check (2025)
+
+ result = (G_PYCHRYSALIDE_PLUGIN(plugin)->py_module != NULL);
+
+ PyGILState_Release(gstate);
+
+ done:
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* POINT D'ENTREE POUR PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Point d'entrée pour l'initialisation de Python. *
+* *
+* Retour : ? *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyMODINIT_FUNC PyInit_pychrysalideui(void)
+{
+ PyObject *result; /* Module Python à retourner */
+ pyinit_details_t details; /* Détails de chargement */
+
+ details.standalone = _standalone;
+
+ details.populate_extra = NULL;
+ details.create_self = g_pychrysalide_plugin_ui_new;
+
+ result = init_python_pychrysalide_module(&details);
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/core-ui.h b/plugins/pychrysalide/core-ui.h
new file mode 100644
index 0000000..658aa19
--- /dev/null
+++ b/plugins/pychrysalide/core-ui.h
@@ -0,0 +1,64 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.h - prototypes pour le plugin permettant des extensions UI en Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_CORE_UI_H
+#define _PLUGINS_PYCHRYSALIDE_CORE_UI_H
+
+
+/**
+ * Note:
+ * Since Python may define some pre-processor definitions which affect the standard headers
+ * on some systems, you must include Python.h before any standard headers are included.
+ *
+ * cf. https://docs.python.org/3.4/c-api/intro.html
+ */
+#include <Python.h>
+
+
+#include <glibext/helpers.h>
+#include <plugins/plugin.h>
+
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+#define G_TYPE_PYCHRYSALIDE_PLUGIN_UI (g_pychrysalide_plugin_ui_get_type())
+
+DECLARE_GTYPE(GPyChrysalidePluginUI, g_pychrysalide_plugin_ui, G, PYCHRYSALIDE_PLUGIN_UI);
+
+
+/* Crée un module pour un greffon de support Python. */
+GPluginModule *g_pychrysalide_plugin_ui_new(GModule *);
+
+
+
+/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */
+
+
+/* Point d'entrée pour l'initialisation de Python. */
+PyMODINIT_FUNC PyInit_pychrysalideui(void);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_CORE_UI_H */
diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index 8d69933..0e72b46 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.c - plugin permettant des extensions en Python
*
- * Copyright (C) 2018-2019 Cyrille Bagard
+ * Copyright (C) 2018-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,6 +21,15 @@
*/
+#include "core.h"
+
+
+#include "core-int.h"
+
+
+
+
+
#undef NO_IMPORT_PYGOBJECT
#include <pygobject.h>
#define NO_IMPORT_PYGOBJECT
@@ -32,9 +41,8 @@
#include <assert.h>
#include <errno.h>
#include <malloc.h>
-#include <pygobject.h>
+//#include <pygobject.h>
#include <stdarg.h>
-#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
@@ -42,741 +50,402 @@
#include <i18n.h>
#include <gleak.h>
-#include <common/cpp.h>
#include <common/environment.h>
#include <common/extstr.h>
#include <core/core.h>
#include <core/logs.h>
#include <core/paths.h>
-#include <plugins/pglist.h>
+#include <plugins/manager-int.h>
+#include <plugins/plugin.h>
#include <plugins/self.h>
+
+
#include "access.h"
-#include "constants.h"
-#include "helpers.h"
-#include "star.h"
-#include "strenum.h"
-#include "struct.h"
-#include "analysis/module.h"
-#include "arch/module.h"
-#include "common/module.h"
-#include "core/module.h"
-#include "glibext/module.h"
-/* #include "debug/module.h" */
-#include "format/module.h"
-/* #ifdef INCLUDE_GTK_SUPPORT */
-/* # include "gtkext/module.h" */
-/* # include "gui/module.h" */
-/* #endif */
-/* #include "mangling/module.h" */
-#include "plugins/module.h"
-#include "plugins/plugin.h"
-
-
-
-DEFINE_CHRYSALIDE_CONTAINER_PLUGIN("PyChrysalide", "Chrysalide bindings to Python",
- PACKAGE_VERSION, CHRYSALIDE_WEBSITE("api/python/pychrysalide"),
- NO_REQ, AL(PGA_PLUGIN_INIT, PGA_PLUGIN_EXIT,
- PGA_NATIVE_PLUGINS_LOADED, PGA_TYPE_BUILDING));
+#include "bindings.h"
+
/* Note la nature du chargement */
static bool _standalone = true;
-/* Réceptacle pour le chargement forcé */
-static PyObject *_chrysalide_module = NULL;
-/* Fournit la révision du programme global. */
-static PyObject *py_chrysalide_revision(PyObject *, PyObject *);
-/* Fournit la version du programme global. */
-static PyObject *py_chrysalide_version(PyObject *, PyObject *);
-/* Fournit la version du greffon pour Python. */
-static PyObject *py_chrysalide_mod_version(PyObject *, PyObject *);
-/* Détermine si l'interpréteur lancé est celui pris en compte. */
-static bool is_current_abi_suitable(void);
-/* Assure une pleine initialisation des objets de Python-GI. */
-static bool install_metaclass_for_python_gobjects(void);
-/* Définit la version attendue de GTK à charger dans Python. */
-#ifdef INCLUDE_GTK_SUPPORT
-static bool set_version_for_gtk_namespace(const char *);
-#endif
-/* Point de sortie pour l'initialisation de Python. */
-static void PyExit_pychrysalide(void);
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des greffons de support Python. */
+static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *);
+
+/* Procède à l'initialisation de l'interface de gestion. */
+static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *);
+
+/* Initialise une instance de greffon de support Python. */
+static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_pychrysalide_plugin_finalize(GPyChrysalidePlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *);
+
+/* Prend acte de la désactivation du greffon. */
+static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *);
+
+
+
+/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */
+
/* Complète les chemins de recherches de Python. */
static void extend_python_path(const char *);
+/* Crée un greffon à partir de code Python. */
+static GPluginModule *create_python_plugin(const char *, const char *);
+
/* Charge autant de greffons composés en Python que possible. */
static void load_python_plugins(GPluginModule *);
-/* Efface un type Python pour greffon de la mémoire. */
-static void free_native_plugin_type(PyTypeObject *);
+/* Prend acte du chargement de l'ensemble des greffons natifs. */
+static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPyChrysalidePlugin *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon de liaison Python */
+G_DEFINE_TYPE_WITH_CODE(GPyChrysalidePlugin, g_pychrysalide_plugin, G_TYPE_NATIVE_PLUGIN,
+ G_IMPLEMENT_INTERFACE(G_TYPE_PLUGIN_MANAGER, g_pychrysalide_plugin_plugin_manager_interface_init));
+
+NATIVE_PLUGIN_ENTRYPOINT(g_pychrysalide_plugin_new);
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : class = classe à initialiser. *
* *
-* Description : Fournit la révision du programme global. *
+* Description : Initialise la classe des greffons de support Python. *
* *
-* Retour : Numéro de révision. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_revision(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_class_init(GPyChrysalidePluginClass *class)
{
- PyObject *result; /* Valeur à retourner */
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
-#define PY_CHRYSALIDE_REVISION_METHOD PYTHON_METHOD_DEF \
-( \
- revision, "/", \
- METH_NOARGS, py_chrysalide, \
- "Provide the revision number of Chrysalide.\n" \
- "\n" \
- "The returned value is provided as a string, for instance: 'r1665'." \
-)
+ object = G_OBJECT_CLASS(class);
- result = PyUnicode_FromString("r" XSTR(REVISION));
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_pychrysalide_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_pychrysalide_plugin_finalize;
- return result;
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_pychrysalide_plugin_enable;
+ plugin->disable = (pg_management_fc)g_pychrysalide_plugin_disable;
}
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : iface = interface GLib à initialiser. *
* *
-* Description : Fournit la version du programme global. *
+* Description : Procède à l'initialisation de l'interface de gestion. *
* *
-* Retour : Numéro de version. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_version(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_plugin_manager_interface_init(GPluginManagerInterface *iface)
{
- PyObject *result; /* Valeur à retourner */
- int major; /* Numéro de version majeur */
- int minor; /* Numéro de version mineur */
- int revision; /* Numéro de révision */
- char version[16]; /* Conservation temporaire */
-
-#define PY_CHRYSALIDE_VERSION_METHOD PYTHON_METHOD_DEF \
-( \
- version, "/", \
- METH_NOARGS, py_chrysalide, \
- "Provide the version number of Chrysalide.\n" \
- "\n" \
- "The returned value is provided as a string, for instance: '1.6.65'." \
-)
-
- major = REVISION / 1000;
- minor = (REVISION - (major * 1000)) / 100;
- revision = REVISION % 100;
-
- snprintf(version, sizeof(version), "%d.%d.%d", major, minor, revision);
-
- result = PyUnicode_FromString(version);
-
- return result;
+ iface->handle_native = (handle_native_plugins_cb)g_pychrysalide_plugin_handle_native_plugins_loaded_event;
}
/******************************************************************************
* *
-* Paramètres : self = NULL car méthode statique. *
-* args = non utilisé ici. *
+* Paramètres : plugin = instance à initialiser. *
* *
-* Description : Fournit la version du greffon pour Python. *
+* Description : Initialise une instance de greffon de support Python. *
* *
-* Retour : Numéro de version. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_chrysalide_mod_version(PyObject *self, PyObject *args)
+static void g_pychrysalide_plugin_init(GPyChrysalidePlugin *plugin)
{
- PyObject *result; /* Valeur à retourner */
- char version[16]; /* Conservation temporaire */
+ STORE_PLUGIN_ABI(plugin);
-#define PY_CHRYSALIDE_MOD_VERSION_METHOD PYTHON_METHOD_DEF \
-( \
- mod_version, "/", \
- METH_NOARGS, py_chrysalide, \
- "Provide the version number of Chrysalide module for Python.\n" \
- "\n" \
- "The returned value is provided as a string, for instance: '0.1.0'." \
-)
-
- snprintf(version, sizeof(version), "%s", _chrysalide_plugin.version);
-
- result = PyUnicode_FromString(version);
-
- return result;
+ plugin->py_module = NULL;
}
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
* *
-* Description : Détermine si l'interpréteur lancé est celui pris en compte. *
+* Description : Supprime toutes les références externes. *
* *
-* Retour : true si l'exécution peut se poursuivre, false sinon. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool is_current_abi_suitable(void)
+static void g_pychrysalide_plugin_dispose(GPyChrysalidePlugin *plugin)
{
- bool result;
- int fds[2];
- int ret;
- char cmds[128];
- char content[64];
- ssize_t got;
-
-#define GRAB_ABI_FLAGS_IN_PYTHON \
- "import sys" "\n" \
- "import os" "\n" \
- "data = bytes(sys.abiflags, 'UTF-8') + b'\\0'" "\n" \
- "os.write(%d, data)" "\n"
-
- result = false;
-
- ret = pipe(fds);
- if (ret == -1)
- {
- perror("pipe()");
- return false;
- }
-
- snprintf(cmds, sizeof(cmds), GRAB_ABI_FLAGS_IN_PYTHON, fds[1]);
-
- ret = PyRun_SimpleString(cmds);
- if (ret != 0) goto icas_exit;
-
- got = read(fds[0], content, sizeof(content));
- if (got < 0)
- {
- perror("read()");
- goto icas_exit;
- }
-
- content[got] = '\0';
-
- result = (strcmp(content, LIBPYTHON_ABI_FLAGS) == 0);
-
- icas_exit:
-
- if (!result)
- PyErr_SetString(PyExc_SystemError, "the ABI flags of the current interpreter do not match " \
- "the ones of the Python library used during the module compilation.");
-
- return result;
+ G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->dispose(G_OBJECT(plugin));
}
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
* *
-* Description : Assure une pleine initialisation des objets de Python-GI. *
+* Description : Procède à la libération totale de la mémoire. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool install_metaclass_for_python_gobjects(void)
+static void g_pychrysalide_plugin_finalize(GPyChrysalidePlugin *plugin)
{
- bool result; /* Bilan à retourner */
- PyObject *gi_types_mod; /* Module Python-GObject */
-
- /**
- * Les extensions Python sont chargées à partir de la fonction load_python_plugins(),
- * qui fait appel à create_python_plugin(). Une instance y est construite via un
- * appel à PyObject_CallFunction() avec la classe spécifiée par l'alias AutoLoad
- * dans le fichier __init__.py présent dans chaque module d'extension.
- *
- * Le constructeur py_plugin_module_new() renvoie in fine à la fonction générique
- * python_abstract_constructor_with_dynamic_gtype(), laquelle conduit à la fonction
- * pygobject_register_class() définie dans <python3-gi>/gi/pygobject-object.c.
- * Le code de cette dernière comprend notamment la portion suivante :
- *
- * [...]
- * Py_SET_TYPE(type, PyGObject_MetaType);
- * [...]
- * if (PyType_Ready(type) < 0) {
- * g_warning ("couldn't make the type `%s' ready", type->tp_name);
- * return;
- * }
- * [...]
- *
- * La fonction PyType_Ready() est définie dans <python3>/Objects/typeobject.c
- * et commence par :
- *
- * int PyType_Ready(PyTypeObject *type)
- * {
- * if (type->tp_flags & Py_TPFLAGS_READY) {
- * assert(_PyType_CheckConsistency(type));
- * return 0;
- * }
- * [...]
- * }
- *
- * La vérification de cohérencce commence par analyser le type et son propre
- * type :
- *
- * - cf. _PyType_CheckConsistency() dans <python3>/Objects/typeobject.c :
- *
- * int _PyType_CheckConsistency(PyTypeObject *type)
- * {
- * [...]
- * CHECK(!_PyObject_IsFreed((PyObject *)type));
- * [...]
- * }
- *
- * - cf. _PyObject_IsFreed() dans <python3>/Objects/object.c :
- *
- * int _PyObject_IsFreed(PyObject *op)
- * {
- * if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
- * return 1;
- * }
- *
- * La fonction _PyMem_IsPtrFreed() recherche entre autres la valeur NULL.
- *
- * Or le type du type est écrasé dans la fonction pygobject_register_class()
- * avec la valeur de la variable PyGObject_MetaType. Cette variable n'est
- * définie qu'à un seul endroit, dans <python3-gi>/gi/gimodule.c :
- *
- * static PyObject *
- * pyg__install_metaclass(PyObject *dummy, PyTypeObject *metaclass)
- * {
- * Py_INCREF(metaclass);
- * PyGObject_MetaType = metaclass;
- * Py_INCREF(metaclass);
- *
- * Py_SET_TYPE(&PyGObject_Type, metaclass);
- *
- * Py_INCREF(Py_None);
- * return Py_None;
- * }
- *
- * Afin de valider la vérification de _PyType_CheckConsistency() pour les
- * modules externes qui entraînent un enregistrement tout en portant le drapeau
- * Py_TPFLAGS_READY (typiquement ceux du répertoire "plugins/python/", il faut
- * initialiser au besoin la variable PyGObject_MetaType.
- *
- * Une ligne suffit donc à enregistrer le type intermédiaire :
- *
- * from _gi import types
- *
- * On simule ici une déclaration similaire si nécessaire
- */
-
- result = false;
-
- if (PyType_CheckExact(&PyGObject_Type))
- {
- gi_types_mod = PyImport_ImportModule("gi.types");
-
- result = (PyErr_Occurred() == NULL);
-
- if (result)
- result = (PyType_CheckExact(&PyGObject_Type) == 0);
-
- Py_XDECREF(gi_types_mod);
-
- }
-
- return result;
+ G_OBJECT_CLASS(g_pychrysalide_plugin_parent_class)->finalize(G_OBJECT(plugin));
}
/******************************************************************************
* *
-* Paramètres : version = idenfiant de la version de GTK à stipuler. *
+* Paramètres : filename = nom du fichier à charger. *
* *
-* Description : Définit la version attendue de GTK à charger dans Python. *
+* Description : Crée un module pour un greffon de support Python. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Adresse de la structure mise en place. *
* *
-* Remarques : - *
+* Remarques : Le transfert de propriétée du module est total. *
* *
******************************************************************************/
-#ifdef INCLUDE_GTK_SUPPORT
-static bool set_version_for_gtk_namespace(const char *version)
-{
- bool result; /* Bilan à retourner */
- PyObject *gi_mod; /* Module Python-GObject */
- PyObject *args; /* Arguments à fournir */
-
- result = false;
-
- /**
- * On cherche ici à éviter le message suivant si on charge 'gi.repository.Gtk' directement :
- *
- *
- * PyGIWarning: Gtk was imported without specifying a version first. \
- * Use gi.require_version('Gtk', '3.0') before import to ensure that the right version gets loaded.
- *
- */
-
- gi_mod = PyImport_ImportModule("gi");
-
- if (gi_mod != NULL)
- {
- args = Py_BuildValue("ss", "Gtk", version);
-
- run_python_method(gi_mod, "require_version", args);
- result = (PyErr_Occurred() == NULL);
+GPluginModule *g_pychrysalide_plugin_new(GModule *module)
+{
+ GPyChrysalidePlugin *result; /* Structure à retourner */
- Py_DECREF(args);
- Py_DECREF(gi_mod);
+ result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL);
- }
+ if (!g_pychrysalide_plugin_create(result, module))
+ g_clear_object(&result);
- return result;
+ return G_PLUGIN_MODULE(result);
}
-#endif
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = module système correspondant. *
* *
-* Description : Point de sortie pour l'initialisation de Python. *
+* Description : Met en place un module pour un greffon de support Python. *
* *
-* Retour : ? *
+* Retour : Bilan de l'opération. *
* *
-* Remarques : - *
+* Remarques : Le transfert de propriétée du module est total. *
* *
******************************************************************************/
-static void PyExit_pychrysalide(void)
+bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module)
{
- assert(_standalone);
-
- /*
- extern void set_current_project(void *project);
+ bool result; /* Bilan à retourner */
- set_current_project(NULL);
- */
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PyChrysalide",
+ "Chrysalide bindings to Python",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("api/python/pychrysalide"),
+ NO_REQ,
+ module);
-#ifdef TRACK_GOBJECT_LEAKS
- remember_gtypes_for_leaks();
-#endif
+ return result;
- exit_all_plugins();
+}
- //unload_all_core_components(true);
-#ifdef TRACK_GOBJECT_LEAKS
- dump_remaining_gtypes();
-#endif
-}
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
-* Paramètres : - *
+* Paramètres : plugin = greffon à manipuler. *
* *
-* Description : Point d'entrée pour l'initialisation de Python. *
+* Description : Prend acte de l'activation du greffon. *
* *
-* Retour : ? *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-#define PYCHRYSALIDE_DOC \
- "PyChrysalide is a module containing Chrysalide's features and designed for Python users.\n" \
- "\n" \
- "The whole API is defined in a single library named 'pychrysalide.so' and can be used in two ways:\n" \
- "* either from the Chrysalide's GUI, by registering hooks or GLib signals;\n" \
- "* or from a shell command line, by setting PYTHONPATH to point to the directory containing the library.\n" \
- "\n" \
- "In both cases, this is a good start point to have a look at already existing plugins to quickly learn " \
- "how the API works.\n" \
- "\n" \
- "These plugins are located in the 'plugins/python' directory.\n" \
- "\n" \
- "The *pychrysalide* module imports the GLib module (version 2.0) from the GI repository at startup."
-
-PyMODINIT_FUNC PyInit_pychrysalide(void)
+static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *plugin)
{
- PyObject *result; /* Module Python à retourner */
- bool status; /* Bilan des inclusions */
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
int ret; /* Bilan de préparatifs */
-#ifdef PYTHON_PACKAGE
- Dl_info info; /* Informations dynamiques */
-#endif
- GPluginModule *self; /* Représentation interne */
- PluginStatusFlags self_flags; /* Fanions à mettre à jour */
-
- static PyMethodDef py_chrysalide_methods[] = {
- PY_CHRYSALIDE_REVISION_METHOD,
- PY_CHRYSALIDE_VERSION_METHOD,
- PY_CHRYSALIDE_MOD_VERSION_METHOD,
- { NULL }
- };
-
- static PyModuleDef py_chrysalide_module = {
-
- .m_base = PyModuleDef_HEAD_INIT,
-
- .m_name = "pychrysalide",
- .m_doc = PYCHRYSALIDE_DOC,
- .m_size = -1,
+ _standalone = false;
- .m_methods = py_chrysalide_methods
+ /* Chargement du module pour Python */
- };
+ ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
- /**
- * Vérification préalable : dans le cas où on est embarqué directement dans
- * un interpréteur Python, le module se charge et termine par charger à leur
- * tour les différentes extensions trouvées, via load_remaning_plugins() puis
- * chrysalide_plugin_on_native_loaded().
- *
- * Lesquelles vont très probablement charger le module pychrysalide.
- *
- * Comme le chargement de ce dernier n'est alors pas encore terminé,
- * Python va relancer cette procédure, et register_access_to_python_module()
- * va détecter un doublon.
- */
+ if (ret == -1)
+ {
+ g_plugin_module_log_simple_message(G_PLUGIN_MODULE(plugin),
+ LMT_ERROR,
+ _("Can not extend the existing table of Python built-in modules."));
- result = get_access_to_python_module(py_chrysalide_module.m_name);
+ result = false;
+ goto done;
- if (result != NULL)
- {
- Py_INCREF(result);
- return result;
}
- if (!is_current_abi_suitable())
- goto exit;
+ Py_Initialize();
- if (pygobject_init(-1, -1, -1) == NULL)
- {
- PyErr_SetString(PyExc_SystemError, "unable to init GObject in Python.");
- goto exit;
- }
+ gstate = PyGILState_Ensure();
- if (!install_metaclass_for_python_gobjects())
- goto exit;
+ plugin->py_module = PyImport_ImportModule("pychrysalide");
/**
- * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil,
- * à savoir des types convertis de façon incomplète. Par exemple, pour une
- * structure GChecksum, le type à l'exécution est :
- *
- * - sans module GLib : [<class 'gobject.GBoxed'>, <class 'object'>]
- *
- * - avec module GLib : [<class 'gi.repository.GLib.Checksum'>, <class 'gi.Boxed'>, <class 'gobject.GBoxed'>, <class 'object'>]
- *
- * Par ailleurs, il est à noter que le message suivant n'apparaît qu'avec
- * la version debug de Python3 (version de python3-gi : 3.42.2-3) :
- *
- * <frozen importlib._bootstrap>:673: ImportWarning: DynamicImporter.exec_module() not found; falling back to load_module()
- *
- * Code de reproduction dans un interpréteur classique :
+ * Pour mémoire, une situation concrête conduisant à un échec :
+ * le paquet python3-gi-dbg n'est pas installé alors que le
+ * programme est compilé en mode débogage.
*
- * import gi
- * gi.require_version('GLib', '2.0')
- * from gi.repository import GLib
+ * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize()
+ * le laisse rien filtrer...
*
+ * En mode autonome, le shell Python remonte bien l'erreur par contre.
*/
- if (!import_namespace_from_gi_repository("GLib", "2.0"))
- goto exit;
-
-#if 0
-#ifdef INCLUDE_GTK_SUPPORT
- if (!set_version_for_gtk_namespace("3.0"))
- goto exit;
-#endif
-#endif
- if (!load_core_components(ACC_GLOBAL_VARS))
- {
- PyErr_SetString(PyExc_SystemError, "unable to load all basic components.");
- goto exit;
- }
-
- /* Mise en place des fonctionnalités offertes */
-
- result = PyModule_Create(&py_chrysalide_module);
+ // TODO : check (2025)
- register_access_to_python_module(py_chrysalide_module.m_name, result);
+ result = (plugin->py_module != NULL);
- status = true;
-
- if (status) status = add_features_module(result);
-
- if (status) status = add_analysis_module(result);
- if (status) status = add_arch_module(result);
- if (status) status = add_common_module(result);
- if (status) status = add_glibext_module(result);
- if (status) status = add_core_module(result);
- /*
- if (status) status = add_debug_module(result);
- */
- if (status) status = add_format_module(result);
- /*
-#ifdef INCLUDE_GTK_SUPPORT
- if (status) status = add_gtkext_module(result);
- if (status) status = add_gui_module(result);
-#endif
- if (status) status = add_mangling_module(result);
- */
- if (status) status = add_plugins_module(result);
-
- /*
- if (status) status = ensure_python_string_enum_is_registered();
- */
- if (status) status = ensure_python_py_struct_is_registered();
-
- if (status) status = define_data_types_constants(result);
-
- if (status) status = populate_analysis_module();
- if (status) status = populate_arch_module();
- if (status) status = populate_glibext_module();
- if (status) status = populate_common_module();
- if (status) status = populate_core_module();
- /*
- if (status) status = populate_debug_module();
- */
- if (status) status = populate_format_module();
- /*
-#ifdef INCLUDE_GTK_SUPPORT
- if (status) status = populate_gtkext_module();
- if (status) status = populate_gui_module();
-#endif
- if (status) status = populate_mangling_module();
- */
- if (status) status = populate_plugins_module();
+ PyGILState_Release(gstate);
- if (!status)
- {
- PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components.");
- Py_DECREF(result);
- result = NULL;
- goto exit;
- }
+ done:
- if (_standalone)
- {
- ret = Py_AtExit(PyExit_pychrysalide);
+ return result;
- if (ret == -1)
- {
- PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function.");
- Py_DECREF(result);
- result = NULL;
- goto exit;
- }
+}
- /**
- * Comme les sources locales sont prioritaires, le fichier "core/global.h"
- * du greffon masque la fonction suivante, issue du corps principal du
- * programme.
- *
- * On la déclare donc à la main.
- */
- /*
- extern void set_batch_mode(void);
-
- set_batch_mode();
- */
-
- /**
- * Si cette extension pour Python est chargée depuis un dépôt Python,
- * elle ne se trouve pas dans le répertoire classique des extensions et
- * n'est donc pas chargée et enregistrée comme attendu.
- *
- * Cet enregistrement est donc forcé ici.
- */
-
-#ifdef PYTHON_PACKAGE
-
- ret = dladdr(__FUNCTION__, &info);
- if (ret == 0)
- {
- LOG_ERROR_DL_N("dladdr");
- Py_DECREF(result);
- result = NULL;
- goto exit;
- }
- self = g_plugin_module_new(info.dli_fname);
- assert(self != NULL);
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de la désactivation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- register_plugin(self);
+static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *plugin)
+{
+ bool result; /* Bilan à retourner */
+ bool standalone; /* Nature du chargement */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
-#endif
+ result = true;
- init_all_plugins(false);
+ /**
+ * Le champ plugin->py_module n'est défini que via la fonction
+ * g_pychrysalide_plugin_enable(), qui n'est pas sollicitée lorsque
+ * le module PyChrysalide est mis en place directement par Python.
+ *
+ * L'analyse de ce champ pour retrouver la situation courante est
+ * plus fiable que celle du champ _standalone, potentiellement
+ * cohérent dans la version UI du greffon et resté à son état
+ * initial ici.
+ */
- lock_plugin_list_for_reading();
+ standalone = (plugin->py_module == NULL);
- self = get_plugin_by_name("PyChrysalide", NULL);
- assert(self != NULL);
+ /**
+ * Si on se trouve embarqué dans un interpréteur Python, le déchargement
+ * des greffons est organisé à partir de la fonction PyExit_pychrysalide(),
+ * directement appelée depuis un contexte Python.
+ *
+ * Un verrou n'est alors pas souhaité ici :
+ *
+ * python3d: ../Python/pystate.c:1687: PyGILState_Ensure: Assertion `gilstate->autoInterpreterState' failed.
+ *
+ * Avec :
+ *
+ * $ python3d --version
+ * Python 3.11.2
+ *
+ */
- self_flags = g_plugin_module_get_flags(self);
- self_flags &= ~(PSF_FAILURE | PSF_LOADED);
- self_flags |= (status ? PSF_LOADED : PSF_FAILURE);
+ if (!standalone)
+ gstate = PyGILState_Ensure();
- g_plugin_module_override_flags(self, self_flags);
+ clear_all_accesses_to_python_modules();
- unlock_plugin_list_for_reading();
+ Py_XDECREF(plugin->py_module);
+ plugin->py_module = NULL;
- load_remaning_plugins();
+ if (!standalone)
+ PyGILState_Release(gstate);
- /**
- * On laisse fuir ici la référence sur self afin d'avoir
- * l'assurance que le greffon se déchargera toujours en dernier.
- *
- * La fuite mémoire est au final évitée dans PyExit_pychrysalide().
- */
+ return result;
- }
+}
- exit:
- if (result == NULL && !_standalone)
- log_pychrysalide_exception("Loading failed");
- return result;
+/* ---------------------------------------------------------------------------------- */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* ---------------------------------------------------------------------------------- */
-}
/******************************************************************************
* *
@@ -812,6 +481,81 @@ static void extend_python_path(const char *path)
/******************************************************************************
* *
+* Paramètres : modname = nom du module à charger. *
+* filename = chemin d'accès au code Python à charger. *
+* *
+* Description : Crée un greffon à partir de code Python. *
+* *
+* Retour : Adresse de la structure mise en place ou NULL si erreur. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GPluginModule *create_python_plugin(const char *modname, const char *filename)
+{
+ GPluginModule *result; /* Structure à retourner */
+ PyObject *name; /* Chemin d'accès pour Python */
+ PyObject *module; /* Script Python chargé */
+ PyObject *dict; /* Dictionnaire associé */
+ PyObject *class; /* Classe à instancier */
+ PyObject *instance; /* Instance Python du greffon */
+
+ name = PyUnicode_FromString(modname);
+ if (name == NULL) goto bad_exit;
+
+ module = PyImport_Import(name);
+ Py_DECREF(name);
+
+ if (module == NULL) goto no_import;
+
+ dict = PyModule_GetDict(module);
+ class = PyDict_GetItemString(dict, "AutoLoad");
+
+ if (class == NULL) goto no_class;
+ if (!PyType_Check(class->ob_type)) goto no_class;
+
+ instance = PyObject_CallFunction(class, NULL);
+ if (instance == NULL) goto no_instance;
+
+ result = G_PLUGIN_MODULE(pygobject_get(instance));
+
+ /**
+ * L'instance Python et l'objet GLib résultant sont un même PyGObject.
+ *
+ * Donc pas besoin de toucher au comptage des références ici, la libération
+ * se réalisera à la fin, quand l'objet GLib sera libéré.
+ */
+
+ Py_DECREF(module);
+
+ return result;
+
+ no_instance:
+
+ log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance"));
+
+ no_class:
+
+ if (class == NULL)
+ log_plugin_simple_message(LMT_ERROR,
+ _("An error occured when looking for the 'AutoLoad': item not found!"));
+
+ no_import:
+
+ Py_XDECREF(module);
+
+ log_pychrysalide_exception(_("An error occured when importing '%s'"), modname);
+
+ bad_exit:
+
+ return NULL;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : plugin = instance représentant le greffon Python d'origine. *
* *
* Description : Charge autant de greffons composés en Python que possible. *
@@ -836,7 +580,6 @@ static void load_python_plugins(GPluginModule *plugin)
char *filename; /* Chemin d'accès reconstruit */
GPluginModule *pyplugin; /* Lien vers un grffon Python */
bool status; /* Bilan d'une opération */
- //GGenConfig *config; /* Configuration à charger */
/* Définition des zones d'influence */
@@ -847,19 +590,19 @@ static void load_python_plugins(GPluginModule *plugin)
#else
edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python");
+
dir = opendir(edir);
- free(edir);
if (dir != NULL)
{
closedir(dir);
- edir = get_effective_directory(PLUGINS_DATA_DIR G_DIR_SEPARATOR_S "python");
extend_python_path(edir);
- free(edir);
}
+ free(edir);
+
#endif
g_plugin_module_log_variadic_message(plugin, LMT_INFO,
@@ -923,8 +666,7 @@ static void load_python_plugins(GPluginModule *plugin)
goto done_with_plugin;
}
- //g_plugin_module_create_config(pyplugin);
-
+ /*
status = g_plugin_module_manage(pyplugin, PGA_PLUGIN_LOADED);
if (!status)
@@ -934,18 +676,19 @@ static void load_python_plugins(GPluginModule *plugin)
g_object_unref(G_OBJECT(pyplugin));
goto done_with_plugin;
}
-
- /*
- config = g_plugin_module_get_config(pyplugin);
- g_generic_config_read(config);
- g_object_unref(G_OBJECT(config));
*/
g_plugin_module_log_variadic_message(plugin, LMT_PROCESS,
_("Loaded the Python plugin found in the '<b>%s</b>' directory"),
filename);
- _register_plugin(pyplugin);
+ printf(" -> BUG // %p\n", pyplugin);
+
+ printf(" -> BUG // %u\n", ((GObject *)pyplugin)->ref_count);
+
+ //register_plugin(pyplugin);
+
+ /////////unref_object(pyplugin);
done_with_plugin:
@@ -965,116 +708,7 @@ static void load_python_plugins(GPluginModule *plugin)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à manipuler. *
-* *
-* Description : Prend acte du chargement du greffon. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
-{
- bool result; /* Bilan à retourner */
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- int ret; /* Bilan de préparatifs */
-
- _standalone = false;
-
- /* Chargement du module pour Python */
-
- ret = PyImport_AppendInittab("pychrysalide", &PyInit_pychrysalide);
-
- if (ret == -1)
- {
- log_plugin_simple_message(LMT_ERROR, _("Can not extend the existing table of Python built-in modules."));
- result = false;
- goto cpi_done;
- }
-
- Py_Initialize();
-
- gstate = PyGILState_Ensure();
-
- _chrysalide_module = PyImport_ImportModule("pychrysalide");
-
- /**
- * Pour mémoire, une situation concrête conduisant à un échec :
- * le paquet python3-gi-dbg n'est pas installé alors que le
- * programme est compilé en mode débogage.
- *
- * Dans ce cas, pygobject_init(-1, -1, -1) échoue, et Py_Initialize()
- * le laisse rien filtrer...
- *
- * En mode autonome, le shell Python remonte bien l'erreur par contre.
- */
-
- result = (_chrysalide_module != NULL);
-
- PyGILState_Release(gstate);
-
- cpi_done:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* *
-* Description : Prend acte du déchargement du greffon. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
-{
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
-
- gstate = PyGILState_Ensure();
-
- clear_all_accesses_to_python_modules();
-
- Py_XDECREF(_chrysalide_module);
-
- PyGILState_Release(gstate);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : type = informations à libérer de la mémoire. *
-* *
-* Description : Efface un type Python pour greffon de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void free_native_plugin_type(PyTypeObject *type)
-{
- free((char *)type->tp_name);
- free((char *)type->tp_doc);
-
- free(type);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
+* Paramètres : plugin = interface à manipuler. *
* *
* Description : Accompagne la fin du chargement des modules natifs. *
* *
@@ -1084,308 +718,49 @@ static void free_native_plugin_type(PyTypeObject *type)
* *
******************************************************************************/
-G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action)
+static void g_pychrysalide_plugin_handle_native_plugins_loaded_event(GPyChrysalidePlugin *plugin)
{
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- size_t count; /* Quantité de greffons chargés*/
- PyObject *module; /* Module à recompléter */
- PyObject *dict; /* Dictionnaire du module */
- GPluginModule **list; /* Ensemble de ces greffons */
- size_t i; /* Boucle de parcours */
- char *name; /* Désignation complète */
- char *doc; /* Description adaptée */
- int ret; /* Bilan d'un appel */
- PyTypeObject *type; /* Nouveau type dynamique */
gstate = PyGILState_Ensure();
- if (action == PGA_NATIVE_PLUGINS_LOADED)
- {
- /* Intégration des greffons natifs en Python */
-
- if (ensure_python_plugin_module_is_registered())
- {
- module = get_access_to_python_module("pychrysalide.plugins");
- assert(module != NULL);
-
- dict = PyModule_GetDict(module);
-
- list = get_all_plugins(&count);
-
- for (i = 0; i < count; i++)
- {
- ret = asprintf(&name, "pychrysalide.plugins.%s", G_OBJECT_TYPE_NAME(list[i]) + 1);
- if (ret == -1)
- {
- LOG_ERROR_N("asprintf");
- continue;
- }
-
- ret = asprintf(&doc, "Place holder for the native plugin %s documentation",
- G_OBJECT_TYPE_NAME(list[i]) + 1);
- if (ret == -1)
- {
- LOG_ERROR_N("asprintf");
- free(name);
- continue;
- }
-
- type = calloc(1, sizeof(PyTypeObject));
-
- type->tp_name = name;
- type->tp_doc = doc;
- type->tp_flags = Py_TPFLAGS_DEFAULT;
- type->tp_new = no_python_constructor_allowed;
-
- if (register_class_for_pygobject(dict, G_OBJECT_TYPE(list[i]), type))
- g_object_set_data_full(G_OBJECT(list[i]), "python_type", type,
- (GDestroyNotify)free_native_plugin_type);
-
- else
- free_native_plugin_type(type);
-
- }
-
- if (list != NULL)
- free(list);
-
- }
-
- /* Chargement des extensions purement Python */
-
- load_python_plugins(plugin);
-
- }
+ load_python_plugins(G_PLUGIN_MODULE(plugin));
PyGILState_Release(gstate);
}
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
-* type = type d'objet à mettre en place. *
-* *
-* Description : Crée une instance à partir d'un type dynamique externe. *
-* *
-* Retour : Instance d'objet gérée par l'extension ou NULL. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type)
-{
- gpointer result; /* Instance à retourner */
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- PyTypeObject *pytype; /* Classe Python concernée */
- PyObject *instance; /* Initialisation forcée */
-
- result = NULL;
-
- gstate = PyGILState_Ensure();
-
- pytype = pygobject_lookup_class(type);
-
- if (pytype != NULL)
- {
- instance = PyObject_CallObject((PyObject *)pytype, NULL);
- assert(instance != NULL);
-
- result = pygobject_get(instance);
-
- }
-
- PyGILState_Release(gstate);
-
- return result;
-
-}
+/* ---------------------------------------------------------------------------------- */
+/* POINT D'ENTREE POUR PYTHON */
+/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
-* Paramètres : namespace = module particulier à charger à partir de gi. *
-* version = idenfiant de la version à stipuler. *
+* Paramètres : - *
* *
-* Description : Charge un module GI dans Python avec une version attendue. *
+* Description : Point d'entrée pour l'initialisation de Python. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : ? *
* *
* Remarques : - *
* *
******************************************************************************/
-bool import_namespace_from_gi_repository(const char *namespace, const char *version)
+PyMODINIT_FUNC PyInit_pychrysalide(void)
{
- bool result; /* Bilan à retourner */
- PyObject *module; /* Module Python-GObject */
- PyObject *args; /* Arguments à fournir */
- int ret; /* Bilan d'une mise en place */
-
- result = false;
-
- /* Sélection d'une version */
-
- module = PyImport_ImportModule("gi");
-
- if (module != NULL)
- {
- args = Py_BuildValue("ss", namespace, version);
-
- run_python_method(module, "require_version", args);
-
- result = (PyErr_Occurred() == NULL);
-
- Py_DECREF(args);
- Py_DECREF(module);
-
- }
-
- /* Importation du module visé */
-
- if (result)
- {
- args = PyTuple_New(1);
-
- ret = PyTuple_SetItem(args, 0, PyUnicode_FromString(namespace));
- if (ret != 0)
- {
- result = false;
- goto args_error;
- }
-
- module = PyImport_ImportModuleEx("gi.repository", NULL, NULL, args);
-
- result = (module != NULL);
-
- Py_XDECREF(module);
+ PyObject *result; /* Module Python à retourner */
+ pyinit_details_t details; /* Détails de chargement */
- args_error:
+ details.standalone = _standalone;
- Py_DECREF(args);
+ details.populate_extra = NULL;
+ details.create_self = g_pychrysalide_plugin_new;
- }
+ result = init_python_pychrysalide_module(&details);
return result;
}
-
-
-/******************************************************************************
-* *
-* Paramètres : prefix = message d'introduction à faire apparaître à l'écran.*
-* *
-* Description : Présente dans le journal une exception survenue. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void log_pychrysalide_exception(const char *prefix, ...)
-{
- va_list ap; /* Compléments argumentaires */
- char *msg; /* Message complet à imprimer */
- PyObject *err_type; /* Type d'erreur Python */
- PyObject *err_value; /* Instance Python d'erreur */
- PyObject *err_traceback; /* Trace Python associée */
- PyObject *err_string; /* Description Python d'erreur */
- const char *err_msg; /* Représentation humaine */
-
- assert(PyGILState_Check() == 1);
-
- if (PyErr_Occurred())
- {
- /* Base de la communication */
-
- va_start(ap, prefix);
-
- vasprintf(&msg, prefix, ap);
-
- va_end(ap);
-
- /* Détails complémentaires */
-
- PyErr_Fetch(&err_type, &err_value, &err_traceback);
-
- PyErr_NormalizeException(&err_type, &err_value, &err_traceback);
-
- if (err_traceback == NULL)
- {
- err_traceback = Py_None;
- Py_INCREF(err_traceback);
- }
-
- PyException_SetTraceback(err_value, err_traceback);
-
- if (err_value == NULL)
- msg = stradd(msg, _(": no extra information is provided..."));
-
- else
- {
- err_string = PyObject_Str(err_value);
- err_msg = PyUnicode_AsUTF8(err_string);
-
- msg = stradd(msg, ": ");
- msg = stradd(msg, err_msg);
-
- Py_DECREF(err_string);
-
- }
-
- /**
- * Bien que la documentation précise que la fonction PyErr_Fetch()
- * transfère la propritété des éléments retournés, la pratique
- * montre que le programme plante à la terminaison en cas d'exception.
- *
- * C'est par exemple le cas quand un greffon Python ne peut se lancer
- * correctement ; l'exception est alors levée à partir de la fonction
- * create_python_plugin() et le plantage intervient en sortie d'exécution,
- * au moment de la libération de l'extension Python :
- *
- * ==14939== Jump to the invalid address stated on the next line
- * ==14939== at 0x1A8FCBC9: ???
- * ==14939== by 0x53DCDB2: g_object_unref (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.5800.3)
- * ==14939== by 0x610F834: on_plugin_ref_toggle (pglist.c:370)
- * ==14939== by 0x610F31A: exit_all_plugins (pglist.c:153)
- * ==14939== by 0x10AD19: main (main.c:440)
- * ==14939== Address 0x1a8fcbc9 is not stack'd, malloc'd or (recently) free'd
- *
- * Curieusement, un appel à PyErr_PrintEx(1) corrige l'effet, alors qu'un
- * appel à PyErr_PrintEx(0) ne change rien.
- *
- * La seule différence de l'instruction set_sys_last_vars réside en quelques
- * lignes dans le code de l'interpréteur Python :
- *
- * if (set_sys_last_vars) {
- * _PySys_SetObjectId(&PyId_last_type, exception);
- * _PySys_SetObjectId(&PyId_last_value, v);
- * _PySys_SetObjectId(&PyId_last_traceback, tb);
- * }
- *
- * L'explication n'est pas encore déterminé : bogue dans Chrysalide ou dans Python ?
- * L'ajout des éléments dans le dictionnaire du module sys ajoute une référence
- * à ces éléments.
- *
- * On reproduit ici le comportement du code correcteur avec PySys_SetObject().
- */
-
- PySys_SetObject("last_type", err_type);
- PySys_SetObject("last_value", err_value);
- PySys_SetObject("last_traceback", err_traceback);
-
- Py_XDECREF(err_traceback);
- Py_XDECREF(err_value);
- Py_XDECREF(err_type);
-
- log_plugin_simple_message(LMT_ERROR, msg);
-
- free(msg);
-
- }
-
-}
diff --git a/plugins/pychrysalide/core.h b/plugins/pychrysalide/core.h
index 5d25d3d..60c6c93 100644
--- a/plugins/pychrysalide/core.h
+++ b/plugins/pychrysalide/core.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.h - prototypes pour le plugin permettant des extensions en Python
*
- * Copyright (C) 2018-2019 Cyrille Bagard
+ * Copyright (C) 2018-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -35,31 +35,29 @@
#include <Python.h>
+#include <glibext/helpers.h>
#include <plugins/plugin.h>
-#include <plugins/plugin-int.h>
-/* Point d'entrée pour l'initialisation de Python. */
-PyMODINIT_FUNC PyInit_pychrysalide(void);
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+#define G_TYPE_PYCHRYSALIDE_PLUGIN (g_pychrysalide_plugin_get_type())
-/* Prend acte du chargement du greffon. */
-G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+DECLARE_GTYPE(GPyChrysalidePlugin, g_pychrysalide_plugin, G, PYCHRYSALIDE_PLUGIN);
-/* Prend acte du déchargement du greffon. */
-G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *);
-/* Accompagne la fin du chargement des modules natifs. */
-G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *, PluginAction);
+/* Crée un module pour un greffon de support Python. */
+GPluginModule *g_pychrysalide_plugin_new(GModule *);
-/* Crée une instance à partir d'un type dynamique externe. */
-G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *, PluginAction, GType);
-/* Charge un module GI dans Python avec une version attendue. */
-bool import_namespace_from_gi_repository(const char *, const char *);
-/* Présente dans le journal une exception survenue. */
-void log_pychrysalide_exception(const char *, ...);
+/* --------------------------- POINT D'ENTREE POUR PYTHON --------------------------- */
+
+
+/* Point d'entrée pour l'initialisation de Python. */
+PyMODINIT_FUNC PyInit_pychrysalide(void);
diff --git a/plugins/pychrysalide/format/executable.c b/plugins/pychrysalide/format/executable.c
index 7d05578..f0d3d6b 100644
--- a/plugins/pychrysalide/format/executable.c
+++ b/plugins/pychrysalide/format/executable.c
@@ -47,9 +47,9 @@
/* Initialise la classe des formats exécutables. */
-static void py_executable_format_init_gclass(GExecutableFormatClass *, gpointer);
+static int py_executable_format_init_gclass(GExecutableFormatClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT, py_executable_format_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_executable_format_init(PyObject *, PyObject *, PyObject *);
@@ -95,23 +95,25 @@ static PyObject *py_executable_format_get_portions(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des formats exécutables. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_executable_format_init_gclass(GExecutableFormatClass *class, gpointer unused)
+static int py_executable_format_init_gclass(GExecutableFormatClass *gclass, PyTypeObject *pyclass)
{
- class->get_machine = py_executable_format_get_target_machine_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_machine, py_executable_format_get_target_machine_wrapper);
- class->get_main_addr = py_executable_format_get_main_address_wrapper;
- class->refine_portions = py_executable_format_refine_portions_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_main_addr, py_executable_format_get_main_address_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->refine_portions, py_executable_format_refine_portions_wrapper);
+
+ return 0;
}
@@ -784,6 +786,8 @@ bool ensure_python_executable_format_is_registered(void)
if (!ensure_python_program_format_is_registered())
return false;
+ pyg_register_class_init(G_TYPE_EXECUTABLE_FORMAT, (PyGClassInitFunc)py_executable_format_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_EXECUTABLE_FORMAT, type))
return false;
diff --git a/plugins/pychrysalide/format/known.c b/plugins/pychrysalide/format/known.c
index 5df2a8f..856c087 100644
--- a/plugins/pychrysalide/format/known.c
+++ b/plugins/pychrysalide/format/known.c
@@ -42,9 +42,9 @@
/* Initialise la classe des descriptions de fichier binaire. */
-static void py_known_format_init_gclass(GKnownFormatClass *, gpointer);
+static int py_known_format_init_gclass(GKnownFormatClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT, py_known_format_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_known_format_init(PyObject *, PyObject *, PyObject *);
@@ -84,23 +84,25 @@ static PyObject *py_known_format_get_content(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des formats connus. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_known_format_init_gclass(GKnownFormatClass *class, gpointer unused)
+static int py_known_format_init_gclass(GKnownFormatClass *gclass, PyTypeObject *pyclass)
{
- class->get_key = py_known_format_get_key_wrapper;
- class->get_desc = py_known_format_get_description_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_key, py_known_format_get_key_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->get_desc, py_known_format_get_description_wrapper);
- class->analyze = py_known_format_analyze_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->analyze, py_known_format_analyze_wrapper);
+
+ return 0;
}
@@ -635,6 +637,8 @@ bool ensure_python_known_format_is_registered(void)
dict = PyModule_GetDict(module);
+ pyg_register_class_init(G_TYPE_KNOWN_FORMAT, (PyGClassInitFunc)py_known_format_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_FORMAT, type))
return false;
diff --git a/plugins/pychrysalide/format/program.c b/plugins/pychrysalide/format/program.c
index 28c1540..57a359a 100644
--- a/plugins/pychrysalide/format/program.c
+++ b/plugins/pychrysalide/format/program.c
@@ -55,9 +55,9 @@
/* Initialise la classe des formats de programmes. */
-static void py_program_format_init_gclass(GProgramFormatClass *, gpointer);
+static int py_program_format_init_gclass(GProgramFormatClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT, py_program_format_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_program_format_init(PyObject *, PyObject *, PyObject *);
@@ -66,7 +66,7 @@ static int py_program_format_init(PyObject *, PyObject *, PyObject *);
static SourceEndian py_program_format_get_endianness_wrapper(const GProgramFormat *);
/* Fournit l'emplacement d'une section donnée. */
-static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *);
+static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *, const char *, mrange_t *);
@@ -86,7 +86,7 @@ static PyObject *py_program_format_has_flag(PyObject *, PyObject *);
/* Fournit l'emplacement d'une section donnée. */
-static PyObject *py_program_format_get_section_range_by_name(PyObject *, PyObject *);
+static PyObject *py_program_format_find_section_range_by_name(PyObject *, PyObject *);
#if 0
@@ -140,21 +140,23 @@ static PyObject *py_program_format_get_errors(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des formats de programmes. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_program_format_init_gclass(GProgramFormatClass *class, gpointer unused)
+static int py_program_format_init_gclass(GProgramFormatClass *gclass, PyTypeObject *pyclass)
{
- class->get_endian = py_program_format_get_endianness_wrapper;
- class->get_range_by_name = py_program_format_get_section_range_by_name_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->get_endian, py_program_format_get_endianness_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->find_range_by_name, py_program_format_find_section_range_by_name_wrapper);
+
+ return 0;
}
@@ -188,7 +190,7 @@ static int py_program_format_init(PyObject *self, PyObject *args, PyObject *kwds
"* pychrysalide.format.ProgramFormat._get_endianness().\n" \
"\n" \
"Other optional method may be defined for new classes:\n" \
- "* pychrysalide.format.ProgramFormat._get_section_range_by_name().\n" \
+ "* pychrysalide.format.ProgramFormat._find_section_range_by_name().\n" \
"\n" \
"Calls to the *__init__* constructor of this abstract object expect" \
" only one argument: a binary content, provided as a" \
@@ -295,7 +297,7 @@ static SourceEndian py_program_format_get_endianness_wrapper(const GProgramForma
* *
******************************************************************************/
-static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range)
+static bool py_program_format_find_section_range_by_name_wrapper(const GProgramFormat *format, const char *name, mrange_t *range)
{
bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
@@ -303,15 +305,15 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo
PyObject *pyret; /* Valeur retournée */
int ret; /* Bilan d'une conversion */
-#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _get_section_range_by_name_wrapper, "$self, name", \
- METH_VARARGS, \
- "Abstract method used to compute the area of a section identified by" \
- " its name.\n" \
- "\n" \
- "The expected returned value is a pychrysalide.arch.mrange instance or" \
- " *None* in case of failure." \
+#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _find_section_range_by_name_wrapper, "$self, name", \
+ METH_VARARGS, \
+ "Abstract method used to compute the area of a section identified by" \
+ " its name.\n" \
+ "\n" \
+ "The expected returned value is a pychrysalide.arch.mrange instance or" \
+ " *None* in case of failure." \
)
result = false;
@@ -320,9 +322,9 @@ static bool py_program_format_get_section_range_by_name_wrapper(const GProgramFo
pyobj = pygobject_new(G_OBJECT(format));
- if (has_python_method(pyobj, "_get_section_range_by_name"))
+ if (has_python_method(pyobj, "_find_section_range_by_name"))
{
- pyret = run_python_method(pyobj, "_get_section_range_by_name", NULL);
+ pyret = run_python_method(pyobj, "_find_section_range_by_name", NULL);
if (pyret != NULL)
{
@@ -520,7 +522,7 @@ static PyObject *py_program_format_has_flag(PyObject *self, PyObject *args)
* *
******************************************************************************/
-static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyObject *args)
+static PyObject *py_program_format_find_section_range_by_name(PyObject *self, PyObject *args)
{
PyObject *result; /* Emplacement à retourner */
const char *name; /* Nom de section ciblée */
@@ -529,9 +531,9 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO
mrange_t range; /* Emplacement obtenu ? */
bool status; /* Bilan de l'opération */
-#define PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \
+#define PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD PYTHON_METHOD_DEF \
( \
- get_section_range_by_name, "$self, name, /", \
+ find_section_range_by_name, "$self, name, /", \
METH_VARARGS, py_program_format, \
"Compute the area of a section identified by its name.\n" \
"\n" \
@@ -546,7 +548,7 @@ static PyObject *py_program_format_get_section_range_by_name(PyObject *self, PyO
format = G_PROGRAM_FORMAT(pygobject_get(self));
- status = g_program_format_get_section_range_by_name(format, name, &range);
+ status = g_program_format_find_section_range_by_name(format, name, &range);
if (status)
result = build_from_internal_mrange(&range);
@@ -1194,8 +1196,8 @@ PyTypeObject *get_python_program_format_type(void)
{
static PyMethodDef py_program_format_methods[] = {
PROGRAM_FORMAT_GET_ENDIANNESS_WRAPPER,
- PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_WRAPPER,
- PROGRAM_FORMAT_GET_SECTION_RANGE_BY_NAME_METHOD,
+ PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_WRAPPER,
+ PROGRAM_FORMAT_FIND_SECTION_RANGE_BY_NAME_METHOD,
/*
PROGRAM_FORMAT_SET_FLAG_METHOD,
PROGRAM_FORMAT_UNSET_FLAG_METHOD,
@@ -1273,6 +1275,8 @@ bool ensure_python_program_format_is_registered(void)
if (!ensure_python_known_format_is_registered())
return false;
+ pyg_register_class_init(G_TYPE_PROGRAM_FORMAT, (PyGClassInitFunc)py_program_format_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_PROGRAM_FORMAT, type))
return false;
diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am
index 8b021bb..af1d9f2 100644
--- a/plugins/pychrysalide/glibext/Makefile.am
+++ b/plugins/pychrysalide/glibext/Makefile.am
@@ -5,12 +5,10 @@ noinst_LTLIBRARIES = libpychrysaglibext.la
# binarycursor.h binarycursor.c \
# buffercache.h buffercache.c \
# bufferline.h bufferline.c \
-# comparison.h comparison.c \
# configuration.h configuration.c \
# linecursor.h linecursor.c \
# linegen.h linegen.c \
-# module.h module.c \
-# singleton.h singleton.c
+# module.h module.c
# if BUILD_GTK_SUPPORT
@@ -22,9 +20,15 @@ noinst_LTLIBRARIES = libpychrysaglibext.la
# endif
libpychrysaglibext_la_SOURCES = \
+ comparable.h comparable.c \
constants.h constants.c \
+ hashable.h hashable.c \
module.h module.c \
+ objhole.h objhole.c \
portion.h portion.c \
+ secstorage.h secstorage.c \
+ singleton.h singleton.c \
+ strbuilder.h strbuilder.c \
work.h work.c \
workqueue.h workqueue.c
diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c
new file mode 100644
index 0000000..e4982d7
--- /dev/null
+++ b/plugins/pychrysalide/glibext/comparable.c
@@ -0,0 +1,482 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable.c - équivalent Python du fichier "glibext/comparable.c"
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "comparable.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/comparable-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Procède à l'initialisation de l'interface de détermination. */
+static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *);
+
+/* Réalise une comparaison étendue entre objets. */
+static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Transmet le statut d'une comparaison effectuée par le parent. */
+static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *);
+
+/* Effectue une comparaison avec un objet 'ComparableObject'. */
+static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* unused = adresse non utilisée ici. *
+* *
+* Description : Procède à l'initialisation de l'interface de détermination. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused)
+{
+#define COMPARABLE_OBJECT_DOC \
+ "The ComparableObject class provides an interface to compare" \
+ " objects.\n" \
+ "\n" \
+ "A typical class declaration for a new implementation looks like:\n" \
+ "\n" \
+ " class NewImplem(GObject.Object, ComparableObject):\n" \
+ " ...\n" \
+ "\n" \
+ "The following method has to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.ComparableObject._compare().\n"
+
+ iface->compare = py_comparable_object_compare_wrapper;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = premier objet à consulter pour une comparaison. *
+* other = second objet à consulter pour une comparaison. *
+* *
+* Description : Réalise une comparaison étendue entre objets. *
+* *
+* Retour : Bilan de la comparaison. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other)
+{
+ int result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *args; /* Arguments pour l'appel */
+ PyObject *pyret; /* Bilan de consultation */
+
+#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _compare, "$self, other", \
+ METH_VARARGS, \
+ "Abstract method allowing to compare two objects implementing" \
+ " the interface. This method is used to handle rich comparisons"\
+ " automatically.\n" \
+ "\n" \
+ "The result has to be an integer lesser than, equal to, or" \
+ " greater than zero if *self* is found, respectively, to be" \
+ " lesser than, to match, or to be greater than *other*.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " an integer." \
+)
+
+ result = 0;
+
+ gstate = PyGILState_Ensure();
+
+ pyobj = pygobject_new(G_OBJECT(object));
+
+ args = PyTuple_New(1);
+ PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other)));
+
+ pyret = run_python_method(pyobj, "_compare", args);
+
+ if (pyret != NULL)
+ {
+ if (PyLong_Check(pyret))
+ result = PyLong_AsLong(pyret);
+
+ else
+ PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer"));
+
+ }
+
+ Py_XDECREF(pyret);
+
+ Py_DECREF(args);
+
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet dont l'instance se veut unique. *
+* args = adresse non utilisée ici. *
+* *
+* Description : Transmet le statut d'une comparaison effectuée par le parent.*
+* *
+* Retour : Bilan de la comparaison. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ GComparableObject *other; /* Second objet à comparer */
+ int ret; /* Bilan de lecture des args. */
+ GComparableObject *object; /* Mécanismes natifs */
+ GComparableObjectInterface *iface; /* Interface utilisée */
+ GComparableObjectInterface *parent_iface; /* Interface parente */
+ int status; /* Bilan d'une comparaison */
+
+#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \
+( \
+ parent_compare, "$sel, otherf", \
+ METH_VARARGS, py_comparable_object, \
+ "Provide the comparison status defined by the interface" \
+ " implementation from the object native parent.\n" \
+ "\n" \
+ "The result is a signed integer.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the object parent does" \
+ " not implement the pychrysalide.glibext.ComparableObject" \
+ " interface.\n" \
+ "\n" \
+ "A *RuntimeError* exception is raised if the direct parent type"\
+ " of the object has not a native implementation. For Python" \
+ " implementations, the super()._compare() function has to be" \
+ " used instead." \
+)
+
+ if (!check_for_native_parent(self))
+ return NULL;
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other);
+ if (!ret) return NULL;
+
+ object = G_COMPARABLE_OBJECT(pygobject_get(self));
+
+ iface = G_COMPARABLE_OBJECT_GET_IFACE(object);
+
+ parent_iface = g_type_interface_peek_parent(iface);
+
+ if (parent_iface == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface"));
+
+ result = NULL;
+
+ }
+ else
+ {
+ status = parent_iface->compare(object, other);
+
+ result = PyLong_FromLong(status);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : a = premier object Python à consulter. *
+* b = second object Python à consulter. *
+* op = type de comparaison menée. *
+* *
+* Description : Effectue une comparaison avec un objet 'ComparableObject'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op)
+{
+ PyObject *result; /* Bilan à retourner */
+ int ret; /* Bilan de lecture des args. */
+ GComparableObject *obj_a; /* Instance à manipuler #1 */
+ GComparableObject *obj_b; /* Instance à manipuler #2 */
+ int status; /* Bilan d'une comparaison */
+
+ ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type());
+ if (!ret)
+ {
+ result = Py_NotImplemented;
+ goto cmp_done;
+ }
+
+ obj_a = G_COMPARABLE_OBJECT(pygobject_get(a));
+ obj_b = G_COMPARABLE_OBJECT(pygobject_get(b));
+
+ status = g_comparable_object_compare(obj_a, obj_b);
+
+ switch (op)
+ {
+ case Py_LT:
+ result = (status < 0 ? Py_True : Py_False);
+ break;
+
+ case Py_LE:
+ result = (status <= 0 ? Py_True : Py_False);
+ break;
+
+ case Py_EQ:
+ result = (status == 0 ? Py_True : Py_False);
+ break;
+
+ case Py_NE:
+ result = (status != 0 ? Py_True : Py_False);
+ break;
+
+ case Py_GT:
+ result = (status > 0 ? Py_True : Py_False);
+ break;
+
+ case Py_GE:
+ result = (status >= 0 ? Py_True : Py_False);
+ break;
+
+ default:
+ assert(false);
+ result = Py_NotImplemented;
+ break;
+
+ }
+
+ cmp_done:
+
+ Py_INCREF(result);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ return result;
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_comparable_object_type(void)
+{
+ static PyMethodDef py_comparable_object_methods[] = {
+ COMPARABLE_OBJECT_COMPARE_WRAPPER,
+ COMPARABLE_OBJECT_PARENT_COMPARE_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_comparable_object_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_comparable_object_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.ComparableObject",
+ .tp_basicsize = sizeof(PyObject),
+
+ /**
+ * Une valeur de .tp_richcompare non nulle écarte la définition du
+ * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type
+ * dans les préparatifs de la fonction Python inherit_slots().
+ *
+ * Ces préparatifs se poursuivent avec type_ready_set_hash(),
+ * qui initialise .tp_hash avec PyObject_HashNotImplemented(),
+ * qui n'est donc pas un comportement par défaut.
+ *
+ * Côté PyGObject, la fonction pygobject_inherit_slots() y voit
+ * une implémentation de .tp_hash personnalisée, ce qui bloque
+ * la défintion d'autres personnalisations, comme celle de
+ * l'interface HashableObject.
+ *
+ * La valeur nominale nulle est ainsi écartée au préalable ici.
+ */
+ .tp_hash = (hashfunc)_Py_HashPointer,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = COMPARABLE_OBJECT_DOC,
+
+ .tp_richcompare = py_comparable_object_richcompare,
+
+ .tp_methods = py_comparable_object_methods,
+ .tp_getset = py_comparable_object_getseters
+
+ };
+
+ return &py_comparable_object_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_comparable_object_is_registered(void)
+{
+ PyTypeObject *type; /* Type 'ComparableObject' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ static GInterfaceInfo info = { /* Paramètres d'inscription */
+
+ .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init,
+ .interface_finalize = NULL,
+ .interface_data = NULL,
+
+ };
+
+ type = get_python_comparable_object_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en interface d'objet comparable. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_comparable_object(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable object");
+ break;
+
+ case 1:
+ *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h
new file mode 100644
index 0000000..d4c6ecf
--- /dev/null
+++ b/plugins/pychrysalide/glibext/comparable.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h"
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_comparable_object_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */
+bool ensure_python_comparable_object_is_registered(void);
+
+/* Tente de convertir en interface d'objet comparable. */
+int convert_to_comparable_object(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */
diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/hashable.c
index 548f700..c870d55 100644
--- a/plugins/pychrysalide/glibext/comparison.c
+++ b/plugins/pychrysalide/glibext/hashable.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison.c - équivalent Python du fichier "glibext/comparison.h"
+ * hashable.c - équivalent Python du fichier "glibext/hashable.c"
*
- * Copyright (C) 2018-2019 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,38 +22,40 @@
*/
-#include "comparison.h"
+#include "hashable.h"
+#include <assert.h>
#include <pygobject.h>
-#include <glibext/comparison-int.h>
+#include <glibext/hashable-int.h>
-#include "constants.h"
#include "../access.h"
#include "../helpers.h"
-#include "../analysis/content.h"
/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
-/* Procède à l'initialisation de l'interface de comparaison. */
-static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *);
+/* Procède à l'initialisation de l'interface de détermination. */
+static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *);
-/* Réalise une comparaison entre objets selon un critère précis. */
-static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *);
+/* Calcule l'empreinte sur 32 bits d'un objet. */
+static guint py_hashable_object_hash_wrapper(const GHashableObject *);
/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
-/* Effectue une comparaison avec un objet 'ComparableItem'. */
-static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int);
+/* Transmet l'empreinte d'un objet calculée par son parent. */
+static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *);
+
+/* Calcule l'empreinte sur 32 bits d'un objet. */
+static Py_hash_t py_hashable_object_hash(PyObject *);
@@ -67,7 +69,7 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int);
* Paramètres : iface = interface GLib à initialiser. *
* unused = adresse non utilisée ici. *
* *
-* Description : Procède à l'initialisation de l'interface de comparaison. *
+* Description : Procède à l'initialisation de l'interface de détermination. *
* *
* Retour : - *
* *
@@ -75,72 +77,78 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int);
* *
******************************************************************************/
-static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused)
+static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused)
{
-
-#define COMPARABLE_ITEM_DOC \
- "ComparableItem provides an interface to compare objects.\n" \
+#define HASHABLE_OBJECT_DOC \
+ "The HashableObject class defines a interface ensuring that a" \
+ " customized hashing method is available for an object.\n" \
"\n" \
"A typical class declaration for a new implementation looks like:\n" \
"\n" \
- " class NewImplem(GObject.Object, ComparableItem):\n" \
+ " class NewImplem(GObject.Object, HashableObject):\n" \
" ...\n" \
- "\n"
+ "\n" \
+ "The following method has to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.HashableObject._hash().\n"
- iface->cmp_rich = py_comparable_item_compare_rich;
+ iface->hash = py_hashable_object_hash_wrapper;
}
/******************************************************************************
* *
-* Paramètres : item = premier objet à cnsulter pour une comparaison. *
-* other = second objet à cnsulter pour une comparaison. *
-* op = opération de comparaison à réaliser. *
-* status = bilan des opérations de comparaison. [OUT] *
+* Paramètres : object = objet dont l'instance est à consulter. *
* *
-* Description : Réalise une comparaison entre objets selon un critère précis.*
+* Description : Calcule l'empreinte sur 32 bits d'un objet. *
* *
-* Retour : true si la comparaison a pu être effectuée, false sinon. *
+* Retour : Valeur de représentation, unique pour l'objet ou non. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status)
+static guint py_hashable_object_hash_wrapper(const GHashableObject *object)
{
- bool result; /* Etat à retourner */
+ guint result; /* Valeur à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- PyObject *pyitem; /* Objet Python concerné #1 */
- PyObject *pyother; /* Objet Python concerné #2 */
+ PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan de consultation */
- int ret; /* Bilan d'une conversion */
- result = false;
+#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _hash, "$self", \
+ METH_NOARGS, \
+ "Abstract method computing a hash from an object which is used" \
+ " as the default implementation of the __hash__() method.\n" \
+ "\n" \
+ "The result has to be an unsigned integer.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " an integer." \
+)
+
+ result = 0;
gstate = PyGILState_Ensure();
- pyitem = pygobject_new(G_OBJECT(item));
- pyother = pygobject_new(G_OBJECT(other));
+ pyobj = pygobject_new(G_OBJECT(object));
- pyret = PyObject_RichCompare(pyitem, pyother, op);
+ pyret = run_python_method(pyobj, "_hash", NULL);
if (pyret != NULL)
{
- ret = PyBool_Check(pyret);
+ if (PyLong_Check(pyret))
+ result = PyLong_AsUnsignedLong(pyret);
- if (ret)
- {
- *status = (pyret == Py_True);
- result = true;
- }
-
- Py_DECREF(pyret);
+ else
+ PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer"));
}
- Py_DECREF(pyother);
- Py_DECREF(pyitem);
+ Py_XDECREF(pyret);
+
+ Py_DECREF(pyobj);
PyGILState_Release(gstate);
@@ -157,47 +165,97 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G
/******************************************************************************
* *
-* Paramètres : a = premier object Python à consulter. *
-* b = second object Python à consulter. *
-* op = type de comparaison menée. *
+* Paramètres : self = objet dont l'instance se veut unique. *
+* args = adresse non utilisée ici. *
* *
-* Description : Effectue une comparaison avec un objet 'ComparableItem'. *
+* Description : Transmet l'empreinte d'un objet calculée par son parent. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Valeur de représentation, unique pour l'objet ou non. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op)
+static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args)
{
- PyObject *result; /* Bilan à retourner */
- int ret; /* Bilan de lecture des args. */
- GComparableItem *item_a; /* Instance à manipuler #1 */
- GComparableItem *item_b; /* Instance à manipuler #2 */
- bool valid; /* Indication de validité */
- bool status; /* Résultat d'une comparaison */
-
- ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type());
- if (!ret)
+ PyObject *result; /* Valeur à retourner */
+ GHashableObject *object; /* Mécanismes natifs */
+ GHashableObjectInterface *iface; /* Interface utilisée */
+ GHashableObjectInterface *parent_iface; /* Interface parente */
+ guint hash; /* Valeur d'empreitne */
+
+#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \
+( \
+ parent_hash, "$self", \
+ METH_NOARGS, py_hashable_object, \
+ "Provide the hash value defined by the interface implementation"\
+ " from the object native parent.\n" \
+ "\n" \
+ "The result is an unsigned integer.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the object parent does" \
+ " not implement the pychrysalide.glibext.HashableObject" \
+ " interface.\n" \
+ "\n" \
+ "A *RuntimeError* exception is raised if the direct parent type"\
+ " of the object has not a native implementation. For Python" \
+ " implementations, the super()._hash() function has to be used" \
+ " instead." \
+)
+
+ if (!check_for_native_parent(self))
+ return NULL;
+
+ object = G_HASHABLE_OBJECT(pygobject_get(self));
+
+ iface = G_HASHABLE_OBJECT_GET_IFACE(object);
+
+ parent_iface = g_type_interface_peek_parent(iface);
+
+ if (parent_iface == NULL)
{
- result = Py_NotImplemented;
- goto cmp_done;
+ PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface"));
+
+ result = NULL;
+
}
+ else
+ {
+ hash = parent_iface->hash(object);
- item_a = G_COMPARABLE_ITEM(pygobject_get(a));
- item_b = G_COMPARABLE_ITEM(pygobject_get(b));
+ result = PyLong_FromUnsignedLong(hash);
- valid = g_comparable_item_compare_rich(item_a, item_b, op, &status);
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ }
+
+ return result;
+
+}
- if (valid)
- result = status ? Py_True : Py_False;
- else
- result = Py_NotImplemented;
- cmp_done:
+/******************************************************************************
+* *
+* Paramètres : self = objet manipulé ici. *
+* *
+* Description : Calcule l'empreinte sur 32 bits d'un objet. *
+* *
+* Retour : Valeur de représentation, unique pour l'objet ou non. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- Py_INCREF(result);
+static Py_hash_t py_hashable_object_hash(PyObject *self)
+{
+ Py_hash_t result; /* Empreinte à retourner */
+ GHashableObject *object; /* Mécanismes natifs */
+
+ object = G_HASHABLE_OBJECT(pygobject_get(self));
+
+ result = g_hashable_object_hash(object);
+
+ UPDATE_RESULT_IF_RAISED_EXCEPTION(-1);
return result;
@@ -216,44 +274,46 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op
* *
******************************************************************************/
-PyTypeObject *get_python_comparable_item_type(void)
+PyTypeObject *get_python_hashable_object_type(void)
{
- static PyMethodDef py_comparable_item_methods[] = {
+ static PyMethodDef py_hashable_object_methods[] = {
+ HASHABLE_OBJECT_HASH_WRAPPER,
+ HASHABLE_OBJECT_PARENT_HASH_METHOD,
{ NULL }
};
- static PyGetSetDef py_comparable_item_getseters[] = {
+ static PyGetSetDef py_hashable_object_getseters[] = {
{ NULL }
};
- static PyTypeObject py_comparable_item_type = {
+ static PyTypeObject py_hashable_object_type = {
PyVarObject_HEAD_INIT(NULL, 0)
- .tp_name = "pychrysalide.glibext.ComparableItem",
+ .tp_name = "pychrysalide.glibext.HashableObject",
.tp_basicsize = sizeof(PyObject),
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_hash = py_hashable_object_hash,
- .tp_doc = COMPARABLE_ITEM_DOC,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
- .tp_richcompare = py_comparable_item_richcompare,
+ .tp_doc = HASHABLE_OBJECT_DOC,
- .tp_methods = py_comparable_item_methods,
- .tp_getset = py_comparable_item_getseters,
+ .tp_methods = py_hashable_object_methods,
+ .tp_getset = py_hashable_object_getseters,
};
- return &py_comparable_item_type;
+ return &py_hashable_object_type;
}
/******************************************************************************
* *
-* Paramètres : module = module dont la définition est à compléter. *
+* Paramètres : - *
* *
-* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. *
+* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. *
* *
* Retour : Bilan de l'opération. *
* *
@@ -261,21 +321,21 @@ PyTypeObject *get_python_comparable_item_type(void)
* *
******************************************************************************/
-bool ensure_python_comparable_item_is_registered(void)
+bool ensure_python_hashable_object_is_registered(void)
{
- PyTypeObject *type; /* Type Python 'ComparableItem' */
+ PyTypeObject *type; /* Type Python 'HashableObject'*/
PyObject *module; /* Module à recompléter */
PyObject *dict; /* Dictionnaire du module */
static GInterfaceInfo info = { /* Paramètres d'inscription */
- .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init,
+ .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init,
.interface_finalize = NULL,
.interface_data = NULL,
};
- type = get_python_comparable_item_type();
+ type = get_python_hashable_object_type();
if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
{
@@ -283,10 +343,7 @@ bool ensure_python_comparable_item_is_registered(void)
dict = PyModule_GetDict(module);
- if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info))
- return false;
-
- if (!define_comparable_item_constants(type))
+ if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info))
return false;
}
@@ -301,7 +358,7 @@ bool ensure_python_comparable_item_is_registered(void)
* Paramètres : arg = argument quelconque à tenter de convertir. *
* dst = destination des valeurs récupérées en cas de succès. *
* *
-* Description : Tente de convertir en élément comparable. *
+* Description : Tente de convertir en interface d'objet réductible. *
* *
* Retour : Bilan de l'opération, voire indications supplémentaires. *
* *
@@ -309,11 +366,11 @@ bool ensure_python_comparable_item_is_registered(void)
* *
******************************************************************************/
-int convert_to_comparable_item(PyObject *arg, void *dst)
+int convert_to_hashable_object(PyObject *arg, void *dst)
{
int result; /* Bilan à retourner */
- result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type());
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type());
switch (result)
{
@@ -323,11 +380,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst)
break;
case 0:
- PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item");
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object");
break;
case 1:
- *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg));
+ *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg));
break;
default:
diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h
index 79f7092..8583118 100644
--- a/plugins/pychrysalide/glibext/comparison.h
+++ b/plugins/pychrysalide/glibext/hashable.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h"
+ * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h"
*
- * Copyright (C) 2018 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,8 +22,8 @@
*/
-#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H
-#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H
#include <Python.h>
@@ -32,14 +32,14 @@
/* Fournit un accès à une définition de type à diffuser. */
-PyTypeObject *get_python_comparable_item_type(void);
+PyTypeObject *get_python_hashable_object_type(void);
-/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */
-bool ensure_python_comparable_item_is_registered(void);
+/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */
+bool ensure_python_hashable_object_is_registered(void);
-/* Tente de convertir en élément comparable. */
-int convert_to_comparable_item(PyObject *, void *);
+/* Tente de convertir en interface d'objet réductible. */
+int convert_to_hashable_object(PyObject *, void *);
-#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */
diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c
index e62d587..6ce0709 100644
--- a/plugins/pychrysalide/glibext/module.c
+++ b/plugins/pychrysalide/glibext/module.c
@@ -33,15 +33,19 @@
#include "buffercache.h"
#include "bufferline.h"
#include "bufferview.h"
-#include "comparison.h"
#include "configuration.h"
#include "linecursor.h"
#include "linegen.h"
#include "loadedpanel.h"
#include "named.h"
-#include "singleton.h"
*/
+#include "comparable.h"
+#include "hashable.h"
+#include "objhole.h"
#include "portion.h"
+#include "secstorage.h"
+#include "singleton.h"
+#include "strbuilder.h"
#include "work.h"
#include "workqueue.h"
#include "../helpers.h"
@@ -110,12 +114,19 @@ bool populate_glibext_module(void)
result = true;
+ if (result) result = ensure_python_comparable_object_is_registered();
+ if (result) result = ensure_python_hashable_object_is_registered();
+ if (result) result = ensure_python_singleton_candidate_is_registered();
+ if (result) result = ensure_python_string_builder_is_registered();
+
+ if (result) result = ensure_python_thick_object_is_registered();
if (result) result = ensure_python_binary_portion_is_registered();
if (result) result = ensure_python_generic_work_is_registered();
+ if (result) result = ensure_python_secret_storage_is_registered();
+ if (result) result = ensure_python_singleton_factory_is_registered();
if (result) result = ensure_python_work_queue_is_registered();
/*
- if (result) result = ensure_python_singleton_candidate_is_registered();
if (result) result = ensure_python_binary_cursor_is_registered();
if (result) result = ensure_python_buffer_cache_is_registered();
@@ -123,7 +134,6 @@ bool populate_glibext_module(void)
#ifdef INCLUDE_GTK_SUPPORT
if (result) result = ensure_python_buffer_view_is_registered();
#endif
- if (result) result = ensure_python_comparable_item_is_registered();
if (result) result = ensure_python_config_param_is_registered();
if (result) result = ensure_python_config_param_iterator_is_registered();
if (result) result = ensure_python_generic_config_is_registered();
@@ -133,7 +143,6 @@ bool populate_glibext_module(void)
if (result) result = ensure_python_loaded_panel_is_registered();
if (result) result = ensure_python_named_widget_is_registered();
#endif
- if (result) result = ensure_python_singleton_factory_is_registered();
*/
assert(result);
diff --git a/plugins/pychrysalide/glibext/objhole.c b/plugins/pychrysalide/glibext/objhole.c
new file mode 100644
index 0000000..2a3ad6f
--- /dev/null
+++ b/plugins/pychrysalide/glibext/objhole.c
@@ -0,0 +1,329 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * objhole.c - équivalent Python du fichier "glibext/objhole.c"
+ *
+ * Copyright (C) 2024 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "objhole.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <glibext/objhole-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(thick_object, G_TYPE_THICK_OBJECT);
+
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_thick_object_init(PyObject *, PyObject *, PyObject *);
+
+/* Indique le nombre de bits accaparés par la GLib. */
+static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *, void *);
+
+/* Fournit la valeur courante de la zone de stockage d'un objet. */
+static PyObject *py_thick_object_get_extra(PyObject *, void *);
+
+/* Définit la valeur courante de la zone de stockage d'un objet. */
+static int py_thick_object_set_extra(PyObject *, PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_thick_object_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define THICK_OBJECT_DOC \
+ "The ThickObject class is an alternative version of the GObject class." \
+ " It provides an access to a 32-bit wide memory hole inside the native" \
+ " GObject structure in order to exploit the area as various values" \
+ " storage.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " ThickObject()"
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique le nombre de bits accaparés par la GLib. *
+* *
+* Retour : Nombre de bits, à priori inférieur à 32. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_thick_object_get__GOBJECT_RESERVED_EXTRA_BITS(PyObject *self, void *closure)
+{
+ PyObject *result; /* Résultat à retourner */
+
+#define THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ _GOBJECT_RESERVED_EXTRA_BITS, py_thick_object, \
+ "Quantity of lower bits used by the GLib inside the memory hole.\n" \
+ "\n" \
+ "The returned value should be less then 32 and is provided for" \
+ " pychrysalide.glibext.ThickObject subclass implementation." \
+)
+
+ result = PyLong_FromUnsignedLong(GOBJECT_RESERVED_EXTRA_BITS);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit la valeur courante de la zone de stockage d'un objet.*
+* *
+* Retour : Valeur de 32 lues et expurgées des bits GLib. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_thick_object_get_extra(PyObject *self, void *closure)
+{
+ PyObject *result; /* Résultat à retourner */
+ GThickObject *obj; /* Version GLib de l'instance */
+ guint extra; /* Valeur à transmettre */
+
+#define THICK_OBJECT_EXTRA_ATTRIB PYTHON_GETSET_DEF_FULL \
+( \
+ extra, py_thick_object, \
+ "Data stored inside the structure memory hole of all GObject" \
+ " native instances.\n" \
+ "\n" \
+ "Less than 32 bits are available for storing arbitrary values." \
+)
+
+ obj = G_THICK_OBJECT(pygobject_get(self));
+
+ extra = g_thick_object_get_extra(obj);
+
+ result = PyLong_FromUnsignedLong(extra);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* value = valeur fournie à intégrer ou prendre en compte. *
+* closure = non utilisé ici. *
+* *
+* Description : Définit la valeur courante de la zone de stockage d'un objet.*
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_thick_object_set_extra(PyObject *self, PyObject *value, void *closure)
+{
+ unsigned long extra; /* Valeur à inscrire */
+ GThickObject *obj; /* Version GLib de l'instance */
+
+ if (!PyLong_Check(value))
+ {
+ PyErr_SetString(PyExc_TypeError, _("Invalid integer value"));
+ return -1;
+ }
+
+ extra = PyLong_AsUnsignedLong(value);
+
+ obj = G_THICK_OBJECT(pygobject_get(self));
+
+ g_thick_object_set_extra(obj, extra);
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_thick_object_type(void)
+{
+ static PyMethodDef py_thick_object_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_thick_object_getseters[] = {
+ THICK_OBJECT__GOBJECT_RESERVED_EXTRA_BITS_ATTRIB,
+ THICK_OBJECT_EXTRA_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_thick_object_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.ThickObject",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = THICK_OBJECT_DOC,
+
+ .tp_methods = py_thick_object_methods,
+ .tp_getset = py_thick_object_getseters,
+
+ .tp_init = py_thick_object_init,
+ .tp_new = py_thick_object_new,
+
+ };
+
+ return &py_thick_object_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.ThickObject'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_thick_object_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'ThickObject' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_thick_object_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_THICK_OBJECT, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en objet dense. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_thick_object(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_thick_object_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to thick object");
+ break;
+
+ case 1:
+ *((GThickObject **)dst) = G_THICK_OBJECT(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/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/secstorage.c b/plugins/pychrysalide/glibext/secstorage.c
new file mode 100644
index 0000000..b5adb7c
--- /dev/null
+++ b/plugins/pychrysalide/glibext/secstorage.c
@@ -0,0 +1,624 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage.c - équivalent Python du fichier "glibext/secstorage.c"
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "secstorage.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/secstorage-int.h>
+
+
+#include "../access.h"
+#include "../convert.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+CREATE_DYN_CONSTRUCTOR(secret_storage, G_TYPE_SECRET_STORAGE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_secret_storage_init(PyObject *, PyObject *, PyObject *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Définit un mot de passe pour protéger une clef maître. */
+static PyObject *py_secret_storage_set_password(PyObject *, PyObject *);
+
+/* Déverrouille la clef de chiffrement maître. */
+static PyObject *py_secret_storage_unlock(PyObject *, PyObject *);
+
+/* Verrouille la clef de chiffrement maître. */
+static PyObject *py_secret_storage_lock(PyObject *, PyObject *);
+
+/* Chiffre des données avec la clef de chiffrement maître. */
+static PyObject *py_secret_storage_encrypt_data(PyObject *, PyObject *);
+
+/* Déchiffre des données avec la clef de chiffrement maître. */
+static PyObject *py_secret_storage_decrypt_data(PyObject *, PyObject *);
+
+/* Détermine si une clef de chiffrement protégée est en place. */
+static PyObject *py_secret_storage_has_key(PyObject *, void *);
+
+/* Détermine si la clef de chiffrement maître est vérouillée. */
+static PyObject *py_secret_storage_is_locked(PyObject *, void *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_secret_storage_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GSettings *settings; /* Configuration à considérer */
+ int ret; /* Bilan de lecture des args. */
+ GSecretStorage *storage; /* Stockage natif à manipuler */
+
+#define SECRET_STORAGE_DOC \
+ "SecretStorage acts as guardian for secrets using ecryption," \
+ " mainly for sensitive information stored as configuration" \
+ " parameters.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " SecretStorage(settings)" \
+ "\n" \
+ "The *settings* arguement must point to a GSettings intance;" \
+ " the main configuration settings are used by default." \
+
+ settings = NULL;
+
+ ret = PyArg_ParseTuple(args, "|O&", convert_to_gsettings, &settings);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ if (!g_secret_storage_create(storage, settings))
+ return -1;
+
+ return 0;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Définit un mot de passe pour protéger une clef maître. *
+* *
+* Retour : Bilan de la mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_set_password(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *passwd; /* Mot de passe associé */
+ int ret; /* Bilan de lecture des args. */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+
+#define SECRET_STORAGE_SET_PASSWORD_METHOD PYTHON_METHOD_DEF \
+( \
+ set_password, "/, password=''", \
+ METH_VARARGS, py_secret_storage, \
+ "Create a master key used for protecting secrets. This key is" \
+ " itself protected by the provided password.\n" \
+ "\n" \
+ "The supplied *password* has to be a string.\n" \
+ "\n" \
+ "The result is a boolean status: *True* if the operation successed,"\
+ " *False* otherwise." \
+)
+
+ passwd = "";
+
+ ret = PyArg_ParseTuple(args, "|s", &passwd);
+ if (!ret) return NULL;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_set_password(storage, passwd);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Déverrouille la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_unlock(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *passwd; /* Mot de passe associé */
+ int ret; /* Bilan de lecture des args. */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+
+#define SECRET_STORAGE_UNLOCK_METHOD PYTHON_METHOD_DEF \
+( \
+ unlock, "/, password=''", \
+ METH_VARARGS, py_secret_storage, \
+ "Decrypt in memory the master key used for protecting secrets.\n" \
+ "\n" \
+ "The supplied *password* is the primary password used to protect" \
+ " this key.\n" \
+ "\n" \
+ "The result is a boolean status: *True* if the operation successed" \
+ " or if the master key is already unlocked, *False* otherwise." \
+)
+
+ passwd = "";
+
+ ret = PyArg_ParseTuple(args, "|s", &passwd);
+ if (!ret) return NULL;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_unlock(storage, passwd);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Verrouille la clef de chiffrement maître. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_lock(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+
+#define SECRET_STORAGE_LOCK_METHOD PYTHON_METHOD_DEF \
+( \
+ lock, "", \
+ METH_NOARGS, py_secret_storage, \
+ "Clear from memory the master key used for protecting secrets." \
+)
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ g_secret_storage_lock(storage);
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Chiffre des données avec la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_encrypt_data(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *data_in; /* Données d'entrée à chiffrer */
+ Py_ssize_t size_in; /* Quantité de ces données */
+ int ret; /* Bilan de lecture des args. */
+ sized_binary_t in; /* Données à chiffer */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+ sized_binary_t out; /* Données chiffrées */
+
+#define SECRET_STORAGE_ENCRYPT_DATA_METHOD PYTHON_METHOD_DEF \
+( \
+ encrypt_data, "data", \
+ METH_VARARGS, py_secret_storage, \
+ "Encrypt data using an unlocked the master key.\n" \
+ "\n" \
+ "The *data* arguement points to bytes to process." \
+ "\n" \
+ "The result is either encrypted *data* as bytes in case of success,"\
+ " or *None* in case of failure." \
+)
+
+ ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in);
+ if (!ret) return NULL;
+
+ in.static_data = data_in;
+ in.size = size_in;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_encrypt_data(storage, &in, &out);
+
+ if (status)
+ {
+ result = PyBytes_FromStringAndSize(out.static_data, out.size);
+ exit_sized_binary(&out);
+ }
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Déchiffre des données avec la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_decrypt_data(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Conversion à retourner */
+ const char *data_in; /* Données d'entrée à chiffrer */
+ Py_ssize_t size_in; /* Quantité de ces données */
+ int ret; /* Bilan de lecture des args. */
+ sized_binary_t in; /* Données à chiffer */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de situation */
+ sized_binary_t out; /* Données chiffrées */
+
+#define SECRET_STORAGE_DECRYPT_DATA_METHOD PYTHON_METHOD_DEF \
+( \
+ decrypt_data, "data", \
+ METH_VARARGS, py_secret_storage, \
+ "Decrypt data using an unlocked the master key.\n" \
+ "\n" \
+ "The *data* arguement points to bytes to process." \
+ "\n" \
+ "The result is either decrypted *data* as bytes in case of success,"\
+ " or *None* in case of failure." \
+)
+
+ ret = PyArg_ParseTuple(args, "s#", &data_in, &size_in);
+ if (!ret) return NULL;
+
+ in.static_data = data_in;
+ in.size = size_in;
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_decrypt_data(storage, &in, &out);
+
+ if (status)
+ {
+ result = PyBytes_FromStringAndSize(out.static_data, out.size);
+ exit_sized_binary(&out);
+ }
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Détermine si une clef de chiffrement protégée est en place. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_has_key(PyObject *self, void *closure)
+{
+ PyObject *result; /* Instance Python à retourner */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de consultation */
+
+#define PY_SECRET_STORAGE_HAS_KEY_ATTRIB PYTHON_HAS_DEF_FULL \
+( \
+ key, py_secret_storage, \
+ "Indicate if a master key used for protecting secrets seems to have"\
+ " been defined. Without any unlocking attempt, the test only relies"\
+ " on the length of saved master data.\n" \
+ "\n" \
+ "The returned status is a boolean status: *True* if the master key" \
+ " seems to exist, *False* otherwise." \
+)
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_has_key(storage);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Détermine si la clef de chiffrement maître est vérouillée. *
+* *
+* Retour : Bilan de la détermination. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_secret_storage_is_locked(PyObject *self, void *closure)
+{
+ PyObject *result; /* Instance Python à retourner */
+ GSecretStorage *storage; /* Stockage sécurisé visé */
+ bool status; /* Bilan de consultation */
+
+#define SECRET_STORAGE_IS_LOCKED_ATTRIB PYTHON_IS_DEF_FULL \
+( \
+ locked, py_secret_storage, \
+ "Indicate if the master key used for protecting secrets is" \
+ " currently decrypted in memory.\n" \
+ "\n" \
+ "The *settings* arguement must point to a GSettings intance; the" \
+ " main configuration settings are used by default.\n" \
+ "\n" \
+ "The returned status is a boolean status: *True* if the master key" \
+ " is unlocked and ready for use, *False* otherwise." \
+)
+
+ storage = G_SECRET_STORAGE(pygobject_get(self));
+
+ status = g_secret_storage_is_locked(storage);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_secret_storage_type(void)
+{
+ static PyMethodDef py_secret_storage_methods[] = {
+ SECRET_STORAGE_SET_PASSWORD_METHOD,
+ SECRET_STORAGE_UNLOCK_METHOD,
+ SECRET_STORAGE_LOCK_METHOD,
+ SECRET_STORAGE_ENCRYPT_DATA_METHOD,
+ SECRET_STORAGE_DECRYPT_DATA_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_secret_storage_getseters[] = {
+ PY_SECRET_STORAGE_HAS_KEY_ATTRIB,
+ SECRET_STORAGE_IS_LOCKED_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_secret_storage_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.SecretStorage",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = SECRET_STORAGE_DOC,
+
+ .tp_methods = py_secret_storage_methods,
+ .tp_getset = py_secret_storage_getseters,
+
+ .tp_init = py_secret_storage_init,
+ .tp_new = py_secret_storage_new,
+
+ };
+
+ return &py_secret_storage_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_secret_storage_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'SecretStorage' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_secret_storage_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_SECRET_STORAGE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en gardien des secrets avec stockage. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_secret_storage(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_secret_storage_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to secret storage");
+ break;
+
+ case 1:
+ *((GSecretStorage **)dst) = G_SECRET_STORAGE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/secstorage.h b/plugins/pychrysalide/glibext/secstorage.h
new file mode 100644
index 0000000..68726c3
--- /dev/null
+++ b/plugins/pychrysalide/glibext/secstorage.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage.h - prototypes pour l'équivalent Python du fichier "glibext/secstorage.h"
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_secret_storage_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.SecretStorage'. */
+bool ensure_python_secret_storage_is_registered(void);
+
+/* Tente de convertir en gardien des secrets avec stockage. */
+int convert_to_secret_storage(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_SECSTORAGE_H */
diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c
index 8491473..8712506 100644
--- a/plugins/pychrysalide/glibext/singleton.c
+++ b/plugins/pychrysalide/glibext/singleton.c
@@ -30,7 +30,6 @@
#include <glibext/singleton-int.h>
-#include <plugins/dt.h>
#include "../access.h"
@@ -42,7 +41,7 @@
/* Procède à l'initialisation de l'interface de candidature. */
-static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *);
+static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *, gpointer *);
/* Fournit une liste de candidats embarqués par un candidat. */
static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *);
@@ -50,37 +49,27 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper
/* Met à jour une liste de candidats embarqués par un candidat. */
static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t);
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *);
-
-/* Détermine si deux candidats à l'unicité sont identiques. */
-static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *);
-
/* Marque un candidat comme figé. */
-static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *);
+static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *);
/* Indique si le candidat est figé. */
-static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *);
-
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *);
-
-/* Fournit une liste de candidats embarqués par un candidat. */
-static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *);
+static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *);
/* Indique si le candidat est figé. */
-static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *);
+static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *);
+
+/* Crée une copie modifiable d'un object unique. */
+static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *);
-/* Effectue une comparaison avec un objet 'SingletonCandidate'. */
-static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int);
+/* Crée une copie modifiable d'un object unique. */
+static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *);
/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */
-/* Accompagne la création d'une instance dérivée en Python. */
-static PyObject *py_singleton_factory_new(PyTypeObject *, PyObject *, PyObject *);
+CREATE_DYN_CONSTRUCTOR(singleton_factory, G_TYPE_SINGLETON_FACTORY);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_singleton_factory_init(PyObject *, PyObject *, PyObject *);
@@ -108,37 +97,44 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *);
* *
******************************************************************************/
-static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused)
+static void py_singleton_candidate_interface_init(GSingletonCandidateInterface *iface, gpointer *unused)
{
#define SINGLETON_CANDIDATE_DOC \
"The SingletonCandidate class is a required interface for objects" \
" aiming at becoming singleton instances. All shared singletons are" \
" registered within a pychrysalide.glibext.SingletonFactory object.\n" \
"\n" \
+ "Implementations of the pychrysalide.glibext.HashableObject and" \
+ " pychrysalide.glibext.ComparableObject interfaces are required for" \
+ " types implementing the SingletonCandidate interface.\n" \
+ "\n" \
"The main implemantations come with types derived from" \
- " pychrysalide.analysis.DataType.\n" \
+ " pychrysalide.analysis.DataType (with possible recursivity) or from" \
+ " pychrysalide.arch.ArchOperand.\n" \
"\n" \
"A typical class declaration for a new implementation looks like:\n" \
"\n" \
- " class NewImplem(GObject.Object, SingletonCandidate):\n" \
+ " class NewImplem(GObject.Object, HashableObject, ComparableObject," \
+ " SingletonCandidate):\n" \
" ...\n" \
"\n" \
"The following methods have to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \
+ "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \
+ "* pychrysalide.glibext.SingletonCandidate._dup().\n" \
+ "\n" \
+ "The following methods may bbe defined for new implementations if" \
+ " inner SingletonCandidate objets are carried:\n" \
"* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \
- "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\
- "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \
- "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \
- "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \
- "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n"
+ "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n"
iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper;
iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper;
- iface->hash = py_singleton_candidate___hash__wrapper;
- iface->is_equal = py_singleton_candidate___eq__wrapper;
+ iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper;
+ iface->is_ro = py_singleton_candidate_is_read_only_wrapper;
- iface->set_ro = py_singleton_candidate_set_ro_wrapper;
- iface->is_ro = py_singleton_candidate_is_ro_wrapper;
+ iface->dup = py_singleton_candidate_dup_wrapper;
}
@@ -313,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
* *
-* Description : Fournit l'empreinte d'un candidat à une centralisation. *
+* Description : Marque un candidat comme figé. *
* *
-* Retour : Empreinte de l'élément représenté. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate)
+static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate)
{
- guint result; /* Empreinte à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan de consultation */
-#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- __hash__, "$self, /", \
- METH_NOARGS, \
- "Abstract method used to produce a hash of the object.\n" \
- "\n" \
- "The result must be an integer value up to 64 bits." \
- "\n" \
- "Inner instances which are listed through the" \
- " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \
- " method do not need to get processed here as they are handled" \
- " automatically by the interface core." \
+#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _mark_as_read_only, "$self", \
+ METH_NOARGS, \
+ "Abstract method used to seal the object as unmodifiable.\n" \
+ "\n" \
+ "No result is expected." \
)
- result = 0;
-
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(candidate));
- if (has_python_method(pyobj, "__hash__"))
- {
- pyret = run_python_method(pyobj, "__hash__", NULL);
+ pyret = run_python_method(pyobj, "_mark_as_read_only", NULL);
- if (pyret != NULL)
- {
- if (PyLong_Check(pyret))
- result = PyLong_AsUnsignedLongMask(pyret);
-
- Py_DECREF(pyret);
-
- }
-
- }
+ Py_XDECREF(pyret);
Py_DECREF(pyobj);
PyGILState_Release(gstate);
- return result;
-
}
/******************************************************************************
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
-* other = second élément à analyser. *
* *
-* Description : Détermine si deux candidats à l'unicité sont identiques. *
+* Description : Indique si le candidat est figé. *
* *
-* Retour : Bilan de la comparaison. *
+* Retour : true si le contenu du candidat ne peut plus être modifié. *
* *
* Remarques : - *
* *
******************************************************************************/
-static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other)
+static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate)
{
- guint result; /* Empreinte à retourner */
+ bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
- PyObject *args; /* Arguments pour l'appel */
PyObject *pyret; /* Bilan de consultation */
-#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- __eq__, "$self, other, /", \
- METH_NOARGS, \
- "Abstract method used to provide the *__eq__* method for" \
- " rich comparison.\n" \
- "\n" \
- "The expected result is a boolean value." \
+#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _is_read_only, "$self", \
+ METH_NOARGS, \
+ "Abstract method used to provide the state of the object: are" \
+ " its properties frozen (*True*) or can it be modified" \
+ " (*False*)?\n" \
+ "\n" \
+ "The result has to be a boolean status.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " a boolean." \
)
- result = 0;
+ result = false;
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(candidate));
- if (has_python_method(pyobj, "__eq__"))
- {
- args = PyTuple_New(1);
- PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other)));
-
- pyret = run_python_method(pyobj, "__eq__", args);
-
- if (pyret != NULL)
- {
- if (PyLong_Check(pyret))
- result = PyLong_AsUnsignedLong(pyret);
+ pyret = run_python_method(pyobj, "_is_read_only", NULL);
- Py_DECREF(pyret);
-
- }
+ if (pyret != NULL)
+ {
+ if (PyBool_Check(pyret))
+ result = (pyret == Py_True);
- Py_DECREF(args);
+ else
+ PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value"));
}
+ Py_XDECREF(pyret);
+
Py_DECREF(pyobj);
PyGILState_Release(gstate);
@@ -442,80 +413,38 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
* *
-* Description : Marque un candidat comme figé. *
+* Description : Crée une copie modifiable d'un object unique. *
* *
-* Retour : - *
+* Retour : Nouvelle instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate)
+static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate)
{
+ GSingletonCandidate *result; /* Instance à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan de consultation */
+ PyObject *state; /* Validation du mode */
-#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \
+#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \
( \
- _set_read_only, "$self, /", \
+ _dup, "$self", \
METH_NOARGS, \
- "Abstract method used to mark the content of a singleton" \
- " candidate as read-only.\n" \
+ "Abstract method used to create a copy of the object. This" \
+ " has to be able to get modified (ie. its" \
+ " pychrysalide.glibext.SingletonCandidate.read_only status" \
+ " has to be *False*).\n" \
"\n" \
- "The read-only state is mandatory once the candidate is" \
- " registered inside a pychrysalide.glibext.SingletonFactory"\
- " instance as official singleton." \
-)
-
- gstate = PyGILState_Ensure();
-
- pyobj = pygobject_new(G_OBJECT(candidate));
-
- if (has_python_method(pyobj, "_set_read_only"))
- {
- pyret = run_python_method(pyobj, "_set_read_only", NULL);
-
- Py_XDECREF(pyret);
-
- }
-
- Py_DECREF(pyobj);
-
- PyGILState_Release(gstate);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : candidate = objet dont l'instance se veut unique. *
-* *
-* Description : Indique si le candidat est figé. *
-* *
-* Retour : true si le contenu du candidat ne peut plus être modifié. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *candidate)
-{
- bool result; /* Etat à retourner */
- PyGILState_STATE gstate; /* Sauvegarde d'environnement */
- PyObject *pyobj; /* Objet Python concerné */
- PyObject *pyret; /* Bilan de consultation */
-
-#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _is_read_only, "$self, /", \
- METH_NOARGS, \
- "Abstract method used to retrieve the status of the data" \
- " contained by a singleton candidate.\n" \
+ "The result has to be a new intance of type(self).\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the type of the" \
+ " return value is different from the type of self.\n" \
"\n" \
- "The retured value is *True* if the candidate is" \
- " registered inside a pychrysalide.glibext.SingletonFactory"\
- " instance as official singleton, *False* otherwise." \
+ "A *ValueError* exception is raised of the return object" \
+ " is in read-only mode." \
)
result = false;
@@ -524,16 +453,32 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand
pyobj = pygobject_new(G_OBJECT(candidate));
- if (has_python_method(pyobj, "_is_read_only"))
+ pyret = run_python_method(pyobj, "_dup", NULL);
+
+ if (pyret != NULL)
{
- pyret = run_python_method(pyobj, "_is_read_only", NULL);
+ if (Py_TYPE(pyret) != Py_TYPE(pyobj))
+ PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type"));
- result = (pyret == Py_True);
+ else
+ {
+ state = py_singleton_candidate_is_read_only(pyret, NULL);
- Py_XDECREF(pyret);
+ if (state != NULL)
+ {
+ if (state != Py_False)
+ PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode"));
+
+ Py_DECREF(state);
+
+ }
+
+ }
}
+ Py_XDECREF(pyret);
+
Py_DECREF(pyobj);
PyGILState_Release(gstate);
@@ -545,97 +490,51 @@ static bool py_singleton_candidate_is_ro_wrapper(const GSingletonCandidate *cand
/******************************************************************************
* *
-* Paramètres : self = objet dont l'instance se veut unique. *
+* Paramètres : self = objet manipulé ici. *
* args = adresse non utilisée ici. *
* *
-* Description : Fournit l'empreinte d'un candidat à une centralisation. *
+* Description : Crée une copie modifiable d'un object unique. *
* *
-* Retour : Empreinte de l'élément représenté. *
+* Retour : Nouvelle instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args)
+static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args)
{
PyObject *result; /* Emplacement à retourner */
GSingletonCandidate *candidate; /* Mécanismes natifs */
- guint hash; /* Valeur d'empreitne */
+ GSingletonCandidate *copy; /* Copie des mécanismes natifs */
-#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \
-( \
- hash, "$self", \
- METH_NOARGS, py_singleton_candidate, \
- "Compute the hash value of the singleton candidate.\n" \
- "\n" \
- "The method relies on the interface core to include in the" \
- " process the optional embedded instances which may become" \
- " singletons.\n" \
- "\n" \
- "The result is an integer value.\n" \
- "\n" \
- "Even if the Python *hash()* method, relying on the" \
- " pychrysalide.glibext.SingletonCandidate.__hash__()" \
- " implementation, provides values up to 64 bits, the final" \
- " hashes processed by the native GLib hash methods are" \
- " limited to 32 bits values." \
+#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \
+( \
+ dup, "$self", \
+ METH_NOARGS, py_singleton_candidate, \
+ "Create a copy of the object. This has to be able to get" \
+ " modified (ie. its" \
+ " pychrysalide.glibext.SingletonCandidate.read_only status" \
+ " has to be *False*).\n" \
+ "\n" \
+ "The result has to be a new intance of type(self)." \
)
candidate = G_SINGLETON_CANDIDATE(pygobject_get(self));
- hash = g_singleton_candidate_hash(candidate);
+ copy = g_singleton_candidate_dup(candidate);
- result = PyLong_FromUnsignedLong(hash);
+ if (copy == NULL)
+ result = NULL;
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : self = objet Python concerné par l'appel. *
-* closure = non utilisé ici. *
-* *
-* Description : Fournit une liste de candidats embarqués par un candidat. *
-* *
-* Retour : Liste de candidats internes, vide si aucun. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure)
-{
- PyObject *result; /* Valeur à retourner */
- GSingletonCandidate *candidate; /* Mécanismes natifs */
- size_t count; /* Quantité d'objets internes */
- GSingletonCandidate **instances; /* Liste des embarqués */
- size_t i; /* Boucle de parcours */
-
-#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \
-( \
- inner_instances, py_singleton_candidate, \
- "List of optional internal singleton candidate instances.\n" \
- "\n" \
- "The result has to be a tuple containing zero or more" \
- " pychrysalide.glibext.SingletonCandidate instances." \
-)
-
- candidate = G_SINGLETON_CANDIDATE(pygobject_get(self));
-
- instances = g_singleton_candidate_list_inner_instances(candidate, &count);
+ else
+ {
+ result = pygobject_new(G_OBJECT(candidate));
- result = PyTuple_New(count);
+ unref_object(copy);
- for (i = 0; i < count; i++)
- {
- PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i])));
- g_object_unref(G_OBJECT(instances[i]));
}
- if (instances != NULL)
- free(instances);
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
return result;
@@ -649,87 +548,33 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void
* *
* Description : Indique si le candidat est figé. *
* *
-* Retour : true si le contenu du candidat ne peut plus être modifié. *
+* Retour : True si le contenu du candidat ne peut plus être modifié. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure)
+static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GSingletonCandidate *candidate; /* Mécanismes natifs */
- bool status; /* Etat de l'élément consulté */
+ bool state; /* Etat de l'objet courant */
-#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \
-( \
- read_only, py_singleton_candidate, \
- "State of the singleton candidate content.\n" \
- "\n" \
- "The result is a boolean: *True* if the object is registered" \
- " as singleton, *False* otherwise.\n" \
- "\n" \
- "Once a singleton, the object must not change its content as" \
- " it is a shared instance." \
+#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \
+( \
+ read_only, py_singleton_candidate, \
+ "Boolean state of the object: *True* if all its properties are" \
+ " frozen, *False* if the object can be modified." \
)
candidate = G_SINGLETON_CANDIDATE(pygobject_get(self));
- status = g_singleton_candidate_is_read_only(candidate);
+ state = g_singleton_candidate_is_read_only(candidate);
- result = status ? Py_True : Py_False;
+ result = state ? Py_True : Py_False;
Py_INCREF(result);
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : a = premier object Python à consulter. *
-* b = second object Python à consulter. *
-* op = type de comparaison menée. *
-* *
-* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op)
-{
- PyObject *result; /* Bilan à retourner */
- int ret; /* Bilan de lecture des args. */
- GSingletonCandidate *cand_a; /* Premier élément à traiter */
- GSingletonCandidate *cand_b; /* Second élément à traiter */
- gboolean status; /* Résultat d'une comparaison */
-
- if (op != Py_EQ)
- {
- result = Py_NotImplemented;
- goto cmp_done;
- }
-
- ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type());
- if (!ret)
- {
- result = Py_NotImplemented;
- goto cmp_done;
- }
-
- cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a));
- cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b));
-
- status = g_singleton_candidate_is_equal(cand_a, cand_b);
-
- result = (status ? Py_True : Py_False);
-
- cmp_done:
-
- Py_INCREF(result);
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
return result;
@@ -753,16 +598,14 @@ PyTypeObject *get_python_singleton_candidate_type(void)
static PyMethodDef py_singleton_candidate_methods[] = {
SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER,
SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER,
- SINGLETON_CANDIDATE_HASH_WRAPPER,
- SINGLETON_CANDIDATE_EQ_WRAPPER,
- SINGLETON_CANDIDATE_SET_RO_WRAPPER,
- SINGLETON_CANDIDATE_IS_RO_WRAPPER,
- SINGLETON_CANDIDATE_HASH_METHOD,
+ SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER,
+ SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER,
+ SINGLETON_CANDIDATE_DUP_WRAPPER,
+ SINGLETON_CANDIDATE_DUP_METHOD,
{ NULL }
};
static PyGetSetDef py_singleton_candidate_getseters[] = {
- SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB,
SINGLETON_CANDIDATE_READ_ONLY_ATTRIB,
{ NULL }
};
@@ -778,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void)
.tp_doc = SINGLETON_CANDIDATE_DOC,
- .tp_richcompare = py_singleton_candidate_richcompare,
-
.tp_methods = py_singleton_candidate_methods,
.tp_getset = py_singleton_candidate_getseters
@@ -804,7 +645,7 @@ PyTypeObject *get_python_singleton_candidate_type(void)
bool ensure_python_singleton_candidate_is_registered(void)
{
- PyTypeObject *type; /* Type Python 'BinContent' */
+ PyTypeObject *type; /* Type 'SingletonCandidate' */
PyObject *module; /* Module à recompléter */
PyObject *dict; /* Dictionnaire du module */
@@ -887,66 +728,6 @@ int convert_to_singleton_candidate(PyObject *arg, void *dst)
/******************************************************************************
* *
-* Paramètres : type = type du nouvel objet à mettre en place. *
-* args = éventuelle liste d'arguments. *
-* kwds = éventuel dictionnaire de valeurs mises à disposition. *
-* *
-* Description : Accompagne la création d'une instance dérivée en Python. *
-* *
-* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_singleton_factory_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
- PyObject *result; /* Objet à retourner */
- PyTypeObject *base; /* Type de base à dériver */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
-
- /* Validations diverses */
-
- base = get_python_singleton_factory_type();
-
- if (type == base)
- goto simple_way;
-
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(G_TYPE_SINGLETON_FACTORY, type->tp_name, NULL, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
- /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
-
- simple_way:
-
- result = PyType_GenericNew(type, args, kwds);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : self = objet à initialiser (théoriquement). *
* args = arguments fournis à l'appel. *
* kwds = arguments de type key=val fournis. *
@@ -973,11 +754,6 @@ static int py_singleton_factory_init(PyObject *self, PyObject *args, PyObject *k
"\n" \
"The first processed instance defines the type handled by the factory."
- /* Récupération des paramètres */
-
- ret = PyArg_ParseTuple(args, "");
- if (!ret) return -1;
-
/* Initialisation d'un objet GLib */
ret = forward_pygobjet_init(self);
diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c
new file mode 100644
index 0000000..a6de0f0
--- /dev/null
+++ b/plugins/pychrysalide/glibext/strbuilder.c
@@ -0,0 +1,542 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c"
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "strbuilder.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <glibext/strbuilder-int.h>
+
+
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Procède à l'initialisation de l'interface d'exportation. */
+static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *);
+
+/* Exporte une chaîne de caractères à partir d'un objet. */
+static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *);
+
+
+
+/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */
+
+
+/* Transmet la description d'un objet définie par son parent. */
+static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *);
+
+/* Exporte une chaîne de caractères à partir d'un objet. */
+static PyObject *py_string_builder_to_string(PyObject *, PyObject *);
+
+/* Fournit une représentation de l'objet exportable. */
+static PyObject *py_string_builder_str(PyObject *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* unused = adresse non utilisée ici. *
+* *
+* Description : Procède à l'initialisation de l'interface d'exportation. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpointer *unused)
+{
+#define STRING_BUILDER_DOC \
+ "The StringBuilder class defines a interface for native objects aiming" \
+ " at providing a string representation of themselves.\n" \
+ "\n" \
+ "A typical class declaration for a new implementation looks like:\n" \
+ "\n" \
+ " class NewImplem(GObject.Object, StringBuilder):\n" \
+ " ...\n" \
+ "\n" \
+ "The following method has to be defined for new implementations:\n" \
+ "* pychrysalide.glibext.StringBuilder._to_string().\n"
+
+ iface->to_string = py_string_builder_to_string_wrapper;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : builder = objet dont l'instance est exportable. *
+* flags = éventuelles indications pour l'opération. *
+* out = chaîne de caractères mise en place. [OUT] *
+* *
+* Description : Exporte une chaîne de caractères à partir d'un objet. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après *
+* usage. *
+* *
+******************************************************************************/
+
+static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out)
+{
+ bool result; /* Bilan à retourner */
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *args; /* Arguments pour l'appel */
+ PyObject *pyret; /* Bilan de consultation */
+ const char *utf8; /* Chaîne UTF-8 portée */
+ Py_ssize_t size; /* Taille de ces données */
+
+#define STRING_BUILDER_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _to_string, "$self, /, flags=0", \
+ METH_VARARGS, \
+ "Abstract method providing a string representation for the" \
+ " object which is used as the default implementation of the" \
+ " __repr__() method.\n" \
+ "\n" \
+ "The optional *flags* argument define hints for the operation" \
+ " (for instance the Intel or AT&T flavor for x86 assembly).\n" \
+ "\n" \
+ "The result has to be a string." \
+ "\n" \
+ "A *TypeError* exception is raised if the return value is not" \
+ " a string." \
+)
+
+ result = false;
+
+ gstate = PyGILState_Ensure();
+
+ pyobj = pygobject_new(G_OBJECT(builder));
+
+ args = PyTuple_New(1);
+ PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags));
+
+ pyret = run_python_method(pyobj, "_to_string", args);
+
+ if (pyret != NULL)
+ {
+ if (PyUnicode_Check(pyret))
+ {
+ utf8 = PyUnicode_AsUTF8AndSize(pyret, &size);
+
+ if (utf8 != NULL)
+ {
+ assert(size >= 0);
+
+ add_to_sized_binary(out, utf8, size);
+ result = true;
+
+ }
+
+ }
+
+ if (!result)
+ PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value"));
+
+ }
+
+ Py_XDECREF(pyret);
+
+ Py_DECREF(args);
+
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONNEXION AVEC L'API DE PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet dont l'instance se veut unique. *
+* args = adresse non utilisée ici. *
+* *
+* Description : Transmet la description d'un objet définie par son parent. *
+* *
+* Retour : Présentation de l'élément construite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Valeur à retourner */
+ unsigned int flags; /* Eventuelles indications */
+ int ret; /* Bilan de lecture des args. */
+ GStringBuilder *builder; /* Mécanismes natifs */
+ GStringBuilderInterface *iface; /* Interface utilisée */
+ GStringBuilderInterface *parent_iface; /* Interface parente */
+ sized_binary_t out; /* Description construite */
+ bool status; /* Bilan de l'opération */
+
+#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \
+( \
+ parent_to_string, "$self, /, flags=0", \
+ METH_VARARGS, py_string_builder, \
+ "Provide a string representation defined by the interface" \
+ " implementation from the object native parent.\n" \
+ "\n" \
+ "The result is a string.\n" \
+ "\n" \
+ "A *TypeError* exception is raised if the object parent does" \
+ " not implement the pychrysalide.glibext.StringBuilder" \
+ " interface." \
+ "\n" \
+ "A *RuntimeError* exception is raised if the direct parent type"\
+ " of the object has not a native implementation. For Python" \
+ " implementations, the super()._to_string() function has to be" \
+ " used instead.\n" \
+ "\n" \
+ "A *BufferError* exception is raised if the description has" \
+ " not been able to get created." \
+)
+
+ if (!check_for_native_parent(self))
+ return NULL;
+
+ flags = 0;
+
+ ret = PyArg_ParseTuple(args, "|I", &flags);
+ if (!ret) return NULL;
+
+ builder = G_STRING_BUILDER(pygobject_get(self));
+
+ iface = G_STRING_BUILDER_GET_IFACE(builder);
+
+ parent_iface = g_type_interface_peek_parent(iface);
+
+ if (parent_iface == NULL)
+ {
+ PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface"));
+
+ result = NULL;
+
+ }
+ else
+ {
+ init_sized_binary(&out);
+
+ status = parent_iface->to_string(builder, flags, &out);
+
+ if (status)
+ result = PyUnicode_FromStringAndSize(out.data, out.size);
+
+ else
+ {
+ result = NULL;
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_BufferError, _("unable to create a description"));
+
+ }
+
+ exit_sized_binary(&out);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet manipulé ici. *
+* args = adresse non utilisée ici. *
+* *
+* Description : Exporte une chaîne de caractères à partir d'un objet. *
+* *
+* Retour : Présentation de l'élément construite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Emplacement à retourner */
+ unsigned int flags; /* Eventuelles indications */
+ int ret; /* Bilan de lecture des args. */
+ GStringBuilder *builder; /* Mécanismes natifs */
+ sized_binary_t out; /* Description construite */
+ bool status; /* Bilan de l'opération */
+
+#define STRING_BUILDER_TO_STRING_METHOD PYTHON_METHOD_DEF \
+( \
+ to_string, "$self, /, flags=0", \
+ METH_VARARGS, py_string_builder, \
+ "Provide a string representation for the object which is used" \
+ " as the default implementation of the __repr__() method.\n" \
+ "\n" \
+ "The optional *flags* argument define hints for the operation" \
+ " (for instance the Intel or AT&T flavor for x86 assembly).\n" \
+ "\n" \
+ "The result is a string.\n" \
+ "\n" \
+ "A *BufferError* exception is raised if the description has" \
+ " not been able to get created." \
+)
+
+ flags = 0;
+
+ ret = PyArg_ParseTuple(args, "|I", &flags);
+ if (!ret) return NULL;
+
+ builder = G_STRING_BUILDER(pygobject_get(self));
+
+ init_sized_binary(&out);
+
+ status = g_string_builder_to_string(builder, flags, &out);
+
+ if (status)
+ result = PyUnicode_FromStringAndSize(out.data, out.size);
+
+ else
+ {
+ result = NULL;
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_BufferError, _("unable to create a description"));
+
+ }
+
+ exit_sized_binary(&out);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet manipulé ici. *
+* *
+* Description : Fournit une représentation de l'objet exportable. *
+* *
+* Retour : Présentation de l'élément construite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_string_builder_str(PyObject *self)
+{
+ PyObject *result; /* Emplacement à retourner */
+ GStringBuilder *builder; /* Mécanismes natifs */
+ sized_binary_t out; /* Description construite */
+ bool status; /* Bilan de l'opération */
+
+ builder = G_STRING_BUILDER(pygobject_get(self));
+
+ init_sized_binary(&out);
+
+ status = g_string_builder_to_string(builder, 0, &out);
+
+ if (status)
+ result = PyUnicode_FromStringAndSize(out.data, out.size);
+
+ else
+ {
+ result = NULL;
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_BufferError, _("unable to create a description"));
+
+ }
+
+ exit_sized_binary(&out);
+
+ CLEAN_RESULT_IF_RAISED_EXCEPTION(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_string_builder_type(void)
+{
+ static PyMethodDef py_string_builder_methods[] = {
+ STRING_BUILDER_TO_STRING_WRAPPER,
+ STRING_BUILDER_PARENT_TO_STRING_METHOD,
+ STRING_BUILDER_TO_STRING_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_string_builder_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_string_builder_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.glibext.StringBuilder",
+ .tp_basicsize = sizeof(PyObject),
+
+ .tp_str = py_string_builder_str,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = STRING_BUILDER_DOC,
+
+ .tp_methods = py_string_builder_methods,
+ .tp_getset = py_string_builder_getseters
+
+ };
+
+ return &py_string_builder_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_string_builder_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'StringBuilder' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ static GInterfaceInfo info = { /* Paramètres d'inscription */
+
+ .interface_init = (GInterfaceInitFunc)py_string_builder_interface_init,
+ .interface_finalize = NULL,
+ .interface_data = NULL,
+
+ };
+
+ type = get_python_string_builder_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.glibext");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_interface_for_pygobject(dict, G_TYPE_STRING_BUILDER, type, &info))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en interface d'exportation en chaîne. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_string_builder(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_string_builder_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to string builder");
+ break;
+
+ case 1:
+ *((GStringBuilder **)dst) = G_STRING_BUILDER(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h
new file mode 100644
index 0000000..1881cae
--- /dev/null
+++ b/plugins/pychrysalide/glibext/strbuilder.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h"
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H
+#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_string_builder_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.glibext.StringBuilder'. */
+bool ensure_python_string_builder_is_registered(void);
+
+/* Tente de convertir en interface d'exportation en chaîne. */
+int convert_to_string_builder(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_STRBUILDER_H */
diff --git a/plugins/pychrysalide/glibext/work.c b/plugins/pychrysalide/glibext/work.c
index 6a15984..e6791e3 100644
--- a/plugins/pychrysalide/glibext/work.c
+++ b/plugins/pychrysalide/glibext/work.c
@@ -41,9 +41,9 @@
/* Initialise la classe des travaux programmés. */
-static void py_generic_work_init_gclass(GGenericWorkClass *, gpointer);
+static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK, py_generic_work_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_generic_work_init(PyObject *, PyObject *, PyObject *);
@@ -68,20 +68,22 @@ static PyObject *py_generic_work_process(PyObject *, PyObject *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des travaux programmés. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_generic_work_init_gclass(GGenericWorkClass *class, gpointer unused)
+static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass)
{
- class->run = py_generic_work_run_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper);
+
+ return 0;
}
@@ -296,6 +298,8 @@ bool ensure_python_generic_work_is_registered(void)
dict = PyModule_GetDict(module);
+ pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass);
+
if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type))
return false;
diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c
index d8126be..ca6c73c 100644
--- a/plugins/pychrysalide/glibext/workqueue.c
+++ b/plugins/pychrysalide/glibext/workqueue.c
@@ -94,9 +94,9 @@ static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds)
{
int ret; /* Bilan de lecture des args. */
-#define WORK_QUEUE_DOC \
- "WorkQueue defines a basic work aimed to get processed in a" \
- " thread setup by a pychrysalide.glibext.WorkQueue instance.\n" \
+#define WORK_QUEUE_DOC \
+ "WorkQueue creates threads in order to process" \
+ " pychrysalide.glibext.Work instances.\n" \
"\n" \
"Instances can be created using the following constructor:\n" \
"\n" \
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index d361c3e..0c84278 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* helpers.c - simplification des interactions de base avec Python
*
- * Copyright (C) 2018-2020 Cyrille Bagard
+ * Copyright (C) 2018-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -39,7 +39,6 @@
#include <i18n.h>
#include <common/extstr.h>
-#include <plugins/dt.h>
#include "access.h"
@@ -217,6 +216,53 @@ bool has_python_method(PyObject *module, const char *method)
* *
* Paramètres : target = propriétaire de la routine visée. *
* method = désignation de la fonction à appeler. *
+* *
+* Description : Indique si une routine Python possède une implémentation. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool has_python_implementation_method(PyObject *module, const char *method)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *func; /* Fonction visée */
+ const PyMethodDef *def; /* Définition de la fonction */
+
+ result = (PyObject_HasAttrString(module, method) == 1);
+
+ if (result)
+ {
+ func = PyObject_GetAttrString(module, method);
+ assert(func != NULL);
+
+ result = PyCallable_Check(func);
+
+ if (func->ob_type == &PyCFunction_Type)
+ {
+ def = ((PyCFunctionObject *)func)->m_ml;
+
+ assert(strcmp(def->ml_name, method) == 0);
+
+ result = (def->ml_meth != not_yet_implemented_method);
+
+ }
+
+ Py_DECREF(func);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : target = propriétaire de la routine visée. *
+* method = désignation de la fonction à appeler. *
* args = arguments à associer à l'opération. *
* *
* Description : Appelle une routine Python. *
@@ -517,75 +563,10 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type)
* *
******************************************************************************/
-PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds)
-{
- PyObject *result; /* Objet à retourner */
- PyTypeObject *base; /* Type parent version Python */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
-
- /* Validations diverses */
-
- base = pygobject_lookup_class(gbase);
-
- if (type == base)
- goto simple_way;
-
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(gbase, type->tp_name, NULL, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
- /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
-
- simple_way:
-
- result = PyType_GenericNew(type, args, kwds);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : type = type du nouvel objet à mettre en place. *
-* gbase = type de base natif. *
-* cinit = procédure d'initialisation de la classe associée. *
-* args = éventuelle liste d'arguments. *
-* kwds = éventuel dictionnaire de valeurs mises à disposition.*
-* *
-* Description : Accompagne la création d'une instance dérivée en Python. *
-* *
-* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds)
+PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds)
{
PyObject *result; /* Objet à retourner */
PyTypeObject *base; /* Type parent version Python */
- bool first_time; /* Evite les multiples passages*/
- GType gtype; /* Nouveau type de processeur */
- bool status; /* Bilan d'un enregistrement */
/* Validations diverses */
@@ -598,24 +579,6 @@ PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *type, GTy
goto exit;
}
- /* Mise en place d'un type dédié */
-
- first_time = (g_type_from_name(type->tp_name) == 0);
-
- gtype = build_dynamic_type(gbase, type->tp_name, cinit, NULL, NULL);
-
- if (first_time)
- {
- status = register_class_for_dynamic_pygobject(gtype, type);
-
- if (!status)
- {
- result = NULL;
- goto exit;
- }
-
- }
-
/* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
result = PyType_GenericNew(type, args, kwds);
@@ -1159,6 +1122,60 @@ int forward_pygobjet_init(PyObject *self)
/******************************************************************************
* *
+* Paramètres : type = type Python à ausculter. *
+* *
+* Description : Détermine si un type Python est implémenté en C ou non. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool pytype_has_native_implementation(const PyTypeObject *type)
+{
+ bool result; /* Bilan à retourner */
+ GType gtype; /* Type associé à la classe */
+
+ static GQuark pygobject_custom_key = 0; /* Clef d'accès direct */
+
+ /**
+ * Dans les sources de PyGObject, la fonction pyg_type_register() de
+ * gi/gimodule.c, appelée depuis gi/types.py, contient la bribe de code
+ * suivante :
+ *
+ * // Mark this GType as a custom python type
+ * g_type_set_qdata(instance_type, pygobject_custom_key,
+ * GINT_TO_POINTER (1));
+ *
+ * La fonction pyi_object_register_types() de gi/pygobject-object.c indique
+ * la clef associée au Quark :
+ *
+ * pygobject_custom_key = g_quark_from_static_string("PyGObject::custom");
+ *
+ * Enfin, une fonction inspirante est codée dans le fichier gi/pygi-type.c :
+ *
+ * gboolean pyg_gtype_is_custom(GType gtype)
+ * {
+ * return g_type_get_qdata (gtype, pygobject_custom_key) != NULL;
+ * }
+ *
+ */
+
+ if (pygobject_custom_key == 0)
+ pygobject_custom_key = g_quark_from_static_string("PyGObject::custom");
+
+ gtype = pyg_type_from_object((PyObject *)type);
+
+ result = (g_type_get_qdata(gtype, pygobject_custom_key) == NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : arg = argument quelconque à tenter de convertir. *
* dst = destination des valeurs récupérées en cas de succès. *
* *
@@ -1486,6 +1503,109 @@ int convert_to_gdk_rgba(PyObject *arg, void *dst)
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en tableau de chaînes de caractères. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_sequence_to_charp_array(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+ charp_array_t *array; /* Tableau à constituer */
+ size_t i; /* Boucle de parcours */
+ PyObject *value; /* Valeur brute d'un élément */
+
+ array = (charp_array_t *)dst;
+
+ /* Nettoyage ? */
+ if (arg == NULL)
+ {
+ result = 1;
+ goto clean;
+ }
+
+ else
+ {
+ result = 0;
+
+ if (PySequence_Check(arg) != 1)
+ goto done;
+
+ array->length = PySequence_Length(arg);
+
+ array->values = calloc(array->length, sizeof(char *));
+
+ for (i = 0; i < array->length; i++)
+ {
+ value = PySequence_ITEM(arg, i);
+
+ if (!PyUnicode_Check(value))
+ {
+ Py_DECREF(value);
+ goto clean;
+ }
+
+ array->values[i] = strdup(PyUnicode_DATA(value));
+
+ Py_DECREF(value);
+
+ }
+
+ result = Py_CLEANUP_SUPPORTED;
+
+ }
+
+ done:
+
+ return result;
+
+ clean:
+
+ clean_charp_array(array);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = tableau de chaînes de caractères à traiter. *
+* *
+* Description : Libère de la mémoire un tableau de chaînes de caractères. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void clean_charp_array(charp_array_t *array)
+{
+ size_t i; /* Boucle de parcours */
+
+ for (i = 0; i < array->length; i++)
+ if (array->values[i] != NULL)
+ free(array->values[i]);
+
+ if (array->values != NULL)
+ free(array->values);
+
+ array->values = NULL;
+ array->length = 0;
+
+}
+
+
+
/* ---------------------------------------------------------------------------------- */
/* TRANSFERT DES VALEURS CONSTANTES */
/* ---------------------------------------------------------------------------------- */
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index cc82bba..745d013 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* helpers.h - prototypes pour la simplification des interactions de base avec Python
*
- * Copyright (C) 2018-2020 Cyrille Bagard
+ * Copyright (C) 2018-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -34,6 +34,9 @@
#endif
+#include <i18n.h>
+
+
/* ---------------------- ACCELERATEURS POUR PYTHON UNIQUEMENT ---------------------- */
@@ -47,6 +50,9 @@ int convert_to_callable(PyObject *, void *);
/* Indique si une routine Python existe ou non. */
bool has_python_method(PyObject *, const char *);
+/* Indique si une routine Python possède une implémentation. */
+bool has_python_implementation_method(PyObject *, const char *);
+
/* Appelle une routine Python. */
PyObject *run_python_method(PyObject *, const char *, PyObject *);
@@ -126,7 +132,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
PYTHON_GETSET_DEF("is_" #name, base ## _is_ ## name, NULL, ATTRIB_RO doc, NULL)
#define PYTHON_HAS_DEF_FULL(name, base, doc) \
- PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL)
+ PYTHON_GETSET_DEF("has_" #name, base ## _has_ ## name, NULL, ATTRIB_RO doc, NULL)
#define PYTHON_RAWGET_DEF_FULL(name, base, doc) \
PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, ATTRIB_RO doc, NULL)
@@ -152,14 +158,11 @@ bool register_python_module_object(PyObject *, PyTypeObject *);
*
* Cf. http://stackoverflow.com/questions/20432335/can-python-abstract-base-classes-inherit-from-c-extensions
*/
-#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new
+#define APPLY_ABSTRACT_FLAG(tp) tp->tp_new = PyBaseObject_Type.tp_new // REMME
/* Accompagne la création d'une instance dérivée en Python. */
-PyObject *python_constructor_with_dynamic_gtype(PyTypeObject *, GType, PyObject *, PyObject *);
-
-/* Accompagne la création d'une instance dérivée en Python. */
-PyObject *python_abstract_constructor_with_dynamic_gtype(PyTypeObject *, GType, GClassInitFunc, PyObject *, PyObject *);
+PyObject *python_abstract_constructor(PyTypeObject *, GType, PyObject *, PyObject *);
#define CREATE_DYN_CONSTRUCTOR(pyname, gbase) \
@@ -167,22 +170,41 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *);
static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \
{ \
PyObject *result; /* Objet à retourner */ \
- result = python_constructor_with_dynamic_gtype(type, gbase, args, kwds); \
+ result = PyType_GenericNew(type, args, kwds); \
return result; \
}
-#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase, cinit) \
+#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase) \
static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); \
static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \
{ \
PyObject *result; /* Objet à retourner */ \
- result = python_abstract_constructor_with_dynamic_gtype(type, gbase, (GClassInitFunc)cinit, \
- args, kwds); \
+ result = python_abstract_constructor(type, gbase, args, kwds); \
return result; \
}
+/**
+ * Les initialisations de classes engagées par les appels à pyg_register_class_init()
+ * ne se déclenchent qu'après les initialisations complètes des classes côté GObject.
+ *
+ * Typiquement, pour une déclinaison Python du type PythonModule, sont appelées
+ * successivement les fonctions suivantes :
+ * - g_plugin_module_class_init() ;
+ * - g_python_plugin_class_init() ;
+ * - py_plugin_module_init_gclass().
+ *
+ * Il est alors impératif de considérer les pointeurs de fonction déjà en place
+ * afin de ne par remplacer les implémentations de GPythonPlugin par les
+ * wrappers par défaut de PythonModule.
+ */
+
+#define PY_CLASS_SET_WRAPPER(field, ptr) \
+ if (field == NULL) \
+ field = ptr;
+
+
/* Marque l'interdiction d'une instanciation depuis Python. */
PyObject *no_python_constructor_allowed(PyTypeObject *, PyObject *, PyObject *);
@@ -206,6 +228,41 @@ PyTypeObject *define_python_dynamic_type(const PyTypeObject *);
/**
+ * Prise en compte d'éventuelles exceptions levées dans les implémentations.
+ *
+ * Par exemple :
+ * - du code Python exécute une fonction implémentée en C ;
+ * - cette dernière fait appel à un Wrapper C qui sollicite du code
+ * d'implémentation Python.
+ *
+ * Cette seconde étape peut lever une exception (impletation manquante ou
+ * implémentation levant une exception.
+ *
+ * Les codes C des étapes 1 et 2 ne dispose pas de mécanismes pour transmettre
+ * le détail des éventuelles exceptions, mais Python le mémorise.
+ */
+
+#define UPDATE_RESULT_IF_RAISED_EXCEPTION(val) \
+ do \
+ { \
+ if (PyErr_Occurred() != NULL) \
+ result = val; \
+ } \
+ while (0)
+
+#define CLEAN_RESULT_IF_RAISED_EXCEPTION(val) \
+ do \
+ { \
+ if (PyErr_Occurred() != NULL) \
+ { \
+ Py_XDECREF(result); \
+ result = NULL; \
+ } \
+ } \
+ while (0)
+
+
+/**
* pygobject_new() prend en compte les références flottantes au moment de la
* construction d'un objet Python.
*
@@ -231,11 +288,23 @@ bool register_class_for_pygobject(PyObject *, GType, PyTypeObject *);
bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *);
/* Enregistre un type Python dérivant d'un type GLib dynamique. */
-bool register_class_for_dynamic_pygobject(GType, PyTypeObject *);
+bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); // REMME
/* Fait suivre à la partie GObject une initialisation nouvelle. */
int forward_pygobjet_init(PyObject *);
+/* Détermine si un type Python est implémenté en C ou non. */
+bool pytype_has_native_implementation(const PyTypeObject *);
+
+#define check_for_native_parent(obj) \
+ ({ \
+ bool __result; \
+ __result = pytype_has_native_implementation((obj)->ob_type->tp_base); \
+ if (!__result) \
+ PyErr_SetString(PyExc_RuntimeError, _("object parent is not a native type")); \
+ __result; \
+ })
+
/* Tente de convertir en valeur GType. */
int convert_to_gtype(PyObject *, void *);
@@ -283,6 +352,21 @@ int convert_to_gdk_rgba(PyObject *, void *);
#endif
+/* Tableau de chaînes de caractères converti */
+typedef struct _charp_array_t
+{
+ char **values; /* Liste de chaînes textuelles */
+ size_t length; /* Taille de cette liste */
+
+} charp_array_t;
+
+/* Tente de convertir en tableau de chaînes de caractères. */
+int convert_to_sequence_to_charp_array(PyObject *, void *);
+
+/* Libère de la mémoire un tableau de chaînes de caractères. */
+void clean_charp_array(charp_array_t *);
+
+
/* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */
diff --git a/plugins/pychrysalide/plugins/Makefile.am b/plugins/pychrysalide/plugins/Makefile.am
index bb9ed5d..abadb7f 100644
--- a/plugins/pychrysalide/plugins/Makefile.am
+++ b/plugins/pychrysalide/plugins/Makefile.am
@@ -2,10 +2,10 @@
noinst_LTLIBRARIES = libpychrysaplugins.la
libpychrysaplugins_la_SOURCES = \
- constants.h constants.c \
- plugin.h plugin.c \
module.h module.c \
- translate.h translate.c
+ plugin.h plugin.c \
+ python-int.h \
+ python.h python.c
libpychrysaplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_INTERPRETER_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
-I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
diff --git a/plugins/pychrysalide/plugins/constants.c b/plugins/pychrysalide/plugins/constants.c
deleted file mode 100644
index 7e20e4c..0000000
--- a/plugins/pychrysalide/plugins/constants.c
+++ /dev/null
@@ -1,147 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * constants.c - ajout des constantes principales
- *
- * Copyright (C) 2020 Cyrille Bagard
- *
- * This file is part of Chrysalide.
- *
- * Chrysalide is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chrysalide is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include "constants.h"
-
-
-#include <plugins/plugin-def.h>
-
-
-#include "../helpers.h"
-
-
-
-/******************************************************************************
-* *
-* Paramètres : type = type dont le dictionnaire est à compléter. *
-* *
-* Description : Définit les constantes relatives aux greffons Python. *
-* *
-* Retour : true en cas de succès de l'opération, false sinon. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool define_plugin_module_constants(PyTypeObject *type)
-{
- bool result; /* Bilan à retourner */
- PyObject *values; /* Groupe de valeurs à établir */
-
- result = true;
-
- values = PyDict_New();
-
- if (result) result = add_const_to_group(values, "BASIC_NONE", PGA_BASIC_NONE);
- if (result) result = add_const_to_group(values, "PLUGIN_INIT", PGA_PLUGIN_INIT);
- if (result) result = add_const_to_group(values, "PLUGIN_LOADED", PGA_PLUGIN_LOADED);
- if (result) result = add_const_to_group(values, "PLUGIN_EXIT", PGA_PLUGIN_EXIT);
- if (result) result = add_const_to_group(values, "NATIVE_PLUGINS_LOADED", PGA_NATIVE_PLUGINS_LOADED);
- if (result) result = add_const_to_group(values, "ALL_PLUGINS_LOADED", PGA_ALL_PLUGINS_LOADED);
- if (result) result = add_const_to_group(values, "TYPE_BUILDING", PGA_TYPE_BUILDING);
- if (result) result = add_const_to_group(values, "GUI_THEME", PGA_GUI_THEME);
- if (result) result = add_const_to_group(values, "PANEL_CREATION", PGA_PANEL_CREATION);
- if (result) result = add_const_to_group(values, "PANEL_DOCKING", PGA_PANEL_DOCKING);
- if (result) result = add_const_to_group(values, "CONTENT_EXPLORER", PGA_CONTENT_EXPLORER);
- if (result) result = add_const_to_group(values, "CONTENT_RESOLVER", PGA_CONTENT_RESOLVER);
- if (result) result = add_const_to_group(values, "CONTENT_ANALYZED", PGA_CONTENT_ANALYZED);
- if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_STARTED", PGA_FORMAT_ANALYSIS_STARTED);
- if (result) result = add_const_to_group(values, "FORMAT_PRELOAD", PGA_FORMAT_PRELOAD);
- if (result) result = add_const_to_group(values, "FORMAT_ATTACH_DEBUG", PGA_FORMAT_ATTACH_DEBUG);
- if (result) result = add_const_to_group(values, "FORMAT_ANALYSIS_ENDED", PGA_FORMAT_ANALYSIS_ENDED);
- if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_STARTED", PGA_FORMAT_POST_ANALYSIS_STARTED);
- if (result) result = add_const_to_group(values, "FORMAT_POST_ANALYSIS_ENDED", PGA_FORMAT_POST_ANALYSIS_ENDED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_STARTED", PGA_DISASSEMBLY_STARTED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_RAW", PGA_DISASSEMBLY_RAW);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_LINK", PGA_DISASSEMBLY_HOOKED_LINK);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_HOOKED_POST", PGA_DISASSEMBLY_HOOKED_POST);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_LIMITED", PGA_DISASSEMBLY_LIMITED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_LOOPS", PGA_DISASSEMBLY_LOOPS);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_LINKED", PGA_DISASSEMBLY_LINKED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_GROUPED", PGA_DISASSEMBLY_GROUPED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_RANKED", PGA_DISASSEMBLY_RANKED);
- if (result) result = add_const_to_group(values, "DISASSEMBLY_ENDED", PGA_DISASSEMBLY_ENDED);
- if (result) result = add_const_to_group(values, "DETECTION_OBFUSCATORS", PGA_DETECTION_OBFUSCATORS);
-
- if (!result)
- {
- Py_DECREF(values);
- goto exit;
- }
-
- result = attach_constants_group_to_type(type, true, "PluginAction", values,
- "Features with which plugins can extend the core of Chrysalide.");
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : arg = argument quelconque à tenter de convertir. *
-* dst = destination des valeurs récupérées en cas de succès. *
-* *
-* Description : Tente de convertir en constante PluginAction. *
-* *
-* Retour : Bilan de l'opération, voire indications supplémentaires. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-int convert_to_plugin_action(PyObject *arg, void *dst)
-{
- int result; /* Bilan à retourner */
- unsigned long value; /* Valeur récupérée */
-
- result = PyObject_IsInstance(arg, (PyObject *)&PyLong_Type);
-
- switch (result)
- {
- case -1:
- /* L'exception est déjà fixée par Python */
- result = 0;
- break;
-
- case 0:
- PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to PluginAction");
- break;
-
- case 1:
- value = PyLong_AsUnsignedLong(arg);
- *((PluginAction *)dst) = value;
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- return result;
-
-}
diff --git a/plugins/pychrysalide/plugins/module.c b/plugins/pychrysalide/plugins/module.c
index 1c7b326..ce94611 100644
--- a/plugins/pychrysalide/plugins/module.c
+++ b/plugins/pychrysalide/plugins/module.c
@@ -33,8 +33,8 @@
#include <plugins/pglist.h>
-#include "constants.h"
#include "plugin.h"
+#include "python.h"
#include "../helpers.h"
@@ -46,7 +46,7 @@ static PyObject *py_plugins_get_plugin_by_name(PyObject *, PyObject *);
static PyObject *py_plugins_get_all_plugins(PyObject *, PyObject *);
/* Fournit les greffons offrant le service demandé. */
-static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *);
+//static PyObject *py_plugins_get_all_plugins_for_action(PyObject *, PyObject *);
@@ -162,7 +162,7 @@ static PyObject *py_plugins_get_all_plugins(PyObject *self, PyObject *args)
}
-
+#if 0
/******************************************************************************
* *
* Paramètres : self = NULL car méthode statique. *
@@ -219,7 +219,7 @@ static PyObject *py_plugins_get_all_plugins_for_action(PyObject *self, PyObject
return result;
}
-
+#endif
/******************************************************************************
* *
@@ -248,7 +248,7 @@ bool add_plugins_module(PyObject *super)
static PyMethodDef py_plugins_methods[] = {
PY_PLUGINS_GET_PLUGIN_BY_NAME_METHOD,
PY_PLUGINS_GET_ALL_PLUGINS_METHOD,
- PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD,
+ //PY_PLUGINS_GET_ALL_PLUGINS_FOR_ACTION_METHOD,
{ NULL }
};
@@ -293,6 +293,7 @@ bool populate_plugins_module(void)
result = true;
if (result) result = ensure_python_plugin_module_is_registered();
+ if (result) result = ensure_python_python_plugin_is_registered();
assert(result);
diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c
index de070cb..78f57ba 100644
--- a/plugins/pychrysalide/plugins/plugin.c
+++ b/plugins/pychrysalide/plugins/plugin.c
@@ -25,22 +25,17 @@
#include "plugin.h"
-#include <assert.h>
-#include <libgen.h>
#include <malloc.h>
#include <pygobject.h>
#include <string.h>
-#include <common/extstr.h>
-#include <plugins/dt.h>
+#include <common/compiler.h>
#include <plugins/plugin-int.h>
#include <plugins/pglist.h>
#include <plugins/self.h>
-#include "constants.h"
-#include "translate.h"
#include "../access.h"
#include "../core.h"
#include "../helpers.h"
@@ -52,85 +47,53 @@
/* Initialise la classe des greffons d'extension. */
-static void py_plugin_module_init_gclass(GPluginModuleClass *, gpointer);
+static int py_plugin_module_init_gclass(GPluginModuleClass *, PyTypeObject *);
-CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE, py_plugin_module_init_gclass);
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE);
/* Initialise une instance sur la base du dérivé de GObject. */
static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds);
-/* Encadre une étape de la vie d'un greffon. */
-static bool py_plugin_module_manage_wrapper(GPluginModule *);
-
-/* Assiste la désactivation d'un greffon. */
-static bool py_plugin_module_exit(GPluginModule *);
-
-/* Accompagne la fin du chargement des modules natifs. */
-static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *, PluginAction);
-
-/* Fournit le nom brut associé au greffon par défaut. */
-static PyObject *py_plugin_module_get_modname_by_default(PyObject *, PyObject *);
+/* Pointe le fichier contenant le greffon manipulé. */
+static char *py_plugin_module_get_filename_wrapper(const GPluginModule *);
/* Fournit le nom brut associé au greffon. */
static char *py_plugin_module_get_modname_wrapper(const GPluginModule *);
-#if 0
-
-#ifdef INCLUDE_GTK_SUPPORT
-
-/* Complète une liste de resources pour thème. */
-static void py_plugin_module_include_theme_wrapper(const GPluginModule *, PluginAction, gboolean, char ***, size_t *);
-
-/* Rend compte de la création d'un panneau. */
-static void py_plugin_module_notify_panel_creation_wrapper(const GPluginModule *, PluginAction, GPanelItem *);
-
-/* Rend compte d'un affichage ou d'un retrait de panneau. */
-static void py_plugin_module_notify_panel_docking_wrapper(const GPluginModule *, PluginAction, GPanelItem *, bool);
-
-#endif
-
-/* Procède à une opération liée à un contenu binaire. */
-static void py_plugin_module_handle_binary_content_wrapper(const GPluginModule *, PluginAction, GBinContent *, wgroup_id_t, GtkStatusStack *);
-
-/* Procède à une opération liée à un contenu chargé. */
-static void py_plugin_module_handle_loaded_content_wrapper(const GPluginModule *, PluginAction, GLoadedContent *, wgroup_id_t, GtkStatusStack *);
+/* Prend acte de l'activation du greffon. */
+static bool py_plugin_module_enable_wrapper(GPluginModule *);
-/* Procède à une opération liée à l'analyse d'un format. */
-static bool py_plugin_module_handle_known_format_analysis_wrapper(const GPluginModule *, PluginAction, GKnownFormat *, wgroup_id_t, GtkStatusStack *);
+/* Prend acte de la désactivation du greffon. */
+static bool py_plugin_module_disable_wrapper(GPluginModule *);
-/* Procède à un préchargement de format de fichier. */
-static bool py_plugin_module_preload_binary_format_wrapper(const GPluginModule *, PluginAction, GBinFormat *, GPreloadInfo *, GtkStatusStack *);
-/* Procède au rattachement d'éventuelles infos de débogage. */
-static void py_plugin_module_attach_debug_format_wrapper(const GPluginModule *, PluginAction, GExeFormat *);
-/* Exécute une action pendant un désassemblage de binaire. */
-static void py_plugin_module_process_disassembly_event_wrapper(const GPluginModule *, PluginAction, GLoadedBinary *, GtkStatusStack *, GProcContext *);
-
-/* Effectue la détection d'effets d'outils externes. */
-static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *, PluginAction, const GLoadedContent *, bool, char ***, size_t *);
-
-#endif
+/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */
+/* Affiche un message dans le journal des messages système. */
+static PyObject *py_plugin_module_log_message(PyObject *, PyObject *);
-/* ------------------------- MODULE PYTHON POUR LES SCRIPTS ------------------------- */
+/* Indique le nom associé à un greffon. */
+static PyObject *py_plugin_module_get_name(PyObject *, void *);
+/* Fournit une description fonctionnelle d'un greffon. */
+static PyObject *py_plugin_module_get_desc(PyObject *, void *);
-/* Construit le nom d'un fichier de configuration du greffon. */
-static PyObject *py_plugin_module_build_config_filename(PyObject *, PyObject *);
+/* Fournit la version d'un greffon et de ses fonctionnalités. */
+static PyObject *py_plugin_module_get_version(PyObject *, void *);
-/* Affiche un message dans le journal des messages système. */
-static PyObject *py_plugin_module_log_message(PyObject *, PyObject *);
+/* Fournit l'URL des ressources en ligne liées à un greffon. */
+static PyObject *py_plugin_module_get_url(PyObject *, void *);
-/* Fournit le nom brut associé au greffon. */
-static PyObject *py_plugin_module_get_modname(PyObject *, void *);
+/* Fournit la liste des dépendances d'un greffon donné. */
+static PyObject *py_plugin_module_get_requirements(PyObject *, void *);
/* Indique le fichier contenant le greffon manipulé. */
static PyObject *py_plugin_module_get_filename(PyObject *, void *);
-/* Fournit la description du greffon dans son intégralité. */
-static PyObject *py_plugin_module_get_interface(PyObject *, void *);
+/* Fournit le nom brut associé au greffon. */
+static PyObject *py_plugin_module_get_modname(PyObject *, void *);
@@ -141,47 +104,26 @@ static PyObject *py_plugin_module_get_interface(PyObject *, void *);
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* unused = données non utilisées ici. *
+* Paramètres : gclass = classe GLib à initialiser. *
+* pyclass = classe Python à initialiser. *
* *
* Description : Initialise la classe des greffons d'extension. *
* *
-* Retour : - *
+* Retour : 0 pour indiquer un succès de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unused)
+static int py_plugin_module_init_gclass(GPluginModuleClass *gclass, PyTypeObject *pyclass)
{
- class->init = NULL;
- class->manage = py_plugin_module_manage_wrapper;
- class->exit = py_plugin_module_exit;
+ PY_CLASS_SET_WRAPPER(gclass->get_filename, py_plugin_module_get_filename_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->get_modname, py_plugin_module_get_modname_wrapper);
- class->plugins_loaded = py_plugin_module_notify_plugins_loaded_wrapper;
+ PY_CLASS_SET_WRAPPER(gclass->enable, py_plugin_module_enable_wrapper);
+ PY_CLASS_SET_WRAPPER(gclass->disable, py_plugin_module_disable_wrapper);
- class->get_modname = py_plugin_module_get_modname_wrapper;
-
-#if 0
-
-#ifdef INCLUDE_GTK_SUPPORT
- class->include_theme = py_plugin_module_include_theme_wrapper;
- class->notify_panel = py_plugin_module_notify_panel_creation_wrapper;
- class->notify_docking = py_plugin_module_notify_panel_docking_wrapper;
-#endif
-
- class->handle_content = py_plugin_module_handle_binary_content_wrapper;
- class->handle_loaded = py_plugin_module_handle_loaded_content_wrapper;
-
- class->handle_fmt_analysis = py_plugin_module_handle_known_format_analysis_wrapper;
- class->preload_format = py_plugin_module_preload_binary_format_wrapper;
- class->attach_debug = py_plugin_module_attach_debug_format_wrapper;
-
- class->process_disass = py_plugin_module_process_disassembly_event_wrapper;
-
- class->detect = py_plugin_module_detect_external_tools_wrapper;
-
-#endif
+ return 0;
}
@@ -202,45 +144,56 @@ static void py_plugin_module_init_gclass(GPluginModuleClass *class, gpointer unu
static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
{
+ const char *desc; /* Description plus loquace */
+ const char *version; /* Version du greffon */
+ const char *url; /* Site Web associé */
+ const char *name; /* Désignation humaine courte */
+ charp_array_t required; /* Liste des dépendances */
int ret; /* Bilan d'un appel */
GPluginModule *plugin; /* Greffon à manipuler */
- plugin_interface *iface; /* Interface à constituer */
- GPluginModule *dependency; /* Module nécessaire */
- PyObject *value; /* Valeur à présence imposée */
- size_t i; /* Boucle de parcours */
- PyObject *action; /* Identifiant d'une action */
+
+ static char *kwlist[] = { "name", "desc", "version", "url", "required", NULL };
#define PLUGIN_MODULE_DOC \
- "PythonModule is the class allowing the creation of Chrysalide plugins" \
- " for Python." \
+ "PluginModule is the core class handling the Chrysalide plugins." \
+ " Python plugins should inherit the pychrysalide.plugins.PythonPlugin" \
+ " instead of this one." \
"\n" \
"Calls to the *__init__* constructor of this abstract object expect" \
- " no particular argument.\n" \
+ " the following arguments as keyword parameters:\n" \
+ "* *name*: a string providing a key name for the plugin;\n" \
+ "* *desc* (optional): a string for a human readable description of the" \
+ " features provided by the plugin;\n" \
+ "* *version* (optional): a string providing the version of the plugin;" \
+ " Version format is free;\n" \
+ "* *url* (optional): a string for the homepage describing the plugin;\n"\
+ "* *required* (optional): dependencies of the plugin." \
"\n" \
- "Several items have to be defined as class attributes in the final" \
- " class:\n" \
- "* *_name*: a string providing a small name for the plugin;\n" \
- "* *_desc*: a string for a human readable description of the plugin;\n" \
- "* *_version*: a string providing the version of the plugin;\n" \
- "* *_url*: a string for the homepage describing the plugin;\n" \
- "* *_actions*: a tuple of" \
- " pychrysalide.plugins.PluginModule.PluginAction defining the features" \
- " the plugin is bringing; this list can be empty.\n" \
+ "Dependency to the *PyChrysalide* plugin is added automatically if not" \
+ " specified.\n" \
"\n" \
- "Depending on the implemented actions, some of the following methods" \
- " have to be defined for new classes:\n" \
- "* pychrysalide.plugins.PluginModule._init_config();\n" \
- "* pychrysalide.plugins.PluginModule._notify_plugins_loaded();\n" \
- "* pychrysalide.plugins.PluginModule._include_theme();\n" \
- "* pychrysalide.plugins.PluginModule._on_panel_creation;\n" \
- "* pychrysalide.plugins.PluginModule._on_panel_docking();\n" \
- "* pychrysalide.plugins.PluginModule._handle_binary_content();\n" \
- "* pychrysalide.plugins.PluginModule._handle_loaded_content();\n" \
- "* pychrysalide.plugins.PluginModule._handle_format_analysis();\n" \
- "* pychrysalide.plugins.PluginModule._preload_format();\n" \
- "* pychrysalide.plugins.PluginModule._attach_debug_format();\n" \
- "* pychrysalide.plugins.PluginModule._process_disassembly_event();\n" \
- "* pychrysalide.plugins.PluginModule._detect_external_tools()."
+ "The following methods have to be defined for new classes:\n" \
+ "* pychrysalide.plugins.PluginModule._get_filename();\n" \
+ "* pychrysalide.plugins.PluginModule._get_modname();\n" \
+ "* pychrysalide.plugins.PluginModule._enable();\n" \
+ "* pychrysalide.plugins.PluginModule._disable()."
+
+ /* Récupération des paramètres */
+
+ desc = NULL;
+ version = NULL;
+ url = NULL;
+ required.values = NULL;
+ required.length = 0;
+
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|sssO&", kwlist,
+ &name, &desc, &version, &url,
+ convert_to_sequence_to_charp_array, &required);
+ if (!ret) return -1;
+
+ required.values = realloc(required.values, ++required.length * sizeof(char *));
+
+ required.values[required.length - 1] = strdup("PyChrysalide");
/* Initialisation d'un objet GLib */
@@ -251,105 +204,17 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- iface = calloc(1, sizeof(plugin_interface));
- plugin->interface = iface;
-
-#define LOAD_PYTHON_IFACE(attr) \
- do \
- { \
- value = PyObject_GetAttrString(self, "_" #attr); \
- if (value == NULL) \
- { \
- PyErr_SetString(PyExc_TypeError, _("A '_" #attr "' class attributes is missing.")); \
- return -1; \
- } \
- if (PyUnicode_Check(value)) \
- { \
- iface->attr = strdup(PyUnicode_AsUTF8(value)); \
- Py_DECREF(value); \
- } \
- else \
- { \
- Py_DECREF(value); \
- PyErr_SetString(PyExc_TypeError, _("The '_" #attr "' class attributes must be a string.")); \
- return -1; \
- } \
- assert(iface->attr != NULL); \
- } \
- while (0);
-
- LOAD_PYTHON_IFACE(name);
- LOAD_PYTHON_IFACE(desc);
- LOAD_PYTHON_IFACE(version);
- LOAD_PYTHON_IFACE(url);
-
- iface->container = false;
-
- /**
- * Comme le greffon n'est pas passé par la résolution des dépendances,
- * orchestrée par la fonction g_plugin_module_resolve_dependencies(),
- * on simule l'effet attendu en obtenant une référence par un appel à
- * get_plugin_by_name().
- *
- * L'incrémentation des références doit coller au plus près de
- * l'inscription nominative du greffon : en cas de sortie impromptue
- * (lorsqu'une erreur intervient pendant un chargement par exemple),
- * l'état de l'ensemble est ainsi cohérent au moment du retrait du
- * greffon fautif via la fonction g_plugin_module_dispose().
- */
-
- lock_plugin_list_for_reading();
- dependency = get_plugin_by_name("PyChrysalide", NULL);
- unlock_plugin_list_for_reading();
-
- assert(dependency != NULL);
-
- if (dependency == NULL)
- {
- PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!"));
- return -1;
- }
-
- iface->required = malloc(sizeof(char *));
- iface->required[0] = "PyChrysalide";
- iface->required_count = 1;
+ STORE_PLUGIN_ABI(plugin);
- /* Validation du reste de l'interface */
-
- value = PyObject_GetAttrString(self, "_actions");
-
- if (value == NULL)
+ if (!g_plugin_module_create(plugin, name, desc, version, url,
+ CONST_ARRAY_CAST(required.values, char), required.length))
{
- PyErr_SetString(PyExc_TypeError, _("An '_actions' class attributes is missing."));
+ clean_charp_array(&required);
+ PyErr_SetString(PyExc_ValueError, _("Unable to create plugin module."));
return -1;
}
- if (!PyTuple_Check(value))
- {
- Py_DECREF(value);
- PyErr_SetString(PyExc_TypeError, _("The '_actions' class attributes must be a tuple."));
- return -1;
- }
-
- iface->actions_count = PyTuple_Size(value);
- iface->actions = malloc(iface->actions_count * sizeof(plugin_action_t));
-
- for (i = 0; i < iface->actions_count; i++)
- {
- action = PyTuple_GetItem(value, i);
-
- if (!PyLong_Check(action))
- {
- Py_DECREF(value);
- PyErr_SetString(PyExc_TypeError, _("invalid type for plugin action."));
- return -1;
- }
-
- iface->actions[i] = PyLong_AsUnsignedLong(action);
-
- }
-
- Py_DECREF(value);
+ clean_charp_array(&required);
return 0;
@@ -358,56 +223,55 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à manipuler. *
+* Paramètres : plugin = greffon à consulter. *
* *
-* Description : Encadre une étape de la vie d'un greffon. *
+* Description : Pointe le fichier contenant le greffon manipulé. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Chemin d'accès au greffon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_plugin_module_manage_wrapper(GPluginModule *plugin)
+static char *py_plugin_module_get_filename_wrapper(const GPluginModule *plugin)
{
- bool result; /* Bilan à faire remonter */
+ char *result; /* Désignation brute à renvoyer*/
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
- PyObject *args; /* Arguments pour l'appel */
PyObject *pyret; /* Bilan d'exécution */
-#define PLUGIN_MODULE_MANAGE_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _manage, "$self, action, /", \
- METH_VARARGS, \
- "Abstract method called to react to several steps of the plugin" \
- " life.\n" \
- "\n" \
- "The expected action is a" \
- " pychrysalide.plugins.PluginModule.PluginAction value.\n" \
- "\n" \
- "This method has to be defined in order to handle actions such as" \
- " *PLUGIN_LOADED*." \
+#define PLUGIN_MODULE_GET_FILENAME_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _get_filename, "$self, /", \
+ METH_NOARGS, \
+ "Abstract method providing the filename of the script.\n" \
+ "\n" \
+ "The result should be the string value pointing to the plugin" \
+ " file path.\n" \
)
- result = true;
+ result = NULL;
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(plugin));
- if (has_python_method(pyobj, "_manage"))
+ if (has_python_method(pyobj, "_get_filename"))
{
- args = PyTuple_New(1);
+ pyret = run_python_method(pyobj, "_get_filename", NULL);
- PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(PGA_PLUGIN_LOADED));
+ if (pyret != NULL)
+ {
+ if (!PyUnicode_Check(pyret))
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("The returned raw name must be a string"));
- pyret = run_python_method(pyobj, "_manage", args);
+ else
+ result = strdup(PyUnicode_DATA(pyret));
- result = (pyret == Py_True);
+ }
Py_XDECREF(pyret);
- Py_DECREF(args);
}
@@ -422,44 +286,62 @@ static bool py_plugin_module_manage_wrapper(GPluginModule *plugin)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à manipuler. *
+* Paramètres : plugin = greffon à valider. *
* *
-* Description : Assiste la désactivation d'un greffon. *
+* Description : Fournit le nom brut associé au greffon. *
* *
-* Retour : Bilan de l'opération. *
+* Retour : Désignation brute du greffon. *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool py_plugin_module_exit(GPluginModule *plugin)
+static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
{
- bool result; /* Bilan à faire remonter */
- plugin_interface *final; /* Interface finale conservée */
+ char *result; /* Désignation brute à renvoyer*/
+ PyGILState_STATE gstate; /* Sauvegarde d'environnement */
+ PyObject *pyobj; /* Objet Python concerné */
+ PyObject *pyret; /* Bilan d'exécution */
- result = true;
+#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _get_modname, "$self, /", \
+ METH_NOARGS, \
+ "Abstract method providing the raw module name of the loaded" \
+ " plugin.\n" \
+ "\n" \
+ "The result should be a short string value.\n" \
+)
- final = (plugin_interface *)G_PLUGIN_MODULE(plugin)->interface;
+ result = NULL;
+
+ gstate = PyGILState_Ensure();
- if (final != NULL)
+ pyobj = pygobject_new(G_OBJECT(plugin));
+
+ if (has_python_method(pyobj, "_get_modname"))
{
- if (final->name != NULL) free(final->name);
- if (final->desc != NULL) free(final->desc);
- if (final->version != NULL) free(final->version);
- if (final->url != NULL) free(final->url);
+ pyret = run_python_method(pyobj, "_get_modname", NULL);
- assert(final->required_count == 1);
+ if (pyret != NULL)
+ {
+ if (!PyUnicode_Check(pyret))
+ g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
+ _("The returned raw name must be a string"));
- if (final->required != NULL)
- free(final->required);
+ else
+ result = strdup(PyUnicode_DATA(pyret));
- if (final->actions != NULL)
- free(final->actions);
+ }
- free(final);
+ Py_XDECREF(pyret);
}
+ Py_DECREF(pyobj);
+
+ PyGILState_Release(gstate);
+
return result;
}
@@ -468,51 +350,45 @@ static bool py_plugin_module_exit(GPluginModule *plugin)
/******************************************************************************
* *
* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
* *
-* Description : Accompagne la fin du chargement des modules natifs. *
+* Description : Prend acte de l'activation du greffon. *
* *
-* Retour : - *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin, PluginAction action)
+static bool py_plugin_module_enable_wrapper(GPluginModule *plugin)
{
+ bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
- PyObject *args; /* Arguments pour l'appel */
PyObject *pyret; /* Bilan d'exécution */
-#define PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER PYTHON_WRAPPER_DEF \
-( \
- _notify_plugins_loaded, "$self, action, /", \
- METH_VARARGS, \
- "Abstract method called once all the (native?) plugins are" \
- " loaded.\n" \
- "\n" \
- "The expected action is a" \
- " pychrysalide.plugins.PluginModule.PluginAction value.\n" \
- "\n" \
- "This method has to be defined in order to handle actions such as" \
- " *NATIVE_PLUGINS_LOADED* or *PLUGINS_LOADED*." \
+#define PLUGIN_MODULE_ENABLE_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _enable, "$self, /", \
+ METH_VARARGS, \
+ "Abstract method called when the plugin gets enabled.\n" \
+ "\n" \
+ "The result is a boolean status: *False* if an implementation" \
+ " failed, *True* otherwise." \
)
+ result = true;
+
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(plugin));
- if (has_python_method(pyobj, "_notify_plugins_loaded"))
+ if (has_python_implementation_method(pyobj, "_enable"))
{
- args = PyTuple_New(1);
-
- PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(action));
+ pyret = run_python_method(pyobj, "_enable", NULL);
- pyret = run_python_method(pyobj, "_notify_plugins_loaded", args);
+ result = (pyret == Py_True);
Py_XDECREF(pyret);
- Py_DECREF(args);
}
@@ -520,36 +396,6 @@ static void py_plugin_module_notify_plugins_loaded_wrapper(GPluginModule *plugin
PyGILState_Release(gstate);
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : self = objet Python concerné par l'appel. *
-* args = arguments fournis à l'appel. *
-* *
-* Description : Fournit le nom brut associé au greffon par défaut. *
-* *
-* Retour : Désignation brute du greffon. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObject *args)
-{
- PyObject *result; /* Bilan à retourner */
- GPluginModule *plugin; /* Version native du greffon */
- char *path; /* Chemin à traiter */
-
- plugin = G_PLUGIN_MODULE(pygobject_get(self));
-
- path = strdup(g_plugin_module_get_filename(plugin));
-
- result = PyUnicode_FromString(basename(path));
-
- free(path);
-
return result;
}
@@ -557,52 +403,44 @@ static PyObject *py_plugin_module_get_modname_by_default(PyObject *self, PyObjec
/******************************************************************************
* *
-* Paramètres : plugin = greffon à valider. *
+* Paramètres : plugin = greffon à manipuler. *
* *
-* Description : Fournit le nom brut associé au greffon. *
+* Description : Prend acte de la désactivation du greffon. *
* *
-* Retour : Désignation brute du greffon. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
+static bool py_plugin_module_disable_wrapper(GPluginModule *plugin)
{
- char *result; /* Désignation brute à renvoyer*/
+ bool result; /* Bilan à retourner */
PyGILState_STATE gstate; /* Sauvegarde d'environnement */
PyObject *pyobj; /* Objet Python concerné */
PyObject *pyret; /* Bilan d'exécution */
-#define PLUGIN_MODULE_GET_MODNAME_WRAPPER PYTHON_WRAPPER_DEF_WITH \
-( \
- _get_modname, "$self, /", \
- METH_VARARGS, py_plugin_module_get_modname_by_default, \
- "(Abstract) method providing the raw module name of the plugin.\n" \
- " loaded.\n" \
- "\n" \
- "The result should be a short string value.\n" \
- "\n" \
- "A default implementation builds the module name from the Python" \
- " script filename." \
+#define PLUGIN_MODULE_DISABLE_WRAPPER PYTHON_WRAPPER_DEF \
+( \
+ _disable, "$self, /", \
+ METH_VARARGS, \
+ "Abstract method called when the plugin gets disabled.\n" \
+ "\n" \
+ "The result is a boolean status: *False* if an implementation" \
+ " failed, *True* otherwise." \
)
- result = NULL;
+ result = true;
gstate = PyGILState_Ensure();
pyobj = pygobject_new(G_OBJECT(plugin));
- if (has_python_method(pyobj, "_get_modname"))
+ if (has_python_implementation_method(pyobj, "_disable"))
{
- pyret = run_python_method(pyobj, "_get_modname", NULL);
+ pyret = run_python_method(pyobj, "_disable", NULL);
- if (!PyUnicode_Check(pyret))
- g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
- _("The returned raw name must be a string"));
-
- else
- result = strdup(PyUnicode_DATA(pyret));
+ result = (pyret == Py_True);
Py_XDECREF(pyret);
@@ -617,6 +455,15 @@ static char *py_plugin_module_get_modname_wrapper(const GPluginModule *plugin)
}
+
+
+
+
+
+
+
+
+
#if 0
#ifdef INCLUDE_GTK_SUPPORT
@@ -1382,51 +1229,42 @@ static void py_plugin_module_detect_external_tools_wrapper(const GPluginModule *
* Paramètres : self = objet Python concerné par l'appel. *
* args = arguments fournis à l'appel. *
* *
-* Description : Construit le nom d'un fichier de configuration du greffon. *
+* Description : Affiche un message dans le journal des messages système. *
* *
-* Retour : Chemin d'accès déterminé, ou NULL en cas d'erreur. *
+* Retour : Rien en équivalent Python. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject *args)
+static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
{
PyObject *result; /* Bilan à retourner */
- const char *final; /* Suffixe de fichier imposé */
- int create; /* Volonté de création */
- char *filename; /* Nom de fichier déterminé */
+ LogMessageType type; /* Espèce du message */
+ const char *msg; /* Contenu du message */
-#define PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD PYTHON_METHOD_DEF \
+#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \
( \
- build_config_filename, "final, /, create=False", \
+ log_message, "type, msg, /", \
METH_VARARGS, py_plugin_module, \
- "Build a filename suitable for the plugin configuration, ending with" \
- " the *final* suffix.\n" \
+ "Display a message in the log window, in graphical mode, or in the" \
+ " console output if none.\n" \
"\n" \
- "If the *create* parameter is set, the path to this filename is" \
- " created.\n" \
+ "The type of the message has to be a pychrysalide.core.LogMessageType" \
+ " value." \
"\n" \
- "The result is a string or None on failure." \
+ "The only difference with the main pychrysalide.core.log_message()" \
+ " function is that messages are automatically prefixed with the plugin" \
+ " name here." \
)
- create = 0;
-
- if (!PyArg_ParseTuple(args, "s|p", &final, &create))
+ if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg))
return NULL;
- filename = g_plugin_module_build_config_filename(G_PLUGIN_MODULE(pygobject_get(self)), final, create);
+ g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg);
- if (filename != NULL)
- {
- result = PyUnicode_FromString(filename);
- free(filename);
- }
- else
- {
- result = Py_None;
- Py_INCREF(result);
- }
+ result = Py_None;
+ Py_INCREF(result);
return result;
@@ -1435,45 +1273,35 @@ static PyObject *py_plugin_module_build_config_filename(PyObject *self, PyObject
/******************************************************************************
* *
-* Paramètres : self = objet Python concerné par l'appel. *
-* args = arguments fournis à l'appel. *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
* *
-* Description : Affiche un message dans le journal des messages système. *
+* Description : Indique le nom associé à un greffon. *
* *
-* Retour : Rien en équivalent Python. *
+* Retour : Désignation interne de l'extension, pour référence(s). *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
+static PyObject *py_plugin_module_get_name(PyObject *self, void *closure)
{
- PyObject *result; /* Bilan à retourner */
- LogMessageType type; /* Espèce du message */
- const char *msg; /* Contenu du message */
+ PyObject *result; /* Valeur à retourner */
+ GPluginModule *plugin; /* Version native du greffon */
+ const char *name; /* Nom attribué au greffon */
-#define PLUGIN_MODULE_LOG_MESSAGE_METHOD PYTHON_METHOD_DEF \
-( \
- log_message, "type, msg, /", \
- METH_VARARGS, py_plugin_module, \
- "Display a message in the log window, in graphical mode, or in the" \
- " console output if none.\n" \
- "\n" \
- "The type of the message has to be a pychrysalide.core.LogMessageType" \
- " value." \
- "\n" \
- "The only difference with the main pychrysalide.core.log_message()" \
- " function is that messages are automatically prefixed with the plugin" \
- " name here." \
+#define PLUGIN_MODULE_NAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ name, py_plugin_module, \
+ "Name of the plugin. This string value is used to" \
+ " reference the plugin as dependency." \
)
- if (!PyArg_ParseTuple(args, "O&s", convert_to_log_message_type, &type, &msg))
- return NULL;
+ plugin = G_PLUGIN_MODULE(pygobject_get(self));
- g_plugin_module_log_simple_message(G_PLUGIN_MODULE(pygobject_get(self)), type, msg);
+ name = g_plugin_module_get_name(plugin);
- result = Py_None;
- Py_INCREF(result);
+ result = PyUnicode_FromString(name);
return result;
@@ -1485,32 +1313,31 @@ static PyObject *py_plugin_module_log_message(PyObject *self, PyObject *args)
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Fournit le nom brut associé au greffon. *
+* Description : Fournit une description fonctionnelle d'un greffon. *
* *
-* Retour : Désignation brute du greffon. *
+* Retour : Description textuelle associée à une extension ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_desc(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- char *modname; /* Désignation brute */
+ const char *desc; /* Description du greffon */
-#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \
-( \
- modname, py_plugin_module, \
- "Raw module name of the plugin." \
+#define PLUGIN_MODULE_DESC_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ desc, py_plugin_module, \
+ "Optional plugin description as string or *None*." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- modname = g_plugin_module_get_modname(plugin);
- result = PyUnicode_FromString(modname);
+ desc = g_plugin_module_get_desc(plugin);
- free(modname);
+ result = PyUnicode_FromString(desc);
return result;
@@ -1522,30 +1349,32 @@ static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure)
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Indique le fichier contenant le greffon manipulé. *
+* Description : Fournit la version d'un greffon et de ses fonctionnalités. *
* *
-* Retour : Chemin d'accès au greffon. *
+* Retour : Version sous forme de chaîne de caractères ou None. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_version(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- const char *filename; /* Chemin d'accès associé */
+ const char *version; /* Version du greffon */
-#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \
+#define PLUGIN_MODULE_VERSION_ATTRIB PYTHON_GET_DEF_FULL \
( \
- filename, py_plugin_module, \
- "Filename of the plugin." \
+ version, py_plugin_module, \
+ "Optional plugin version, in free format, as string," \
+ " or *None*." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- filename = g_plugin_module_get_filename(plugin);
- result = PyUnicode_FromString(filename);
+ version = g_plugin_module_get_version(plugin);
+
+ result = PyUnicode_FromString(version);
return result;
@@ -1557,113 +1386,204 @@ static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure)
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Fournit la description du greffon dans son intégralité. *
+* Description : Fournit l'URL des ressources en ligne liées à un greffon. *
* *
-* Retour : Interfaçage renseigné. *
+* Retour : URL de renvoi associée à une extension ou None. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_interface(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_url(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- const plugin_interface *iface; /* Interface liée à traduire */
+ const char *url; /* URL associée au greffon */
-#define PLUGIN_MODULE_INTERFACE_ATTRIB PYTHON_GET_DEF_FULL \
+#define PLUGIN_MODULE_URL_ATTRIB PYTHON_GET_DEF_FULL \
( \
- interface, py_plugin_module, \
- "Interface exported by the plugin..\n" \
- "\n" \
- "This property is a pychrysalide.StructObject instance." \
- "\n" \
- "The provided information is composed of the following" \
- " properties :\n" \
- "\n" \
- "* gtp_name;\n" \
- "* name;\n" \
- "* desc;\n" \
- "* version;\n" \
- "* url;\n" \
- "* container;\n" \
- "* required;\n" \
- "* actions.\n" \
- "\n" \
- "The *gtp_name* value may be *None* for non-native plugin." \
- " All other fields carry a string value except:\n" \
- "* *container*: a boolean status indicating if the plugin" \
- " can embed other plugins;\n" \
- "* *required*: a tuple of depedencies names;\n" \
- "* *actions*: a tuple of available features from the plugin"\
- " coded as pychrysalide.plugins.PluginModule.PluginAction" \
- " values." \
+ url, py_plugin_module, \
+ "Optional URL pointing to the plugin homepage as string" \
+ " or *None*." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- iface = g_plugin_module_get_interface(plugin);
- result = translate_plugin_interface_to_python(iface);
+ url = g_plugin_module_get_url(plugin);
+
+ result = PyUnicode_FromString(url);
return result;
}
-#if 0
+
/******************************************************************************
* *
* Paramètres : self = objet Python concerné par l'appel. *
* closure = non utilisé ici. *
* *
-* Description : Fournit la configuration mise en place pour le greffon. *
+* Description : Fournit la liste des dépendances d'un greffon donné. *
* *
-* Retour : Configuration dédiée à l'extension. *
+* Retour : Liste des noms d'extensions requises pour une extension. *
* *
* Remarques : - *
* *
******************************************************************************/
-static PyObject *py_plugin_module_get_config(PyObject *self, void *closure)
+static PyObject *py_plugin_module_get_requirements(PyObject *self, void *closure)
{
PyObject *result; /* Valeur à retourner */
GPluginModule *plugin; /* Version native du greffon */
- GGenConfig *config; /* Configuration associée */
+ const char * const *required; /* Liste de dépendances */
+ size_t count; /* Nombre de ces dépendances */
+ size_t i; /* Boucle de parcours */
-#define PLUGIN_MODULE_CONFIG_ATTRIB PYTHON_GET_DEF_FULL \
+#define PLUGIN_MODULE_REQUIREMENTS_ATTRIB PYTHON_GET_DEF_FULL \
( \
- config, py_plugin_module, \
- "Dedicated configuration for the plugin." \
- "\n" \
- "The value is a pychrysalide.glibext.GenConfig instance" \
- " or None if the configuration is not yet created.\n" \
- "\n" \
- "As configuration storage path depends on the plugin name," \
- " all plugin properties have to get fully loaded by the" \
- " core before the configuration can be setup." \
- "automatically" \
+ requirements, py_plugin_module, \
+ "Tuple of the plugin dependencies." \
+)
+
+ plugin = G_PLUGIN_MODULE(pygobject_get(self));
+
+ required = g_plugin_module_get_requirements(plugin, &count);
+
+ result = PyTuple_New(count);
+
+ for (i = 0; i < count; i++)
+ PyTuple_SetItem(result, i, PyUnicode_FromString(required[i]));
+
+ return result;
+
+}
+
+
+
+
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique le fichier contenant le greffon manipulé. *
+* *
+* Retour : Chemin d'accès au greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_plugin_module_get_filename(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GPluginModule *plugin; /* Version native du greffon */
+ char *filename; /* Chemin d'accès associé */
+
+#define PLUGIN_MODULE_FILENAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ filename, py_plugin_module, \
+ "Filename of the plugin." \
+)
+
+ plugin = G_PLUGIN_MODULE(pygobject_get(self));
+
+ filename = g_plugin_module_get_filename(plugin);
+
+ if (filename != NULL)
+ {
+ result = PyUnicode_FromString(filename);
+
+ free(filename);
+
+ }
+
+ else
+ {
+ /**
+ * La méthode de classe sollicitée a renvoyé une valeur nulle.
+ *
+ * Si cette méthode correspond à une implémentation Python
+ * (avec un appel à not_yet_implemented_method()), une exception
+ * est déjà en place.
+ *
+ * Si aucune exception n'a été prévue, un rattrapage est effectué ici.
+ */
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as filename"));
+
+ result = NULL;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit le nom brut associé au greffon. *
+* *
+* Retour : Désignation brute du greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_plugin_module_get_modname(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GPluginModule *plugin; /* Version native du greffon */
+ char *modname; /* Désignation brute */
+
+#define PLUGIN_MODULE_MODNAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ modname, py_plugin_module, \
+ "Raw module name of the plugin." \
)
plugin = G_PLUGIN_MODULE(pygobject_get(self));
- config = g_plugin_module_get_config(plugin);
- if (config == NULL)
+ modname = g_plugin_module_get_modname(plugin);
+
+ if (modname != NULL)
{
- result = Py_None;
- Py_INCREF(result);
+ result = PyUnicode_FromString(modname);
+
+ free(modname);
+
}
else
{
- result = pygobject_new(G_OBJECT(config));
+ /**
+ * La méthode de classe sollicitée a renvoyé une valeur nulle.
+ *
+ * Si cette méthode correspond à une implémentation Python
+ * (avec un appel à not_yet_implemented_method()), une exception
+ * est déjà en place.
+ *
+ * Si aucune exception n'a été prévue, un rattrapage est effectué ici.
+ */
+
+ if (PyErr_Occurred() == NULL)
+ PyErr_SetString(PyExc_NotImplementedError, _("unexpected NULL value as modname"));
- g_object_unref(G_OBJECT(config));
+ result = NULL;
}
return result;
}
-#endif
/******************************************************************************
@@ -1681,33 +1601,22 @@ static PyObject *py_plugin_module_get_config(PyObject *self, void *closure)
PyTypeObject *get_python_plugin_module_type(void)
{
static PyMethodDef py_plugin_module_methods[] = {
- PLUGIN_MODULE_MANAGE_WRAPPER,
- PLUGIN_MODULE_NOTIFY_PLUGINS_LOADED_WRAPPER,
+ PLUGIN_MODULE_GET_FILENAME_WRAPPER,
PLUGIN_MODULE_GET_MODNAME_WRAPPER,
-#if 0
-#ifdef INCLUDE_GTK_SUPPORT
- PLUGIN_MODULE_INCLUDE_THEME_WRAPPER,
- PLUGIN_MODULE_ON_PANEL_CREATION_WRAPPER,
- PLUGIN_MODULE_ON_PANEL_DOCKING_WRAPPER,
-#endif
- PLUGIN_MODULE_HANDLE_BINARY_CONTENT_WRAPPER,
- PLUGIN_MODULE_HANDLE_LOADED_CONTENT_WRAPPER,
- PLUGIN_MODULE_HANDLE_BINARY_FORMAT_ANALYSIS_WRAPPER,
- PLUGIN_MODULE_PRELOAD_BINARY_FORMAT_WRAPPER,
- PLUGIN_MODULE_ATTACH_DEBUG_FORMAT_WRAPPER,
- PLUGIN_MODULE_PROCESS_DISASSEMBLY_EVENT_WRAPPER,
- PLUGIN_MODULE_DETECT_EXTERNAL_TOOLS_WRAPPER,
- PLUGIN_MODULE_BUILD_CONFIG_FILENAME_METHOD,
-#endif
+ PLUGIN_MODULE_ENABLE_WRAPPER,
+ PLUGIN_MODULE_DISABLE_WRAPPER,
PLUGIN_MODULE_LOG_MESSAGE_METHOD,
{ NULL }
};
static PyGetSetDef py_plugin_module_getseters[] = {
- PLUGIN_MODULE_MODNAME_ATTRIB,
+ PLUGIN_MODULE_NAME_ATTRIB,
+ PLUGIN_MODULE_DESC_ATTRIB,
+ PLUGIN_MODULE_VERSION_ATTRIB,
+ PLUGIN_MODULE_URL_ATTRIB,
+ PLUGIN_MODULE_REQUIREMENTS_ATTRIB,
PLUGIN_MODULE_FILENAME_ATTRIB,
- PLUGIN_MODULE_INTERFACE_ATTRIB,
- //PLUGIN_MODULE_CONFIG_ATTRIB,
+ PLUGIN_MODULE_MODNAME_ATTRIB,
{ NULL }
};
@@ -1761,10 +1670,9 @@ bool ensure_python_plugin_module_is_registered(void)
dict = PyModule_GetDict(module);
- if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type))
- return false;
+ pyg_register_class_init(G_TYPE_PLUGIN_MODULE, (PyGClassInitFunc)py_plugin_module_init_gclass);
- if (!define_plugin_module_constants(type))
+ if (!register_class_for_pygobject(dict, G_TYPE_PLUGIN_MODULE, type))
return false;
}
@@ -1772,80 +1680,3 @@ bool ensure_python_plugin_module_is_registered(void)
return true;
}
-
-
-/******************************************************************************
-* *
-* Paramètres : modname = nom du module à charger. *
-* filename = chemin d'accès au code Python à charger. *
-* *
-* Description : Crée un greffon à partir de code Python. *
-* *
-* Retour : Adresse de la structure mise en place ou NULL si erreur. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GPluginModule *create_python_plugin(const char *modname, const char *filename)
-{
- GPluginModule *result; /* Structure à retourner */
- PyObject *name; /* Chemin d'accès pour Python */
- PyObject *module; /* Script Python chargé */
- PyObject *dict; /* Dictionnaire associé */
- PyObject *class; /* Classe à instancier */
- PyObject *instance; /* Instance Python du greffon */
-
- name = PyUnicode_FromString(modname);
- if (name == NULL) goto bad_exit;
-
- module = PyImport_Import(name);
- Py_DECREF(name);
-
- if (module == NULL) goto no_import;
-
- dict = PyModule_GetDict(module);
- class = PyDict_GetItemString(dict, "AutoLoad");
-
- if (class == NULL) goto no_class;
- if (!PyType_Check(class->ob_type)) goto no_class;
-
- instance = PyObject_CallFunction(class, NULL);
- if (instance == NULL) goto no_instance;
-
- result = G_PLUGIN_MODULE(pygobject_get(instance));
-
- result->filename = strdup(filename);
-
- /**
- * L'instance Python et l'objet GLib résultante sont un même PyGObject.
- *
- * Donc pas besoin de toucher au comptage des références ici, la libération
- * se réalisera à la fin, quand l'objet GLib sera libéré.
- */
-
- Py_DECREF(module);
-
- return result;
-
- no_instance:
-
- log_pychrysalide_exception(_("An error occured when building the 'AutoLoad' instance"));
-
- no_class:
-
- if (class == NULL)
- log_plugin_simple_message(LMT_ERROR,
- _("An error occured when looking for the 'AutoLoad': item not found!"));
-
- no_import:
-
- Py_XDECREF(module);
-
- log_pychrysalide_exception(_("An error occured when importing '%s'"), modname);
-
- bad_exit:
-
- return NULL;
-
-}
diff --git a/plugins/pychrysalide/plugins/plugin.h b/plugins/pychrysalide/plugins/plugin.h
index ad54b8e..fd3d239 100644
--- a/plugins/pychrysalide/plugins/plugin.h
+++ b/plugins/pychrysalide/plugins/plugin.h
@@ -27,13 +27,9 @@
#include <Python.h>
-#include <glib-object.h>
#include <stdbool.h>
-#include <plugins/plugin.h>
-
-
/* Fournit un accès à une définition de type à diffuser. */
PyTypeObject *get_python_plugin_module_type(void);
@@ -41,9 +37,6 @@ PyTypeObject *get_python_plugin_module_type(void);
/* Prend en charge l'objet 'pychrysalide.plugins.PluginModule'. */
bool ensure_python_plugin_module_is_registered(void);
-/* Crée un greffon à partir de code Python. */
-GPluginModule *create_python_plugin(const char *, const char *);
-
#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PLUGIN_H */
diff --git a/plugins/pychrysalide/plugins/python-int.h b/plugins/pychrysalide/plugins/python-int.h
new file mode 100644
index 0000000..7408d18
--- /dev/null
+++ b/plugins/pychrysalide/plugins/python-int.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * python-int.h - prototypes internes pour la déclinaison Python de greffons
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H
+#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H
+
+
+#include "python.h"
+
+
+#include <plugins/plugin-int.h>
+
+
+
+/* Greffon Python pour Chrysalide (instance) */
+struct _GPythonPlugin
+{
+ GPluginModule parent; /* A laisser en premier */
+
+ char *file; /* Valeur initiale de __file__ */
+
+};
+
+
+/* Greffon Python pour Chrysalide (classe) */
+struct _GPythonPluginClass
+{
+ GPluginModuleClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un greffon Python. */
+bool g_python_plugin_create(GPythonPlugin *, const char *, const char *, const char *, const char *, char ***, size_t *, const char *);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_INT_H */
diff --git a/plugins/pychrysalide/plugins/python.c b/plugins/pychrysalide/plugins/python.c
new file mode 100644
index 0000000..d6b9281
--- /dev/null
+++ b/plugins/pychrysalide/plugins/python.c
@@ -0,0 +1,489 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * python.c - déclinaison Python de greffons
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "python.h"
+
+
+#include <malloc.h>
+#include <pygobject.h>
+
+
+#include <common/compiler.h>
+#include <common/cpp.h>
+
+
+#include "plugin.h"
+#include "python-int.h"
+#include "../access.h"
+#include "../helpers.h"
+
+
+
+/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */
+
+
+/* Initialise la classe des greffons Python. */
+static void g_python_plugin_class_init(GPythonPluginClass *);
+
+/* Initialise une instance de greffon Python. */
+static void g_python_plugin_init(GPythonPlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_python_plugin_dispose(GPythonPlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_python_plugin_finalize(GPythonPlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Pointe le fichier contenant le greffon manipulé. */
+static char *g_python_plugin_get_filename(const GPythonPlugin *);
+
+/* Fournit le nom brut associé au greffon. */
+static char *g_python_plugin_get_modname(const GPythonPlugin *);
+
+
+
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon Python. */
+G_DEFINE_TYPE(GPythonPlugin, g_python_plugin, G_TYPE_PLUGIN_MODULE);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_class_init(GPythonPluginClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_python_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_python_plugin_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->get_filename = (get_plugin_filename_fc)g_python_plugin_get_filename;
+ plugin->get_modname = (get_plugin_modname_fc)g_python_plugin_get_modname;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_init(GPythonPlugin *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
+
+ plugin->file = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_dispose(GPythonPlugin *plugin)
+{
+ G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_plugin_finalize(GPythonPlugin *plugin)
+{
+ if (plugin->file != NULL)
+ free(plugin->file);
+
+ G_OBJECT_CLASS(g_python_plugin_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* name = nom du greffon pour référence, principalement. *
+* desc = présentation éventuelle à destination humaine. *
+* version = indication de version éventuelle. *
+* url = référence vers une ressource en ligne. *
+* required = liste de dépendances éventuelles ou NULL. *
+* count = taille de cette liste. *
+* file = emplacement du script considéré. *
+* *
+* Description : Met en place un greffon Python. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_python_plugin_create(GPythonPlugin *plugin, const char *name, const char *desc, const char *version, const char *url, char ***required, size_t *count, const char *file)
+{
+ bool result; /* Bilan à retourner */
+
+ /* Ajout imposé d'une dépendance à Python */
+
+ *required = realloc(*required, ++(*count) * sizeof(char *));
+
+ (*required)[*count - 1] = strdup("PyChrysalide");
+
+ /* Poursuite de la mise en place */
+
+ result = g_plugin_module_create(G_PLUGIN_MODULE(plugin),
+ name, desc, version, url,
+ CONST_ARRAY_CAST(*required, char), *count);
+
+ if (result)
+ plugin->file = strdup(file);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à consulter. *
+* *
+* Description : Pointe le fichier contenant le greffon manipulé. *
+* *
+* Retour : Chemin d'accès au greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_python_plugin_get_filename(const GPythonPlugin *plugin)
+{
+ char *result; /* Chemin d'accès à renvoyer */
+
+ result = strdup(plugin->file);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à valider. *
+* *
+* Description : Fournit le nom brut associé au greffon. *
+* *
+* Retour : Désignation brute du greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_python_plugin_get_modname(const GPythonPlugin *plugin)
+{
+ char *result; /* Désignation brute à renvoyer*/
+ char *filename; /* Chemin du script Python */
+ char *name; /* Nom du fichier associé */
+ size_t length; /* Taille du nom */
+ int ret; /* Bilan d'une comparaison */
+
+ filename = g_python_plugin_get_filename(plugin);
+
+ name = basename(filename);
+
+ length = strlen(name);
+
+#define PYTHON_SUFFIX ".py"
+
+ ret = strncmp(&name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)],
+ PYTHON_SUFFIX,
+ STATIC_STR_SIZE(PYTHON_SUFFIX));
+
+ if (ret == 0)
+ name[length - STATIC_STR_SIZE(PYTHON_SUFFIX)] = '\0';
+
+ result = strdup(name);
+
+ free(filename);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GLUE POUR CREATION DEPUIS PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ const char *version; /* Version du greffon */
+ const char *url; /* Site Web associé */
+ charp_array_t required; /* Liste des dépendances */
+ const char *file; /* Emplacement du script */
+ int ret; /* Bilan de lecture des args. */
+ PyObject *value; /* Valeur d'un arg. implicite */
+ const char *name; /* Désignation humaine courte */
+ const char *desc; /* Description plus loquace */
+ GPythonPlugin *plugin; /* Greffon à manipuler */
+
+ static char *kwlist[] = { "file", "version", "url", "required", NULL };
+
+#define PYTHON_PLUGIN_DOC \
+ "The PythonPlugin class helps to build custom Python plugins:\n" \
+ "* some required information (*name* and *desc* for the parent" \
+ " constructor) is automatically extracted from the final class name" \
+ " or its documentation;\n" \
+ "* implementations for pychrysalide.plugins.PluginModule._get_filename()" \
+ " and pychrysalide.plugins.PluginModule._get_modname() are provided," \
+ " relying on the *file* argument.\n" \
+ "\n" \
+ "Calls to the *__init__* constructor of this abstract object expect the" \
+ " following arguments as keyword parameters:\n" \
+ "* *file*: path to the Python script; the value should be equal to the" \
+ " *__file__* keyword;\n" \
+ "* *version* (optional): a string providing the version of the plugin;" \
+ " Version format is free;\n" \
+ "* *url* (optional): a string for the homepage describing the plugin;\n" \
+ "* *required* (optional): dependencies of the plugin."
+
+ /* Récupération des paramètres */
+
+ version = NULL;
+ url = NULL;
+ required.values = NULL;
+ required.length = 0;
+
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "s|ssO&", kwlist,
+ &file, &version, &url,
+ convert_to_sequence_to_charp_array, &required);
+ if (!ret) return -1;
+
+ name = self->ob_type->tp_name;
+
+ value = PyObject_GetAttrString(self, "__doc__");
+
+ if (value != NULL && PyUnicode_Check(value))
+ desc = PyUnicode_AsUTF8(value);
+ else
+ desc = NULL;
+
+ Py_XDECREF(value);
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ plugin = G_PYTHON_PLUGIN(pygobject_get(self));
+
+ STORE_PLUGIN_ABI(plugin);
+
+ if (!g_python_plugin_create(plugin, name, desc, version, url,
+ &required.values, &required.length,
+ file))
+ {
+ clean_charp_array(&required);
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Python plugin."));
+ return -1;
+ }
+
+ clean_charp_array(&required);
+
+ return 0;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION POUR SUPPORT PYTHON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_python_plugin_type(void)
+{
+ static PyMethodDef py_python_plugin_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_python_plugin_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_python_plugin_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.PythonPlugin",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = PYTHON_PLUGIN_DOC,
+
+ .tp_methods = py_python_plugin_methods,
+ .tp_getset = py_python_plugin_getseters,
+
+ .tp_init = py_python_plugin_init,
+ .tp_new = py_python_plugin_new,
+
+ };
+
+ return &py_python_plugin_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_python_plugin_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'PythonPlugin' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_python_plugin_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_plugin_module_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_PYTHON_PLUGIN, type))
+ return false;
+
+ }
+
+ return true;
+
+}
diff --git a/plugins/pychrysalide/plugins/python.h b/plugins/pychrysalide/plugins/python.h
new file mode 100644
index 0000000..8613bd1
--- /dev/null
+++ b/plugins/pychrysalide/plugins/python.h
@@ -0,0 +1,57 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * python.h - prototypes pour la déclinaison Python de greffons
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H
+#define _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+#include <glibext/helpers.h>
+
+
+
+/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */
+
+
+#define G_TYPE_PYTHON_PLUGIN (g_python_plugin_get_type())
+
+DECLARE_GTYPE(GPythonPlugin, g_python_plugin, G, PYTHON_PLUGIN);
+
+
+
+/* ------------------------- DEFINITION POUR SUPPORT PYTHON ------------------------- */
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_python_plugin_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.PythonPlugin'. */
+bool ensure_python_python_plugin_is_registered(void);
+
+
+
+#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_PYTHON_H */
diff --git a/plugins/pychrysalide/plugins/translate.c b/plugins/pychrysalide/plugins/translate.c
deleted file mode 100644
index 055788c..0000000
--- a/plugins/pychrysalide/plugins/translate.c
+++ /dev/null
@@ -1,110 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * translate.c - conversion de structures liées aux greffons en objets Python
- *
- * Copyright (C) 2021 Cyrille Bagard
- *
- * This file is part of Chrysalide.
- *
- * Chrysalide is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chrysalide is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-#include "translate.h"
-
-
-#include <assert.h>
-
-
-#include "plugin.h"
-#include "../helpers.h"
-#include "../struct.h"
-
-
-
-/******************************************************************************
-* *
-* Paramètres : iface = ensemble d'informations à transcrire en Python. *
-* *
-* Description : Traduit une description d'interface de greffon. *
-* *
-* Retour : Structure mise en place ou NULL en cas d'erreur. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PyObject *translate_plugin_interface_to_python(const plugin_interface *iface)
-{
- PyObject *result; /* Construction à retourner */
- PyTypeObject *base; /* Modèle d'objet à créer */
- bool status; /* Bilan d'une traduction */
- PyObject *array; /* Tableau à insérer */
- size_t i; /* Boucle de parcours */
- PyTypeObject *itype; /* Type d'élément à créer */
- PyObject *item; /* Elément mis en place */
-
- base = get_python_py_struct_type();
-
- result = PyObject_CallFunction((PyObject *)base, NULL);
- assert(result != NULL);
-
- status = true;
-
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, gtp_name);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, name);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, desc);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, version);
- if (status) status = TRANSLATE_STRING_FIELD(result, iface, url);
-
- if (status) status = TRANSLATE_BOOLEAN_FIELD(result, iface, container);
-
- if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, required, &array);
-
- if (status)
- {
- itype = get_python_plugin_module_type();
-
- for (i = 0; i < iface->required_count; i++)
- {
- item = PyUnicode_FromString(iface->required[i]);
- PyTuple_SetItem(array, i, item);
- }
-
- }
-
- if (status) status = TRANSLATE_ARRAY_FIELD(result, iface, actions, &array);
-
- if (status)
- {
- itype = get_python_plugin_module_type();
-
- for (i = 0; i < iface->actions_count; i++)
- {
- item = cast_with_constants_group_from_type(itype, "PluginAction", iface->actions[i]);
- PyTuple_SetItem(array, i, item);
- }
-
- }
-
- if (!status)
- {
- Py_DECREF(result);
- result = NULL;
- }
-
- return result;
-
-}
diff --git a/plugins/pynb/Makefile.am b/plugins/pynb/Makefile.am
new file mode 100644
index 0000000..50e549f
--- /dev/null
+++ b/plugins/pynb/Makefile.am
@@ -0,0 +1,77 @@
+
+BUILT_SOURCES = resources.h resources.c
+
+
+lib_LTLIBRARIES = libpynbui.la
+
+libdir = $(pluginslibdir)
+
+
+# if BUILD_PYTHON3_BINDINGS
+
+# PYTHON3_LIBADD = python/libpynbpython.la
+
+# if BUILD_DISCARD_LOCAL
+
+# PYTHON3_LDFLAGS = -Wl,-rpath,$(pluginslibdir) \
+# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+# else
+
+# PYTHON3_LDFLAGS = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs \
+# -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+# endif
+
+# PYTHON3_SUBDIRS = python
+
+# endif
+
+
+libpynbui_la_SOURCES = \
+ core-ui-int.h \
+ core-ui.h core-ui.c \
+ panel-int.h \
+ panel.h panel.c \
+ params-int.h \
+ params.h params.c \
+ prefs-int.h \
+ prefs.h prefs.c \
+ resources.h resources.c
+
+libpynbui_la_LIBADD = \
+ $(PYTHON3_LIBADD)
+
+libpynbui_la_CFLAGS = $(LIBGTK4_CFLAGS)
+
+libpynbui_la_LDFLAGS = \
+ $(LIBGTK4_LIBS) $(PYTHON3_LDFLAGS)
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libpynbui_la_SOURCES:%c=)
+
+
+RES_FILES = \
+ panel.ui \
+ params.ui \
+ prefs.ui \
+ data/images/pynb-symbolic.svg
+
+
+resources.c: gresource.xml $(RES_FILES)
+ glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name plugins_pynb gresource.xml
+
+resources.h: gresource.xml
+ glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name plugins_pynb gresource.xml
+
+
+CLEANFILES = resources.h resources.c
+
+EXTRA_DIST = gresource.xml $(RES_FILES)
+
+
+AM_CPPFLAGS = -I$(top_srcdir)/src $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
+
+SUBDIRS = $(PYTHON3_SUBDIRS)
diff --git a/plugins/pynb/core-ui-int.h b/plugins/pynb/core-ui-int.h
new file mode 100644
index 0000000..caf5713
--- /dev/null
+++ b/plugins/pynb/core-ui-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui-int.h - prototypes internes pour le plugin présentant des notes avec code Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_CORE_UI_INT_H
+#define _PLUGINS_PYNB_CORE_UI_INT_H
+
+
+#include <plugins/native-int.h>
+
+
+#include "core-ui.h"
+
+
+
+/* Greffon natif pour la présentation de notes avec texte et code Python (instance) */
+struct _GPythonNotebookPluginUI
+{
+ GNativePlugin parent; /* A laisser en premier */
+
+};
+
+
+/* Greffon natif pour la présentation de notes avec texte et code Python (classe) */
+struct _GPythonNotebookPluginUIClass
+{
+ GNativePluginClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un module pour un module pour présentation. */
+bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *, GModule *);
+
+
+
+#endif /* _PLUGINS_PYNB_CORE_UI_INT_H */
diff --git a/plugins/pynb/core-ui.c b/plugins/pynb/core-ui.c
new file mode 100644
index 0000000..dd49a83
--- /dev/null
+++ b/plugins/pynb/core-ui.c
@@ -0,0 +1,363 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.c - présentation de notes sous forme de texte et de code Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "core-ui.h"
+
+
+#include <i18n.h>
+#include <gui/core/panels.h>
+#include <plugins/self.h>
+#include <plugins/tweakable-int.h>
+
+
+#include "core-ui-int.h"
+#include "panel.h"
+#include "params.h"
+#include "prefs.h"
+
+
+
+/* ---------------------- COMPOSITION DE NOUVEAU GREFFON NATIF ---------------------- */
+
+
+/* Initialise la classe des recherches et identifications. */
+static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *);
+
+/* Procède à l'initialisation de l'interface d'intervention. */
+static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *);
+
+/* Initialise une instance de recherches et identifications. */
+static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *);
+
+/* Supprime toutes les références externes. */
+static void g_python_notebook_plugin_ui_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_python_notebook_plugin_ui_finalize(GObject *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Prend acte de l'activation du greffon. */
+static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *);
+
+/* Prend acte de la désactivation du greffon. */
+static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *);
+
+
+
+/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */
+
+
+/* Fournit une liste de sections de configuration. */
+static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *, size_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON NATIF */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une présentation de notes texte et code Python. */
+G_DEFINE_TYPE_WITH_CODE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G_TYPE_NATIVE_PLUGIN,
+ G_IMPLEMENT_INTERFACE(G_TYPE_TWEAKABLE_PLUGIN, g_python_notebook_plugin_ui_tweakable_plugin_interface_init));
+
+
+NATIVE_PLUGIN_ENTRYPOINT(g_python_notebook_plugin_ui_new);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des recherches et identifications. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_class_init(GPythonNotebookPluginUIClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = g_python_notebook_plugin_ui_dispose;
+ object->finalize = g_python_notebook_plugin_ui_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->enable = (pg_management_fc)g_python_notebook_plugin_ui_enable;
+ plugin->disable = (pg_management_fc)g_python_notebook_plugin_ui_disable;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface d'intervention. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_tweakable_plugin_interface_init(GTweakablePluginInterface *iface)
+{
+ iface->get_info = g_python_notebook_plugin_ui_get_tweak_info;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de recherches et identifications. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_init(GPythonNotebookPluginUI *plugin)
+{
+ STORE_PLUGIN_ABI(plugin);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_dispose(GObject *object)
+{
+ G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_python_notebook_plugin_ui_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(g_python_notebook_plugin_ui_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = extension vue du système. *
+* *
+* Description : Crée un module pour présentation de notes. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+GPluginModule *g_python_notebook_plugin_ui_new(GModule *module)
+{
+ GPythonNotebookPluginUI *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI, NULL);
+
+ if (!g_python_notebook_plugin_ui_create(result, module))
+ g_clear_object(&result);
+
+ return G_PLUGIN_MODULE(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* module = extension vue du système. *
+* *
+* Description : Met en place un module pour un module pour présentation. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_python_notebook_plugin_ui_create(GPythonNotebookPluginUI *plugin, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
+#ifdef INCLUDE_PYTHON3_BINDINGS
+# define PG_REQ REQ_LIST("PyChrysalide")
+#else
+# define PG_REQ NO_REQ
+#endif
+
+ result = g_native_plugin_create(G_NATIVE_PLUGIN(plugin),
+ "PythonNotebook",
+ "Edit notebook with text and code to support binary analysis",
+ PACKAGE_VERSION,
+ CHRYSALIDE_WEBSITE("doc/plugins/pynb"),
+ PG_REQ,
+ module);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de l'activation du greffon. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_python_notebook_plugin_ui_enable(GPythonNotebookPluginUI *plugin)
+{
+ bool result; /* Bilan à retourner */
+ panel_info_t info; /* Infos d'enregistrement */
+
+ info.category = "Main";
+
+ info.image = "pynb-symbolic";
+ info.title = _("Python notebook");
+ info.desc = _("Edit notebook with text and code to support binary analysis");
+
+ info.personality = FPP_MAIN_PANEL;
+
+ info.panel_type = GTK_TYPE_PYTHON_NOTEBOOK_PANEL;
+ info.params_type = GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS;
+
+ result = register_framework_panel_definition(&info);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte de la désactivation du greffon. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_python_notebook_plugin_ui_disable(GPythonNotebookPluginUI *plugin)
+{
+ bool result; /* Bilan à retourner */
+
+
+ // TODO : unregister
+
+ result = true;
+
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* INTEGRATION DANS L'EDITION DES PREFERENCES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = interface à manipuler. *
+* count = taille de la liste renvoyée. [OUT] *
+* *
+* Description : Fournit une liste de sections de configuration. *
+* *
+* Retour : Définition(s) de section de configuration ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static tweak_info_t *g_python_notebook_plugin_ui_get_tweak_info(const GTweakablePlugin *plugin, size_t *count)
+{
+ tweak_info_t *result; /* Liste à renvoyer */
+
+ tweak_info_t infos[] = {
+ TWEAK_SIMPLE_DEF("root", "Basics",
+ "pynb-symbolic", "pynotebook", "Notebook", GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL),
+ };
+
+ *count = 1;
+ result = malloc(*count * sizeof(tweak_info_t));
+
+ memcpy(result, infos, *count * sizeof(tweak_info_t));
+
+ return result;
+
+}
diff --git a/plugins/pynb/core-ui.h b/plugins/pynb/core-ui.h
new file mode 100644
index 0000000..8ab9dd9
--- /dev/null
+++ b/plugins/pynb/core-ui.h
@@ -0,0 +1,43 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core-ui.h - prototypes pour la présentation de notes sous forme de texte et de code Python
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_FIDO_CORE_UI_H
+#define _PLUGINS_FIDO_CORE_UI_H
+
+
+#include <plugins/plugin.h>
+#include <plugins/plugin-int.h>
+
+
+
+#define G_TYPE_PYTHON_NOTEBOOK_PLUGIN_UI (g_python_notebook_plugin_ui_get_type())
+
+DECLARE_GTYPE(GPythonNotebookPluginUI, g_python_notebook_plugin_ui, G, PYTHON_NOTEBOOK_PLUGIN_UI);
+
+
+/* Crée un module pour présentation de notes. */
+GPluginModule *g_python_notebook_plugin_ui_new(GModule *);
+
+
+
+#endif /* _PLUGINS_FIDO_CORE_UI_H */
diff --git a/plugins/pynb/data/images/pynb-symbolic.svg b/plugins/pynb/data/images/pynb-symbolic.svg
new file mode 100644
index 0000000..f8cae60
--- /dev/null
+++ b/plugins/pynb/data/images/pynb-symbolic.svg
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="256"
+ height="256"
+ viewBox="0 0 67.73333 67.733333"
+ version="1.1"
+ id="svg2759"
+ sodipodi:docname="pynotebook.svg"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview2761"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="false"
+ inkscape:deskcolor="#d1d1d1"
+ inkscape:document-units="px"
+ showgrid="false"
+ inkscape:zoom="2.9329997"
+ inkscape:cx="126.83261"
+ inkscape:cy="128.1964"
+ inkscape:window-width="1920"
+ inkscape:window-height="1011"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1" />
+ <defs
+ id="defs2756">
+ <linearGradient
+ id="b"
+ x1="28.809"
+ y1="28.882"
+ x2="45.803001"
+ y2="45.162998"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)">
+ <stop
+ stop-color="#FFE052"
+ id="stop3586" />
+ <stop
+ offset="1"
+ stop-color="#FFC331"
+ id="stop3588" />
+ </linearGradient>
+ <linearGradient
+ id="a"
+ x1="19.075001"
+ y1="18.782"
+ x2="34.897999"
+ y2="34.658001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(12.026515,0,0,11.963752,-37.015053,-73.520624)">
+ <stop
+ stop-color="#387EB8"
+ id="stop3581" />
+ <stop
+ offset="1"
+ stop-color="#366994"
+ id="stop3583" />
+ </linearGradient>
+ </defs>
+ <g
+ inkscape:label="Calque 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3524"
+ transform="translate(2.9562937,-0.62320885)"
+ sodipodi:insensitive="true"
+ style="display:none">
+ <path
+ id="rect2966"
+ style="fill:#3a1616;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 49.272443,10.836031 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 V 57.03476 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 16.30288 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 14.59084 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z"
+ sodipodi:nodetypes="csssscsssssc" />
+ <path
+ id="rect2964"
+ style="display:inline;fill:#a44040;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 13.657273,5.3857002 h 29.21476 c 2.168053,0 3.913452,1.7453645 3.913452,3.9133733 V 54.388991 c 0,2.168009 -1.745399,3.913373 -3.913452,3.913373 h -29.21476 c -1.084026,0 -2.062389,-0.436341 -2.769745,-1.143683 -0.707357,-0.707342 -1.1437069,-1.685685 -1.1437069,-2.76969 V 9.2990735 c 0,-2.1680088 1.7453999,-3.9133733 3.9134519,-3.9133733 z"
+ sodipodi:nodetypes="ssssssssss" />
+ <path
+ id="rect3518"
+ style="fill:#2d3a16;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 10.887528,57.158681 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ <g
+ id="g3807"
+ transform="matrix(0.275,0,0,0.27644269,10.179141,20.324238)"
+ style="display:none;stroke-width:3.62685">
+ <path
+ d="m 76.135321,-13.223349 c -26.868437,0 -25.19164,11.6515879 -25.19164,11.6515879 l 0.03307,12.0716141 h 25.638125 v 3.621485 h -35.82789 c 0,0 -17.187994,-1.951302 -17.187994,25.161874 0,27.106562 15.005182,26.147447 15.005182,26.147447 h 8.956145 V 52.849722 c 0,0 -0.482864,-15.005182 14.767057,-15.005182 h 25.426458 c 0,0 14.287506,0.231511 14.287506,-13.807942 V 0.82271803 c 0,0 2.16958,-14.04606703 -25.906021,-14.04606703 z m -14.138672,8.1160932 c 2.549922,0 4.613672,2.0637499 4.613672,4.61367177 0,2.54992183 -2.06375,4.61367183 -4.613672,4.61367183 a 4.6070572,4.6070572 0 0 1 -4.613672,-4.61367183 c 0,-2.54992187 2.06375,-4.61367177 4.613672,-4.61367177 z"
+ fill="url(#a)"
+ id="path3577"
+ style="fill:url(#a);stroke-width:11.9951" />
+ <path
+ d="m 76.895998,92.057664 c 26.868432,0 25.191642,-11.651588 25.191642,-11.651588 l -0.0331,-12.071615 h -25.6381 v -3.621484 h 35.82458 c 0,0 17.1913,1.951302 17.1913,-25.158567 0,-27.109869 -15.00518,-26.147447 -15.00518,-26.147447 H 105.471 v 12.57763 c 0,0 0.48286,15.005182 -14.76706,15.005182 H 65.277482 c 0,0 -14.287499,-0.231511 -14.287499,13.807942 v 23.21388 c 0,0 -2.169584,14.046067 25.906015,14.046067 z m 14.138671,-8.116093 a 4.6070572,4.6070572 0 0 1 -4.613671,-4.613672 c 0,-2.546615 2.06375,-4.610365 4.613671,-4.610365 2.549922,0 4.613672,2.060443 4.613672,4.610365 0,2.553229 -2.06375,4.613672 -4.613672,4.613672 z"
+ fill="url(#b)"
+ id="path3579"
+ style="fill:url(#b);stroke-width:11.9951" />
+ </g>
+ <g
+ id="g3859"
+ style="display:none;fill:#000000;fill-opacity:1">
+ <path
+ id="path3544"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 52.228737,10.212822 c 0.102978,0.351405 0.158647,0.723392 0.158647,1.108976 v 45.089753 c 0,2.168007 -1.745406,3.913456 -3.913456,3.913456 H 19.259174 c -0.385538,0 -0.75709,-0.05622 -1.108459,-0.159163 0.237938,0.812336 0.730853,1.513585 1.387112,2.01175 0.65626,0.498165 1.475864,0.793246 2.367181,0.793246 h 29.214753 c 2.16805,0 3.913456,-1.745449 3.913456,-3.913456 V 13.967631 c 0,-1.782422 -1.180022,-3.278791 -2.80448,-3.754809 z"
+ sodipodi:nodetypes="csssscsssssc" />
+ <path
+ id="path3546"
+ style="display:inline;fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 16.613456,4.7624999 C 14.445408,4.7625 12.7,6.5079516 12.7,8.675956 v 45.089753 c 0,1.084003 0.436244,2.062515 1.143599,2.769857 0.707354,0.70734 1.685833,1.143599 2.769857,1.143599 h 29.214753 c 2.168049,0 3.913456,-1.745451 3.913456,-3.913456 V 8.675956 c 0,-2.1680044 -1.745407,-3.9134561 -3.913456,-3.9134561 z M 31.116446,16.66875 c 7.720775,0 7.124113,3.882967 7.124113,3.882967 v 6.417179 c 0,3.881096 -3.928959,3.817338 -3.928959,3.817338 h -6.992338 c -4.19372,0 -4.061251,4.148067 -4.061251,4.148067 v 3.477824 h -2.462898 c 0,0 -4.126363,0.264897 -4.126363,-7.2285 0,-7.495223 4.726843,-6.955647 4.726843,-6.955647 h 9.852628 v -1.000973 h -7.050215 l -0.0093,-3.337264 c 0,0 -0.461064,-3.220991 6.927742,-3.220991 z m -3.888135,2.24379 c -0.701226,0 -1.268656,0.570469 -1.268656,1.275374 a 1.2669408,1.2735872 0 0 0 1.268656,1.275375 c 0.701229,0 1.268657,-0.570467 1.268657,-1.275375 0,-0.704905 -0.567428,-1.275374 -1.268657,-1.275374 z m 14.46475,5.117 c 0.453342,-0.0018 4.079854,0.203094 4.079854,7.229016 0,7.494318 -4.727359,6.955131 -4.727359,6.955131 h -9.852112 v 1.000973 h 7.050732 l 0.0093,3.337263 c 0,0 0.461069,3.220992 -6.927742,3.220992 -7.720783,0 -7.124113,-3.882967 -7.124113,-3.882967 v -6.417179 c 0,-3.881101 3.928959,-3.817338 3.928959,-3.817338 h 6.992338 c 4.193725,0 4.060734,-4.148067 4.060734,-4.148067 v -3.47679 h 2.462898 c 0,0 0.01629,-9.15e-4 0.04651,-0.001 z M 35.21387,40.97941 c -0.701227,0 -1.269173,0.570348 -1.269173,1.27434 a 1.2669408,1.2735872 0 0 0 1.269173,1.275375 c 0.701228,0 1.268657,-0.569554 1.268657,-1.275375 0,-0.704906 -0.567429,-1.27434 -1.268657,-1.27434 z" />
+ <path
+ id="path3548"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="m 13.843822,56.535472 5.291742,4.9e-5 5.29299,5.29246 h -5.291667 z"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ <path
+ id="rect3934"
+ style="display:none;fill:#00ba2b;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ id="path4924"
+ style="fill:#000000;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 24.551 4.7625 C 22.3829 4.7625 20.1615 6.50795 19.5702 8.67596 L 7.27299 53.7657 C 6.97735 54.8497 7.14673 55.8282 7.66117 56.5356 L 11.5111 61.8277 L 11.5147 61.8277 C 11.6056 61.9525 11.7049 62.0706 11.8167 62.1776 C 12.3371 62.6758 13.0763 62.9708 13.9676 62.9708 L 43.1824 62.9708 C 45.3504 62.9708 47.5719 61.2254 48.1631 59.0574 L 60.4603 13.9676 C 60.9465 12.1852 60.1745 10.6888 58.6799 10.2128 C 58.687 10.5642 58.6413 10.9362 58.5361 11.3218 L 46.2389 56.4115 C 45.6476 58.5795 43.4262 60.325 41.2581 60.325 L 15.7098 60.325 L 13.7851 57.6792 L 39.3339 57.6792 C 41.5019 57.6792 43.7234 55.9337 44.3147 53.7657 L 56.6119 8.67596 C 57.2031 6.50795 55.9338 4.7625 53.7657 4.7625 L 24.551 4.7625 Z M 35.8068 16.6688 C 43.5276 16.6688 41.8719 20.5517 41.8719 20.5517 L 40.1218 26.9689 C 39.0633 30.85 35.1517 30.7862 35.1517 30.7862 L 28.1594 30.7862 C 23.9657 30.7862 22.9668 34.9343 22.9668 34.9343 L 22.0183 38.4121 L 19.5554 38.4121 C 19.5554 38.4121 15.3568 38.677 17.4005 31.1836 C 19.4446 23.6884 24.0243 24.228 24.0243 24.228 L 33.877 24.228 L 34.1499 23.227 L 27.0997 23.227 L 28.0006 19.8897 C 28.0006 19.8897 28.418 16.6688 35.8068 16.6688 Z M 31.3067 18.9125 C 30.6055 18.9125 29.8825 19.483 29.6902 20.1879 C 29.5975 20.5263 29.6424 20.851 29.8152 21.0902 C 29.9879 21.3295 30.2743 21.4637 30.611 21.4633 C 31.3123 21.4633 32.0353 20.8928 32.2275 20.1879 C 32.4198 19.483 32.0079 18.9125 31.3067 18.9125 Z M 44.3759 24.0295 C 44.8297 24.0277 48.4004 24.2326 46.4842 31.2586 C 44.4403 38.7529 39.86 38.2137 39.86 38.2137 L 30.0079 38.2137 L 29.7349 39.2147 L 36.7856 39.2147 L 35.8848 42.5519 C 35.8848 42.5519 35.4674 45.7729 28.0786 45.7729 C 20.3578 45.7729 22.0135 41.8899 22.0135 41.8899 L 23.7636 35.4728 C 24.8221 31.5917 28.7336 31.6554 28.7336 31.6554 L 35.726 31.6554 C 39.9197 31.6554 40.918 27.5074 40.918 27.5074 L 41.8662 24.0306 L 44.3291 24.0306 C 44.3291 24.0306 44.3457 24.0296 44.3759 24.0295 Z M 33.274 40.9794 C 32.5728 40.9794 31.8493 41.5498 31.6573 42.2538 C 31.5645 42.5922 31.6095 42.917 31.7824 43.1563 C 31.9553 43.3956 32.2418 43.5297 32.5787 43.5291 C 33.2799 43.5291 34.0026 42.9596 34.1951 42.2538 C 34.3874 41.5488 33.9753 40.9794 33.274 40.9794 Z" />
+ <path
+ style="display:none;fill:#af00ba;fill-opacity:1;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;paint-order:fill markers stroke"
+ d="M 4.7624999,62.970832 20.637499,4.7624999 h 42.333333 l -15.875,58.2083321 z"
+ id="path5158"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+</svg>
diff --git a/plugins/pynb/gresource.xml b/plugins/pynb/gresource.xml
new file mode 100644
index 0000000..71eb8ed
--- /dev/null
+++ b/plugins/pynb/gresource.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/re/chrysalide/framework/gui/panels">
+ <file compressed="true" alias="pynb-panel.ui">panel.ui</file>
+ <file compressed="true" alias="pynb-params.ui">params.ui</file>
+ <file compressed="true" alias="pynb-prefs.ui">prefs.ui</file>
+ </gresource>
+ <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions">
+ <file compressed="true" alias="pynb-symbolic.svg">data/images/pynb-symbolic.svg</file>
+ </gresource>
+</gresources>
diff --git a/plugins/pynb/panel-int.h b/plugins/pynb/panel-int.h
new file mode 100644
index 0000000..23884c7
--- /dev/null
+++ b/plugins/pynb/panel-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * panel-int.h - prototypes internes pour le panneau dédié à la présentation de notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYNB_PANEL_INT_H
+#define _PLUGINS_PYNB_PANEL_INT_H
+
+
+#include <gtkext/panel-int.h>
+
+
+#include "panel.h"
+
+
+
+/* Panneau de cartopgraphie des dispositions d'échantillons (instance) */
+struct _GtkPythonNotebookPanel
+{
+ GtkTiledPanel parent; /* A laisser en premier */
+
+};
+
+/* Panneau de cartopgraphie des dispositions d'échantillons (classe) */
+struct _GtkPythonNotebookPanelClass
+{
+ GtkTiledPanelClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place nouvelle instance de panneau de présentation. */
+bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *);
+
+
+
+#endif /* _PLUGINS_PYNB_PANEL_INT_H */
diff --git a/plugins/pynb/panel.c b/plugins/pynb/panel.c
new file mode 100644
index 0000000..8ce9cdb
--- /dev/null
+++ b/plugins/pynb/panel.c
@@ -0,0 +1,210 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * panel.c - panneau dédié à la présentation de notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "panel.h"
+
+
+#include <gtkext/helpers.h>
+
+
+#include "panel-int.h"
+
+
+
+/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
+
+
+/* Initialise la classe des panneaux pour binaires. */
+static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *);
+
+/* Initialise une instance de panneau pour binaire. */
+static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *);
+
+/* Supprime toutes les références externes. */
+static void gtk_python_notebook_panel_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_python_notebook_panel_finalize(GObject *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Fournit les composants adaptés pour la barre de titre. */
+//static GListStore *gtk_python_notebook_panel_get_title_widgets(GtkTiledPanel *, bool);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COEUR D'UN PANNEAU D'AFFICHAGE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un panneau de présentation de notes. */
+G_DEFINE_TYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK_TYPE_TILED_PANEL);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des panneaux pour binaires. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_class_init(GtkPythonNotebookPanelClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+ GtkTiledPanelClass *panel; /* Version parente de classe */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_python_notebook_panel_dispose;
+ object->finalize = gtk_python_notebook_panel_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-panel.ui");
+
+ //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookPanel, summary);
+
+ panel = GTK_TILED_PANEL_CLASS(class);
+
+ //panel->get_widgets = (get_tiled_panel_widgets_cb)gtk_python_notebook_panel_get_title_widgets;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = instance à initialiser. *
+* *
+* Description : Initialise une instance de panneau pour binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_init(GtkPythonNotebookPanel *panel)
+{
+ gtk_widget_init_template(GTK_WIDGET(panel));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PANEL);
+
+ G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_panel_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_python_notebook_panel_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Crée une nouvelle instance de panneau de présentation. *
+* *
+* Retour : Composant GTK mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GtkTiledPanel *gtk_python_notebook_panel_new(void)
+{
+ GtkTiledPanel *result; /* Instance à retourner */
+
+ result = g_object_new(GTK_TYPE_PYTHON_NOTEBOOK_PANEL, NULL);
+
+ if (!gtk_python_notebook_panel_create(GTK_PYTHON_NOTEBOOK_PANEL(result)))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = panneau de recherche et récupération à remplir. *
+* *
+* Description : Met en place nouvelle instance de panneau de présentation. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool gtk_python_notebook_panel_create(GtkPythonNotebookPanel *panel)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ return result;
+
+}
diff --git a/plugins/pynb/panel.h b/plugins/pynb/panel.h
new file mode 100644
index 0000000..f9f8016
--- /dev/null
+++ b/plugins/pynb/panel.h
@@ -0,0 +1,47 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * panel.h - prototypes pour le panneau dédié à la présentation de notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_PYNB_PANEL_H
+#define _PLUGINS_PYNB_PANEL_H
+
+
+#include <gtk/gtk.h>
+
+
+#include <glibext/helpers.h>
+#include <gtkext/panel.h>
+
+
+
+#define GTK_TYPE_PYTHON_NOTEBOOK_PANEL (gtk_python_notebook_panel_get_type())
+
+DECLARE_GTYPE(GtkPythonNotebookPanel, gtk_python_notebook_panel, GTK, PYTHON_NOTEBOOK_PANEL);
+
+
+/* Crée une nouvelle instance de panneau de présentation. */
+GtkTiledPanel *gtk_python_notebook_panel_new(void);
+
+
+
+#endif /* _PLUGINS_PYNB_PANEL_H */
diff --git a/plugins/pynb/panel.ui b/plugins/pynb/panel.ui
new file mode 100644
index 0000000..d16af80
--- /dev/null
+++ b/plugins/pynb/panel.ui
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkPythonNotebookPanel" parent="GtkTiledPanel">
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="hscrollbar-policy">automatic</property>
+ <property name="vscrollbar-policy">automatic</property>
+ <property name="hexpand">true</property>
+ <property name="vexpand">true</property>
+ <property name="has-frame">true</property>
+ <property name="margin-start">8</property>
+ <property name="margin-top">8</property>
+ <property name="margin-end">8</property>
+ <property name="margin-bottom">8</property>
+
+ </object>
+ </child>
+ </template>
+
+</interface>
diff --git a/plugins/pynb/params-int.h b/plugins/pynb/params-int.h
new file mode 100644
index 0000000..7f3bc8b
--- /dev/null
+++ b/plugins/pynb/params-int.h
@@ -0,0 +1,50 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * params-int.h - définitions internes pour l'édition des paramètres initiaux d'une présentation de notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PARAMS_INT_H
+#define _PLUGINS_PYNB_PARAMS_INT_H
+
+
+#include "params.h"
+
+
+
+/* Composant pour les paramètres de chargement d'un binaire (instance) */
+struct _GtkPythonNotebookParameters
+{
+ GtkGrid parent; /* A laisser en premier */
+
+ //GtkEntry *filename; /* CHemin d'un binaire */
+
+};
+
+/* Composant pour les paramètres de chargement d'un binaire (classe) */
+struct _GtkPythonNotebookParametersClass
+{
+ GtkGridClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* _PLUGINS_PYNB_PARAMS_INT_H */
diff --git a/plugins/pynb/params.c b/plugins/pynb/params.c
new file mode 100644
index 0000000..690ff95
--- /dev/null
+++ b/plugins/pynb/params.c
@@ -0,0 +1,171 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * params.c - édition des paramètres initiaux d'une présentation de notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "params.h"
+
+
+#include "panel.h"
+#include "params-int.h"
+#include <gtkext/helpers.h>
+#include <gui/window.h>
+
+
+
+/* Initialise la classe des composants d'édition de paramètres. */
+static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *);
+
+/* Initialise une instance de composant d'édition de paramètres. */
+static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *);
+
+/* Supprime toutes les références externes. */
+static void gtk_python_notebook_parameters_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_python_notebook_parameters_finalize(GObject *);
+
+/* Réagit à une demande de création de notes. */
+static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *, GtkPythonNotebookParameters *);
+
+
+
+/* Indique le type du composant d'édition des paramètres de chargement. */
+G_DEFINE_TYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK_TYPE_GRID);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe des composants d'édition de paramètres. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_class_init(GtkPythonNotebookParametersClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_python_notebook_parameters_dispose;
+ object->finalize = gtk_python_notebook_parameters_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-params.ui");
+
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_parameters_on_create_clicked));
+
+ //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookParameters, filename);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : params = composant GTK à initialiser. *
+* *
+* Description : Initialise une instance de composant d'édition de paramètres.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_init(GtkPythonNotebookParameters *params)
+{
+ gtk_widget_init_template(GTK_WIDGET(params));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS);
+
+ G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_python_notebook_parameters_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : button = bouton GTK concerné par l'appel. *
+* params = paramètres du panneau à mettre en place. *
+* *
+* Description : Réagit à une demande de création de notes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_parameters_on_create_clicked(GtkButton *button, GtkPythonNotebookParameters *params)
+{
+ GtkRoot *root; /* Racine du composant */
+ GtkTiledPanel *tiled; /* Panneau d'affichage complet */
+
+ root = gtk_widget_get_root(GTK_WIDGET(button));
+
+ tiled = gtk_python_notebook_panel_new();
+
+ gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled);
+
+}
diff --git a/plugins/pynb/params.h b/plugins/pynb/params.h
new file mode 100644
index 0000000..2d4b39b
--- /dev/null
+++ b/plugins/pynb/params.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * params.h - prototypes pour l'édition des paramètres initiaux d'une présentation de notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PARAMS_H
+#define _PLUGINS_PYNB_PARAMS_H
+
+
+#include <gtk/gtk.h>
+
+
+#include <glibext/helpers.h>
+
+
+
+#define GTK_TYPE_PYTHON_NOTEBOOK_PARAMETERS (gtk_python_notebook_parameters_get_type())
+
+DECLARE_GTYPE(GtkPythonNotebookParameters, gtk_python_notebook_parameters, GTK, PYTHON_NOTEBOOK_PARAMETERS);
+
+
+
+#endif /* _PLUGINS_PYNB_PARAMS_H */
diff --git a/plugins/pynb/params.ui b/plugins/pynb/params.ui
new file mode 100644
index 0000000..21a4788
--- /dev/null
+++ b/plugins/pynb/params.ui
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkPythonNotebookParameters" parent="GtkGrid">
+ <property name="margin-bottom">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">12</property>
+ <property name="column-spacing">12</property>
+ <property name="row-spacing">8</property>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Load and analyze a new notebook file:</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEntry" id="filename">
+ <property name="secondary-icon-name">document-open-symbolic</property>
+ <property name="placeholder-text">File location</property>
+ <property name="hexpand">TRUE</property>
+ <property name="hexpand-set">TRUE</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ <style>
+ <class name="background"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="create">
+ <property name="label">Create</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ </layout>
+ <signal name="clicked" handler="gtk_python_notebook_parameters_on_create_clicked"/>
+ </object>
+ </child>
+
+ </template>
+
+</interface>
diff --git a/plugins/pynb/prefs-int.h b/plugins/pynb/prefs-int.h
new file mode 100644
index 0000000..9c44c68
--- /dev/null
+++ b/plugins/pynb/prefs-int.h
@@ -0,0 +1,48 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * prefs-int.h - définitions internes pour la configuration des paramètres liés aux notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PREFS_INT_H
+#define _PLUGINS_PYNB_PREFS_INT_H
+
+
+#include "prefs.h"
+
+
+
+/* Composant d'édition des paramètres liés aux notes (instance) */
+struct _GtkPythonNotebookTweakPanel
+{
+ GtkBox parent; /* A laisser en premier */
+
+};
+
+/* Composant d'édition des paramètres liés aux notes (classe) */
+struct _GtkPythonNotebookTweakPanelClass
+{
+ GtkBoxClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* _PLUGINS_PYNB_PREFS_INT_H */
diff --git a/plugins/pynb/prefs.c b/plugins/pynb/prefs.c
new file mode 100644
index 0000000..9363e9b
--- /dev/null
+++ b/plugins/pynb/prefs.c
@@ -0,0 +1,143 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * prefs.c - configuration des paramètres liés aux notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "prefs.h"
+
+
+#include <gtkext/helpers.h>
+
+
+#include "prefs-int.h"
+
+
+
+/* Procède à l'initialisation de classe des configurations. */
+static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *);
+
+/* Procède à l'initialisation des configurations de sécurité. */
+static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *);
+
+/* Supprime toutes les références externes. */
+static void gtk_python_notebook_tweak_panel_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_python_notebook_tweak_panel_finalize(GObject *);
+
+
+
+/* Indique le type du composant de configuration des notes. */
+G_DEFINE_TYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK_TYPE_BOX);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation de classe des configurations. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_class_init(GtkPythonNotebookTweakPanelClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_python_notebook_tweak_panel_dispose;
+ object->finalize = gtk_python_notebook_tweak_panel_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/pynb-prefs.ui");
+
+ /* Stockage sécurisé */
+
+ //gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_python_notebook_tweak_panel_on_new_passwords_changed));
+
+ //gtk_widget_class_bind_template_child(widget, GtkPythonNotebookTweakPanel, current_primary_passwd);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = composant GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation des configurations de sécurité. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_init(GtkPythonNotebookTweakPanel *panel)
+{
+ gtk_widget_init_template(GTK_WIDGET(panel));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL);
+
+ G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_python_notebook_tweak_panel_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_python_notebook_tweak_panel_parent_class)->finalize(object);
+
+}
diff --git a/plugins/pynb/prefs.h b/plugins/pynb/prefs.h
new file mode 100644
index 0000000..6551983
--- /dev/null
+++ b/plugins/pynb/prefs.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * prefs.h - prototypes pour la configuration des paramètres liés aux notes
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_PYNB_PREFS_H
+#define _PLUGINS_PYNB_PREFS_H
+
+
+#include <gtk/gtk.h>
+
+
+#include <glibext/helpers.h>
+
+
+
+#define GTK_TYPE_PYTHON_NOTEBOOK_TWEAK_PANEL (gtk_python_notebook_tweak_panel_get_type())
+
+DECLARE_GTYPE(GtkPythonNotebookTweakPanel, gtk_python_notebook_tweak_panel, GTK, PYTHON_NOTEBOOK_TWEAK_PANEL);
+
+
+
+#endif /* _PLUGINS_PYNB_PREFS_H */
diff --git a/plugins/pynb/prefs.ui b/plugins/pynb/prefs.ui
new file mode 100644
index 0000000..b8a6962
--- /dev/null
+++ b/plugins/pynb/prefs.ui
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkPythonNotebookTweakPanel" parent="GtkBox">
+
+ <property name="orientation">vertical</property>
+
+ <!-- Conservation de paramètres sécurisée -->
+ <child>
+ <object class="GtkGrid">
+ <property name="margin-start">20</property>
+ <property name="margin-end">20</property>
+ <property name="margin-top">20</property>
+ <property name="margin-bottom">20</property>
+ <property name="row-spacing">10</property>
+ <property name="column-spacing">10</property>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Notes config</property>
+ <property name="use-markup">true</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+
+ </object>
+ </child>
+
+ </template>
+</interface>
diff --git a/src/arch/archbase.h b/src/arch/archbase.h
index 59bf18e..b29d2a1 100644
--- a/src/arch/archbase.h
+++ b/src/arch/archbase.h
@@ -50,6 +50,7 @@ typedef uint64_t vmpa_t;
#define VMPA_MAX_SIZE 19
+#if 0
/* Taille des données intégrées */
typedef enum _MemoryDataSize
{
@@ -68,6 +69,7 @@ typedef enum _MemoryDataSize
MDS_64_BITS_SIGNED = 0x85 /* Opérande sur 64 bits signés */
} MemoryDataSize;
+#endif
#define MDS_RANGE(mds) ((mds & 0xf) - 1)
@@ -99,13 +101,13 @@ typedef enum _MemoryDataSize
__result; \
})
-
+#if 0
#define MDS_4_BITS MDS_4_BITS_UNSIGNED
#define MDS_8_BITS MDS_8_BITS_UNSIGNED
#define MDS_16_BITS MDS_16_BITS_UNSIGNED
#define MDS_32_BITS MDS_32_BITS_UNSIGNED
#define MDS_64_BITS MDS_64_BITS_UNSIGNED
-
+#endif
/* Etablit la comparaison entre deux adresses. */
diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h
index 42136e2..8a9b961 100644
--- a/src/arch/vmpa.h
+++ b/src/arch/vmpa.h
@@ -30,7 +30,6 @@
#include <stdint.h>
-#include "archbase.h"
#include "../common/cpp.h"
#include "../common/datatypes.h"
#include "../common/packed.h"
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 7925b66..d09b661 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -50,6 +50,7 @@ libcommon4_la_SOURCES = \
asm.h asm.c \
bits.h bits.c \
compiler.h \
+ cpp.h \
datatypes.h \
dllist.h dllist.c \
entropy.h entropy.c \
@@ -57,7 +58,6 @@ libcommon4_la_SOURCES = \
extstr.h extstr.c \
fnv1a.h fnv1a.c \
io.h io.c \
- json.h json.c \
leb128.h leb128.c \
macros.h \
packed.h packed.c \
@@ -74,6 +74,13 @@ libcommon4_la_SOURCES += \
endif
+if BUILD_JSONGLIB_SUPPORT
+
+libcommon4_la_SOURCES += \
+ json.h json.c
+
+endif
+
libcommon4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) $(LIBJSONGLIB_CFLAGS)
if BUILD_CURL_SUPPORT
@@ -82,6 +89,12 @@ libcommon4_la_CFLAGS += $(LIBCURL_CFLAGS)
endif
+if BUILD_JSONGLIB_SUPPORT
+
+libcommon4_la_CFLAGS += $(LIBJSONGLIB_CFLAGS)
+
+endif
+
devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%)
diff --git a/src/common/compiler.h b/src/common/compiler.h
index 2585e47..65df8a4 100644
--- a/src/common/compiler.h
+++ b/src/common/compiler.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* compiler.h - prototypes pour le regroupement d'astuces à destination du compilateur
*
- * Copyright (C) 2024 Cyrille Bagard
+ * Copyright (C) 2024-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -34,5 +34,19 @@
#define __weak __attribute__((weak))
+/**
+ * Contournement des avertissements de la forme suivante :
+ *
+ * assignment to 'const char * const*' from incompatible pointer type 'char **' [-Wincompatible-pointer-types]
+ *
+ * Références :
+ * - https://www.reddit.com/r/C_Programming/comments/qa2231/const_char_const_and_char_are_incompatible/
+ * - https://stackoverflow.com/questions/78125/why-cant-i-convert-char-to-a-const-char-const-in-c
+ * - https://c-faq.com/ansi/constmismatch.html
+ */
+
+#define CONST_ARRAY_CAST(a, tp) (const tp **)a
+
+
#endif /* _COMMON_COMPILER_H */
diff --git a/src/common/cpp.h b/src/common/cpp.h
index 39e7676..2644281 100644
--- a/src/common/cpp.h
+++ b/src/common/cpp.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* cpp.h - prototypes pour avoir à disposition un langage C plus plus mieux
*
- * Copyright (C) 2010-2020 Cyrille Bagard
+ * Copyright (C) 2010-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -31,6 +31,12 @@
/**
+ * Fournit la taille d'une chaîne statique.
+ */
+#define STATIC_STR_SIZE(s) (sizeof(s) - 1)
+
+
+/**
* Fournit la taille d'un tableau statique.
*/
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
diff --git a/src/common/datatypes.h b/src/common/datatypes.h
index 3983267..681e232 100644
--- a/src/common/datatypes.h
+++ b/src/common/datatypes.h
@@ -52,5 +52,31 @@ typedef enum _SourceEndian
} SourceEndian;
+/* Taille des données intégrées */
+typedef enum _MemoryDataSize
+{
+ MDS_UNDEFINED = 0x0, /* Taille non définie */
+
+ MDS_4_BITS_UNSIGNED = 0x1, /* Opérande sur 4 bits n.-s. */
+ MDS_8_BITS_UNSIGNED = 0x2, /* Opérande sur 8 bits n.-s. */
+ MDS_16_BITS_UNSIGNED = 0x3, /* Opérande sur 16 bits n.-s. */
+ MDS_32_BITS_UNSIGNED = 0x4, /* Opérande sur 32 bits n.-s. */
+ MDS_64_BITS_UNSIGNED = 0x5, /* Opérande sur 64 bits n.-s. */
+
+ MDS_4_BITS_SIGNED = 0x8 | 0x1, /* Opérande sur 4 bits signés */
+ MDS_8_BITS_SIGNED = 0x8 | 0x2, /* Opérande sur 8 bits signés */
+ MDS_16_BITS_SIGNED = 0x8 | 0x3, /* Opérande sur 16 bits signés */
+ MDS_32_BITS_SIGNED = 0x8 | 0x4, /* Opérande sur 32 bits signés */
+ MDS_64_BITS_SIGNED = 0x8 | 0x5 /* Opérande sur 64 bits signés */
+
+} MemoryDataSize;
+
+#define MDS_4_BITS MDS_4_BITS_UNSIGNED
+#define MDS_8_BITS MDS_8_BITS_UNSIGNED
+#define MDS_16_BITS MDS_16_BITS_UNSIGNED
+#define MDS_32_BITS MDS_32_BITS_UNSIGNED
+#define MDS_64_BITS MDS_64_BITS_UNSIGNED
+
+
#endif /* _COMMON_DATATYPES_H */
diff --git a/src/common/entropy.h b/src/common/entropy.h
index b677a77..cfd51ef 100644
--- a/src/common/entropy.h
+++ b/src/common/entropy.h
@@ -26,9 +26,10 @@
#include <stdbool.h>
+#include <stddef.h>
-#include "../arch/archbase.h"
+#include "../common/datatypes.h"
diff --git a/src/common/szbin.h b/src/common/szbin.h
index 5891449..23aac67 100644
--- a/src/common/szbin.h
+++ b/src/common/szbin.h
@@ -97,6 +97,16 @@ typedef struct _sized_binary_t
while (0)
+#define resize_sized_binary(sb, s) \
+ do \
+ { \
+ (sb)->size = s; \
+ (sb)->data = realloc((sb)->data, \
+ (sb)->size); \
+ } \
+ while (0)
+
+
#define add_to_sized_binary(sb, d, s) \
do \
{ \
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index e1e3c4e..17fd2bf 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -22,7 +22,7 @@ libcore4_la_SOURCES = \
nproc.h nproc.c \
paths.h paths.c
-libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS)
+libcore4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS)
devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%)
diff --git a/src/core/core.c b/src/core/core.c
index 9514ee1..8fe12f5 100644
--- a/src/core/core.c
+++ b/src/core/core.c
@@ -53,6 +53,8 @@ bool load_core_components(AvailableCoreComponent flags)
if ((flags & ACC_GLOBAL_VARS) != 0 && (__loaded & ACC_GLOBAL_VARS) == 0)
{
+ set_secret_storage(g_secret_storage_new(NULL));
+
set_work_queue(g_work_queue_new());
__loaded |= ACC_GLOBAL_VARS;
@@ -82,6 +84,8 @@ void unload_core_components(AvailableCoreComponent flags)
{
set_work_queue(NULL);
+ set_secret_storage(NULL);
+
__loaded &= ~ACC_GLOBAL_VARS;
}
diff --git a/src/core/core.h b/src/core/core.h
index 7c50f6c..e5f0a60 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -32,9 +32,11 @@
/* Eléments à (dé)charger disponibles */
typedef enum _AvailableCoreComponent
{
- ACC_NONE = (0 << 0), /* Statut initial */
- ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */
- ACC_SCAN_FEATURES = (1 << 0), /* Espace de noms pour scan */
+ ACC_NONE = (0 << 0), /* Statut initial */
+ ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */
+ ACC_SCAN_FEATURES = (1 << 1), /* Espace de noms pour scan */
+
+ ACC_ALL_COMPONENTS = (1 << 2) - 1
} AvailableCoreComponent;
diff --git a/src/core/global.c b/src/core/global.c
index d38656b..0275e09 100644
--- a/src/core/global.c
+++ b/src/core/global.c
@@ -36,6 +36,9 @@ static size_t _bytes_sent = 0;
/* Gestionnaire de tâches parallèles */
static GWorkQueue *_queue = NULL;
+/* Gardien des secrets avec support des stockages */
+static GSecretStorage *_storage = NULL;
+
/******************************************************************************
@@ -131,6 +134,49 @@ GWorkQueue *get_work_queue(void)
}
+/******************************************************************************
+* *
+* Paramètres : queue = nouveau stockage sécurisé à mémoriser ou NULL. *
+* *
+* Description : Définit le stockage sécurisé principal. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void set_secret_storage(/* __steal */GSecretStorage *storage)
+{
+ if (_storage != NULL)
+ unref_object(_storage);
+
+ _storage = storage;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit le stockage sécurisé principal. *
+* *
+* Retour : Gestionnaire de traitements parallèles courant. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GSecretStorage *get_secret_storage(void)
+{
+ ref_object(_storage);
+
+ return _storage;
+
+}
+
+
diff --git a/src/core/global.h b/src/core/global.h
index b861ad8..f5d8a62 100644
--- a/src/core/global.h
+++ b/src/core/global.h
@@ -25,6 +25,7 @@
#define _CORE_GLOBAL_H
+#include "../glibext/secstorage.h"
#include "../glibext/workqueue.h"
@@ -41,6 +42,12 @@ void set_work_queue(/* __steal */GWorkQueue *);
/* Fournit le gestionnaire de traitements parallèles courant. */
GWorkQueue *get_work_queue(void);
+/* Définit le stockage sécurisé principal. */
+void set_secret_storage(/* __steal */GSecretStorage *);
+
+/* Fournit le stockage sécurisé principal. */
+GSecretStorage *get_secret_storage(void);
+
diff --git a/src/format/program-int.h b/src/format/program-int.h
index 1549a0a..8464fe4 100644
--- a/src/format/program-int.h
+++ b/src/format/program-int.h
@@ -36,7 +36,7 @@
typedef SourceEndian (* program_get_endian_fc) (const GProgramFormat *);
/* Fournit l'emplacement d'une section donnée. */
-typedef bool (* get_range_by_name_fc) (const GProgramFormat *, const char *, mrange_t *);
+typedef bool (* find_range_by_name_fc) (const GProgramFormat *, const char *, mrange_t *);
/* Format de programme générique (instance) */
@@ -52,7 +52,7 @@ struct _GProgramFormatClass
GKnownFormatClass parent; /* A laisser en premier */
program_get_endian_fc get_endian; /* Boutisme employé */
- get_range_by_name_fc get_range_by_name; /* Emplacement de sections */
+ find_range_by_name_fc find_range_by_name; /* Emplacement de sections */
};
diff --git a/src/format/program.c b/src/format/program.c
index d44f988..9b9df81 100644
--- a/src/format/program.c
+++ b/src/format/program.c
@@ -480,18 +480,18 @@ SourceEndian g_program_format_get_endianness(const GProgramFormat *format)
* *
******************************************************************************/
-bool g_program_format_get_section_range_by_name(const GProgramFormat *format, const char *name, mrange_t *range)
+bool g_program_format_find_section_range_by_name(const GProgramFormat *format, const char *name, mrange_t *range)
{
bool result; /* Bilan à retourner */
GProgramFormatClass *class; /* Classe de l'instance */
class = G_PROGRAM_FORMAT_GET_CLASS(format);
- if (class->get_range_by_name == NULL)
+ if (class->find_range_by_name == NULL)
result = false;
else
- result = class->get_range_by_name(format, name, range);
+ result = class->find_range_by_name(format, name, range);
return result;
diff --git a/src/format/program.h b/src/format/program.h
index 0eb26ae..12b095c 100644
--- a/src/format/program.h
+++ b/src/format/program.h
@@ -43,7 +43,7 @@ DECLARE_GTYPE(GProgramFormat, g_program_format, G, PROGRAM_FORMAT);
SourceEndian g_program_format_get_endianness(const GProgramFormat *);
/* Fournit l'emplacement d'une section donnée. */
-bool g_program_format_get_section_range_by_name(const GProgramFormat *, const char *, mrange_t *);
+bool g_program_format_find_section_range_by_name(const GProgramFormat *, const char *, mrange_t *);
diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am
index adbec4c..81d13d2 100644
--- a/src/glibext/Makefile.am
+++ b/src/glibext/Makefile.am
@@ -4,8 +4,6 @@ BUILT_SOURCES = chrysamarshal.h chrysamarshal.c resources.h resources.c
noinst_LTLIBRARIES = libglibext.la libglibextui.la
# libglibext_la_SOURCES = \
-# comparison-int.h \
-# comparison.h comparison.c \
# configuration-int.h \
# configuration.h configuration.c \
# gbinarycursor.h gbinarycursor.c \
@@ -13,7 +11,7 @@ noinst_LTLIBRARIES = libglibext.la libglibextui.la
# glinecursor.h glinecursor.c \
# gnhash.h gnhash.c \
# notifier.h \
-# objhole.h \
+# \
# proto.h \
# seq.h seq.c \
# _signal.h signal.c \
@@ -40,9 +38,21 @@ noinst_LTLIBRARIES = libglibext.la libglibextui.la
libglibext_la_SOURCES = \
chrysamarshal.h chrysamarshal.c \
+ comparable-int.h \
+ comparable.h comparable.c \
+ hashable-int.h \
+ hashable.h hashable.c \
helpers.h \
+ objhole-int.h \
+ objhole.h objhole.c \
portion-int.h \
portion.h portion.c \
+ secstorage-int.h \
+ secstorage.h secstorage.c \
+ singleton-int.h \
+ singleton.h singleton.c \
+ strbuilder-int.h \
+ strbuilder.h strbuilder.c \
work-int.h \
work.h work.c \
workgroup-int.h \
diff --git a/src/glibext/comparable-int.h b/src/glibext/comparable-int.h
new file mode 100644
index 0000000..18972c2
--- /dev/null
+++ b/src/glibext/comparable-int.h
@@ -0,0 +1,47 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable-int.h - définitions internes propres aux opérations de comparaison d'objets
+ *
+ * Copyright (C) 2022-2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_COMPARABLE_INT_H
+#define _GLIBEXT_COMPARABLE_INT_H
+
+
+#include "comparable.h"
+
+
+
+/* Réalise une comparaison étendue entre objets. */
+typedef int (* compare_object_fc) (const GComparableObject *, const GComparableObject *);
+
+
+/* Instance d'objet comparable (interface) */
+struct _GComparableObjectInterface
+{
+ GTypeInterface base_iface; /* A laisser en premier */
+
+ compare_object_fc compare; /* Comparaison étendue */
+
+};
+
+
+
+#endif /* _GLIBEXT_COMPARABLE_INT_H */
diff --git a/src/glibext/comparable.c b/src/glibext/comparable.c
new file mode 100644
index 0000000..95e7de7
--- /dev/null
+++ b/src/glibext/comparable.c
@@ -0,0 +1,110 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable.c - opérations de comparaison d'objets
+ *
+ * Copyright (C) 2022-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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "comparable.h"
+
+
+#include "comparable-int.h"
+
+
+
+/* Procède à l'initialisation de l'interface de comparaison. */
+static void g_comparable_object_default_init(GComparableObjectInterface *);
+
+
+
+/* Détermine le type d'une interface pour un objet comparable. */
+G_DEFINE_INTERFACE(GComparableObject, g_comparable_object, G_TYPE_OBJECT)
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface de comparaison. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_comparable_object_default_init(GComparableObjectInterface *iface)
+{
+ iface->compare = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* 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 : - *
+* *
+******************************************************************************/
+
+int g_comparable_object_compare(const GComparableObject *object, const GComparableObject *other)
+{
+ int result; /* Bilan à retourner */
+ GComparableObjectInterface *iface; /* Interface utilisée */
+
+ iface = G_COMPARABLE_OBJECT_GET_IFACE(object);
+
+ result = iface->compare(object, other);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = premier objet à consulter pour une comparaison. *
+* other = second objet à consulter pour une comparaison. *
+* *
+* Description : Détermine si deux objets sont fonctionnellement identiques. *
+* *
+* Retour : Bilan de la comparaison. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gboolean g_comparable_object_is_equal(const GComparableObject *object, const GComparableObject *other)
+{
+ gboolean result; /* Bilan à renvoyer */
+ int ret; /* Bilan d'une comparaison */
+
+ ret = g_comparable_object_compare(object, other);
+
+ result = (ret == 0);
+
+ return result;
+
+}
diff --git a/src/glibext/comparable.h b/src/glibext/comparable.h
new file mode 100644
index 0000000..7a652ff
--- /dev/null
+++ b/src/glibext/comparable.h
@@ -0,0 +1,46 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * comparable.h - prototypes pour les opérations de comparaison d'objets
+ *
+ * Copyright (C) 2022-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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_COMPARABLE_H
+#define _GLIBEXT_COMPARABLE_H
+
+
+#include "helpers.h"
+
+
+
+#define G_TYPE_COMPARABLE_OBJECT (g_comparable_object_get_type())
+
+DECLARE_INTERFACE(GComparableObject, g_comparable_object, G, COMPARABLE_OBJECT);
+
+
+
+/* Réalise une comparaison étendue entre objets. */
+int g_comparable_object_compare(const GComparableObject *, const GComparableObject *);
+
+/* Détermine si deux objets sont fonctionnellement identiques. */
+gboolean g_comparable_object_is_equal(const GComparableObject *, const GComparableObject *);
+
+
+
+#endif /* _GLIBEXT_COMPARABLE_H */
diff --git a/src/glibext/comparison-int.h b/src/glibext/comparison-int.h
deleted file mode 100644
index 446f25d..0000000
--- a/src/glibext/comparison-int.h
+++ /dev/null
@@ -1,58 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison-int.h - définitions internes propres aux opérations de comparaison d'objets
- *
- * Copyright (C) 2022 Cyrille Bagard
- *
- * This file is part of Chrysalide.
- *
- * Chrysalide is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chrysalide is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef _GLIBEXT_COMPARISON_INT_H
-#define _GLIBEXT_COMPARISON_INT_H
-
-
-#include "comparison.h"
-
-
-
-/* Réalise une comparaison entre objets selon un critère précis. */
-typedef bool (* compare_rich_fc) (const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *);
-
-
-/* Instance d'élément comparable (interface) */
-struct _GComparableItemIface
-{
- GTypeInterface base_iface; /* A laisser en premier */
-
- compare_rich_fc cmp_rich; /* Comparaison de façon précise*/
-
-};
-
-
-/* Redéfinition */
-typedef GComparableItemIface GComparableItemInterface;
-
-
-/* Réalise une comparaison riche entre valeurs entière. */
-bool compare_rich_integer_values_signed(long long, long long, RichCmpOperation);
-
-/* Réalise une comparaison riche entre valeurs entière. */
-bool compare_rich_integer_values_unsigned(unsigned long long, unsigned long long, RichCmpOperation);
-
-
-
-#endif /* _GLIBEXT_COMPARISON_INT_H */
diff --git a/src/glibext/comparison.c b/src/glibext/comparison.c
deleted file mode 100644
index 8ce6941..0000000
--- a/src/glibext/comparison.c
+++ /dev/null
@@ -1,199 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison.c - opérations de comparaison d'objets
- *
- * Copyright (C) 2022 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "comparison.h"
-
-
-#include <assert.h>
-
-
-#include "comparison-int.h"
-
-
-
-/* Procède à l'initialisation de l'interface de comparaison. */
-static void g_comparable_item_default_init(GComparableItemInterface *);
-
-
-
-/* Détermine le type d'une interface pour un objet comparable. */
-G_DEFINE_INTERFACE(GComparableItem, g_comparable_item, G_TYPE_OBJECT)
-
-
-/******************************************************************************
-* *
-* Paramètres : iface = interface GLib à initialiser. *
-* *
-* Description : Procède à l'initialisation de l'interface de comparaison. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_comparable_item_default_init(GComparableItemInterface *iface)
-{
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = premier objet à consulter pour une comparaison. *
-* other = second objet à consulter pour une comparaison. *
-* op = opération de comparaison à réaliser. *
-* status = bilan des opérations de comparaison. [OUT] *
-* *
-* Description : Réalise une comparaison entre objets selon un critère précis.*
-* *
-* Retour : true si la comparaison a pu être effectuée, false sinon. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool g_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status)
-{
- bool result; /* Etat à retourner */
- GComparableItemIface *iface; /* Interface utilisée */
-
- iface = G_COMPARABLE_ITEM_GET_IFACE(item);
-
- result = iface->cmp_rich(item, other, op, status);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : a = premier élément à consulter pour une comparaison. *
-* b = second objet à consulter pour une comparaison. *
-* op = opération de comparaison à réaliser. *
-* *
-* Description : Réalise une comparaison riche entre valeurs entière. *
-* *
-* Retour : Bilan des opérations de comparaison. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool compare_rich_integer_values_signed(long long a, long long b, RichCmpOperation op)
-{
- bool result; /* Bilan à retourner */
-
- switch (op)
- {
- case RCO_LT:
- result = (a < b);
- break;
-
- case RCO_LE:
- result = (a <= b);
- break;
-
- case RCO_EQ:
- result = (a == b);
- break;
-
- case RCO_NE:
- result = (a != b);
- break;
-
- case RCO_GT:
- result = (a > b);
- break;
-
- case RCO_GE:
- result = (a >= b);
- break;
-
- default:
- assert(false);
- result = false;
- break;
-
- }
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : a = premier élément à consulter pour une comparaison. *
-* b = second objet à consulter pour une comparaison. *
-* op = opération de comparaison à réaliser. *
-* *
-* Description : Réalise une comparaison riche entre valeurs entière. *
-* *
-* Retour : Bilan des opérations de comparaison. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool compare_rich_integer_values_unsigned(unsigned long long a, unsigned long long b, RichCmpOperation op)
-{
- bool result; /* Bilan à retourner */
-
- switch (op)
- {
- case RCO_LT:
- result = (a < b);
- break;
-
- case RCO_LE:
- result = (a <= b);
- break;
-
- case RCO_EQ:
- result = (a == b);
- break;
-
- case RCO_NE:
- result = (a != b);
- break;
-
- case RCO_GT:
- result = (a > b);
- break;
-
- case RCO_GE:
- result = (a >= b);
- break;
-
- default:
- assert(false);
- result = false;
- break;
-
- }
-
- return result;
-
-}
diff --git a/src/glibext/comparison.h b/src/glibext/comparison.h
deleted file mode 100644
index 8d43210..0000000
--- a/src/glibext/comparison.h
+++ /dev/null
@@ -1,80 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * comparison.h - prototypes pour les opérations de comparaison d'objets
- *
- * Copyright (C) 2022 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef _GLIBEXT_COMPARISON_H
-#define _GLIBEXT_COMPARISON_H
-
-
-#include <glib-object.h>
-#include <stdbool.h>
-
-
-
-#define G_TYPE_COMPARABLE_ITEM (g_comparable_item_get_type())
-#define G_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_COMPARABLE_ITEM, GComparableItem))
-#define G_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_COMPARABLE_ITEM, GComparableItemIface))
-#define G_IS_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_COMPARABLE_ITEM))
-#define G_IS_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_COMPARABLE_ITEM))
-#define G_COMPARABLE_ITEM_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_COMPARABLE_ITEM, GComparableItemIface))
-
-
-/* Instance d'élément comparable (coquille vide) */
-typedef struct _GComparableItem GComparableItem;
-
-/* Instance d'élément comparable (interface) */
-typedef struct _GComparableItemIface GComparableItemIface;
-
-
-/* Modes de comparaison */
-typedef enum _RichCmpOperation
-{
- RCO_LT, /* Equivalent de '<' */
- RCO_LE, /* Equivalent de '<=' */
- RCO_EQ, /* Equivalent de '==' */
- RCO_NE, /* Equivalent de '!=' */
- RCO_GT, /* Equivalent de '>' */
- RCO_GE, /* Equivalent de '>°' */
-
-} RichCmpOperation;
-
-/* Détermination d'un besoin de comparaison supplémentaire */
-#define STATUS_NOT_EQUAL(_s, _o) \
- ({ \
- bool __result; \
- if (_o == RCO_LE || _o == RCO_EQ || _o == RCO_GE) \
- __result = !_s; \
- else \
- __result = _s; \
- __result; \
- })
-
-
-/* Détermine le type d'une interface pour un objet comparable. */
-GType g_comparable_item_get_type(void) G_GNUC_CONST;
-
-/* Réalise une comparaison entre objets selon un critère précis. */
-bool g_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *);
-
-
-
-#endif /* _GLIBEXT_COMPARISON_H */
diff --git a/src/glibext/hashable-int.h b/src/glibext/hashable-int.h
new file mode 100644
index 0000000..f8a85e1
--- /dev/null
+++ b/src/glibext/hashable-int.h
@@ -0,0 +1,47 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * hashable-int.h - définitions internes propres aux calculs de l'empreinte d'un objet
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_HASHABLE_INT_H
+#define _GLIBEXT_HASHABLE_INT_H
+
+
+#include "hashable.h"
+
+
+
+/* Calcule l'empreinte sur 32 bits d'un objet. */
+typedef guint (* hash_object_fc) (const GHashableObject *);
+
+
+/* Instance d'objet visant à être unique (interface) */
+struct _GHashableObjectInterface
+{
+ GTypeInterface base_iface; /* A laisser en premier */
+
+ hash_object_fc hash; /* Réduction en valeur */
+
+};
+
+
+
+#endif /* _GLIBEXT_HASHABLE_INT_H */
diff --git a/src/glibext/hashable.c b/src/glibext/hashable.c
new file mode 100644
index 0000000..f988a90
--- /dev/null
+++ b/src/glibext/hashable.c
@@ -0,0 +1,82 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * hashable.c - calculs de l'empreinte d'un objet
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "hashable.h"
+
+
+#include "hashable-int.h"
+
+
+
+/* Procède à l'initialisation de l'interface de détermination. */
+static void g_hashable_object_default_init(GHashableObjectInterface *);
+
+
+
+/* Détermine le type d'une interface pour la réduction d'un objet à une valeur. */
+G_DEFINE_INTERFACE(GHashableObject, g_hashable_object, G_TYPE_OBJECT)
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface de détermination. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_hashable_object_default_init(GHashableObjectInterface *iface)
+{
+ iface->hash = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* 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 : - *
+* *
+******************************************************************************/
+
+guint g_hashable_object_hash(const GHashableObject *object)
+{
+ guint result; /* Valeur à retourner */
+ GHashableObjectInterface *iface; /* Interface utilisée */
+
+ iface = G_HASHABLE_OBJECT_GET_IFACE(object);
+
+ result = iface->hash(object);
+
+ return result;
+
+}
diff --git a/src/glibext/hashable.h b/src/glibext/hashable.h
new file mode 100644
index 0000000..165c744
--- /dev/null
+++ b/src/glibext/hashable.h
@@ -0,0 +1,42 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * hashable.h - prototypes pour les calculs de l'empreinte d'un objet
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_HASHABLE_H
+#define _GLIBEXT_HASHABLE_H
+
+
+#include "helpers.h"
+
+
+
+#define G_TYPE_HASHABLE_OBJECT (g_hashable_object_get_type())
+
+DECLARE_INTERFACE(GHashableObject, g_hashable_object, G, HASHABLE_OBJECT);
+
+
+/* Calcule l'empreinte sur 32 bits d'un objet. */
+guint g_hashable_object_hash(const GHashableObject *);
+
+
+
+#endif /* _GLIBEXT_HASHABLE_H */
diff --git a/src/glibext/objhole-int.h b/src/glibext/objhole-int.h
new file mode 100644
index 0000000..b4abf6f
--- /dev/null
+++ b/src/glibext/objhole-int.h
@@ -0,0 +1,166 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * objhole.h - définitions internes pour ll'utilisation d'un espace inutilisé dans la structure GObject
+ *
+ * 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_OBJHOLE_INT_H
+#define _GLIBEXT_OBJHOLE_INT_H
+
+
+#include "objhole.h"
+
+
+#include "../common/cpp.h"
+
+
+
+/**
+ * Une structure GObject a la définition suivante :
+ *
+ * struct _GObject
+ * {
+ * GTypeInstance g_type_instance;
+ * volatile guint ref_count;
+ * GData *qdata;
+ * };
+ *
+ * Chaque objet GLib alloué comporte ainsi 4 octets inutilisés :
+ *
+ * (gdb) pt /o GObject
+ * type = struct _GObject {
+ * / 0 | 8 / GTypeInstance g_type_instance;
+ * / 8 | 4 / guint ref_count;
+ * / XXX 4-byte hole /
+ * / 16 | 8 / GData *qdata;
+ *
+ * / total size (bytes): 24 /
+ * }
+ *
+ * La situation n'a pas échappé aux développeurs GLib, avec la définition réelle
+ * de la structure (cf. https://github.com/GNOME/glib/blob/main/gobject/gobject.c) :
+ *
+ * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8
+ * #define HAVE_OPTIONAL_FLAGS
+ * #endif
+ *
+ * typedef struct
+ * {
+ * GTypeInstance g_type_instance;
+ * guint ref_count;
+ * #ifdef HAVE_OPTIONAL_FLAGS
+ * guint optional_flags;
+ * #endif
+ * GData *qdata;
+ * } GObjectReal;
+ *
+ * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal));
+ * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count));
+ * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata));
+ *
+ * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois.
+ */
+
+/**
+ * Les bits effectivements utilisés par GLib s'identifie ainsi (02/12/24) :
+ *
+ * $ wget -qO /dev/stdout https://raw.githubusercontent.com/GNOME/glib/refs/heads/main/gobject/gobject.c \
+ * | grep ' * #define OPTIONAL_FLAG_'
+ * #define OPTIONAL_FLAG_IN_CONSTRUCTION (1 << 0)
+ * #define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1)
+ * #define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2)
+ * #define OPTIONAL_FLAG_LOCK (1 << 3)
+ * #define OPTIONAL_FLAG_EVER_HAD_WEAK_REF (1 << 4)
+ *
+ * Les n premiers bits doivent ainsi être préservés, même s'il est possible de
+ * partager le bit de verrouilage OPTIONAL_FLAG_LOCK.
+ */
+
+
+#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT
+
+# if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P >= 8
+# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 1
+# else
+# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 0
+# endif
+
+#endif
+
+
+/* Nouvelle version dense des objets (instance) */
+typedef struct _GThickObject
+{
+ /**
+ * (cf. structure GObjectReal officielle).
+ */
+
+ GTypeInstance g_type_instance; /* Type d'objet */
+ guint ref_count; /* Décompte des références */
+
+#ifdef HAVE_OPTIONAL_FLAGS_IN_GOBJECT
+ guint extra; /* Zone partagée avec GLib */
+#endif
+
+ GData *qdata; /* Données complémentaires ? */
+
+#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT
+ guint extra; /* Zone supplémentaire propre */
+#endif
+
+} GThickObject;
+
+/* Nouvelle version dense des objets (classe) */
+struct _GThickObjectClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/**
+ * Définition du périmètre et des moyens d'accès.
+ */
+
+/* GLib 2.83.0 - cfa36f5e9 */
+#define GOBJECT_RESERVED_EXTRA_BITS 5
+
+#define GET_GOBJECT_EXTRA(obj, tp) \
+ ({ \
+ BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \
+ tp *___result; \
+ guint __val; \
+ __val = g_thick_object_get_extra(obj); \
+ ___result = (tp *)(guint []){ __val }; \
+ ___result; \
+ })
+
+#define SET_GOBJECT_EXTRA(obj, tp, data) \
+ ({ \
+ BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \
+ BUILD_BUG_ON(sizeof(data) > sizeof(guint *)); \
+ guint __val; \
+ __val = *(guint *)data; \
+ g_thick_object_set_extra(obj, __val); \
+ })
+
+
+
+#endif /* _GLIBEXT_OBJHOLE_INT_H */
diff --git a/src/glibext/objhole.c b/src/glibext/objhole.c
new file mode 100644
index 0000000..20bb2a8
--- /dev/null
+++ b/src/glibext/objhole.c
@@ -0,0 +1,191 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * objhole.c - utilisation d'un espace inutilisé dans la structure GObject
+ *
+ * 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 Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "objhole.h"
+
+
+#include <assert.h>
+
+
+#include "objhole-int.h"
+
+
+
+/* Initialise la classe des objets à la structure dense. */
+static void g_thick_object_class_init(GThickObjectClass *);
+
+/* Initialise une instance d'objet à la structure dense. */
+static void g_thick_object_init(GThickObject *);
+
+/* Supprime toutes les références externes. */
+static void g_thick_object_dispose(GThickObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_thick_object_finalize(GThickObject *);
+
+
+
+G_DEFINE_TYPE(GThickObject, g_thick_object, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des objets à la structure dense. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_thick_object_class_init(GThickObjectClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_thick_object_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_thick_object_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : obj = instance à initialiser. *
+* *
+* Description : Initialise une instance d'objet à la structure dense. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_thick_object_init(GThickObject *obj)
+{
+ obj->extra = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : obj = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_thick_object_dispose(GThickObject *obj)
+{
+ G_OBJECT_CLASS(g_thick_object_parent_class)->dispose(G_OBJECT(obj));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : obj = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_thick_object_finalize(GThickObject *obj)
+{
+ G_OBJECT_CLASS(g_thick_object_parent_class)->finalize(G_OBJECT(obj));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : obj = instance d'objet GLib à consulter. *
+* *
+* 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 : - *
+* *
+******************************************************************************/
+
+guint g_thick_object_get_extra(const GThickObject *obj)
+{
+ guint result; /* Valeur à retourner */
+ guint mask; /* Masque à appliquer */
+
+ result = g_atomic_int_get(&obj->extra);
+
+ assert(GOBJECT_RESERVED_EXTRA_BITS < 30);
+
+ mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1;
+
+ result &= ~mask;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : obj = instance d'objet GLib à consulter. *
+* val = valeur de 32 à conserver. *
+* *
+* Description : Définit la valeur courante de la zone de stockage d'un objet.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_thick_object_set_extra(GThickObject *obj, guint val)
+{
+ guint mask; /* Masque à appliquer */
+
+ assert(GOBJECT_RESERVED_EXTRA_BITS < 30);
+
+ mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1;
+
+ assert((val & mask) == 0);
+
+ val &= ~mask;
+
+ g_atomic_int_and(&obj->extra, val | mask);
+
+ g_atomic_int_or(&obj->extra, val);
+
+}
diff --git a/src/glibext/objhole.h b/src/glibext/objhole.h
index 38f4bd5..c1e8cf1 100644
--- a/src/glibext/objhole.h
+++ b/src/glibext/objhole.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* objhole.h - prototypes pour l'utilisation d'un espace inutilisé dans la structure GObject
*
- * Copyright (C) 2020 Cyrille Bagard
+ * Copyright (C) 2020-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,118 +25,20 @@
#define _GLIBEXT_OBJHOLE_H
-#include <glib.h>
-#include <glib-object.h>
+#include "../glibext/helpers.h"
-#include "../common/cpp.h"
+#define G_TYPE_THICK_OBJECT (g_thick_object_get_type())
+DECLARE_GTYPE(GThickObject, g_thick_object, G, THICK_OBJECT);
-/**
- * Une structure GObject a la définition suivante :
- *
- * struct _GObject
- * {
- * GTypeInstance g_type_instance;
- * volatile guint ref_count;
- * GData *qdata;
- * };
- *
- * En revanche, le fichier "glib/gobject/gobject.c" précise la définition
- * réelle de la structure selon l'environnement :
- *
- * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8
- * #define HAVE_OPTIONAL_FLAGS
- * #endif
- *
- * typedef struct
- * {
- * GTypeInstance g_type_instance;
- * guint ref_count;
- * #ifdef HAVE_OPTIONAL_FLAGS
- * guint optional_flags;
- * #endif
- * GData *qdata;
- * } GObjectReal;
- *
- * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal));
- * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count));
- * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata));
- *
- * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois.
- */
-
-#if 0
-
-# define GET_GOBJECT_EXTRA(obj, tp) \
- ({ \
- BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \
- tp *___result; \
- ___result = (tp *)(((guint *)&obj->ref_count) + 1); \
- BUILD_BUG_ON((___result + 1) == (tp *)&obj->qdata); \
- ___result; \
- })
-
-#endif
-
-
-/**
- * Choix du bit de verrou pour le champ "lock".
- *
- * Dans la structure exploitant le mot utilisé ici, ce verrou est généralement
- * placé dans le bit de poids fort pour les objets qui l'utilisent.
- */
-
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-
-# define HOLE_LOCK_BIT 31
-
-#elif __BYTE_ORDER == __BIG_ENDIAN
-
-# define HOLE_LOCK_BIT 0
-
-#else
-
-# error "Unknown byte order"
-
-#endif
-
-
-/* Verrou d'accès pour une encapsulation */
-typedef struct _lockable_obj_extra_t
-{
- gint lock; /* Gestion d'accès aux fanions */
-
-} lockable_obj_extra_t;
-
-
-#define INIT_GOBJECT_EXTRA_LOCK(xtr) \
- do \
- { \
- lockable_obj_extra_t *__lockable; \
- __lockable = (lockable_obj_extra_t *)xtr; \
- __lockable->lock = 0; \
- } \
- while (0)
-#define LOCK_GOBJECT_EXTRA(xtr) \
- do \
- { \
- lockable_obj_extra_t *__lockable; \
- __lockable = (lockable_obj_extra_t *)xtr; \
- g_bit_lock(&__lockable->lock, HOLE_LOCK_BIT); \
- } \
- while (0)
+/* Fournit la valeur courante de la zone de stockage d'un objet. */
+guint g_thick_object_get_extra(const GThickObject *);
-#define UNLOCK_GOBJECT_EXTRA(xtr) \
- do \
- { \
- lockable_obj_extra_t *__lockable; \
- __lockable = (lockable_obj_extra_t *)xtr; \
- g_bit_unlock(&__lockable->lock, HOLE_LOCK_BIT); \
- } \
- while (0)
+/* Définit la valeur courante de la zone de stockage d'un objet. */
+void g_thick_object_set_extra(GThickObject *, guint);
diff --git a/src/glibext/secstorage-int.h b/src/glibext/secstorage-int.h
new file mode 100644
index 0000000..bbac133
--- /dev/null
+++ b/src/glibext/secstorage-int.h
@@ -0,0 +1,60 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage-int.h - définitions internes pour la conservation sécurisée d'éléments de configuration
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_SECSTORAGE_INT_H
+#define _GLIBEXT_SECSTORAGE_INT_H
+
+
+#include "secstorage.h"
+
+
+
+/* Gardien des secrets avec support des stockages (instance) */
+struct _GSecretStorage
+{
+ GObject parent; /* A laisser en premier */
+
+ GSettings *settings; /* Configuration sollicitée */
+
+ void *master_key; /* Clef déverrouillée */
+
+};
+
+/* Gardien des secrets avec support des stockages (classe) */
+struct _GSecretStorageClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ /* Signaux */
+
+ void (* lock_update) (GSecretStorage *);
+
+};
+
+
+/* Met en place un gardien des secrets avec support de stockage. */
+bool g_secret_storage_create(GSecretStorage *, GSettings *);
+
+
+
+#endif /* _GLIBEXT_SECSTORAGE_INT_H */
diff --git a/src/glibext/secstorage.c b/src/glibext/secstorage.c
new file mode 100644
index 0000000..b118aa6
--- /dev/null
+++ b/src/glibext/secstorage.c
@@ -0,0 +1,950 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage.c - conservation sécurisée d'éléments de configuration
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "secstorage.h"
+
+
+#include <assert.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+
+#include "secstorage-int.h"
+#include "../core/logs.h"
+
+
+
+/**
+ * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel,
+ * et la longueur du sel doit être d’au moins 128 bits.
+ *
+ * Cette note concerne le hachage de mots de passe et non la dérivation de secrets
+ * cependant.
+ *
+ * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf
+ */
+
+#define SECRET_STORAGE_SALT_SIZE (256 / 8)
+
+
+/**
+ * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations
+ * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512).
+ *
+ * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
+ */
+
+#define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20)
+
+
+/**
+ * AES 256 : clef de 256 bits, IV de 128 bits
+ */
+
+#define SECRET_STORAGE_KEK_SIZE (256 / 8)
+
+#define SECRET_STORAGE_KEY_SIZE (256 / 8)
+
+#define SECRET_STORAGE_BLOCK_SIZE (128 / 8)
+
+#define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE
+
+
+
+/* Initialise la classe des stockages de secrets. */
+static void g_secret_storage_class_init(GSecretStorageClass *);
+
+/* Initialise une instance de stockage de secrets. */
+static void g_secret_storage_init(GSecretStorage *);
+
+/* Supprime toutes les références externes. */
+static void g_secret_storage_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_secret_storage_finalize(GObject *);
+
+/* Teste un mot de passe par Déverrouillage de clef maître. */
+static bool g_secret_storage_check_primary_password(GSecretStorage *, const char *, void **);
+
+
+
+/* Indique le type défini pour un gardien des secrets avec support des stockages. */
+G_DEFINE_TYPE(GSecretStorage, g_secret_storage, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des stockages de secrets. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_secret_storage_class_init(GSecretStorageClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = g_secret_storage_dispose;
+ object->finalize = g_secret_storage_finalize;
+
+ g_signal_new("lock-update",
+ G_TYPE_SECRET_STORAGE,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET(GSecretStorageClass, lock_update),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = instance à initialiser. *
+* *
+* Description : Initialise une instance de stockage de secrets. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_secret_storage_init(GSecretStorage *storage)
+{
+ storage->settings = NULL;
+
+ storage->master_key = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_secret_storage_dispose(GObject *object)
+{
+ GSecretStorage *storage; /* Gestion de stockage sécurisé*/
+
+ storage = G_SECRET_STORAGE(object);
+
+ g_clear_object(&storage->settings);
+
+ G_OBJECT_CLASS(g_secret_storage_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_secret_storage_finalize(GObject *object)
+{
+ GSecretStorage *storage; /* Gestion de stockage sécurisé*/
+
+ storage = G_SECRET_STORAGE(object);
+
+ if (storage->master_key != NULL)
+ free(storage->master_key);
+
+ G_OBJECT_CLASS(g_secret_storage_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : settings = éventuel espace de configuration à utiliser. *
+* *
+* Description : Créé un nouveau gardien des secrets avec support de stockage.*
+* *
+* Retour : Gestionnaire de stockage sécurisé mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GSecretStorage *g_secret_storage_new(GSettings *settings)
+{
+ GSecretStorage *result; /* Instance à retourner */
+
+ result = g_object_new(G_TYPE_SECRET_STORAGE, NULL);
+
+ if (!g_secret_storage_create(result, settings))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = stockage sécurisé à initialiser. *
+* settings = éventuel espace de configuration à utiliser. *
+* *
+* Description : Met en place un gardien des secrets avec support de stockage.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_create(GSecretStorage *storage, GSettings *settings)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ if (settings != NULL)
+ {
+ ref_object(settings);
+ storage->settings = settings;
+ }
+ else
+ storage->settings = g_settings_new("re.chrysalide.framework.secstorage");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* *
+* Description : Détermine si une clef de chiffrement protégée est en place. *
+* *
+* Retour : Bilan de l'analyse. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_has_key(const GSecretStorage *storage)
+{
+ bool result; /* Bilan à retourner */
+ GVariant *value; /* Valeur de configuration */
+ gsize length; /* Taille d'une valeur donnée */
+
+ result = false;
+
+ value = g_settings_get_value(storage->settings, "master");
+
+ g_variant_get_fixed_array(value, &length, 1);
+
+ result = (length > SECRET_STORAGE_IV_SIZE);
+
+ g_variant_unref(value);
+
+ return result;;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* password = mot de passe principal à appliquer. *
+* *
+* Description : Définit un mot de passe pour protéger une clef maître. *
+* *
+* Retour : Bilan de la mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_set_password(GSecretStorage *storage, const char *passwd)
+{
+ bool result; /* Bilan à retourner */
+ unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/
+ int ret; /* Bilan à d'un appel */
+ GVariant *value; /* Valeur de configuration */
+ unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */
+ unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */
+ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */
+ EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/
+ unsigned char encrypted[64]; /* Partie chiffrée à conserver */
+ unsigned char *iter; /* Tête d'écriture */
+ int outlen; /* Taille des données utiles */
+
+ result = false;
+
+ if (g_secret_storage_has_key(storage))
+ goto exit;
+
+ /* Création d'un sel pour la dérivation du mot de passe */
+
+ ret = RAND_bytes(salt, sizeof(salt));
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ /**
+ * La fonction g_variant_new_fixed_array() retourne un variant
+ * avec un décompte de référence flottant.
+ */
+
+ value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char));
+
+ /**
+ * Comme le variant à une référence flottante, la fonction
+ * g_settings_set_value() consomme cette référence.
+ *
+ * Il n'y a donc pas lieu d'appeler g_variant_unref().
+ */
+
+ g_settings_set_value(storage->settings, "salt", value);
+
+ /* Dérivation du mot de passe */
+
+ ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd),
+ salt, SECRET_STORAGE_SALT_SIZE,
+ PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(),
+ SECRET_STORAGE_KEK_SIZE, kek);
+
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ /* Définition de la clef maître et de son IV de chiffrement */
+
+ ret = RAND_bytes(key, sizeof(key));
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ ret = RAND_bytes(iv, sizeof(iv));
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ /* Chiffrement de la clef maître */
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ if (ctx == NULL)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+
+ ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE);
+
+ iter = encrypted + SECRET_STORAGE_IV_SIZE;
+
+ ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ ret = EVP_EncryptFinal_ex(ctx, iter, &outlen);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ assert((iter - encrypted) < 64);
+
+ /* Conservation de la clef protégée */
+
+ value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
+ encrypted, iter - encrypted, sizeof(unsigned char));
+
+ g_settings_set_value(storage->settings, "master", value);
+
+ g_signal_emit_by_name(storage, "lock-update");
+
+ result = true;
+
+ exit_with_ctx:
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* old = ancien mot de passe principal à vérifier. *
+* new = nouveau mot de passe principal à appliquer. *
+* *
+* Description : Modifie le mot de passe protégeant une clef maître. *
+* *
+* Retour : Bilan de la mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_change_password(GSecretStorage *storage, const char *old, const char *new)
+{
+ bool result; /* Bilan à retourner */
+
+ result = false;
+
+ if (!g_secret_storage_has_key(storage))
+ goto exit;
+
+ if (!g_secret_storage_check_primary_password(storage, old, NULL))
+ goto exit;
+
+ if (!g_secret_storage_is_locked(storage))
+ g_secret_storage_lock(storage);
+
+ g_settings_reset(storage->settings, "salt");
+ g_settings_reset(storage->settings, "master");
+
+ result = g_secret_storage_set_password(storage, new);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* password = mot de passe principal à appliquer. *
+* *
+* Description : Supprime le mot de passe protégeant une clef maître. *
+* *
+* Retour : Bilan de la mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_remove_password(GSecretStorage *storage, const char *passwd)
+{
+ bool result; /* Bilan à retourner */
+
+ result = false;
+
+ if (!g_secret_storage_has_key(storage))
+ goto exit;
+
+ if (!g_secret_storage_check_primary_password(storage, passwd, NULL))
+ goto exit;
+
+ if (!g_secret_storage_is_locked(storage))
+ g_secret_storage_lock(storage);
+
+ g_settings_reset(storage->settings, "salt");
+ g_settings_reset(storage->settings, "master");
+
+ g_signal_emit_by_name(storage, "lock-update");
+
+ result = true;
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* *
+* Description : Détermine si la clef de chiffrement maître est vérouillée. *
+* *
+* Retour : Bilan de la détermination. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_is_locked(const GSecretStorage *storage)
+{
+ bool result; /* Bilan à retourner */
+
+ result = (storage->master_key == NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à manipuler. *
+* password = mot de passe principal à utiliser. *
+* master = éventuelle adresse pour un stockage de clef. [OUT]*
+* *
+* Description : Teste un mot de passe par Déverrouillage de clef maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_secret_storage_check_primary_password(GSecretStorage *storage, const char *passwd, void **master)
+{
+ bool result; /* Bilan à retourner */
+ GVariant *salt_value; /* Valeur du sel configuré */
+ gsize salt_length; /* Taille du sel conservé */
+ gconstpointer salt; /* Données associées #1 */
+ unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */
+ int ret; /* Bilan à d'un appel */
+ GVariant *enc_value; /* Paramètres de chiffrement */
+ gsize enc_length; /* Taille de ces paramètrs */
+ gconstpointer encrypted; /* Données associées #2 */
+ EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */
+ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */
+ unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */
+ unsigned char *iter; /* Tête d'écriture */
+ int outlen; /* Taille des données utiles */
+
+ result = false;
+
+ if (!g_secret_storage_is_locked(storage))
+ {
+ result = true;
+ goto quick_exit;
+ }
+
+ /* Récupération du sel mis en place */
+
+ salt_value = g_settings_get_value(storage->settings, "salt");
+
+ salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t));
+
+ if (salt_length != SECRET_STORAGE_SALT_SIZE)
+ goto exit_with_salt;
+
+ /* Dérivation du mot de passe */
+
+ ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd),
+ salt, SECRET_STORAGE_SALT_SIZE,
+ PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(),
+ SECRET_STORAGE_KEK_SIZE, kek);
+
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_salt;
+ }
+
+ /* Récupération des paramètres chiffrés */
+
+ enc_value = g_settings_get_value(storage->settings, "master");
+
+ encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t));
+
+ if (enc_length <= SECRET_STORAGE_IV_SIZE)
+ goto exit_with_enc;
+
+ /* Déhiffrement de la clef maître */
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ if (ctx == NULL)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_enc;
+ }
+
+ EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+
+ memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE);
+
+ ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter = key;
+
+ ret = EVP_DecryptUpdate(ctx, iter, &outlen,
+ ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE,
+ enc_length - SECRET_STORAGE_IV_SIZE);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ ret = EVP_DecryptFinal_ex(ctx, iter, &outlen);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ assert((iter - key) == SECRET_STORAGE_KEY_SIZE);
+
+ /* Stockage de la clef maître en mémoire */
+
+ if (master != NULL)
+ {
+ *master = malloc(SECRET_STORAGE_KEY_SIZE);
+ memcpy(*master, key, SECRET_STORAGE_KEY_SIZE);
+ }
+
+ result = true;
+
+ /* Sortie */
+
+ exit_with_ctx:
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ exit_with_enc:
+
+ g_variant_unref(enc_value);
+
+ exit_with_salt:
+
+ g_variant_unref(salt_value);
+
+ quick_exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à manipuler. *
+* password = mot de passe principal à utiliser. *
+* *
+* Description : Déverrouille la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_unlock(GSecretStorage *storage, const char *passwd)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_secret_storage_check_primary_password(storage, passwd, &storage->master_key);
+
+ if (result)
+ g_signal_emit_by_name(storage, "lock-update");
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à manipuler. *
+* *
+* Description : Verrouille la clef de chiffrement maître. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_secret_storage_lock(GSecretStorage *storage)
+{
+ if (storage->master_key != NULL)
+ {
+ free(storage->master_key);
+ storage->master_key = NULL;
+
+ g_signal_emit_by_name(storage, "lock-update");
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* in = séquence d'octets à traiter. *
+* out = séquence d'octets résultantes. [OUT] *
+* *
+* Description : Chiffre des données avec la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_encrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out)
+{
+ bool result; /* Bilan à retourner */
+ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */
+ int ret; /* Bilan à d'un appel */
+ EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/
+ size_t needed; /* Taille de la sortie */
+ unsigned char *iter; /* Tête d'écriture */
+ int outlen; /* Taille des données utiles */
+
+ result = false;
+
+ if (g_secret_storage_is_locked(storage))
+ goto quick_exit;
+
+ /* Récupération de la clef maître et d'un IV de chiffrement */
+
+ ret = RAND_bytes(iv, sizeof(iv));
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ /* Préparation de la zone de réception */
+
+ needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE;
+
+ setup_sized_binary(out, needed);
+
+ /* Chiffrement des données */
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ if (ctx == NULL)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE);
+
+ iter = out->bin_data + SECRET_STORAGE_IV_SIZE;
+
+ ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ ret = EVP_EncryptFinal_ex(ctx, iter, &outlen);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ assert((iter - out->bin_data) == out->size);
+
+ result = true;
+
+ /* Sortie */
+
+ exit_with_ctx:
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (!result)
+ exit_sized_binary(out);
+
+ exit:
+ quick_exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = espace de stockage sécurisé à consulter. *
+* in = séquence d'octets à traiter. *
+* out = séquence d'octets résultantes. [OUT] *
+* *
+* Description : Déchiffre des données avec la clef de chiffrement maître. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_secret_storage_decrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out)
+{
+ bool result; /* Bilan à retourner */
+ unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */
+ int ret; /* Bilan à d'un appel */
+ EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/
+ size_t needed; /* Taille de la sortie */
+ unsigned char *iter; /* Tête d'écriture */
+ int outlen; /* Taille des données utiles */
+
+ result = false;
+
+ if (g_secret_storage_is_locked(storage))
+ goto quick_exit;
+
+ /* Récupération d'un IV de déchiffrement */
+
+ if (in->size < SECRET_STORAGE_IV_SIZE)
+ goto exit;
+
+ memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE);
+
+ /* Préparation de la zone de réception */
+
+ needed = in->size - SECRET_STORAGE_IV_SIZE;
+
+ setup_sized_binary(out, needed);
+
+ /* Chiffrement des données */
+
+ ctx = EVP_CIPHER_CTX_new();
+
+ if (ctx == NULL)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit;
+ }
+
+ ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter = out->bin_data;
+
+ ret = EVP_DecryptUpdate(ctx, iter, &outlen,
+ in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ ret = EVP_DecryptFinal_ex(ctx, iter, &outlen);
+ if (ret != 1)
+ {
+ LOG_ERROR_OPENSSL;
+ goto exit_with_ctx;
+ }
+
+ iter += outlen;
+
+ assert((iter - out->bin_data) <= out->size);
+
+ resize_sized_binary(out, iter - out->bin_data);
+
+ result = true;
+
+ /* Sortie */
+
+ exit_with_ctx:
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (!result)
+ exit_sized_binary(out);
+
+ exit:
+ quick_exit:
+
+ return result;
+
+}
diff --git a/src/glibext/secstorage.h b/src/glibext/secstorage.h
new file mode 100644
index 0000000..ed3f79c
--- /dev/null
+++ b/src/glibext/secstorage.h
@@ -0,0 +1,74 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * secstorage.h - prototypes pour la conservation sécurisée d'éléments de configuration
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_SECSTORAGE_H
+#define _GLIBEXT_SECSTORAGE_H
+
+
+#include <stdbool.h>
+#include <gio/gio.h>
+
+
+#include "helpers.h"
+#include "../common/szbin.h"
+
+
+
+#define G_TYPE_SECRET_STORAGE (g_secret_storage_get_type())
+
+DECLARE_GTYPE(GSecretStorage, g_secret_storage, G, SECRET_STORAGE);
+
+
+/* Créé un nouveau gardien des secrets avec support de stockage. */
+GSecretStorage *g_secret_storage_new(GSettings *);
+
+/* Détermine si une clef de chiffrement protégée est en place. */
+bool g_secret_storage_has_key(const GSecretStorage *);
+
+/* Définit un mot de passe pour protéger une clef maître. */
+bool g_secret_storage_set_password(GSecretStorage *, const char *);
+
+/* Modifie le mot de passe protégeant une clef maître. */
+bool g_secret_storage_change_password(GSecretStorage *, const char *, const char *);
+
+/* Supprime le mot de passe protégeant une clef maître. */
+bool g_secret_storage_remove_password(GSecretStorage *, const char *);
+
+/* Détermine si la clef de chiffrement maître est vérouillée. */
+bool g_secret_storage_is_locked(const GSecretStorage *);
+
+/* Déverrouille la clef de chiffrement maître. */
+bool g_secret_storage_unlock(GSecretStorage *, const char *);
+
+/* Verrouille la clef de chiffrement maître. */
+void g_secret_storage_lock(GSecretStorage *);
+
+/* Chiffre des données avec la clef de chiffrement maître. */
+bool g_secret_storage_encrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *);
+
+/* Déchiffre des données avec la clef de chiffrement maître. */
+bool g_secret_storage_decrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *);
+
+
+
+#endif /* _GLIBEXT_SECSTORAGE_H */
diff --git a/src/glibext/singleton-int.h b/src/glibext/singleton-int.h
index 3db17f9..747e64a 100644
--- a/src/glibext/singleton-int.h
+++ b/src/glibext/singleton-int.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* singleton-int.h - définitions internes propres aux réductions du nombre d'instances d'un même type
*
- * Copyright (C) 2021 Cyrille Bagard
+ * Copyright (C) 2021-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -29,44 +29,61 @@
+/* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */
+
+
/* Fournit une liste de candidats embarqués par un candidat. */
typedef GSingletonCandidate ** (* list_inner_instances_fc) (const GSingletonCandidate *, size_t *);
/* Met à jour une liste de candidats embarqués par un candidat. */
typedef void (* update_inner_instances_fc) (GSingletonCandidate *, GSingletonCandidate **, size_t);
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-typedef guint (* hash_candidate_fc) (const GSingletonCandidate *);
-
-/* Détermine si deux candidats à l'unicité sont identiques. */
-typedef gboolean (* is_candidate_equal_fc) (const GSingletonCandidate *, const GSingletonCandidate *);
-
/* Marque un candidat comme figé. */
-typedef void (* set_candidate_ro_fc) (GSingletonCandidate *);
+typedef void (* mark_candidate_as_ro_fc) (GSingletonCandidate *);
/* Indique si le candidat est figé. */
typedef bool (* is_candidate_ro_fc) (const GSingletonCandidate *);
+/* Crée une copie modifiable d'un object unique. */
+typedef GSingletonCandidate * (* dup_candidate_fc) (const GSingletonCandidate *);
+
/* Instance d'objet visant à être unique (interface) */
-struct _GSingletonCandidateIface
+struct _GSingletonCandidateInterface
{
GTypeInterface base_iface; /* A laisser en premier */
list_inner_instances_fc list_inner; /* Récupération d'internes */
update_inner_instances_fc update_inner; /* Mise à jour des éléments */
- hash_candidate_fc hash; /* Prise d'empreinte */
- is_candidate_equal_fc is_equal; /* Comparaison */
-
- set_candidate_ro_fc set_ro; /* Bascule en mode figé */
+ mark_candidate_as_ro_fc mark_as_ro; /* Bascule en mode figé */
is_candidate_ro_fc is_ro; /* Consultation de l'état */
+ dup_candidate_fc dup; /* Création de copie modifiable*/
+
+};
+
+
+
+/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */
+
+
+/* Définition d'un compacteur d'instances de types (instance) */
+struct _GSingletonFactory
+{
+ GObject parent; /* A laisser en premier */
+
+ GHashTable *table; /* Suivi des conservations */
+ GMutex access; /* Verrou pour la concurrence */
+
};
+/* Définition d'un compacteur d'instances de types (classe) */
+struct _GSingletonFactoryClass
+{
+ GObjectClass parent; /* A laisser en premier */
-/* Redéfinition */
-typedef GSingletonCandidateIface GSingletonCandidateInterface;
+};
diff --git a/src/glibext/singleton.c b/src/glibext/singleton.c
index 78a3ad4..0a50d19 100644
--- a/src/glibext/singleton.c
+++ b/src/glibext/singleton.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* singleton.c - réduction du nombre d'instances d'un même type
*
- * Copyright (C) 2021 Cyrille Bagard
+ * Copyright (C) 2021-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,52 +25,35 @@
#include <assert.h>
+#include <malloc.h>
+#include "comparable.h"
+#include "hashable.h"
#include "singleton-int.h"
/* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */
+
/* Procède à l'initialisation de l'interface de rassemblement. */
static void g_singleton_candidate_default_init(GSingletonCandidateInterface *);
+/* Fournit une liste de candidats embarqués par un candidat. */
+static GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *);
+
/* Met à jour une liste de candidats embarqués par un candidat. */
static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *, GSingletonCandidate **, size_t);
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-static guint _g_singleton_candidate_hash(GSingletonCandidate *, GList **);
-
-/* Détermine si deux candidats à l'unicité sont identiques. */
-static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *, GList **);
-
/* Marque un candidat comme figé. */
-static void _g_singleton_candidate_set_read_only(GSingletonCandidate *, GList **);
+static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *);
/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */
-/* Définition d'un compacteur d'instances de types (instance) */
-struct _GSingletonFactory
-{
- GObject parent; /* A laisser en premier */
-
- GHashTable *table; /* Suivi des conservations */
- GMutex access; /* Verrou pour la concurrence */
-
-};
-
-/* Définition d'un compacteur d'instances de types (classe) */
-struct _GSingletonFactoryClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-};
-
-
/* Initialise la classe des compacteurs d'instances de types. */
static void g_singleton_factory_class_init(GSingletonFactoryClass *);
@@ -90,8 +73,10 @@ static void g_singleton_factory_finalize(GSingletonFactory *);
/* ---------------------------------------------------------------------------------- */
-/* Détermine le type d'une interface pour la lecture de binaire. */
-G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT)
+/* Détermine le type d'une interface pour la constitution d'objets uniques. */
+G_DEFINE_INTERFACE_WITH_CODE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT,
+ g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_HASHABLE_OBJECT);
+ g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_COMPARABLE_OBJECT))
/******************************************************************************
@@ -108,6 +93,13 @@ G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT)
static void g_singleton_candidate_default_init(GSingletonCandidateInterface *iface)
{
+ iface->list_inner = NULL;
+ iface->update_inner = NULL;
+
+ iface->mark_as_ro = NULL;
+ iface->is_ro = NULL;
+
+ iface->dup = NULL;
}
@@ -128,14 +120,17 @@ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *ifa
GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *candidate, size_t *count)
{
GSingletonCandidate **result; /* Instances à retourner */
- GSingletonCandidateIface *iface; /* Interface utilisée */
+ GSingletonCandidateInterface *iface; /* Interface utilisée */
iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
if (iface->list_inner == NULL)
{
+ assert(iface->update_inner == NULL);
+
*count = 0;
result = NULL;
+
}
else
@@ -162,7 +157,7 @@ GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingleto
static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *candidate, GSingletonCandidate **instances, size_t count)
{
- GSingletonCandidateIface *iface; /* Interface utilisée */
+ GSingletonCandidateInterface *iface; /* Interface utilisée */
iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
@@ -181,82 +176,36 @@ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *ca
/******************************************************************************
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
-* processed = liste de candidats déjà traités. *
* *
-* 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 _g_singleton_candidate_hash(GSingletonCandidate *candidate, GList **processed)
+static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *candidate)
{
- guint result; /* Valeur à retourner */
- GList *skip; /* Détection de boucle */
- GSingletonCandidateIface *iface; /* Interface utilisée */
+ GSingletonCandidateInterface *iface; /* Interface utilisée */
GSingletonCandidate **children; /* Instances internes */
size_t count; /* Quantité de ces instances */
size_t i; /* Boucle de parcours */
- skip = g_list_find(*processed, candidate);
-
- if (skip != NULL)
- result = 0;
-
- else
- {
- iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
-
- result = iface->hash(candidate);
-
- *processed = g_list_append(*processed, candidate);
-
- children = g_singleton_candidate_list_inner_instances(candidate, &count);
+ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
- for (i = 0; i < count; i++)
- {
- result ^= _g_singleton_candidate_hash(children[i], processed);
- g_object_unref(G_OBJECT(children[i]));
- }
+ iface->mark_as_ro(candidate);
- if (children != NULL)
- free(children);
+ children = g_singleton_candidate_list_inner_instances(candidate, &count);
+ for (i = 0; i < count; i++)
+ {
+ g_singleton_candidate_mark_as_read_only(children[i]);
+ unref_object(G_OBJECT(children[i]));
}
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : candidate = objet dont l'instance se veut unique. *
-* *
-* Description : Fournit l'empreinte d'un candidat à une centralisation. *
-* *
-* Retour : Empreinte de l'élément représenté. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-guint g_singleton_candidate_hash(GSingletonCandidate *candidate)
-{
- guint result; /* Valeur à retourner */
- GList *processed; /* Suivi des traitements */
-
- processed = NULL;
-
- result = _g_singleton_candidate_hash(candidate, &processed);
-
- assert(processed != NULL);
-
- g_list_free(processed);
-
- return result;
+ if (children != NULL)
+ free(children);
}
@@ -264,116 +213,43 @@ guint g_singleton_candidate_hash(GSingletonCandidate *candidate)
/******************************************************************************
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
-* other = second élément à analyser. *
-* processed = liste de candidats déjà traités. *
* *
-* 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 _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other, GList **processed)
+bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate)
{
- gboolean result; /* Bilan à renvoyer */
- GList *skip; /* Détection de boucle */
- GSingletonCandidateIface *iface; /* Interface utilisée */
- GSingletonCandidate **children[2]; /* Instances internes */
- size_t count[2]; /* Quantité de ces instances */
+ bool result; /* Etat à retourner */
+ GSingletonCandidateInterface *iface; /* Interface utilisée */
+#ifndef NDEBUG
+ GSingletonCandidate **children; /* Instances internes */
+ size_t count; /* Quantité de ces instances */
size_t i; /* Boucle de parcours */
+#endif
- skip = g_list_find(processed[0], candidate);
-
- if (skip != NULL)
- result = (g_list_find(processed[1], other) != NULL);
-
- else
- {
- iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
-
- result = iface->is_equal(candidate, other);
-
- processed[0] = g_list_append(processed[0], candidate);
- processed[1] = g_list_append(processed[1], other);
-
- if (!result)
- goto done;
-
- children[0] = g_singleton_candidate_list_inner_instances(candidate, &count[0]);
- children[1] = g_singleton_candidate_list_inner_instances(other, &count[1]);
-
- if (count[0] != count[1])
- {
- for (i = 0; i < count[0]; i++)
- g_object_unref(G_OBJECT(children[0][i]));
-
- for (i = 0; i < count[1]; i++)
- g_object_unref(G_OBJECT(children[1][i]));
-
- }
-
- else
- {
- for (i = 0; i < count[0] && result; i++)
- {
- result = _g_singleton_candidate_is_equal(children[0][i], children[1][i], processed);
- g_object_unref(G_OBJECT(children[0][i]));
- g_object_unref(G_OBJECT(children[1][i]));
- }
-
- for (; i < count[0]; i++)
- {
- g_object_unref(G_OBJECT(children[0][i]));
- g_object_unref(G_OBJECT(children[1][i]));
- }
+ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
- if (children[0] != NULL)
- free(children[0]);
+ result = iface->is_ro(candidate);
- if (children[1] != NULL)
- free(children[1]);
+#ifndef NDEBUG
- }
+ children = g_singleton_candidate_list_inner_instances(candidate, &count);
+ for (i = 0; i < count; i++)
+ {
+ assert(result == g_singleton_candidate_is_read_only(children[i]));
+ unref_object(G_OBJECT(children[i]));
}
- done:
-
- 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. *
-* *
-* Retour : Bilan de la comparaison. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other)
-{
- gboolean result; /* Bilan à renvoyer */
- GList *processed[2]; /* Suivi des traitements */
+ if (children != NULL)
+ free(children);
- processed[0] = NULL;
- processed[1] = NULL;
-
- result = _g_singleton_candidate_is_equal(candidate, other, processed);
-
- assert(processed[0] != NULL);
- assert(processed[1] != NULL);
-
- g_list_free(processed[0]);
- g_list_free(processed[1]);
+#endif
return result;
@@ -383,97 +259,60 @@ gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSinglet
/******************************************************************************
* *
* Paramètres : candidate = objet dont l'instance se veut unique. *
-* processed = liste de candidats déjà traités. *
* *
-* 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 _g_singleton_candidate_set_read_only(GSingletonCandidate *candidate, GList **processed)
+GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *candidate)
{
- GList *skip; /* Détection de boucle */
- GSingletonCandidateIface *iface; /* Interface utilisée */
- GSingletonCandidate **children; /* Instances internes */
- size_t count; /* Quantité de ces instances */
+ GSingletonCandidate *result; /* Instance à retourner */
+ GSingletonCandidateInterface *iface; /* Interface utilisée */
+ size_t count; /* Quantité d'objets internes */
+ GSingletonCandidate **children; /* Liste d'instances internes */
size_t i; /* Boucle de parcours */
+ GSingletonCandidate **new_children; /* Nouvelle liste d'instances */
- skip = g_list_find(*processed, candidate);
+ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
- if (skip == NULL)
- {
- iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
+ result = iface->dup(candidate);
- iface->set_ro(candidate);
+ assert(!g_singleton_candidate_is_read_only(result));
- *processed = g_list_append(*processed, candidate);
+ children = g_singleton_candidate_list_inner_instances(candidate, &count);
- children = g_singleton_candidate_list_inner_instances(candidate, &count);
+ if (count > 0)
+ {
+ new_children = malloc(count * sizeof(GSingletonCandidate *));
for (i = 0; i < count; i++)
{
- _g_singleton_candidate_set_read_only(candidate, processed);
- g_object_unref(G_OBJECT(children[i]));
- }
-
- if (children != NULL)
- free(children);
-
- }
-
-}
+ new_children[i] = g_singleton_candidate_dup(children[i]);
+ assert(!g_singleton_candidate_is_read_only(new_children[i]));
-/******************************************************************************
-* *
-* Paramètres : candidate = objet dont l'instance se veut unique. *
-* *
-* Description : Marque un candidat comme figé. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_singleton_candidate_set_read_only(GSingletonCandidate *candidate)
-{
- GList *processed; /* Suivi des traitements */
-
- processed = NULL;
-
- _g_singleton_candidate_set_read_only(candidate, &processed);
-
- assert(processed != NULL);
+ }
- g_list_free(processed);
-
-}
+ g_singleton_candidate_update_inner_instances(result, new_children, count);
+ for (i = 0; i < count; i++)
+ {
+ unref_object(G_OBJECT(new_children[i]));
+ unref_object(G_OBJECT(children[i]));
+ }
-/******************************************************************************
-* *
-* 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 : - *
-* *
-******************************************************************************/
+ free(new_children);
-bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate)
-{
- bool result; /* Etat à retourner */
- GSingletonCandidateIface *iface; /* Interface utilisée */
+ }
- iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate);
+ if (children != NULL)
+ free(children);
- result = iface->is_ro(candidate);
+ assert(G_OBJECT_TYPE(result) == G_OBJECT_TYPE(candidate));
return result;
@@ -528,8 +367,8 @@ static void g_singleton_factory_class_init(GSingletonFactoryClass *klass)
static void g_singleton_factory_init(GSingletonFactory *factory)
{
- factory->table = g_hash_table_new_full((GHashFunc)g_singleton_candidate_hash,
- (GEqualFunc)g_singleton_candidate_is_equal,
+ factory->table = g_hash_table_new_full((GHashFunc)g_hashable_object_hash,
+ (GEqualFunc)g_comparable_object_is_equal,
g_object_unref, NULL);
g_mutex_init(&factory->access);
@@ -688,7 +527,7 @@ GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory
g_hash_table_add(factory->table, candidate);
#endif
- g_singleton_candidate_set_read_only(candidate);
+ g_singleton_candidate_mark_as_read_only(candidate);
result = candidate;
diff --git a/src/glibext/singleton.h b/src/glibext/singleton.h
index 629687a..11afffd 100644
--- a/src/glibext/singleton.h
+++ b/src/glibext/singleton.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* singleton.h - prototypes pour la réduction du nombre d'instances d'un même type
*
- * Copyright (C) 2021 Cyrille Bagard
+ * Copyright (C) 2021-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,71 +25,36 @@
#define _GLIBEXT_SINGLETON_H
-#include <glib-object.h>
#include <stdbool.h>
-
-/* Définition d'un compacteur d'instances de types (instance) */
-typedef struct _GSingletonFactory GSingletonFactory;
+#include "helpers.h"
/* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */
-#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type())
-#define G_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidate))
-#define G_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface))
-#define G_IS_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_CANDIDATE))
-#define G_IS_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_SINGLETON_CANDIDATE))
-#define G_SINGLETON_CANDIDATE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface))
-
-
-/* Instance d'objet visant à être unique (coquille vide) */
-typedef struct _GSingletonCandidate GSingletonCandidate;
-
-/* Instance d'objet visant à être unique (interface) */
-typedef struct _GSingletonCandidateIface GSingletonCandidateIface;
-
-
-/* Détermine le type d'une interface pour la lecture de binaire. */
-GType g_singleton_candidate_get_type(void) G_GNUC_CONST;
+#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type())
-/* Fournit une liste de candidats embarqués par un candidat. */
-GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *);
+DECLARE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G, SINGLETON_CANDIDATE);
-/* Fournit l'empreinte d'un candidat à une centralisation. */
-guint g_singleton_candidate_hash(GSingletonCandidate *);
-
-/* Détermine si deux candidats à l'unicité sont identiques. */
-gboolean g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *);
-
-/* Marque un candidat comme figé. */
-void g_singleton_candidate_set_read_only(GSingletonCandidate *);
/* Indique si le candidat est figé. */
bool g_singleton_candidate_is_read_only(const GSingletonCandidate *);
+/* Crée une copie modifiable d'un object unique. */
+GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *);
-/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */
-
-#define G_TYPE_SINGLETON_FACTORY g_singleton_factory_get_type()
-#define G_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactory))
-#define G_IS_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_FACTORY))
-#define G_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass))
-#define G_IS_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SINGLETON_FACTORY))
-#define G_SINGLETON_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass))
+/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */
-/* Définition d'un compacteur d'instances de types (classe) */
-typedef struct _GSingletonFactoryClass GSingletonFactoryClass;
+#define G_TYPE_SINGLETON_FACTORY (g_singleton_factory_get_type())
+DECLARE_GTYPE(GSingletonFactory, g_singleton_factory, G, SINGLETON_FACTORY);
-/* Indique le type défini pour une mémoire de types d'objets. */
-GType g_singleton_factory_get_type(void);
/* Crée un compacteur d'instances de types. */
GSingletonFactory *g_singleton_factory_new(void);
diff --git a/src/glibext/strbuilder-int.h b/src/glibext/strbuilder-int.h
new file mode 100644
index 0000000..d74e65c
--- /dev/null
+++ b/src/glibext/strbuilder-int.h
@@ -0,0 +1,47 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder-int.h - définitions internes propres aux éléments exportant une chaîne de caractères
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_STRBUILDER_INT_H
+#define _GLIBEXT_STRBUILDER_INT_H
+
+
+#include "strbuilder.h"
+
+
+
+/* Exporte une chaîne de caractères à partir d'un objet. */
+typedef bool (* strbuilder_to_string_fc) (const GStringBuilder *, unsigned int, sized_binary_t *);
+
+
+/* Instance d'objet visant à être unique (interface) */
+struct _GStringBuilderInterface
+{
+ GTypeInterface base_iface; /* A laisser en premier */
+
+ strbuilder_to_string_fc to_string; /* Conversion en chaîne */
+
+};
+
+
+
+#endif /* _GLIBEXT_STRBUILDER_INT_H */
diff --git a/src/glibext/strbuilder.c b/src/glibext/strbuilder.c
new file mode 100644
index 0000000..e48674c
--- /dev/null
+++ b/src/glibext/strbuilder.c
@@ -0,0 +1,89 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder.c - exportation d'une chaîne de caractères depuis un élément
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "strbuilder.h"
+
+
+#include "strbuilder-int.h"
+
+
+
+/* Procède à l'initialisation de l'interface d'exportation. */
+static void g_string_builder_default_init(GStringBuilderInterface *);
+
+
+
+/* Détermine le type d'une interface pour la constitution d'objets uniques. */
+G_DEFINE_INTERFACE(GStringBuilder, g_string_builder, G_TYPE_OBJECT)
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface d'exportation. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_string_builder_default_init(GStringBuilderInterface *iface)
+{
+ iface->to_string = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* 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. *
+* *
+******************************************************************************/
+
+bool g_string_builder_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out)
+{
+ bool result; /* Bilan à retourner */
+ GStringBuilderInterface *iface; /* Interface utilisée */
+
+ iface = G_STRING_BUILDER_GET_IFACE(builder);
+
+ if (iface->to_string == NULL)
+ result = false;
+
+ else
+ result = iface->to_string(builder, flags, out);
+
+ return result;
+
+}
diff --git a/src/glibext/strbuilder.h b/src/glibext/strbuilder.h
new file mode 100644
index 0000000..b6422f7
--- /dev/null
+++ b/src/glibext/strbuilder.h
@@ -0,0 +1,46 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * strbuilder.h - prototypes pour l'exportation d'une chaîne de caractères depuis un élément
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GLIBEXT_STRBUILDER_H
+#define _GLIBEXT_STRBUILDER_H
+
+
+#include <stdbool.h>
+
+
+#include "helpers.h"
+#include "../common/szbin.h"
+
+
+
+#define G_TYPE_STRING_BUILDER (g_string_builder_get_type())
+
+DECLARE_INTERFACE(GStringBuilder, g_string_builder, G, STRING_BUILDER);
+
+
+/* Exporte une chaîne de caractères à partir d'un objet. */
+bool g_string_builder_to_string(const GStringBuilder *, unsigned int, sized_binary_t *);
+
+
+
+#endif /* _GLIBEXT_STRBUILDER_H */
diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am
index 2a2738a..839ee8f 100644
--- a/src/gtkext/Makefile.am
+++ b/src/gtkext/Makefile.am
@@ -26,10 +26,17 @@ libgtkext_la_LIBADD = \
libgtkext_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)
+IMG_PATH = ../../data/images
+
RES_FILES = \
hexview.css \
hexview.ui \
- statusstack.ui
+ launcher.ui \
+ statusstack.ui \
+ $(IMG_PATH)/nolock-symbolic.svg \
+ $(IMG_PATH)/locked-symbolic.svg \
+ $(IMG_PATH)/unlocked-symbolic.svg \
+ tweak.ui
libgtkext4_la_SOURCES = \
area-int.h \
@@ -45,17 +52,22 @@ libgtkext4_la_SOURCES = \
helpers.h \
hexview-int.h \
hexview.h hexview.c \
+ launcher-int.h \
+ launcher.h launcher.c \
panel-int.h \
panel.h panel.c \
resources.h resources.c \
- statusstack.h statusstack.c
+ statusstack-int.h \
+ statusstack.h statusstack.c \
+ tweak-int.h \
+ tweak.h tweak.c
libgtkext4_la_CFLAGS = $(LIBGTK4_CFLAGS)
devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%)
-dev_HEADERS = $(libgtkext_la_SOURCES:%c=)
+dev_HEADERS = $(libgtkext4_la_SOURCES:%c=)
#SUBDIRS = graph
diff --git a/src/gtkext/bufferview.c b/src/gtkext/bufferview.c
index 6b8f17c..13c2632 100644
--- a/src/gtkext/bufferview.c
+++ b/src/gtkext/bufferview.c
@@ -279,9 +279,9 @@ GBufferView *gtk_buffer_view_get_view(const GtkBufferView *view)
/******************************************************************************
* *
-* Paramètres : widget = composant GTK à examiner. *
-* width = largeur affectée au composant graphique. *
-* height = hauteur affectée au composant graphique. *
+* Paramètres : widget = composant GTK à examiner. *
+* width = largeur affectée au composant graphique. *
+* height = hauteur affectée au composant graphique. *
* baseline = ligne de base affectée au composant graphique. *
* *
* Description : Prend acte de la taille allouée au composant d'affichage. *
diff --git a/src/gtkext/gresource.xml b/src/gtkext/gresource.xml
index 3bfd8c5..640985f 100644
--- a/src/gtkext/gresource.xml
+++ b/src/gtkext/gresource.xml
@@ -3,6 +3,13 @@
<gresource prefix="/re/chrysalide/framework/gtkext">
<file compressed="true">hexview.css</file>
<file compressed="true">hexview.ui</file>
+ <file compressed="true">launcher.ui</file>
<file compressed="true">statusstack.ui</file>
+ <file compressed="true">tweak.ui</file>
+ </gresource>
+ <gresource prefix="/re/chrysalide/framework/gui/icons/scalable/actions">
+ <file compressed="true" alias="nolock-symbolic.svg">../../data/images/nolock-symbolic.svg</file>
+ <file compressed="true" alias="locked-symbolic.svg">../../data/images/locked-symbolic.svg</file>
+ <file compressed="true" alias="unlocked-symbolic.svg">../../data/images/unlocked-symbolic.svg</file>
</gresource>
</gresources>
diff --git a/src/gtkext/hexview.c b/src/gtkext/hexview.c
index 6df1140..5a8dd04 100644
--- a/src/gtkext/hexview.c
+++ b/src/gtkext/hexview.c
@@ -438,9 +438,9 @@ void demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot, GtkWidget *parent)
/******************************************************************************
* *
-* Paramètres : widget = composant GTK à examiner. *
-* width = largeur affectée au composant graphique. *
-* height = hauteur affectée au composant graphique. *
+* Paramètres : widget = composant GTK à examiner. *
+* width = largeur affectée au composant graphique. *
+* height = hauteur affectée au composant graphique. *
* baseline = ligne de base affectée au composant graphique. *
* *
* Description : Prend acte de la taille allouée au composant d'affichage. *
diff --git a/src/gtkext/launcher-int.h b/src/gtkext/launcher-int.h
new file mode 100644
index 0000000..07152f0
--- /dev/null
+++ b/src/gtkext/launcher-int.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweak-int.h - définitions internes pour un lanceur de panneau majeur
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GTKEXT_LAUNCHER_INT_H
+#define _GTKEXT_LAUNCHER_INT_H
+
+
+#include "launcher.h"
+
+
+
+/* Elément de lancement d'un panneau majeur pour l'interface (instance) */
+struct _GtkPanelLauncher
+{
+ GtkListBoxRow parent; /* A laisser en premier */
+
+ GtkImage *icon; /* Eventuelle image */
+ GtkLabel *title; /* Etiquette associée */
+ GtkLabel *desc; /* Description du panneau */
+
+};
+
+/* Elément de lancement d'un panneau majeur pour l'interface (classe) */
+struct _GtkPanelLauncherClass
+{
+ GtkListBoxRowClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un nouveau lanceur de panneau majeur. */
+bool gtk_panel_launcher_create(GtkPanelLauncher *, const char *, const char *, const char *);
+
+
+
+#endif /* _GTKEXT_LAUNCHER_INT_H */
diff --git a/src/gtkext/launcher.c b/src/gtkext/launcher.c
new file mode 100644
index 0000000..5bb850b
--- /dev/null
+++ b/src/gtkext/launcher.c
@@ -0,0 +1,211 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * launcher.c - lanceur de panneau majeur
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "launcher.h"
+
+
+#include <malloc.h>
+#include <string.h>
+
+
+#include "helpers.h"
+#include "launcher-int.h"
+#include "../common/extstr.h"
+
+
+
+/* Initialise la classe des sections d'éléments paramétrables. */
+static void gtk_panel_launcher_class_init(GtkPanelLauncherClass *);
+
+/* Initialise une instance de lanceur de panneau majeur. */
+static void gtk_panel_launcher_init(GtkPanelLauncher *);
+
+/* Supprime toutes les références externes. */
+static void gtk_panel_launcher_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_panel_launcher_finalize(GObject *);
+
+
+
+/* Détermine le type du composant d'affichage générique. */
+G_DEFINE_TYPE(GtkPanelLauncher, gtk_panel_launcher, GTK_TYPE_LIST_BOX_ROW);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe des sections d'éléments paramétrables. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_panel_launcher_class_init(GtkPanelLauncherClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_panel_launcher_dispose;
+ object->finalize = gtk_panel_launcher_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/launcher.ui");
+
+ gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, icon);
+ gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, title);
+ gtk_widget_class_bind_template_child(widget, GtkPanelLauncher, desc);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : launcher = composant GTK à initialiser. *
+* *
+* Description : Initialise une instance de lanceur de panneau majeur. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_panel_launcher_init(GtkPanelLauncher *launcher)
+{
+ gtk_widget_init_template(GTK_WIDGET(launcher));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_panel_launcher_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_PANEL_LAUNCHER);
+
+ G_OBJECT_CLASS(gtk_panel_launcher_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_panel_launcher_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_panel_launcher_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : icon = désignation de l'image de représentation. *
+* title = titre principal à afficher. *
+* desc = description du panneau ciblé. *
+* *
+* Description : Crée un nouveau lanceur de panneau majeur. *
+* *
+* Retour : Composant GTK mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GtkPanelLauncher *gtk_panel_launcher_new(const char *icon, const char *title, const char *desc)
+{
+ GtkPanelLauncher *result; /* Instance à retourner */
+
+ result = g_object_new(GTK_TYPE_PANEL_LAUNCHER, NULL);
+
+ if (!gtk_panel_launcher_create(result, icon, title, desc))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : launcher = lanceur à initialiser pleinement. *
+* icon = désignation de l'image de représentation. *
+* title = titre principal à afficher. *
+* desc = description du panneau ciblé. *
+* *
+* Description : Met en place un nouveau lanceur de panneau majeur. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool gtk_panel_launcher_create(GtkPanelLauncher *launcher, const char *icon, const char *title, const char *desc)
+{
+ bool result; /* Bilan à retourner */
+ char *bold; /* Titre sublimé */
+
+ result = true;
+
+ gtk_image_set_from_icon_name(launcher->icon, icon);
+
+ bold = strdup(title);
+ bold = strprep(bold, "<b>");
+ bold = stradd(bold, "</b>");
+
+ gtk_label_set_label(launcher->title, bold);
+
+ free(bold);
+
+ gtk_label_set_label(launcher->desc, desc);
+
+ return result;
+
+}
diff --git a/src/gtkext/launcher.h b/src/gtkext/launcher.h
new file mode 100644
index 0000000..3857216
--- /dev/null
+++ b/src/gtkext/launcher.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * launcher.h - prototypes pour pour un lanceur de panneau majeur
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GTKEXT_LAUNCHER_H
+#define _GTKEXT_LAUNCHER_H
+
+
+#include <gtk/gtk.h>
+
+
+#include "../glibext/helpers.h"
+
+
+
+#define GTK_TYPE_PANEL_LAUNCHER (gtk_panel_launcher_get_type())
+
+DECLARE_GTYPE(GtkPanelLauncher, gtk_panel_launcher, GTK, PANEL_LAUNCHER);
+
+
+/* Crée un nouveau lanceur de panneau majeur. */
+GtkPanelLauncher *gtk_panel_launcher_new(const char *, const char *, const char *);
+
+
+
+#endif /* _GTKEXT_LAUNCHER_H */
diff --git a/src/gtkext/launcher.ui b/src/gtkext/launcher.ui
new file mode 100644
index 0000000..f6f4fec
--- /dev/null
+++ b/src/gtkext/launcher.ui
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkPanelLauncher" parent="GtkListBoxRow">
+
+ <property name="child">
+
+ <object class="GtkGrid">
+ <property name="margin-bottom">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">12</property>
+ <property name="column-spacing">12</property>
+
+ <child>
+ <object class="GtkImage" id="icon">
+ <property name="icon-name"></property>
+ <property name="pixel-size">48</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ <style>
+ <class name="icon-dropshadow"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel" id="title">
+ <property name="label"></property>
+ <property name="use-markup">TRUE</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel" id="desc">
+ <property name="label"></property>
+ <property name="hexpand">true</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">1</property>
+ </layout>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkImage">
+ <property name="icon-name">go-next-symbolic</property>
+ <property name="margin-start">12</property>
+ <layout>
+ <property name="column">2</property>
+ <property name="row">0</property>
+ <property name="row-span">2</property>
+ </layout>
+ <style>
+ <class name="icon-dropshadow"/>
+ </style>
+ </object>
+ </child>
+
+ </object>
+
+ </property>
+
+ </template>
+</interface>
diff --git a/src/gtkext/statusstack-int.h b/src/gtkext/statusstack-int.h
index facc5af..2a9e99e 100644
--- a/src/gtkext/statusstack-int.h
+++ b/src/gtkext/statusstack-int.h
@@ -26,6 +26,7 @@
#include "statusstack.h"
+#include "../glibext/secstorage.h"
@@ -70,6 +71,9 @@ struct _GtkStatusStack
/* Tronc commun */
+ GSecretStorage *storage; /* Stockage des secrets */
+ GtkToggleButton *lock_update; /* Activation des accès */
+
GtkLabel *net_recv_speed; /* Débit en réception */
GtkLabel *net_send_speed; /* Débit en émission */
diff --git a/src/gtkext/statusstack.c b/src/gtkext/statusstack.c
index 0d8ef62..76dbee8 100644
--- a/src/gtkext/statusstack.c
+++ b/src/gtkext/statusstack.c
@@ -53,6 +53,9 @@ static void gtk_status_stack_dispose(GtkStatusStack *);
/* Procède à la libération totale de la mémoire. */
static void gtk_status_stack_finalize(GtkStatusStack *);
+/* Note le changement de verrouillage du stockage sécurisé. */
+static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *, GtkStatusStack *);
+
/* Met à jour dans la barre les débits réseau observés. */
static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *);
@@ -190,6 +193,8 @@ static void gtk_status_stack_class_init(GtkStatusStackClass *class)
gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_message);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_progress);
+ gtk_widget_class_bind_template_child(widget, GtkStatusStack, lock_update);
+
gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_recv_speed);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_send_speed);
@@ -220,6 +225,17 @@ static void gtk_status_stack_init(GtkStatusStack *stack)
stack->activity_info = calloc(1, sizeof(activity_info_t));
init_activity_info(stack->activity_info);
+ /* Suivi des évolutions relatives au stockage sécurisé */
+
+ stack->storage = get_secret_storage();
+
+ g_signal_connect(stack->storage, "lock-update",
+ G_CALLBACK(gtk_status_stack_on_secret_storage_lock_update), stack);
+
+ gtk_status_stack_on_secret_storage_lock_update(stack->storage, stack);
+
+ /* Suivi des débits de connexion */
+
stack->next_index = 0;
stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL,
@@ -242,6 +258,12 @@ static void gtk_status_stack_init(GtkStatusStack *stack)
static void gtk_status_stack_dispose(GtkStatusStack *stack)
{
+ if (stack->storage != NULL)
+ g_signal_handlers_disconnect_by_func(stack->storage,
+ gtk_status_stack_on_secret_storage_lock_update, stack);
+
+ g_clear_object(&stack->storage);
+
g_source_remove(stack->network_update_tag);
gtk_widget_dispose_template(GTK_WIDGET(stack), GTK_TYPE_STATUS_STACK);
@@ -322,6 +344,40 @@ void gtk_status_stack_reset(GtkStatusStack *stack)
/******************************************************************************
* *
+* Paramètres : storage = gardien des secrets impliqué. *
+* stack = barre de statut à actualiser. *
+* *
+* Description : Note le changement de verrouillage du stockage sécurisé. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *storage, GtkStatusStack *stack)
+{
+ if (!g_secret_storage_has_key(stack->storage))
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), false);
+ gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "nolock-symbolic");
+ }
+ else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), true);
+
+ if (g_secret_storage_is_locked(stack->storage))
+ gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "locked-symbolic");
+ else
+ gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "unlocked-symbolic");
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : stack = barre de statut à actualiser. *
* *
* Description : Met à jour dans la barre les débits réseau observés. *
diff --git a/src/gtkext/statusstack.ui b/src/gtkext/statusstack.ui
index 422f95d..8469e6e 100644
--- a/src/gtkext/statusstack.ui
+++ b/src/gtkext/statusstack.ui
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="GtkStatusStack" parent="GtkBox">
@@ -148,6 +149,20 @@
</child>
<child>
+ <object class="GtkToggleButton" id="lock_update">
+ <property name="sensitive">false</property>
+ <property name="has-frame">false</property>
+ <property name="icon-name">nolock-symbolic</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">vertical</property>
+ </object>
+ </child>
+
+ <child>
<object class="GtkImage">
<property name="margin-start">8</property>
<property name="icon-name">pan-down-symbolic</property>
diff --git a/src/gtkext/tweak-int.h b/src/gtkext/tweak-int.h
new file mode 100644
index 0000000..0d2c213
--- /dev/null
+++ b/src/gtkext/tweak-int.h
@@ -0,0 +1,65 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweak-int.h - définitions internes pour une section d'éléments à paramétrer
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GTKEXT_TWEAK_INT_H
+#define _GTKEXT_TWEAK_INT_H
+
+
+#include "tweak.h"
+
+
+
+/* Section de paramétrage pour liste d'éléments à configurer (instance) */
+struct _GtkTweakSection
+{
+ GtkListBoxRow parent; /* A laisser en premier */
+
+ GtkImage *icon; /* Eventuelle image */
+ GtkLabel *label; /* Etiquette associée */
+ GtkWidget *next; /* Eventuelle progression */
+
+ char *category; /* Groupe de rassemblement */
+
+ union
+ {
+ GType panel; /* Accès à la page de config. */
+ char *sub; /* Sous-ensemble à presenter */
+ };
+ bool has_sub_section; /* Choix du champ valide */
+
+};
+
+/* Section de paramétrage pour liste d'éléments à configurer (classe) */
+struct _GtkTweakSectionClass
+{
+ GtkListBoxRowClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une nouvelle section de configuration. */
+bool gtk_tweak_section_create(GtkTweakSection *, const tweak_info_t *);
+
+
+
+#endif /* _GTKEXT_TWEAK_INT_H */
diff --git a/src/gtkext/tweak.c b/src/gtkext/tweak.c
new file mode 100644
index 0000000..b03cf17
--- /dev/null
+++ b/src/gtkext/tweak.c
@@ -0,0 +1,319 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweak.c - section d'éléments à paramétrer
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "tweak.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+#include <string.h>
+
+
+#include "helpers.h"
+#include "tweak-int.h"
+
+
+
+/* Initialise la classe des sections d'éléments paramétrables. */
+static void gtk_tweak_section_class_init(GtkTweakSectionClass *);
+
+/* Initialise une instance de section d'éléments paramétrables. */
+static void gtk_tweak_section_init(GtkTweakSection *);
+
+/* Supprime toutes les références externes. */
+static void gtk_tweak_section_dispose(GtkTweakSection *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_tweak_section_finalize(GtkTweakSection *);
+
+
+
+/* Détermine le type du composant d'affichage générique. */
+G_DEFINE_TYPE(GtkTweakSection, gtk_tweak_section, GTK_TYPE_LIST_BOX_ROW);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe des sections d'éléments paramétrables. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_tweak_section_class_init(GtkTweakSectionClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)gtk_tweak_section_dispose;
+ object->finalize = (GObjectFinalizeFunc)gtk_tweak_section_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/tweak.ui");
+
+ gtk_widget_class_bind_template_child(widget, GtkTweakSection, icon);
+ gtk_widget_class_bind_template_child(widget, GtkTweakSection, label);
+ gtk_widget_class_bind_template_child(widget, GtkTweakSection, next);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = composant GTK à initialiser. *
+* *
+* Description : Initialise une instance de section d'éléments paramétrables. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_tweak_section_init(GtkTweakSection *section)
+{
+ gtk_widget_init_template(GTK_WIDGET(section));
+
+ section->category = NULL;
+
+ section->sub = NULL;
+ section->has_sub_section = true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_tweak_section_dispose(GtkTweakSection *section)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(section), GTK_TYPE_TWEAK_SECTION);
+
+ G_OBJECT_CLASS(gtk_tweak_section_parent_class)->dispose(G_OBJECT(section));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_tweak_section_finalize(GtkTweakSection *section)
+{
+ if (section->category != NULL)
+ free(section->category);
+
+ if (section->has_sub_section && section->sub != NULL)
+ free(section->sub);
+
+ G_OBJECT_CLASS(gtk_tweak_section_parent_class)->finalize(G_OBJECT(section));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations associées à la section. *
+* *
+* Description : Crée une nouvelle section de configuration. *
+* *
+* Retour : Composant GTK mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GtkTweakSection *gtk_tweak_section_new(const tweak_info_t *info)
+{
+ GtkTweakSection *result; /* Instance à retourner */
+
+ result = g_object_new(GTK_TYPE_TWEAK_SECTION, NULL);
+
+ if (!gtk_tweak_section_create(result, info))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = section à initialiser pleinement. *
+* info = informations associées à la section. *
+* *
+* Description : Met en place une nouvelle section de configuration. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool gtk_tweak_section_create(GtkTweakSection *section, const tweak_info_t *info)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ gtk_image_set_from_icon_name(section->icon, info->image);
+ gtk_label_set_label(section->label, info->label);
+
+ gtk_widget_set_visible(section->next, info->has_sub_section);
+
+ section->category = strdup(info->category);
+
+ if (info->has_sub_section)
+ {
+ section->sub = strdup(info->sub);
+ section->has_sub_section = true;
+ }
+ else
+ {
+ section->panel = info->panel;
+ section->has_sub_section = false;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = section à consulter. *
+* *
+* Description : Fournit l'étiquette associée à une section de configuration. *
+* *
+* Retour : Désignation humaine de la section. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *gtk_tweak_section_get_label(const GtkTweakSection *section)
+{
+ const char *result; /* Texte à retourner */
+
+ result = gtk_label_get_text(section->label);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = section à consulter. *
+* *
+* Description : Indique si la section renvoie vers une sous-section. *
+* *
+* Retour : Bilan de la consultation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool gtk_tweak_section_has_sub_section(const GtkTweakSection *section)
+{
+ bool result; /* Statut à retourner */
+
+ result = section->has_sub_section;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = section à consulter. *
+* *
+* Description : Fournit le type d'un éventuel panneau de configuration lié. *
+* *
+* Retour : Bilan de la consultation. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GType gtk_tweak_section_get_panel(const GtkTweakSection *section)
+{
+ GType result; /* Type d'objet à retourner */
+
+ assert(!section->has_sub_section);
+
+ result = section->panel;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : section = section à consulter. *
+* *
+* Description : Fournit la désignation d'une éventuelle sous-section liée. *
+* *
+* Retour : Désignation associée à la sous-section. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *gtk_tweak_section_get_sub_section(const GtkTweakSection *section)
+{
+ const char *result; /* Désignation à renvoyer */
+
+ assert(section->has_sub_section);
+
+ result = section->sub;
+
+ return result;
+
+}
diff --git a/src/gtkext/tweak.h b/src/gtkext/tweak.h
new file mode 100644
index 0000000..8c44844
--- /dev/null
+++ b/src/gtkext/tweak.h
@@ -0,0 +1,90 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweak.h - prototypes pour pour une section d'éléments à paramétrer
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GTKEXT_TWEAK_H
+#define _GTKEXT_TWEAK_H
+
+
+#include <stdbool.h>
+#include <gtk/gtk.h>
+
+
+#include "../glibext/helpers.h"
+
+
+
+/* Définition d'une section de configuration */
+typedef struct _tweak_info_t
+{
+ const char *parent; /* Ensemble d'appartenance */
+
+ const char *category; /* Groupe de rassemblement */
+
+ const char *image; /* Eventuelle image associée */
+ const char *key; /* Désignation de la section */
+ const char *label; /* Désignation humaine */
+
+ union
+ {
+ GType panel; /* Accès à la page de config. */
+ const char *sub; /* Sous-ensemble à presenter */
+ };
+ bool has_sub_section; /* Choix du champ valide */
+
+} tweak_info_t;
+
+
+#define TWEAK_SIMPLE_DEF(p, c, i, k, l, t) \
+ { \
+ .parent = p, \
+ .category = c, \
+ .image = i, \
+ .key = k, \
+ .label = l, \
+ .panel = t, \
+ .has_sub_section = false, \
+ }
+
+#define GTK_TYPE_TWEAK_SECTION (gtk_tweak_section_get_type())
+
+DECLARE_GTYPE(GtkTweakSection, gtk_tweak_section, GTK, TWEAK_SECTION);
+
+
+/* Crée une nouvelle section de configuration. */
+GtkTweakSection *gtk_tweak_section_new(const tweak_info_t *);
+
+/* Fournit l'étiquette associée à une section de configuration. */
+const char *gtk_tweak_section_get_label(const GtkTweakSection *);
+
+/* Indique si la section renvoie vers une sous-section. */
+bool gtk_tweak_section_has_sub_section(const GtkTweakSection *);
+
+/* Fournit le type d'un éventuel panneau de configuration lié. */
+GType gtk_tweak_section_get_panel(const GtkTweakSection *);
+
+/* Fournit la désignation d'une éventuelle sous-section liée. */
+const char *gtk_tweak_section_get_sub_section(const GtkTweakSection *);
+
+
+
+#endif /* _GTKEXT_TWEAK_H */
diff --git a/src/gtkext/tweak.ui b/src/gtkext/tweak.ui
new file mode 100644
index 0000000..576e25e
--- /dev/null
+++ b/src/gtkext/tweak.ui
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkTweakSection" parent="GtkListBoxRow">
+
+ <property name="child">
+
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="margin-top">14</property>
+ <property name="margin-bottom">14</property>
+
+ <child>
+ <object class="GtkImage" id="icon">
+ <property name="icon-name">security-high-symbolic</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <style>
+ <class name="icon-dropshadow"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Security</property>
+ <property name="hexpand">true</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkImage" id="next">
+ <property name="icon-name">go-next-symbolic</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <style>
+ <class name="icon-dropshadow"/>
+ </style>
+ </object>
+ </child>
+
+ </object>
+ </property>
+
+ </template>
+</interface>
diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am
index 42761c4..be70445 100644
--- a/src/gui/Makefile.am
+++ b/src/gui/Makefile.am
@@ -26,8 +26,6 @@ libgui_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)
libgui4_la_SOURCES = \
- panel-int.h \
- panel.h panel.c \
resources.h resources.c \
window-int.h \
window.h window.c
diff --git a/src/gui/core/core.c b/src/gui/core/core.c
index 4a6809c..57a398a 100644
--- a/src/gui/core/core.c
+++ b/src/gui/core/core.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* core.c - chargement et le déchargement du tronc commun pour l'éditeur graphique
*
- * Copyright (C) 2016-2019 Cyrille Bagard
+ * Copyright (C) 2016-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -63,7 +63,7 @@ bool load_gui_components(AvailableGuiComponent flags)
if ((flags & AGC_PANELS) != 0 && (__loaded & AGC_PANELS) == 0)
{
- result = load_main_panels();
+ result = load_main_framework_panel_definitions();
if (!result) goto done;
__loaded |= AGC_PANELS;
@@ -93,7 +93,7 @@ void unload_gui_components(AvailableGuiComponent flags)
{
if ((flags & AGC_PANELS) != 0 && (__loaded & AGC_PANELS) == 0)
{
- unload_all_panels();
+ unload_all_framework_panel_definitions();
__loaded &= ~AGC_PANELS;
diff --git a/src/gui/core/panels.c b/src/gui/core/panels.c
index 69ab2aa..4c113b8 100644
--- a/src/gui/core/panels.c
+++ b/src/gui/core/panels.c
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* panels.c - gestion d'ensemble de tous les panneaux graphiques du framework
*
- * Copyright (C) 2016-2024 Cyrille Bagard
+ * Copyright (C) 2016-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -25,25 +25,137 @@
#include "panels.h"
+#include <assert.h>
#include <malloc.h>
+#include <string.h>
+
+
+#include <i18n.h>
#include "../panels/binary.h"
+#include "../panels/binary-params.h"
#include "../panels/welcome.h"
+#include "../../gtkext/launcher.h"
+
+
+
+/* Définition générique complète d'un panneau */
+typedef struct _ext_panel_info_t
+{
+ /* Début des champs copiés de panel_info_t */
+
+ char *category; /* Groupe de rassemblement */
+
+ char *image; /* Eventuelle image associée */
+ char *title; /* Désignation humaine */
+ char *desc; /* Description humaine */
+ FrameworkPanelPersonality personality; /* Comportement attendu */
+
+ GType panel_type; /* Type du panneau représenté */
+ GType params_type; /* Composant de paramètre */
+
+ /* Fin des champs copiés de panel_info_t */
+
+ GtkTiledPanel *singleton; /* Conservation des allocations*/
+
+} ext_panel_info_t;
/* Liste des panneaux disponibles */
-static GPanelItem **_panels_list = NULL;
+static ext_panel_info_t **_panels_list = NULL;
static size_t _panels_count = 0;
+/* Copie une définition basique de panneau graphqiue. */
+static ext_panel_info_t *copy_panel_info(const panel_info_t *);
+
+/* Efface une définition étendue de panneau graphique. */
+static void delete_panel_info(ext_panel_info_t *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : info = information de base à copier. *
+* *
+* Description : Copie une définition basique de panneau graphqiue. *
+* *
+* Retour : Structure mémorisant l'ensemble des informations. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static ext_panel_info_t *copy_panel_info(const panel_info_t *info)
+{
+ ext_panel_info_t *result; /* Structure à retourner */
+
+ result = calloc(1, sizeof(ext_panel_info_t));
+
+ if (info->category != NULL)
+ result->category = strdup(info->category);
+
+ if (info->image != NULL)
+ result->image = strdup(info->image);
+
+ result->title = strdup(info->title);
+
+ if (info->desc != NULL)
+ result->desc = strdup(info->desc);
+
+ result->personality = info->personality;
+
+ result->panel_type = info->panel_type;
+ result->params_type = info->params_type;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations à supprimer de la mémoire. *
+* *
+* Description : Efface une définition étendue de panneau graphique. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void delete_panel_info(ext_panel_info_t *info)
+{
+ if (info->category != NULL)
+ free(info->category);
+
+ if (info->image != NULL)
+ free(info->image);
+
+ free(info->title);
+
+ if (info->desc != NULL)
+ free(info->desc);
+
+ if (info->singleton != NULL)
+ {
+ assert(info->personality & FPP_SINGLETON);
+ unref_object(info->singleton);
+ }
+
+ free(info);
+
+}
+
/******************************************************************************
* *
* Paramètres : - *
* *
-* Description : Charge les principaux panneaux graphiques du framework. *
+* Description : Charge les définitions des principaux panneaux du framework. *
* *
* Retour : Bilan de l'opération. *
* *
@@ -51,19 +163,45 @@ static size_t _panels_count = 0;
* *
******************************************************************************/
-bool load_main_panels(void)
+bool load_main_framework_panel_definitions(void)
{
bool result; /* Bilan à retourner */
-
- result = true;
+ panel_info_t info; /* Infos d'enregistrement */
// TODO register_panel_item(G_TYPE_LOG_PANEL, config);
/* Chargement du panneau de rapport au plus tôt */
// TODO panel = g_panel_item_new(G_TYPE_LOG_PANEL, NULL);
- register_panel_item(g_binary_panel_new());
- register_panel_item(g_welcome_panel_new());
+ info.category = "Main";
+
+ info.image = "binfile-symbolic";
+ info.title = _("Binary analysis");
+ info.desc = _("Load a binary content and parse its format if recognized");
+
+ info.personality = FPP_MAIN_PANEL;
+
+ info.panel_type = GTK_TYPE_BINARY_PANEL;
+ info.params_type = GTK_TYPE_BINARY_PARAMETERS;
+
+ result = register_framework_panel_definition(&info);
+ if (!result) goto done;
+
+ info.category = NULL;
+
+ info.image = NULL;
+ info.title = _("Welcome");
+ info.desc = NULL;
+
+ info.personality = FPP_MAIN_PANEL | FPP_SINGLETON;
+
+ info.panel_type = GTK_TYPE_WELCOME_PANEL;
+ info.params_type = G_TYPE_INVALID;
+
+ result = register_framework_panel_definition(&info);
+ if (!result) goto done;
+
+ done:
return result;
@@ -82,12 +220,12 @@ bool load_main_panels(void)
* *
******************************************************************************/
-void unload_all_panels(void)
+void unload_all_framework_panel_definitions(void)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < _panels_count; i++)
- unref_object(_panels_list[i]);
+ delete_panel_info(_panels_list[i]);
_panels_list = NULL;
_panels_count = 0;
@@ -97,10 +235,9 @@ void unload_all_panels(void)
/******************************************************************************
* *
-* Paramètres : type = type du composant à présenter à l'affichage. *
-* config = configuration à compléter. *
+* Paramètres : info = information de base à copier. *
* *
-* Description : Enregistre un panneau comme partie intégrante de l'éditeur. *
+* Description : Enregistre la définition d'un panneau graphique. *
* *
* Retour : - *
* *
@@ -108,11 +245,36 @@ void unload_all_panels(void)
* *
******************************************************************************/
-void register_panel_item(/* __steal */ GPanelItem *item)
+bool register_framework_panel_definition(const panel_info_t *info)
{
- _panels_list = realloc(_panels_list, ++_panels_count * sizeof(GPanelItem *));
+ bool result; /* Bilan à retourner */
+ size_t i; /* Boucle de parcours */
+ ext_panel_info_t *ext_info; /* Informations conservées */
+
+ result = false;
- _panels_list[_panels_count - 1] = item;
+ /* Validation */
+
+ for (i = 0; i < _panels_count; i++)
+ if (_panels_list[i]->panel_type == info->panel_type)
+ break;
+
+ if (i < _panels_count)
+ goto done;
+
+ /* Enregistrement */
+
+ ext_info = copy_panel_info(info);
+
+ _panels_list = realloc(_panels_list, ++_panels_count * sizeof(ext_panel_info_t *));
+
+ _panels_list[_panels_count - 1] = ext_info;
+
+ result = true;
+
+ done:
+
+ return result;
}
@@ -121,7 +283,7 @@ void register_panel_item(/* __steal */ GPanelItem *item)
* *
* Paramètres : target = type de définition de panneau recherchée. *
* *
-* Description : Retrouve la définition d'un type de panneau. *
+* Description : Met en place (au besoin) un panneau graphique unique. *
* *
* Retour : Instance de définition identifiée ou NULL en cas d'échec. *
* *
@@ -129,23 +291,27 @@ void register_panel_item(/* __steal */ GPanelItem *item)
* *
******************************************************************************/
-GPanelItem *find_item_panel_by_type(GType target)
+GtkTiledPanel *get_framework_panel_singleton(GType target)
{
- GPanelItem *result; /* Instance à renvoyer */
+ GtkTiledPanel *result; /* Instance à renvoyer */
size_t i; /* Boucle de parcours */
- GPanelItem *item; /* Définition de panneau */
+ ext_panel_info_t *info; /* Informations conservées */
result = NULL;
for (i = 0; i < _panels_count; i++)
{
- item = _panels_list[i];
+ info = _panels_list[i];
- if (G_OBJECT_TYPE(item) == target)
+ if (info->panel_type == target)
{
- result = item;
+ if (info->singleton == NULL)
+ info->singleton = g_object_new(target, NULL);
+
+ result = info->singleton;
ref_object(result);
break;
+
}
}
@@ -157,39 +323,84 @@ GPanelItem *find_item_panel_by_type(GType target)
/******************************************************************************
* *
-* Paramètres : skip = saute le panneau d'accueil lors du parcours ? *
-* handle = routine à appeler pour chaque panneau. *
-* data = données fournies pour accompagner cet appel. *
+* Paramètres : list = liste à compléter. *
* *
-* Description : Effectue le parcours de tous les panneaux chargés. *
+* Description : Intègre une définition de panneau enregistrée. *
* *
-* Retour : true si le parcours a été total, false sinon. *
+* Retour : true pour un parcours complet de la liste des définitions. *
* *
* Remarques : - *
* *
******************************************************************************/
-bool browse_all_item_panels(bool skip, handle_panel_item_fc handle, void *data)
+void populate_framework_panel_launcher_list(GtkListBox *list)
{
- bool result; /* Résultat à renvoyer */
size_t i; /* Boucle de parcours */
- GPanelItem *item; /* Définition de panneau */
-
- result = true;
+ ext_panel_info_t *info; /* Informations conservées */
+ GtkPanelLauncher *launcher; /* Lanceur à intégrer */
for (i = 0; i < _panels_count; i++)
{
- item = _panels_list[i];
+ info = _panels_list[i];
- if (skip && G_OBJECT_TYPE(item) == G_TYPE_WELCOME_PANEL)
+ if (info->category == NULL)
continue;
- result = handle(item, data);
+ launcher = gtk_panel_launcher_new(info->image, info->title, info->desc);
+
+ g_object_set_data(G_OBJECT(launcher), "panel_type", GSIZE_TO_POINTER(info->panel_type));
+
+ gtk_list_box_append(list, GTK_WIDGET(launcher));
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : row = lanceur sélectionné. *
+* *
+* Description : Fournit un composant d'édition de paramètres de panneau. *
+* *
+* Retour : Composant d'édition de paramètres ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GtkWidget *get_framework_panel_parameters(GtkListBoxRow *row)
+{
+ GtkWidget *result; /* Composant à retourner */
+ gpointer *data; /* Valeur incrustée */
+ GType target; /* Type de panneau recherché */
+ ext_panel_info_t *info; /* Informations conservées */
+ size_t i; /* Boucle de parcours */
+
+ data = g_object_get_data(G_OBJECT(row), "panel_type");
+ assert(data != NULL);
+
+ target = GPOINTER_TO_SIZE(data);
- if (!result) break;
+ info = NULL;
+
+ for (i = 0; i < _panels_count; i++)
+ {
+ info = _panels_list[i];
+
+ if (info->panel_type == target)
+ break;
}
+ assert(info != NULL);
+ assert(i < _panels_count);
+
+ if (info->params_type == G_TYPE_INVALID)
+ result = NULL;
+ else
+ result = g_object_new(info->params_type, NULL);
+
return result;
}
diff --git a/src/gui/core/panels.h b/src/gui/core/panels.h
index aaea9e6..4d0ce41 100644
--- a/src/gui/core/panels.h
+++ b/src/gui/core/panels.h
@@ -2,7 +2,7 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
* panels.h - prototypes pour la gestion d'ensemble de tous les panneaux graphiques du framework
*
- * Copyright (C) 2016-2024 Cyrille Bagard
+ * Copyright (C) 2016-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -26,30 +26,59 @@
#define _GUI_CORE_PANELS_H
+#include <glib-object.h>
#include <stdbool.h>
+#include <gtk/gtk.h>
-#include "../panel.h"
+#include "../../gtkext/panel.h"
-/* Charge les principaux panneaux graphiques du framework. */
-bool load_main_panels(void);
+/* Types de panneaux pour éditeur */
+typedef enum _FrameworkPanelPersonality
+{
+ FPP_NONE = (0 << 0), /* Pas de particularité */
+
+ FPP_MAIN_PANEL = (1 << 0), /* Panneau principal */
+ FPP_SINGLETON = (1 << 1), /* Instance unique */
+
+} FrameworkPanelPersonality;
+
+/* Définition générique d'un panneau */
+typedef struct _panel_info_t
+{
+ const char *category; /* Groupe de rassemblement */
+
+ const char *image; /* Eventuelle image associée */
+ const char *title; /* Désignation humaine */
+ const char *desc; /* Description humaine */
+
+ FrameworkPanelPersonality personality; /* Comportement attendu */
+
+ GType panel_type; /* Type du panneau représenté */
+ GType params_type; /* Composant de paramètre */
+
+} panel_info_t;
+
+
+/* Charge les définitions des principaux panneaux du framework. */
+bool load_main_framework_panel_definitions(void);
/* Décharge tous les panneaux graphiques du framework. */
-void unload_all_panels(void);
+void unload_all_framework_panel_definitions(void);
-/* Enregistre un panneau comme partie intégrante de l'éditeur. */
-void register_panel_item(/* __steal */ GPanelItem *);
+/* Enregistre la définition d'un panneau graphique. */
+bool register_framework_panel_definition(const panel_info_t *);
-/* Retrouve la définition d'un type de panneau. */
-GPanelItem *find_item_panel_by_type(GType);
+/* Met en place (au besoin) un panneau graphique unique. */
+GtkTiledPanel *get_framework_panel_singleton(GType);
-/* Réalise un traitement sur un panneau de l'éditeur. */
-typedef bool (* handle_panel_item_fc) (GPanelItem *, void *);
+/* Intègre une définition de panneau enregistrée. */
+void populate_framework_panel_launcher_list(GtkListBox *);
-/* Effectue le parcours de tous les panneaux chargés. */
-bool browse_all_item_panels(bool, handle_panel_item_fc, void *);
+/* Fournit un composant d'édition de paramètres de panneau. */
+GtkWidget *get_framework_panel_parameters(GtkListBoxRow *);
diff --git a/src/gui/dialogs/Makefile.am b/src/gui/dialogs/Makefile.am
index 5a77b99..e910c96 100644
--- a/src/gui/dialogs/Makefile.am
+++ b/src/gui/dialogs/Makefile.am
@@ -3,21 +3,23 @@ BUILT_SOURCES = resources.h resources.c
noinst_LTLIBRARIES = libguidialogs.la
-UI_FILES = \
- about.ui
+UI_FILES = \
+ about.ui \
+ preferences.ui
# bookmark.ui \
# export_graph.ui \
# identity.ui \
# loading.ui \
-# preferences.ui \
# prefs_fgraph.ui \
# prefs_labels.ui \
# snapshots.ui \
# storage.ui
-libguidialogs_la_SOURCES = \
- about-int.h \
- about.h about.c \
+libguidialogs_la_SOURCES = \
+ about-int.h \
+ about.h about.c \
+ preferences-int.h \
+ preferences.h preferences.c \
resources.h resources.c
# bookmark.h bookmark.c \
# export_disass.h export_disass.c \
@@ -26,12 +28,15 @@ libguidialogs_la_SOURCES = \
# gotox.h gotox.c \
# identity.h identity.c \
# loading.h loading.c \
-# preferences.h preferences.c \
+# \
# prefs_fgraph.h prefs_fgraph.c \
# prefs_labels.h prefs_labels.c \
# snapshots.h snapshots.c \
# storage.h storage.c
+libguidialogs_la_LIBADD = \
+ prefs/libguidialogsprefs.la
+
libguidialogs_la_CFLAGS = $(LIBGTK4_CFLAGS)
@@ -40,6 +45,9 @@ devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%)
dev_HEADERS = $(libguidialogs_la_SOURCES:%c=)
+SUBDIRS = prefs
+
+
resources.c: gresource.xml $(UI_FILES)
glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_dialogs gresource.xml
diff --git a/src/gui/dialogs/about-int.h b/src/gui/dialogs/about-int.h
index 382859b..616c73f 100644
--- a/src/gui/dialogs/about-int.h
+++ b/src/gui/dialogs/about-int.h
@@ -1,6 +1,6 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * about.h - définitions internes pour la boîte de dialogue d'information sur le programme
+ * about-int.h - définitions internes pour la boîte de dialogue d'information sur le programme
*
* Copyright (C) 2024 Cyrille Bagard
*
@@ -28,7 +28,7 @@
#include <stdbool.h>
-#include "about-int.h"
+#include "about.h"
diff --git a/src/gui/dialogs/about.c b/src/gui/dialogs/about.c
index 5c938ad..956918b 100644
--- a/src/gui/dialogs/about.c
+++ b/src/gui/dialogs/about.c
@@ -207,8 +207,8 @@ GtkWindow *gtk_app_about_dialog_new(GtkWindow *parent)
/******************************************************************************
* *
-* Paramètres : dialog = boîte de dialogue à initialiser pleinement. *
-* content = contenu binaire à exposer de façon brute. *
+* Paramètres : dialog = boîte de dialogue à initialiser pleinement. *
+* parent = fenêtre parente à surpasser. *
* *
* Description : Met en place la fenêtre d'informations sur le logiciel. *
* *
diff --git a/src/gui/dialogs/about.ui b/src/gui/dialogs/about.ui
index 892b468..989e53b 100644
--- a/src/gui/dialogs/about.ui
+++ b/src/gui/dialogs/about.ui
@@ -94,7 +94,7 @@
<object class="GtkLabel">
<property name="margin-top">22</property>
<property name="margin-bottom">10</property>
- <property name="label" translatable="yes">&lt;span fgcolor='white'&gt;Copyright (C) 2008-2024 Cyrille Bagard&lt;/span&gt;</property>
+ <property name="label" translatable="yes">&lt;span fgcolor='white'&gt;Copyright (C) 2008-2025 Cyrille Bagard&lt;/span&gt;</property>
<property name="use-markup">True</property>
</object>
</child>
diff --git a/src/gui/dialogs/gresource.xml b/src/gui/dialogs/gresource.xml
index b9bc7e2..169f440 100644
--- a/src/gui/dialogs/gresource.xml
+++ b/src/gui/dialogs/gresource.xml
@@ -2,15 +2,7 @@
<gresources>
<gresource prefix="/re/chrysalide/framework/gui/dialogs">
<file compressed="true">about.ui</file>
- <file compressed="true">bookmark.ui</file>
- <file compressed="true">export_graph.ui</file>
- <file compressed="true">identity.ui</file>
- <file compressed="true">loading.ui</file>
<file compressed="true">preferences.ui</file>
- <file compressed="true">prefs_fgraph.ui</file>
- <file compressed="true">prefs_labels.ui</file>
- <file compressed="true">snapshots.ui</file>
- <file compressed="true">storage.ui</file>
</gresource>
<gresource prefix="/org/chrysalide/gui/dialogs/about">
<file compressed="true" alias="chrysalide-full.png">../../../pixmaps/chrysalide-full.png</file>
diff --git a/src/gui/dialogs/preferences-int.h b/src/gui/dialogs/preferences-int.h
new file mode 100644
index 0000000..806f5f6
--- /dev/null
+++ b/src/gui/dialogs/preferences-int.h
@@ -0,0 +1,62 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * preferences-int.h - définitions internes pour la boîte de dialogue d'édition des préférences de l'utilisateur
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GUI_DIALOGS_PREFERENCES_INT_H
+#define _GUI_DIALOGS_PREFERENCES_INT_H
+
+
+#include <stdbool.h>
+
+
+#include "preferences.h"
+
+
+
+/* Fenêtre d'édition générale de la configuration (instance) */
+struct _GtkPreferencesDialog
+{
+ GtkWindow parent; /* A laisser en premier */
+
+ GtkLabel *side_title; /* Titre principal */
+ GtkScrolledWindow *side_content; /* Liste des sections */
+ GtkLabel *main_title; /* Titre de section */
+ GtkScrolledWindow *main_content; /* Page de configuration */
+
+ GHashTable *navigations; /* Liste de sections en place */
+
+};
+
+/* Fenêtre d'édition générale de la configuration (classe) */
+struct _GtkPreferencesDialogClass
+{
+ GtkWindowClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place la boîte de dialogue pour les préférences. */
+bool gtk_preferences_dialog_create(GtkPreferencesDialog *, GtkWindow *);
+
+
+
+#endif /* _GUI_DIALOGS_PREFERENCES_INT_H */
diff --git a/src/gui/dialogs/preferences.c b/src/gui/dialogs/preferences.c
index 4a3fb7c..68e7fd9 100644
--- a/src/gui/dialogs/preferences.c
+++ b/src/gui/dialogs/preferences.c
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * preferences.c - (re)définition de l'identité de l'utilisateur
+ * preferences.c - boîte de dialogue d'édition des préférences de l'utilisateur
*
- * Copyright (C) 2019 Cyrille Bagard
+ * Copyright (C) 2019-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -24,129 +24,104 @@
#include "preferences.h"
-#include <i18n.h>
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <gdk/gdkkeysyms.h>
-#include "prefs_fgraph.h"
-#include "prefs_labels.h"
-#include "../../core/params.h"
-#include "../../gtkext/easygtk.h"
+#include <config.h>
+#include "preferences-int.h"
+#include "prefs/security.h"
+#include "../../common/cpp.h"
+#include "../../gtkext/tweak.h"
+#include "../../plugins/pglist.h"
+#include "../../plugins/tweakable.h"
-/* Constructeur de panneau de paramétrage */
-typedef GtkWidget * (* prefs_panel_creation_cb) (GtkBuilder **);
-/* Chargement de la configuration */
-typedef void (* prefs_config_update_cb) (GtkBuilder *, GGenConfig *);
-/* Description d'un noeud de préférences */
-typedef struct _pref_node_desc_t
-{
- prefs_panel_creation_cb create; /* Procédure de création */
- prefs_config_update_cb load; /* Procédure de chargement */
- prefs_config_update_cb store; /* Procédure d'enregistrement */
-
- const char *name; /* Désignation interne */
- const char *title; /* Désignation humaine */
-
- GtkBuilder *builder; /* Constructeur GTK */
- GtkWidget *panel; /* Panneau GTK */
-
- struct _pref_node_desc_t *children; /* Sous-arborescence */
-
-} pref_node_desc_t;
-
-
-#define PREF_NODE_NULL_ENTRY { .title = NULL }
+/* --------------------------- BASES DE BOITE DE DIALOGUE --------------------------- */
-/* Liste des paramétrages à afficher */
-static pref_node_desc_t _prefs_nodes[] = {
+/* Procède à l'initialisation de la fenêtre des paramètres. */
+static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *);
- {
- .create = NULL,
-
- .title = "Analysis",
+/* Procède à l'initialisation de la fenêtre des paramètres. */
+static void gtk_preferences_dialog_init(GtkPreferencesDialog *);
- .children = (pref_node_desc_t []){
+/* Supprime toutes les références externes. */
+static void gtk_preferences_dialog_dispose(GObject *);
- {
- .create = create_labels_preferences,
- .load = load_labels_configuration,
- .store = store_labels_configuration,
+/* Procède à la libération totale de la mémoire. */
+static void gtk_preferences_dialog_finalize(GObject *);
- .name = "labels",
- .title = "Colored labels",
+/* Fournit la liste de section désignée par un nom. */
+static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *, const char *, bool);
- },
+/* Réagit à un changement de sélection dans les sections. */
+static void gtk_preferences_dialog_on_row_selected(GtkListBox *, GtkListBoxRow *, GtkPreferencesDialog *);
- PREF_NODE_NULL_ENTRY
- }
- },
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
- {
- .create = NULL,
- .title = "Editor",
- .children = (pref_node_desc_t []){
- {
- .create = create_fgraph_preferences,
- .load = load_fgraph_configuration,
- .store = store_fgraph_configuration,
- .name = "fgraph",
- .title = "Function graph",
+/* ---------------------------------------------------------------------------------- */
+/* BASES DE BOITE DE DIALOGUE */
+/* ---------------------------------------------------------------------------------- */
- },
-
- PREF_NODE_NULL_ENTRY
-
- }
- },
+/* Détermine le type du composant d'affichage générique. */
+G_DEFINE_TYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK_TYPE_WINDOW);
- PREF_NODE_NULL_ENTRY
-
-};
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation de la fenêtre des paramètres. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
-/* Eléments de la liste de sections */
-typedef enum _PrefListItem
+static void gtk_preferences_dialog_class_init(GtkPreferencesDialogClass *class)
{
- PLI_TITLE, /* Etiquette de la section */
- PLI_PANEL, /* Panneau graphique associé */
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
-} PrefListItem;
+ object = G_OBJECT_CLASS(class);
+ object->dispose = gtk_preferences_dialog_dispose;
+ object->finalize = gtk_preferences_dialog_finalize;
-/* Ajoute un panneau de paramétrage à la boîte de dialogue. */
-static void add_preferences_node(GtkTreeStore *, GtkTreeIter *, GGenConfig *, GtkStack *, pref_node_desc_t *);
+ widget = GTK_WIDGET_CLASS(class);
-/* Affiche le panneau correspondant au noeud sélectionné. */
-static void on_prefs_selection_changed(GtkTreeSelection *, GtkBuilder *);
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/preferences.ui");
-/* Lance la sauvegarde d'éléments de paramétrage. */
-static void store_preferences_node(GGenConfig *, pref_node_desc_t *);
+ gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_title);
+ gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, side_content);
+ gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_title);
+ gtk_widget_class_bind_template_child(widget, GtkPreferencesDialog, main_content);
-/* Sauvegarde l'ensemble des paramètres de configuration. */
-static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *);
+ /* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */
+ gtk_widget_class_add_binding_action(widget, GDK_KEY_Escape, 0 /* GDK 4.14 : GDK_NO_MODIFIER_MASK */, "window.close", NULL);
+}
/******************************************************************************
* *
-* Paramètres : store = arborescence des sections à compléter. *
-* parent = point d'insertion du parent. *
-* config = configuration globale à charger. *
-* stack = pile de composants GTK à constituer. *
-* node = noeud de description courant à traiter. *
+* Paramètres : dialog = composant GTK à initialiser. *
* *
-* Description : Ajoute un panneau de paramétrage à la boîte de dialogue. *
+* Description : Procède à l'initialisation de la fenêtre des paramètres. *
* *
* Retour : - *
* *
@@ -154,95 +129,150 @@ static void on_prefs_apply_button_clicked(GtkButton *, GtkBuilder *);
* *
******************************************************************************/
-static void add_preferences_node(GtkTreeStore *store, GtkTreeIter *parent, GGenConfig *config, GtkStack *stack, pref_node_desc_t *node)
+static void gtk_preferences_dialog_init(GtkPreferencesDialog *dialog)
{
- GtkTreeIter iter; /* Point d'insertion */
- pref_node_desc_t *child; /* Sous-élément à traiter */
+ size_t i; /* Boucle de parcours */
+ const tweak_info_t *info; /* Informations à considérer */
+ GtkListBox *navigation; /* Liste de sections à afficher*/
+ GtkTweakSection *section; /* Nouvelle section à présenter*/
+ tweak_info_t *dyn_infos; /* Informations supplémentaires*/
+ size_t dyn_count; /* Quantité de ces informations*/
+
+ tweak_info_t infos[] = {
+ TWEAK_SIMPLE_DEF("root", "Basics",
+ "security-high-symbolic", "security", "Security", GTK_TYPE_SECURITY_TWEAK_PANEL),
+ };
+
+ gtk_widget_init_template(GTK_WIDGET(dialog));
- if (node->create == NULL)
+ dialog->navigations = g_hash_table_new_full(g_str_hash, g_str_equal, free, g_object_unref);
+
+ /* Chargement des sections fixes */
+
+ for (i = 0; i < ARRAY_SIZE(infos); i++)
{
- node->builder = NULL;
- node->panel = NULL;
+ info = &infos[i];
+
+ navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true);
+ assert(navigation != NULL);
+
+ section = gtk_tweak_section_new(info);
+
+ gtk_list_box_append(navigation, GTK_WIDGET(section));
+
}
- else
+
+ /* Chargement des sections dynamiques */
+
+ dyn_infos = get_tweakable_plugins_info(&dyn_count);
+
+ for (i = 0; i < dyn_count; i++)
{
- node->panel = node->create(&node->builder);
+ info = &dyn_infos[i];
- node->load(node->builder, config);
+ navigation = gtk_preferences_dialog_get_navigation(dialog, info->parent, true);
+ assert(navigation != NULL);
- gtk_widget_show(node->panel);
+ section = gtk_tweak_section_new(info);
- gtk_stack_add_named(stack, node->panel, node->name);
+ gtk_list_box_append(navigation, GTK_WIDGET(section));
}
- gtk_tree_store_append(store, &iter, parent);
+ if (dyn_infos != NULL)
+ free(dyn_infos);
+
+ /* Affichage de la liste racine */
- gtk_tree_store_set(store, &iter,
- PLI_TITLE, _(node->title),
- PLI_PANEL, node->panel,
- -1);
+ navigation = gtk_preferences_dialog_get_navigation(dialog, "root", false);
+ assert(navigation != NULL);
- if (node->children != NULL)
- for (child = node->children; child->title != NULL; child++)
- add_preferences_node(store, &iter, config, stack, child);
+ g_signal_connect(navigation, "row-selected",
+ G_CALLBACK(gtk_preferences_dialog_on_row_selected), dialog);
+
+ gtk_scrolled_window_set_child(dialog->side_content, GTK_WIDGET(navigation));
+
+ unref_object(navigation);
}
/******************************************************************************
* *
-* Paramètres : parent = fenêtre principale de l'éditeur. *
-* outb = constructeur à détruire après usage. [OUT] *
+* Paramètres : object = instance d'objet GLib à traiter. *
* *
-* Description : Propose une boîte de dialogue pour la configuration générale.*
+* Description : Supprime toutes les références externes. *
* *
-* Retour : Adresse de la fenêtre mise en place. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb)
+static void gtk_preferences_dialog_dispose(GObject *object)
{
- GtkWidget *result; /* Fenêtre à renvoyer */
- GtkBuilder *builder; /* Constructeur utilisé */
- GGenConfig *config; /* Configuration globale */
- GtkStack *stack; /* Pile à mettre à jour */
- GtkTreeStore *store; /* Arborescence des sections */
- pref_node_desc_t *iter; /* Boucle de parcours */
- GtkTreeView *treeview; /* Arborescence principale */
+ GtkPreferencesDialog *dialog; /* Version spécialisée */
+
+ dialog = GTK_PREFERENCES_DIALOG(object);
- builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/preferences.ui");
- *outb = builder;
+ if (dialog->navigations != NULL)
+ {
+ /**
+ * Cf. documentation de g_hash_table_new_full().
+ */
+ g_hash_table_remove_all(dialog->navigations);
- result = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
+ g_hash_table_unref(dialog->navigations);
+ dialog->navigations = NULL;
- gtk_window_set_transient_for(GTK_WINDOW(result), parent);
+ }
- /* Intégration des différentes sections */
+ gtk_widget_dispose_template(GTK_WIDGET(dialog), GTK_TYPE_PREFERENCES_DIALOG);
+
+ G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->dispose(object);
+
+}
- config = get_main_configuration();
- stack = GTK_STACK(gtk_builder_get_object(builder, "stack"));
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- store = GTK_TREE_STORE(gtk_builder_get_object(builder, "pref_list"));
+static void gtk_preferences_dialog_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_preferences_dialog_parent_class)->finalize(object);
- for (iter = _prefs_nodes; iter->title != NULL; iter++)
- add_preferences_node(store, NULL, config, stack, iter);
+}
- treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview"));
- gtk_tree_view_expand_all(treeview);
+/******************************************************************************
+* *
+* Paramètres : parent = fenêtre parente à surpasser. *
+* *
+* Description : Construit une boîte de dialogue pour les préférences. *
+* *
+* Retour : Adresse de la fenêtre mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- /* Connexion des signaux */
+GtkWindow *gtk_preferences_dialog_new(GtkWindow *parent)
+{
+ GtkWindow *result; /* Boite de dialogue à renvoyer*/
- gtk_builder_add_callback_symbols(builder,
- BUILDER_CALLBACK(on_prefs_selection_changed),
- BUILDER_CALLBACK(on_prefs_apply_button_clicked),
- NULL);
+ result = g_object_new(GTK_TYPE_PREFERENCES_DIALOG, NULL);
- gtk_builder_connect_signals(builder, builder);
+ if (!gtk_preferences_dialog_create(GTK_PREFERENCES_DIALOG(result), parent))
+ g_clear_object(&result);
return result;
@@ -251,79 +281,85 @@ GtkWidget *create_preferences_dialog(GtkWindow *parent, GtkBuilder **outb)
/******************************************************************************
* *
-* Paramètres : selection = sélection courante de l'arborescence des options.*
-* builder = constructeur GTK avec toutes les références. *
+* Paramètres : dialog = boîte de dialogue à initialiser pleinement. *
+* parent = fenêtre parente à surpasser. *
* *
-* Description : Affiche le panneau correspondant au noeud sélectionné. *
+* Description : Met en place la boîte de dialogue pour les préférences. *
* *
-* Retour : - *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void on_prefs_selection_changed(GtkTreeSelection *selection, GtkBuilder *builder)
+bool gtk_preferences_dialog_create(GtkPreferencesDialog *dialog, GtkWindow *parent)
{
- GtkTreeModel *model; /* Gestionnaire de données */
- GtkTreeIter iter; /* Position courante */
- GtkWidget *panel; /* Panneau à mettre en avant */
- GtkStack *stack; /* Pile à mettre à jour */
-
- if (gtk_tree_selection_get_selected(selection, &model, &iter))
- {
- gtk_tree_model_get(model, &iter, PLI_PANEL, &panel, -1);
+ bool result; /* Bilan à retourner */
- stack = GTK_STACK(gtk_builder_get_object(builder, "stack"));
+ result = true;
- if (panel == NULL)
- gtk_stack_set_visible_child_name(stack, "empty");
-
- else
- {
- gtk_stack_set_visible_child(stack, panel);
+ gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
- g_object_unref(G_OBJECT(panel));
-
- }
-
- }
+ return result;
}
/******************************************************************************
* *
-* Paramètres : config = configuration globale à actualiser. *
-* node = noeud de description courant à traiter. *
+* Paramètres : dialog = boîte de dialogue à initialiser pleinement. *
+* key = désignation de la liste à fournir. *
+* create = autorisation d'une création si besoin est. *
* *
-* Description : Lance la sauvegarde d'éléments de paramétrage. *
+* Description : Fournit la liste de section désignée par un nom. *
* *
-* Retour : - *
+* Retour : Composant graphique à utiliser. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node)
+static GtkListBox *gtk_preferences_dialog_get_navigation(GtkPreferencesDialog *dialog, const char *key, bool create)
{
- pref_node_desc_t *child; /* Sous-élément à traiter */
+ GtkListBox *result; /* Instance à retourner */
+#ifndef NDEBUG
+ gboolean status; /* Bilan d'une insertion */
+#endif
- if (node->create != NULL)
- node->store(node->builder, config);
+ result = g_hash_table_lookup(dialog->navigations, key);
- if (node->children != NULL)
- for (child = node->children; child->title != NULL; child++)
- store_preferences_node(config, child);
+ if (result == NULL && create)
+ {
+ result = GTK_LIST_BOX(gtk_list_box_new());
+ g_object_ref_sink(G_OBJECT(result));
+
+ gtk_list_box_set_selection_mode(result, GTK_SELECTION_BROWSE);
+ gtk_widget_add_css_class(GTK_WIDGET(result), "navigation-sidebar");
+
+#ifndef NDEBUG
+ status = g_hash_table_insert(dialog->navigations, strdup(key), result);
+ assert(status);
+#else
+ g_hash_table_insert(dialog->navigations, key, result);
+#endif
+
+ }
+
+ if (result != NULL)
+ ref_object(result);
+
+ return result;
}
/******************************************************************************
* *
-* Paramètres : button = bouton GTK à l'origine de l'opération. *
-* builder = constructeur GTK avec toutes les références. *
+* Paramètres : navigation = liste concernée par l'événement. *
+* selected = élément sélectionné (voire NULL). *
+* dialog = boîte de dialogue à initialiser pleinement. *
* *
-* Description : Sauvegarde l'ensemble des paramètres de configuration. *
+* Description : Réagit à un changement de sélection dans les sections. *
* *
* Retour : - *
* *
@@ -331,14 +367,41 @@ static void store_preferences_node(GGenConfig *config, pref_node_desc_t *node)
* *
******************************************************************************/
-static void on_prefs_apply_button_clicked(GtkButton *button, GtkBuilder *builder)
+static void gtk_preferences_dialog_on_row_selected(GtkListBox *navigation, GtkListBoxRow *selected, GtkPreferencesDialog *dialog)
{
- GGenConfig *config; /* Configuration globale */
- pref_node_desc_t *iter; /* Boucle de parcours */
+ GtkTweakSection *section; /* Nature réelle sélectionnée */
+ GType type; /* Type de panneau de config. */
+ GtkWidget *panel; /* Nouveau panneau à présenter */
+
+ if (selected != NULL)
+ {
+ if (!gtk_tweak_section_has_sub_section(GTK_TWEAK_SECTION(selected)))
+ {
+ section = GTK_TWEAK_SECTION(selected);
+
+ gtk_label_set_text(dialog->main_title, gtk_tweak_section_get_label(section));
+
+ type = gtk_tweak_section_get_panel(section);
+ panel = g_object_new(type, NULL);
- config = get_main_configuration();
+ gtk_scrolled_window_set_child(dialog->main_content, panel);
- for (iter = _prefs_nodes; iter->title != NULL; iter++)
- store_preferences_node(config, iter);
+ }
+
+ else
+ {
+
+ // TODO
+
+ }
+
+ }
}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
diff --git a/src/gui/dialogs/preferences.h b/src/gui/dialogs/preferences.h
index d04af72..e291ea8 100644
--- a/src/gui/dialogs/preferences.h
+++ b/src/gui/dialogs/preferences.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * preferences.h - prototypes pour la (re)définition de l'identité de l'utilisateur
+ * preferences.h - prototypes pour la boîte de dialogue d'édition des préférences de l'utilisateur
*
- * Copyright (C) 2019 Cyrille Bagard
+ * Copyright (C) 2019-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -28,9 +28,17 @@
#include <gtk/gtk.h>
+#include "../../glibext/helpers.h"
-/* Propose une boîte de dialogue pour la configuration générale. */
-GtkWidget *create_preferences_dialog(GtkWindow *, GtkBuilder **);
+
+
+#define GTK_TYPE_PREFERENCES_DIALOG (gtk_preferences_dialog_get_type())
+
+DECLARE_GTYPE(GtkPreferencesDialog, gtk_preferences_dialog, GTK, PREFERENCES_DIALOG);
+
+
+/* Construit une boîte de dialogue pour les préférences. */
+GtkWindow *gtk_preferences_dialog_new(GtkWindow *);
diff --git a/src/gui/dialogs/preferences.ui b/src/gui/dialogs/preferences.ui
index 251ef46..950242a 100644
--- a/src/gui/dialogs/preferences.ui
+++ b/src/gui/dialogs/preferences.ui
@@ -1,156 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.21.0 -->
<interface>
- <requires lib="gtk+" version="3.20"/>
- <object class="GtkTreeStore" id="pref_list">
- <columns>
- <!-- column-name title -->
- <column type="gchararray"/>
- <!-- column-name panel -->
- <column type="GObject"/>
- </columns>
- </object>
- <object class="GtkDialog" id="window">
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">General preferences</property>
- <property name="modal">True</property>
- <property name="default_width">800</property>
- <property name="default_height">500</property>
- <property name="type_hint">dialog</property>
- <child internal-child="vbox">
- <object class="GtkBox">
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
- <property name="margin_left">8</property>
- <property name="margin_right">8</property>
- <property name="margin_top">8</property>
- <property name="margin_bottom">8</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkButton" id="button1">
- <property name="label">gtk-cancel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button2">
- <property name="label">gtk-apply</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="use_stock">True</property>
- <signal name="clicked" handler="on_prefs_apply_button_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
+
+ <template class="GtkPreferencesDialog" parent="GtkWindow">
+
+ <property name="default-width">980</property>
+ <property name="default-height">640</property>
+ <property name="modal">true</property>
+ <property name="resizable">true</property>
+
+ <child type="titlebar">
+ <object class="GtkBox">
+ <child>
+ <object class="GtkHeaderBar" id="sidebar-header">
+ <property name="decoration-layout">:</property>
+
+ <property name="title-widget">
+ <object class="GtkLabel" id="side_title">
+ <property name="label" translatable="yes">Parameters</property>
+ <property name="single-line-mode">True</property>
+ <property name="ellipsize">end</property>
+ <property name="width-chars">5</property>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ </property>
+
+ <child type="start">
+ <object class="GtkToggleButton">
+ <property name="icon-name">edit-find-symbolic</property>
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <!-- Séparation centrale -->
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">vertical</property>
+ <property name="css-name">sidebar</property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkHeaderBar" id="main-header">
+ <property name="hexpand">1</property>
+
+ <property name="title-widget">
+ <object class="GtkLabel" id="main_title">
+ <property name="label" translatable="yes"></property>
+ <property name="single-line-mode">True</property>
+ <property name="ellipsize">end</property>
+ <property name="width-chars">5</property>
+ <style>
+ <class name="title"/>
+ </style>
+ </object>
+ </property>
+
+ </object>
+ </child>
+ </object>
</child>
+
<child>
- <object class="GtkPaned">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="position">200</property>
- <property name="wide_handle">True</property>
- <child>
- <object class="GtkScrolledWindow">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="margin_left">8</property>
- <property name="margin_right">8</property>
- <property name="margin_top">8</property>
- <property name="shadow_type">in</property>
+ <object class="GtkBox">
<child>
- <object class="GtkTreeView" id="treeview">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="model">pref_list</property>
- <property name="headers_visible">False</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection">
- <signal name="changed" handler="on_prefs_selection_changed" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn">
- <property name="title" translatable="yes">column</property>
- <child>
- <object class="GtkCellRendererText"/>
- <attributes>
- <attribute name="text">0</attribute>
- </attributes>
- </child>
- </object>
- </child>
- </object>
+ <object class="GtkScrolledWindow" id="side_content">
+ <property name="hexpand">false</property>
+ <property name="vexpand">true</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">automatic</property>
+ </object>
+ </child>
+
+ <!-- Séparation centrale -->
+ <child>
+ <object class="GtkSeparator">
+ <property name="orientation">vertical</property>
+ <property name="css-name">sidebar</property>
+ </object>
</child>
- </object>
- <packing>
- <property name="resize">False</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- <child>
- <object class="GtkStack" id="stack">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">8</property>
- <property name="margin_right">8</property>
- <property name="margin_top">8</property>
+
<child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="name">empty</property>
- </packing>
+ <object class="GtkScrolledWindow" id="main_content">
+ <property name="hexpand">1</property>
+ <property name="vexpand">1</property>
+ <property name="hscrollbar-policy">never</property>
+ <property name="vscrollbar-policy">automatic</property>
+ </object>
</child>
- </object>
- <packing>
- <property name="resize">True</property>
- <property name="shrink">True</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
+ </object>
</child>
- </object>
- </child>
- <action-widgets>
- <action-widget response="-6">button1</action-widget>
- <action-widget response="-10">button2</action-widget>
- </action-widgets>
- <child>
- <placeholder/>
- </child>
- </object>
+
+ <object class="GtkSizeGroup">
+ <widgets>
+ <widget name="sidebar-header"/>
+ <widget name="side_content"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup">
+ <widgets>
+ <widget name="main-header"/>
+ <widget name="main_content"/>
+ </widgets>
+ </object>
+
+ </template>
</interface>
diff --git a/src/gui/dialogs/prefs/Makefile.am b/src/gui/dialogs/prefs/Makefile.am
new file mode 100644
index 0000000..fa1af96
--- /dev/null
+++ b/src/gui/dialogs/prefs/Makefile.am
@@ -0,0 +1,31 @@
+
+BUILT_SOURCES = resources.h resources.c
+
+noinst_LTLIBRARIES = libguidialogsprefs.la
+
+UI_FILES = \
+ security.ui
+
+libguidialogsprefs_la_SOURCES = \
+ resources.h resources.c \
+ security-int.h \
+ security.h security.c
+
+libguidialogsprefs_la_CFLAGS = $(LIBGTK4_CFLAGS)
+
+
+devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%)
+
+dev_HEADERS = $(libguidialogsprefs_la_SOURCES:%c=)
+
+
+resources.c: gresource.xml $(UI_FILES)
+ glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_dialogs_prefs gresource.xml
+
+resources.h: gresource.xml
+ glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name gui_dialogs_prefs gresource.xml
+
+
+CLEANFILES = resources.h resources.c
+
+EXTRA_DIST = gresource.xml $(UI_FILES)
diff --git a/src/gui/dialogs/prefs/gresource.xml b/src/gui/dialogs/prefs/gresource.xml
new file mode 100644
index 0000000..7b18143
--- /dev/null
+++ b/src/gui/dialogs/prefs/gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/re/chrysalide/framework/gui/dialogs/prefs">
+ <file compressed="true">security.ui</file>
+ </gresource>
+</gresources>
diff --git a/src/gui/dialogs/prefs/security-int.h b/src/gui/dialogs/prefs/security-int.h
new file mode 100644
index 0000000..be7867c
--- /dev/null
+++ b/src/gui/dialogs/prefs/security-int.h
@@ -0,0 +1,64 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * security-int.h - définitions internes pour la configuration des paramètres liés à la sécurité
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GUI_DIALOGS_PREFS_SECURITY_INT_H
+#define _GUI_DIALOGS_PREFS_SECURITY_INT_H
+
+
+#include "security.h"
+#include "../../../glibext/secstorage.h"
+
+
+
+/* Composant d'édition des paramètres de sécurité (instance) */
+struct _GtkSecurityTweakPanel
+{
+ GtkBox parent; /* A laisser en premier */
+
+ /* Stockage sécurisé */
+
+ GSecretStorage *storage; /* Stockage des secrets */
+
+ GtkWidget *current_primary_passwd; /* Mot de passe courant */
+
+ GtkWidget *new_primary_passwd; /* Nouveau mot de passe */
+ GtkWidget *new_primary_passwd_lbl; /* Etiquette associée */
+ GtkWidget *new_primary_passwd_2; /* Nouveau mot de passe (#2) */
+ GtkWidget *new_primary_passwd_2_lbl; /* Etiquette associée (#2) */
+
+ GtkWidget *set_password; /* Bouton de définition de mdp */
+ GtkWidget *change_password; /* Bouton de changement de mdp */
+ GtkWidget *remove_password; /* Bouton de suppression de mdp*/
+
+};
+
+/* Composant d'édition des paramètres de sécurité (classe) */
+struct _GtkSecurityTweakPanelClass
+{
+ GtkBoxClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* _GUI_DIALOGS_PREFS_SECURITY_INT_H */
diff --git a/src/gui/dialogs/prefs/security.c b/src/gui/dialogs/prefs/security.c
new file mode 100644
index 0000000..2b82339
--- /dev/null
+++ b/src/gui/dialogs/prefs/security.c
@@ -0,0 +1,400 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * security.c - configuration des paramètres liés à la sécurité
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "security.h"
+
+
+#include <i18n.h>
+
+
+#include "security-int.h"
+#include "../../../core/global.h"
+#include "../../../core/logs.h"
+#include "../../../gtkext/helpers.h"
+
+
+
+/* ---------------------- GESTION DE L'ENSEMBLE DES PARAMETRES ---------------------- */
+
+
+/* Procède à l'initialisation de classe des configurations. */
+static void gtk_security_tweak_panel_class_init(GtkSecurityTweakPanelClass *);
+
+/* Procède à l'initialisation des configurations de sécurité. */
+static void gtk_security_tweak_panel_init(GtkSecurityTweakPanel *);
+
+/* Supprime toutes les références externes. */
+static void gtk_security_tweak_panel_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_security_tweak_panel_finalize(GObject *);
+
+
+
+/* ---------------------- CONSERVATION SECURISEE DE PARAMETRES ---------------------- */
+
+
+/* Initalise la partie relative au stockage sécurisé. */
+static void gtk_security_tweak_panel_init_secret_storage(GtkSecurityTweakPanel *);
+
+/* Supprime toutes les références externes. */
+static void gtk_security_tweak_panel_dispose_secret_storage(GtkSecurityTweakPanel *);
+
+/* Note le changement de verrouillage du stockage sécurisé. */
+static void gtk_security_tweak_panel_on_secret_storage_lock_update(GSecretStorage *, GtkSecurityTweakPanel *);
+
+/* Réagit à un changement dans l'édition d'un mot de passe. */
+static void gtk_security_tweak_panel_on_new_passwords_changed(GtkEditable *, GtkSecurityTweakPanel *);
+
+/* Réagit à une demande de création de mot de passe. */
+static void gtk_security_tweak_panel_on_set_password_clicked(GtkButton *, GtkSecurityTweakPanel *);
+
+/* Réagit à une demande de changement de mot de passe. */
+static void gtk_security_tweak_panel_on_change_password_clicked(GtkButton *, GtkSecurityTweakPanel *);
+
+/* Réagit à une demande de suppression de mot de passe. */
+static void gtk_security_tweak_panel_on_remove_password_clicked(GtkButton *, GtkSecurityTweakPanel *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GESTION DE L'ENSEMBLE DES PARAMETRES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type du composant d'édition des paramètres de sécurité. */
+G_DEFINE_TYPE(GtkSecurityTweakPanel, gtk_security_tweak_panel, GTK_TYPE_BOX);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation de classe des configurations. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_class_init(GtkSecurityTweakPanelClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_security_tweak_panel_dispose;
+ object->finalize = gtk_security_tweak_panel_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/dialogs/prefs/security.ui");
+
+ /* Stockage sécurisé */
+
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_new_passwords_changed));
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_set_password_clicked));
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_change_password_clicked));
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_security_tweak_panel_on_remove_password_clicked));
+
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, current_primary_passwd);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_lbl);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_2);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, new_primary_passwd_2_lbl);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, set_password);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, change_password);
+ gtk_widget_class_bind_template_child(widget, GtkSecurityTweakPanel, remove_password);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = composant GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation des configurations de sécurité. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_init(GtkSecurityTweakPanel *panel)
+{
+ gtk_widget_init_template(GTK_WIDGET(panel));
+
+ gtk_security_tweak_panel_init_secret_storage(panel);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_dispose(GObject *object)
+{
+ GtkSecurityTweakPanel *panel; /* Version spécialisée */
+
+ panel = GTK_SECURITY_TWEAK_PANEL(object);
+
+ gtk_security_tweak_panel_dispose_secret_storage(panel);
+
+ gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_SECURITY_TWEAK_PANEL);
+
+ G_OBJECT_CLASS(gtk_security_tweak_panel_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_security_tweak_panel_parent_class)->finalize(object);
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CONSERVATION SECURISEE DE PARAMETRES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = composant GTK à initialiser. *
+* *
+* Description : Initalise la partie relative au stockage sécurisé. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_init_secret_storage(GtkSecurityTweakPanel *panel)
+{
+ panel->storage = get_secret_storage();
+
+ g_signal_connect(panel->storage, "lock-update",
+ G_CALLBACK(gtk_security_tweak_panel_on_secret_storage_lock_update), panel);
+
+ gtk_security_tweak_panel_on_secret_storage_lock_update(panel->storage, panel);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_dispose_secret_storage(GtkSecurityTweakPanel *panel)
+{
+ if (panel->storage != NULL)
+ g_signal_handlers_disconnect_by_func(panel->storage,
+ gtk_security_tweak_panel_on_secret_storage_lock_update, panel);
+
+ g_clear_object(&panel->storage);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : storage = gardien des secrets impliqué. *
+* panel = support de la partie relative au stockage sécurisé.*
+* *
+* Description : Note le changement de verrouillage du stockage sécurisé. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_on_secret_storage_lock_update(GSecretStorage *storage, GtkSecurityTweakPanel *panel)
+{
+ bool has_key; /* Existence d'une clef ? */
+
+ has_key = g_secret_storage_has_key(panel->storage);
+
+ gtk_widget_set_visible(panel->new_primary_passwd, has_key);
+ gtk_widget_set_visible(panel->new_primary_passwd_lbl, has_key);
+
+ gtk_widget_set_visible(panel->new_primary_passwd_2, has_key);
+ gtk_widget_set_visible(panel->new_primary_passwd_2_lbl, has_key);
+
+ gtk_widget_set_visible(panel->set_password, !has_key);
+ gtk_widget_set_visible(panel->change_password, has_key);
+ gtk_widget_set_visible(panel->remove_password, has_key);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : editable = zone de saisie bouton GTK concerné par l'appel. *
+* panel = support de la partie liée au stockage sécurisé. *
+* *
+* Description : Réagit à un changement dans l'édition d'un mot de passe. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_on_new_passwords_changed(GtkEditable *editable, GtkSecurityTweakPanel *panel)
+{
+ const char *new_passwd; /* Nouveau mot de passe #1 */
+ const char *new_passwd_2; /* Nouveau mot de passe #2 */
+ bool status; /* Bilan de l'opération */
+
+ new_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd));
+
+ new_passwd_2 = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd_2));
+
+ status = (strcmp(new_passwd, new_passwd_2) == 0);
+
+ gtk_widget_set_sensitive(panel->change_password, status);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : button = bouton GTK concerné par l'appel. *
+* panel = support de la partie relative au stockage sécurisé. *
+* *
+* Description : Réagit à une demande de création de mot de passe. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_on_set_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel)
+{
+ const char *passwd; /* Mot de passe à considérer */
+ bool status; /* Bilan de l'opération */
+
+ passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd));
+
+ status = g_secret_storage_set_password(panel->storage, passwd);
+
+ log_variadic_message(LMT_INFO, _("Setting password for secret storage: %s"),
+ status ? _("success") : _("failed"));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : button = bouton GTK concerné par l'appel. *
+* panel = support de la partie relative au stockage sécurisé. *
+* *
+* Description : Réagit à une demande de changement de mot de passe. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_on_change_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel)
+{
+ const char *old_passwd; /* Ancien mot de passe */
+ const char *new_passwd; /* Nouveau mot de passe */
+ bool status; /* Bilan de l'opération */
+
+ old_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd));
+
+ new_passwd = gtk_editable_get_text(GTK_EDITABLE(panel->new_primary_passwd));
+
+ status = g_secret_storage_change_password(panel->storage, old_passwd, new_passwd);
+
+ log_variadic_message(LMT_INFO, _("Changed password for secret storage: %s"),
+ status ? _("success") : _("failed"));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : button = bouton GTK concerné par l'appel. *
+* panel = support de la partie relative au stockage sécurisé. *
+* *
+* Description : Réagit à une demande de suppression de mot de passe. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_security_tweak_panel_on_remove_password_clicked(GtkButton *button, GtkSecurityTweakPanel *panel)
+{
+ const char *passwd; /* Mot de passe à considérer */
+ bool status; /* Bilan de l'opération */
+
+ passwd = gtk_editable_get_text(GTK_EDITABLE(panel->current_primary_passwd));
+
+ status = g_secret_storage_remove_password(panel->storage, passwd);
+
+ log_variadic_message(LMT_INFO, _("Removed password for secret storage: %s"),
+ status ? _("success") : _("failed"));
+
+}
diff --git a/src/gui/dialogs/prefs/security.h b/src/gui/dialogs/prefs/security.h
new file mode 100644
index 0000000..206a123
--- /dev/null
+++ b/src/gui/dialogs/prefs/security.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * security.h - prototypes pour la configuration des paramètres liés à la sécurité
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GUI_DIALOGS_PREFS_SECURITY_H
+#define _GUI_DIALOGS_PREFS_SECURITY_H
+
+
+#include <gtk/gtk.h>
+
+
+#include "../../../glibext/helpers.h"
+
+
+
+#define GTK_TYPE_SECURITY_TWEAK_PANEL (gtk_security_tweak_panel_get_type())
+
+DECLARE_GTYPE(GtkSecurityTweakPanel, gtk_security_tweak_panel, GTK, SECURITY_TWEAK_PANEL);
+
+
+
+#endif /* _GUI_DIALOGS_PREFS_SECURITY_H */
diff --git a/src/gui/dialogs/prefs/security.ui b/src/gui/dialogs/prefs/security.ui
new file mode 100644
index 0000000..ccf2d39
--- /dev/null
+++ b/src/gui/dialogs/prefs/security.ui
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+
+ <template class="GtkSecurityTweakPanel" parent="GtkBox">
+
+ <property name="orientation">vertical</property>
+
+ <!-- Conservation de paramètres sécurisée -->
+ <child>
+ <object class="GtkGrid">
+ <property name="margin-start">20</property>
+ <property name="margin-end">20</property>
+ <property name="margin-top">20</property>
+ <property name="margin-bottom">20</property>
+ <property name="row-spacing">10</property>
+ <property name="column-spacing">10</property>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Secret storage</property>
+ <property name="use-markup">true</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Configuration may handle sensitive data (such as passwords or API keys) which can be saved in plain text or stored encrypted using a Primary Password.
+
+If encryption is activated, entering the Primary Password will be asked on need once per session.</property>
+ <property name="wrap">true</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+
+ <!-- Mot de passe courant -->
+
+ <child>
+ <object class="GtkLabel">
+ <property name="label">Enter current password:</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkPasswordEntry" id="current_primary_passwd">
+ <property name="hexpand">true</property>
+ <property name="show-peek-icon">true</property>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+
+ <!-- Nouveau mot de passe -->
+
+ <child>
+ <object class="GtkLabel" id="new_primary_passwd_lbl">
+ <property name="label">Enter new password:</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkPasswordEntry" id="new_primary_passwd">
+ <property name="hexpand">true</property>
+ <property name="show-peek-icon">true</property>
+ <signal name="changed" handler="gtk_security_tweak_panel_on_new_passwords_changed"/>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+
+ <!-- Confirmation de mot de passe -->
+
+ <child>
+ <object class="GtkLabel" id="new_primary_passwd_2_lbl">
+ <property name="label">Re-enter new password:</property>
+ <property name="xalign">0</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">4</property>
+ </layout>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkPasswordEntry" id="new_primary_passwd_2">
+ <property name="hexpand">true</property>
+ <property name="show-peek-icon">true</property>
+ <signal name="changed" handler="gtk_security_tweak_panel_on_new_passwords_changed"/>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">4</property>
+ </layout>
+ </object>
+ </child>
+
+ <!-- Boutons de contrôle -->
+
+ <child>
+ <object class="GtkBox">
+ <property name="halign">center</property>
+ <property name="homogeneous">true</property>
+ <property name="spacing">8</property>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">5</property>
+ <property name="column-span">2</property>
+ </layout>
+
+ <child>
+ <object class="GtkButton" id="set_password">
+ <property name="label">Set</property>
+ <signal name="clicked" handler="gtk_security_tweak_panel_on_set_password_clicked"/>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="change_password">
+ <property name="label">Change</property>
+ <signal name="clicked" handler="gtk_security_tweak_panel_on_change_password_clicked"/>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkButton" id="remove_password">
+ <property name="label">Remove</property>
+ <signal name="clicked" handler="gtk_security_tweak_panel_on_remove_password_clicked"/>
+ <style>
+ <class name="destructive-action"/>
+ </style>
+ </object>
+ </child>
+
+ </object>
+ </child>
+
+ </object>
+ </child>
+
+ </template>
+</interface>
diff --git a/src/gui/panel-int.h b/src/gui/panel-int.h
deleted file mode 100644
index 7e6f7e3..0000000
--- a/src/gui/panel-int.h
+++ /dev/null
@@ -1,71 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * panel-int.h - prototypes pour les définitions internes liées aux panneaux d'affichage
- *
- * Copyright (C) 2019-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 _GUI_PANEL_INT_H
-#define _GUI_PANEL_INT_H
-
-
-#include "panel.h"
-
-
-
-/* Fournit une indication sur la personnalité du panneau. */
-typedef PanelItemPersonality (* get_panel_item_personality_cb) (const GPanelItem *);
-
-/* Fournit un composant pour lancer l'activité d'un panneau. */
-typedef GtkWidget * (* get_panel_item_widget_cb) (GPanelItem *);
-
-/* Fournit un composant représentant un panneau graphique. */
-typedef GtkTiledPanel * (* get_panel_item_panel_cb) (GPanelItem *, GtkWidget *);
-
-
-/* Elément réactif pour panneaux de l'éditeur (instance) */
-struct _GPanelItem
-{
- GObject parent; /* A laisser en premier */
-
- GtkWidget *launcher; /* Eventuel lanceur associé */
- GtkWidget *properties; /* Propriétés de lancement */
-
- GtkTiledPanel **panels; /* Instances complètes ouvertes*/
- size_t pcount; /* Quantité de ces instances */
-
-};
-
-/* Elément réactif pour panneaux de l'éditeur (classe) */
-struct _GPanelItemClass
-{
- GObjectClass parent; /* A laisser en premier */
-
- get_panel_item_personality_cb get_personality; /* Fourniture de nature */
-
- get_panel_item_widget_cb get_launcher; /* Lancement d'une activité */
- get_panel_item_widget_cb get_properties;/* Préparation au lancement */
- get_panel_item_panel_cb get_panel; /* Panneau principal */
-
-};
-
-
-
-#endif /* _GUI_PANEL_INT_H */
diff --git a/src/gui/panel.c b/src/gui/panel.c
deleted file mode 100644
index b361153..0000000
--- a/src/gui/panel.c
+++ /dev/null
@@ -1,1207 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * panel.c - gestion des éléments réactifs spécifiques aux panneaux
- *
- * Copyright (C) 2019-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 "panel.h"
-
-
-#include "panel-int.h"
-
-
-
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-
-
-
-/* Initialise la classe des panneaux graphiques de l'éditeur. */
-static void g_panel_item_class_init(GPanelItemClass *);
-
-/* Initialise une instance de panneau graphique pour l'éditeur. */
-static void g_panel_item_init(GPanelItem *);
-
-/* Supprime toutes les références externes. */
-static void g_panel_item_dispose(GPanelItem *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_panel_item_finalize(GPanelItem *);
-
-
-
-
-
-/* Indique le type défini pour un élément destiné à un panneau. */
-G_DEFINE_TYPE(GPanelItem, g_panel_item, G_TYPE_OBJECT);
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à initialiser. *
-* *
-* Description : Initialise la classe des panneaux graphiques de l'éditeur. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_panel_item_class_init(GPanelItemClass *class)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(class);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_panel_item_dispose;
- object->finalize = (GObjectFinalizeFunc)g_panel_item_finalize;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance à initialiser. *
-* *
-* Description : Initialise une instance de panneau graphique pour l'éditeur. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_panel_item_init(GPanelItem *item)
-{
- //item->docked = false;
-
- //item->widget = NULL;
- //item->cached_widget = NULL;
-
- //item->filter = NULL;
-
- //g_atomic_int_set(&item->switched, 0);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_panel_item_dispose(GPanelItem *item)
-{
- //g_clear_object(&item->widget);
- //g_clear_object(&item->cached_widget);
-
- G_OBJECT_CLASS(g_panel_item_parent_class)->dispose(G_OBJECT(item));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_panel_item_finalize(GPanelItem *item)
-{
- G_OBJECT_CLASS(g_panel_item_parent_class)->finalize(G_OBJECT(item));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = définition de panneau à consulter. *
-* *
-* Description : Fournit une indication sur la personnalité du panneau. *
-* *
-* Retour : Identifiant lié à la nature du panneau. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PanelItemPersonality g_panel_item_get_personality(const GPanelItem *item)
-{
- PanelItemPersonality result; /* Personnalité à retourner */
- GPanelItemClass *class; /* Classe à actionner */
-
- class = G_PANEL_ITEM_GET_CLASS(item);
-
- result = class->get_personality(item);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = définition de panneau à manipuler. *
-* *
-* Description : Fournit un composant pour lancer l'activité d'un panneau. *
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GtkWidget *g_panel_item_get_launcher(GPanelItem *item)
-{
- GtkWidget *result; /* Composant à retourner */
- GPanelItemClass *class; /* Classe à actionner */
-
- if (item->launcher == NULL)
- {
- class = G_PANEL_ITEM_GET_CLASS(item);
- item->launcher = class->get_launcher(item);
- }
-
- result = item->launcher;
-
- if (result != NULL)
- ref_object(result);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = définition de panneau à manipuler. *
-* *
-* Description : Fournit un composant pour paramétrer l'activité d'un panneau.*
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GtkWidget *g_panel_item_get_properties(GPanelItem *item)
-{
- GtkWidget *result; /* Composant à retourner */
- GPanelItemClass *class; /* Classe à actionner */
-
- if (item->properties == NULL)
- {
- class = G_PANEL_ITEM_GET_CLASS(item);
- item->properties = class->get_properties(item);
- }
-
- result = item->properties;
-
- if (result != NULL)
- ref_object(result);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = définition de panneau à manipuler. *
-* *
-* Description : Fournit un composant représentant un panneau graphique. *
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GtkTiledPanel *g_panel_item_get_panel(GPanelItem *item)
-{
- GtkTiledPanel *result; /* Composant à retourner */
- GPanelItemClass *class; /* Classe à actionner */
-
- if (item->pcount == 0 /* || !singleton */)
- {
- class = G_PANEL_ITEM_GET_CLASS(item);
- result = class->get_panel(item, item->properties);
-
- if (result != NULL)
- {
- item->panels = realloc(item->panels, ++item->pcount * sizeof(GtkTiledPanel *));
- item->panels[item->pcount - 1] = result;
- }
-
- }
-
- else
- result = item->panels[item->pcount - 1];
-
- if (result != NULL)
- ref_object(result);
-
- return result;
-
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-#if 0
-
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-
-
-#include "panel-int.h"
-#include "core/global.h"
-#include "core/items.h"
-#include "../common/extstr.h"
-#include "../core/params.h"
-#include "../gtkext/gtkdockable-int.h"
-#include "../gtkext/named.h"
-#include "../plugins/dt.h"
-#include "../plugins/pglist.h"
-
-
-
-/* ------------------------- COEUR DES PANNEAUX D'AFFICHAGE ------------------------- */
-
-
-
-
-/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
-
-
-/* Construit la chaîne d'accès à un élément de configuration. */
-static char *gtk_panel_item_class_build_configuration_key(const GPanelItemClass *, const char *);
-
-/* Fournit le nom court du composant encapsulable. */
-static char *gtk_panel_item_get_name(const GPanelItem *);
-
-/* Fournit le nom long du composant encapsulable. */
-static char *gtk_panel_item_get_desc(const GPanelItem *);
-
-/* Détermine si un panneau peut être filtré. */
-static bool gtk_panel_item_can_search(const GPanelItem *);
-
-/* Fournit le composant graphique intégrable dans un ensemble. */
-static GtkWidget *gtk_panel_item_get_widget(GPanelItem *);
-
-/* Démarre l'actualisation du filtrage du contenu. */
-static void gtk_panel_item_update_filtered(GPanelItem *, const char *);
-
-
-
-/* ---------------------- MECANISMES DE MISE A JOUR DE PANNEAU ---------------------- */
-
-
-/* Présente une copie de l'affichage du composant rafraîchi. */
-static gboolean g_panel_item_draw_mask(GtkWidget *, cairo_t *, GPanelItem *);
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* COEUR DES PANNEAUX D'AFFICHAGE */
-/* ---------------------------------------------------------------------------------- */
-
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à consulter. *
-* *
-* Description : Fournit une indication sur la personnalité du panneau. *
-* *
-* Retour : Identifiant lié à la nature unique du panneau. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-PanelItemPersonality gtk_panel_item_class_get_personality_singleton(const GPanelItemClass *class)
-{
- PanelItemPersonality result; /* Personnalité à retourner */
-
- result = PIP_SINGLETON;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à consulter. *
-* *
-* Description : Fournit une indication d'accroche du panneau au démarrage. *
-* *
-* Retour : true si le panneau doit être affiché de prime abord. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool gtk_panel_item_class_dock_at_startup(const GPanelItemClass *class)
-{
- bool result; /* Statut à retourner */
- GGenConfig *config; /* Configuration courante */
- char *key; /* Clef d'accès à un paramètre */
-#ifndef NDEBUG
- bool status; /* Bilan de consultation */
-#endif
-
- config = get_main_configuration();
-
- key = gtk_panel_item_class_build_configuration_key(class, "dock_at_startup");
-
-#ifndef NDEBUG
- status = g_generic_config_get_value(config, key, &result);
- assert(status);
-#else
- g_generic_config_get_value(config, key, &result);
-#endif
-
- free(key);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe associée à la consultation. *
-* *
-* Description : Renvoie false lors d'une consultation de la classe. *
-* *
-* Retour : false. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool gtk_panel_item_class_return_false(const GPanelItemClass *class)
-{
- bool result; /* Statut à retourner */
-
- result = false;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe associée à la consultation. *
-* *
-* Description : Renvoie true lors d'une consultation de la classe. *
-* *
-* Retour : true. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool gtk_panel_item_class_return_true(const GPanelItemClass *class)
-{
- bool result; /* Statut à retourner */
-
- result = true;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à consulter. *
-* *
-* Description : Détermine si un panneau peut être filtré. *
-* *
-* Retour : Bilan de la consultation. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool gtk_panel_item_class_can_search(const GPanelItemClass *class)
-{
- bool result; /* Statut à retourner */
-
- result = class->can_search(class);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à consulter. *
-* *
-* Description : Indique le chemin initial de la localisation d'un panneau. *
-* *
-* Retour : Chemin fixé associé à la position initiale. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-char *gtk_panel_item_class_get_path(const GPanelItemClass *class)
-{
- char *result; /* Emplacement à retourner */
- GGenConfig *config; /* Configuration courante */
- char *key; /* Clef d'accès à un paramètre */
- const char *path; /* Nouveau chemin de placement */
-#ifndef NDEBUG
- bool status; /* Statut de l'encapsulation */
-#endif
-
- config = get_main_configuration();
-
- key = gtk_panel_item_class_build_configuration_key(class, "path");
-
-#ifndef NDEBUG
- status = g_generic_config_get_value(config, key, &path);
- assert(status);
-#else
- g_generic_config_get_value(config, key, &path);
-#endif
-
- free(key);
-
- result = strdup(path);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à consulter. *
-* *
-* Description : Indique la définition d'un éventuel raccourci clavier. *
-* *
-* Retour : Description d'un raccourci ou NULL si aucun de défini. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-char *gtk_panel_item_class_get_key_bindings(const GPanelItemClass *class)
-{
- char *result; /* Emplacement à retourner */
-
- if (class->get_bindings != NULL)
- result = class->get_bindings(class);
-
- else
- result = NULL;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe du type de panneau à traiter. *
-* attrib = élément de configuration à inclure dans le résultat.*
-* *
-* Description : Construit la chaîne d'accès à un élément de configuration. *
-* *
-* Retour : Chaîne de caractères à libérer après usage. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static char *gtk_panel_item_class_build_configuration_key(const GPanelItemClass *class, const char *attrib)
-{
- char *result; /* Construction à renvoyer */
- const char *name; /* Nom court du panneau */
-
- name = g_editor_item_class_get_key(G_EDITOR_ITEM_CLASS(class));
-
- asprintf(&result, "gui.panels.%s.%s", attrib, name);
-
- result = strrpl(result, " ", "_");
-
- result = strlower(result);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe de panneau à consulter. *
-* config = configuration à compléter. *
-* *
-* Description : Met en place les bases de la configuration d'un panneau. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool gtk_panel_item_class_setup_configuration(const GPanelItemClass *class, GGenConfig *config)
-{
- bool result; /* Bilan à retourner */
- char *key; /* Clef d'accès à un paramètre */
- bool dock_at_startup; /* Affichage dès le départ ? */
- char *path; /* Localisation du panneau */
-
- key = gtk_panel_item_class_build_configuration_key(class, "dock_at_startup");
-
- dock_at_startup = class->dock_at_startup(class);
-
- result = g_generic_config_create_param_if_not_exist(config, key, CPT_BOOLEAN, dock_at_startup);
-
- free(key);
-
- if (!result)
- goto exit;
-
- key = gtk_panel_item_class_build_configuration_key(class, "path");
-
- path = class->get_path(class);
-
- result = g_generic_config_create_param_if_not_exist(config, key, CPT_STRING, path);
-
- free(path);
-
- free(key);
-
- exit:
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : type = type de panneau à mettre en place. *
-* path = emplacement d'affichage ou NULL. *
-* *
-* Description : Crée un élément de panneau réactif. *
-* *
-* Retour : Adresse de la structure mise en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GPanelItem *g_panel_item_new(GType type, const char *path)
-{
- GPanelItem *result; /* Structure à retourner */
- GPanelItemClass *class; /* Classe associée au type */
- PanelItemPersonality personality; /* Caractéristique de panneau */
- GtkTiledGrid *grid; /* Composant d'affichage */
-
- class = g_type_class_ref(type);
-
- personality = gtk_panel_item_class_get_personality(class);
- assert(path != NULL || personality == PIP_PERSISTENT_SINGLETON);
-
- g_type_class_unref(class);
-
- if (personality == PIP_PERSISTENT_SINGLETON || personality == PIP_SINGLETON)
- {
- result = G_PANEL_ITEM(find_editor_item_by_type(type));
-
- if (result != NULL)
- goto singleton;
-
- }
-
- result = create_object_from_type(type);
-
- grid = get_tiled_grid();
-
- g_signal_connect_swapped(result, "dock-request", G_CALLBACK(gtk_tiled_grid_add), grid);
- g_signal_connect_swapped(result, "undock-request", G_CALLBACK(gtk_tiled_grid_remove), grid);
-
- gtk_dockable_setup_dnd(GTK_DOCKABLE(result));
-
- register_editor_item(G_EDITOR_ITEM(result));
-
- notify_panel_creation(result);
-
- singleton:
-
- if (path != NULL)
- {
- if (path[0] != '\0')
- gtk_panel_item_set_path(result, path);
-
- g_panel_item_dock(result);
-
- }
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance de panneau à consulter. *
-* *
-* Description : Indique le composant graphique principal du panneau. *
-* *
-* Retour : Composant graphique avec nom constituant le panneau. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GNamedWidget *gtk_panel_item_get_named_widget(const GPanelItem *item)
-{
- GNamedWidget *result; /* Composant nommé à retourner */
-
- result = item->widget;
-
- g_object_ref(G_OBJECT(result));
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance GTK dont l'interface est à consulter. *
-* *
-* Description : Fournit le nom court du composant encapsulable. *
-* *
-* Retour : Désignation humaine pour titre d'onglet ou de fenêtre. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static char *gtk_panel_item_get_name(const GPanelItem *item)
-{
- char *result; /* Désignation à retourner */
-
- result = g_named_widget_get_name(G_NAMED_WIDGET(item->widget), false);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance GTK dont l'interface est à consulter. *
-* *
-* Description : Fournit le nom long du composant encapsulable. *
-* *
-* Retour : Désignation humaine pour titre d'onglet ou de fenêtre. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static char *gtk_panel_item_get_desc(const GPanelItem *item)
-{
- char *result; /* Description à retourner */
-
- result = g_named_widget_get_name(G_NAMED_WIDGET(item->widget), true);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance GTK dont l'interface est à consulter. *
-* *
-* Description : Détermine si un panneau peut être filtré. *
-* *
-* Retour : Bilan de la consultation. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static bool gtk_panel_item_can_search(const GPanelItem *item)
-{
- bool result; /* Indication à retourner */
- GPanelItemClass *class; /* Classe de l'élément visé */
-
- class = G_PANEL_ITEM_GET_CLASS(item);
-
- result = gtk_panel_item_class_can_search(class);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance GTK dont l'interface est à consulter. *
-* *
-* Description : Fournit le composant graphique intégrable dans un ensemble. *
-* *
-* Retour : Composant graphique prêt à emploi. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GtkWidget *gtk_panel_item_get_widget(GPanelItem *item)
-{
- GtkWidget *result; /* Composant à retourner */
-
- if (item->cached_widget == NULL)
- item->cached_widget = g_named_widget_get_widget(G_NAMED_WIDGET(item->widget));
-
- result = item->cached_widget;
-
- g_object_ref(G_OBJECT(result));
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance GTK dont l'interface est à sollicitée. *
-* *
-* Description : Démarre l'actualisation du filtrage du contenu. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void gtk_panel_item_update_filtered(GPanelItem *item, const char *filter)
-{
- assert(gtk_panel_item_can_search(item));
-
- if (item->filter != NULL)
- free(item->filter);
-
- item->filter = (filter ? strdup(filter) : NULL);
-
- G_PANEL_ITEM_GET_CLASS(item)->update_filtered(item);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = instance GTK à consulter. *
-* path = nouvelle emplacement d'inclusion. *
-* *
-* Description : Définit le chemin d'accès à utiliser pour les encapsulations.*
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void gtk_panel_item_set_path(GPanelItem *item, const char *path)
-{
- GGenConfig *config; /* Configuration courante */
- char *key; /* Clef d'accès à un paramètre */
-
- config = get_main_configuration();
-
- key = gtk_panel_item_class_build_configuration_key(G_PANEL_ITEM_GET_CLASS(item), "path");
-
- g_generic_config_set_value(config, key, path);
-
- free(key);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = composant à présenter à l'affichage. *
-* *
-* Description : Place un panneau dans l'ensemble affiché. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_panel_item_dock(GPanelItem *item)
-{
- assert(!item->docked);
-
- g_signal_emit_by_name(item, "dock-request");
-
- if (G_PANEL_ITEM_GET_CLASS(item)->ack_dock != NULL)
- G_PANEL_ITEM_GET_CLASS(item)->ack_dock(item);
-
- notify_panel_docking(item, true);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = composant d'affichage à mettre à jour. *
-* status = nouvel état d'encapsulation. *
-* *
-* Description : Définit si le composant repose sur un support de l'éditeur. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_panel_item_set_dock_at_startup(GPanelItem *item, bool status)
-{
- char *key; /* Clef d'accès à un paramètre */
-
- item->docked = status;
-
- key = gtk_panel_item_class_build_configuration_key(G_PANEL_ITEM_GET_CLASS(item), "dock_at_startup");
-
- g_generic_config_set_value(get_main_configuration(), key, status);
-
- free(key);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = composant d'affichage à consulter. *
-* *
-* Description : Indique si le composant repose sur un support de l'éditeur. *
-* *
-* Retour : true si le composant est bien incrusté quelque part. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool g_panel_item_is_docked(const GPanelItem *item)
-{
- bool result; /* Statut à retourner */
-
- result = item->docked;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : item = composant à retirer de l'affichage. *
-* *
-* Description : Supprime un panneau de l'ensemble affiché. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_panel_item_undock(GPanelItem *item)
-{
- PanelItemPersonality personality; /* Caractéristique de panneau */
-
- assert(item->docked);
-
- g_signal_emit_by_name(item, "undock-request");
-
- if (G_PANEL_ITEM_GET_CLASS(item)->ack_undock != NULL)
- G_PANEL_ITEM_GET_CLASS(item)->ack_undock(item);
-
- notify_panel_docking(item, false);
-
- personality = gtk_panel_item_class_get_personality(G_PANEL_ITEM_GET_CLASS(item));
-
- if (personality != PIP_PERSISTENT_SINGLETON)
- unregister_editor_item(G_EDITOR_ITEM(item));
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* MECANISMES DE MISE A JOUR DE PANNEAU */
-/* ---------------------------------------------------------------------------------- */
-
-
-/******************************************************************************
-* *
-* Paramètres : item = panneau ciblé par une mise à jour. *
-* *
-* Description : Obtient le groupe de travail dédié à une mise à jour. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-wgroup_id_t g_panel_item_get_group(const GPanelItem *item)
-{
- wgroup_id_t result; /* Identifiant à retourner */
-
- result = G_PANEL_ITEM_GET_CLASS(item)->gid;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : widget = composant graphique sur lequel dessiner. *
-* cr = contexte graphique pour le dessin. *
-* panel = panneau ciblé par une mise à jour. *
-* *
-* Description : Présente une copie de l'affichage du composant rafraîchi. *
-* *
-* Retour : FALSE afin de poursuivre les traitements. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static gboolean g_panel_item_draw_mask(GtkWidget *widget, cairo_t *cr, GPanelItem *item)
-{
- int width; /* Largeur du composant actuel */
- int height; /* Hauteur du composant actuel */
-
- width = gtk_widget_get_allocated_width(widget);
- height = gtk_widget_get_allocated_height(widget);
-
- cairo_save(cr);
-
- cairo_set_source_surface(cr, item->surface, 0, 0);
- cairo_rectangle(cr, 0, 0, width, height);
-
- cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
- cairo_fill(cr);
-
- cairo_restore(cr);
-
- return FALSE;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = panneau ciblé par une mise à jour. *
-* *
-* Description : Bascule l'affichage d'un panneau avant sa mise à jour. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_panel_item_switch_to_updating_mask(GPanelItem *item)
-{
- GtkBuilder *builder; /* Constructeur sous-jacent */
- GtkWidget *content; /* Composant à faire évoluer */
- GdkWindow *window; /* Fenêtre au contenu à copier */
- int width; /* Largeur du composant actuel */
- int height; /* Hauteur du composant actuel */
- cairo_t *cr; /* Pinceau pour les dessins */
- GtkAdjustment *adj; /* Défilement éventuel */
- GtkStack *stack; /* Pile de composants GTK */
- GtkWidget *mask; /* Masque des travaux */
-
- if (g_atomic_int_add(&item->switched, 1) > 0)
- return;
-
- /* Copie de l'affichage courant */
-
- assert(item->surface == NULL);
-
- builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(item->widget));
-
- content = GTK_WIDGET(gtk_builder_get_object(builder, "content"));
-
- window = gtk_widget_get_window(content);
-
- if (window != NULL)
- {
- width = gtk_widget_get_allocated_width(content);
- height = gtk_widget_get_allocated_height(content);
-
- item->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
-
- cr = cairo_create(item->surface);
-
- gdk_cairo_set_source_window(cr, window, 0, 0);
-
- cairo_paint(cr);
-
- cairo_destroy(cr);
-
- }
-
- /* Sauvegarde de l'éventuelle position */
-
- if (GTK_IS_SCROLLED_WINDOW(content))
- {
- adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(content));
- item->hadj_value = gtk_adjustment_get_value(adj);
-
- adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(content));
- item->vadj_value = gtk_adjustment_get_value(adj);
-
- }
-
- /* Opération de basculement effectif */
-
- stack = GTK_STACK(gtk_builder_get_object(builder, "stack"));
-
- mask = GTK_WIDGET(gtk_builder_get_object(builder, "mask"));
-
- gtk_spinner_start(GTK_SPINNER(mask));
-
- if (item->surface != NULL)
- g_signal_connect(mask, "draw", G_CALLBACK(g_panel_item_draw_mask), item);
-
- gtk_stack_set_visible_child(stack, mask);
-
- g_object_unref(G_OBJECT(builder));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = panneau ciblé par une mise à jour. *
-* *
-* Description : Bascule l'affichage d'un panneau après sa mise à jour. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_panel_item_switch_to_updated_content(GPanelItem *item)
-{
- GtkBuilder *builder; /* Constructeur sous-jacent */
- GtkWidget *content; /* Composant à faire évoluer */
- GtkAdjustment *adj; /* Défilement éventuel */
- GtkStack *stack; /* Pile de composants GTK */
- GtkWidget *mask; /* Masque des travaux */
-
- if (g_atomic_int_get(&item->switched) > 1)
- goto skip;
-
- /* Restauration d'une éventuelle position */
-
- builder = gtk_built_named_widget_get_builder(GTK_BUILT_NAMED_WIDGET(item->widget));
-
- content = GTK_WIDGET(gtk_builder_get_object(builder, "content"));
-
- if (GTK_IS_SCROLLED_WINDOW(content))
- {
- adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(content));
- gtk_adjustment_set_value(adj, item->hadj_value);
-
- adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(content));
- gtk_adjustment_set_value(adj, item->vadj_value);
-
- }
-
- /* Opération de basculement effectif */
-
- stack = GTK_STACK(gtk_builder_get_object(builder, "stack"));
-
- gtk_stack_set_visible_child(stack, content);
-
- mask = GTK_WIDGET(gtk_builder_get_object(builder, "mask"));
-
- g_signal_handlers_disconnect_by_func(mask, G_CALLBACK(g_panel_item_draw_mask), item);
-
- gtk_spinner_stop(GTK_SPINNER(mask));
-
- /* Supression de la copie d'affichage */
-
- if (item->surface != NULL)
- {
- cairo_surface_destroy(item->surface);
- item->surface = NULL;
- }
-
- g_object_unref(G_OBJECT(builder));
-
- skip:
-
- g_atomic_int_dec_and_test(&item->switched);
-
-}
-
-
-#endif
diff --git a/src/gui/panel.h b/src/gui/panel.h
deleted file mode 100644
index 03b67d4..0000000
--- a/src/gui/panel.h
+++ /dev/null
@@ -1,148 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * panel.h - prototypes pour la gestion des éléments réactifs spécifiques aux panneaux
- *
- * Copyright (C) 2019-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 _GUI_PANEL_H
-#define _GUI_PANEL_H
-
-
-#include "../glibext/helpers.h"
-#include "../gtkext/panel.h"
-
-
-#include <gtk/gtk.h>
-
-
-
-#define G_TYPE_PANEL_ITEM (g_panel_item_get_type())
-
-DECLARE_GTYPE(GPanelItem, g_panel_item, G, PANEL_ITEM);
-
-
-/* Types de panneaux pour éditeur */
-typedef enum _PanelItemPersonality
-{
- PIP_NONE, /* Pas de particularité */
-
- PIP_MAIN_PANEL, /* Panneau principal */
- PIP_SINGLETON, /* Instance unique */
-
- PIP_COUNT
-
-} PanelItemPersonality;
-
-
-/* Fournit une indication sur la personnalité du panneau. */
-PanelItemPersonality g_panel_item_get_personality(const GPanelItem *);
-
-/* Fournit un composant pour lancer l'activité d'un panneau. */
-GtkWidget *g_panel_item_get_launcher(GPanelItem *);
-
-/* Fournit un composant pour paramétrer l'activité d'un panneau. */
-GtkWidget *g_panel_item_get_properties(GPanelItem *);
-
-/* Fournit un composant représentant un panneau graphique. */
-GtkTiledPanel *g_panel_item_get_panel(GPanelItem *);
-
-
-
-
-
-
-
-
-
-#if 0
-
-
-#include <stdbool.h>
-#include <gtk/gtk.h>
-
-
-#include "../glibext/configuration.h"
-#include "../glibext/named.h"
-
-
-
-#define G_TYPE_PANEL_ITEM g_panel_item_get_type()
-#define G_PANEL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_PANEL_ITEM, GPanelItem))
-#define G_IS_PANEL_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_PANEL_ITEM))
-#define G_PANEL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_PANEL_ITEM, GPanelItemClass))
-#define G_IS_PANEL_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_PANEL_ITEM))
-#define G_PANEL_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_PANEL_ITEM, GPanelItemClass))
-
-
-/* Elément réactif pour panneaux de l'éditeur (instance) */
-typedef struct _GPanelItem GPanelItem;
-
-/* Elément réactif pour panneaux de l'éditeur (classe) */
-typedef struct _GPanelItemClass GPanelItemClass;
-
-
-
-
-/* Indique le type défini pour un élément destiné à un panneau. */
-GType g_panel_item_get_type(void);
-
-/* Fournit une indication d'accroche du panneau au démarrage. */
-bool gtk_panel_item_class_dock_at_startup(const GPanelItemClass *);
-
-/* Détermine si un panneau peut être filtré. */
-bool gtk_panel_item_class_can_search(const GPanelItemClass *);
-
-/* Indique le chemin initial de la localisation d'un panneau. */
-char *gtk_panel_item_class_get_path(const GPanelItemClass *);
-
-/* Indique la définition d'un éventuel raccourci clavier. */
-char *gtk_panel_item_class_get_key_bindings(const GPanelItemClass *);
-
-/* Met en place les bases de la configuration du panneau. */
-bool gtk_panel_item_class_setup_configuration(const GPanelItemClass *, GGenConfig *);
-
-/* Crée un élément de panneau réactif. */
-GPanelItem *g_panel_item_new(GType, const char *);
-
-/* Indique le composant graphique principal du panneau. */
-GNamedWidget *gtk_panel_item_get_named_widget(const GPanelItem *);
-
-/* Définit le chemin d'accès à utiliser pour les encapsulations. */
-void gtk_panel_item_set_path(GPanelItem *, const char *);
-
-/* Place un panneau dans l'ensemble affiché. */
-void g_panel_item_dock(GPanelItem *);
-
-/* Définit si le composant repose sur un support de l'éditeur. */
-void g_panel_item_set_dock_at_startup(GPanelItem *, bool);
-
-/* Indique si le composant repose sur un support de l'éditeur. */
-bool g_panel_item_is_docked(const GPanelItem *);
-
-/* Supprime un panneau de l'ensemble affiché. */
-void g_panel_item_undock(GPanelItem *);
-
-
-#endif
-
-
-
-#endif /* _GUI_PANEL_H */
diff --git a/src/gui/panels/Makefile.am b/src/gui/panels/Makefile.am
index 476a436..2765405 100644
--- a/src/gui/panels/Makefile.am
+++ b/src/gui/panels/Makefile.am
@@ -38,8 +38,7 @@ IMG_PATH = ../../../data/images
RES_FILES = \
binary.ui \
- binary-launch.ui \
- binary-props.ui \
+ binary-params.ui \
$(IMG_PATH)/binfile-symbolic.svg \
welcome.ui \
welcome-hints.txt \
@@ -48,6 +47,8 @@ RES_FILES = \
libguipanels4_la_SOURCES = \
binary-int.h \
binary.h binary.c \
+ binary-params-int.h \
+ binary-params.h binary-params.c \
resources.h resources.c \
welcome-int.h \
welcome.h welcome.c
diff --git a/src/gui/panels/binary-int.h b/src/gui/panels/binary-int.h
index f68da9b..5116f5c 100644
--- a/src/gui/panels/binary-int.h
+++ b/src/gui/panels/binary-int.h
@@ -27,14 +27,10 @@
#include "binary.h"
-#include "../panel-int.h"
#include "../../gtkext/panel-int.h"
-/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
-
-
/* Panneau d'accueil par défaut (instance) */
struct _GtkBinaryPanel
{
@@ -53,23 +49,4 @@ struct _GtkBinaryPanelClass
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-/* Définition pour gestion par le framework d'un panneau graphique (instance) */
-struct _GBinaryPanel
-{
- GPanelItem parent; /* A laisser en premier */
-
-};
-
-/* Définition pour gestion par le framework d'un panneau graphique (classe) */
-struct _GBinaryPanelClass
-{
- GPanelItemClass parent; /* A laisser en premier */
-
-};
-
-
-
#endif /* _GUI_PANELS_BINARY_INT_H */
diff --git a/src/gui/panels/binary-launch.ui b/src/gui/panels/binary-launch.ui
deleted file mode 100644
index 553f758..0000000
--- a/src/gui/panels/binary-launch.ui
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<interface>
-
- <object class="GtkGrid" id="launcher">
- <property name="margin-bottom">12</property>
- <property name="margin-end">12</property>
- <property name="margin-start">12</property>
- <property name="margin-top">12</property>
- <property name="column-spacing">12</property>
- <child>
- <object class="GtkImage">
- <property name="icon-name">binfile-symbolic</property>
- <property name="pixel-size">48</property>
- <layout>
- <property name="column">0</property>
- <property name="row">0</property>
- <property name="row-span">2</property>
- </layout>
- <style>
- <class name="icon-dropshadow"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label">&lt;b&gt;Binary analysis&lt;/b&gt;</property>
- <property name="use-markup">TRUE</property>
- <property name="xalign">0</property>
- <layout>
- <property name="column">1</property>
- <property name="row">0</property>
- </layout>
- </object>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="label">Load a binary content and parse its format if recognized</property>
- <property name="hexpand">true</property>
- <property name="xalign">0</property>
- <layout>
- <property name="column">1</property>
- <property name="row">1</property>
- </layout>
- <style>
- <class name="dim-label"/>
- </style>
- </object>
- </child>
- <child>
- <object class="GtkImage">
- <property name="icon-name">go-next-symbolic</property>
- <property name="margin-start">12</property>
- <layout>
- <property name="column">2</property>
- <property name="row">0</property>
- <property name="row-span">2</property>
- </layout>
- <style>
- <class name="icon-dropshadow"/>
- </style>
- </object>
- </child>
- </object>
-
-</interface>
diff --git a/src/gui/panels/binary-params-int.h b/src/gui/panels/binary-params-int.h
new file mode 100644
index 0000000..0fbef24
--- /dev/null
+++ b/src/gui/panels/binary-params-int.h
@@ -0,0 +1,50 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * binary-params-int.h - définitions internes pour l'édition des paramètres initiaux d'un chargement de binaire
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GUI_PANELS_BINARY_PARAMS_INT_H
+#define _GUI_PANELS_BINARY_PARAMS_INT_H
+
+
+#include "binary-params.h"
+
+
+
+/* Composant pour les paramètres de chargement d'un binaire (instance) */
+struct _GtkBinaryParameters
+{
+ GtkGrid parent; /* A laisser en premier */
+
+ GtkEntry *filename; /* CHemin d'un binaire */
+
+};
+
+/* Composant pour les paramètres de chargement d'un binaire (classe) */
+struct _GtkBinaryParametersClass
+{
+ GtkGridClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* _GUI_PANELS_BINARY_PARAMS_INT_H */
diff --git a/src/gui/panels/binary-params.c b/src/gui/panels/binary-params.c
new file mode 100644
index 0000000..1059761
--- /dev/null
+++ b/src/gui/panels/binary-params.c
@@ -0,0 +1,178 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * binary-params.c - édition des paramètres initiaux d'un chargement de binaire
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "binary-params.h"
+
+
+#include "binary.h"
+#include "binary-params-int.h"
+#include "../window.h"
+#include "../../analysis/contents/file.h"
+#include "../../gtkext/helpers.h"
+
+
+
+/* Initialise la classe des composants d'édition de paramètres. */
+static void gtk_binary_parameters_class_init(GtkBinaryParametersClass *);
+
+/* Initialise une instance de composant d'édition de paramètres. */
+static void gtk_binary_parameters_init(GtkBinaryParameters *);
+
+/* Supprime toutes les références externes. */
+static void gtk_binary_parameters_dispose(GObject *);
+
+/* Procède à la libération totale de la mémoire. */
+static void gtk_binary_parameters_finalize(GObject *);
+
+/* Ouvre une boîte de dialogue pour récupérer un fichier. */
+static void gtk_binary_parameters_on_new_file_entry_icon_release(GtkEntry *, GtkEntryIconPosition, GtkBinaryParameters *);
+
+
+
+/* Détermine le type du composant d'édition des paramètres de chargement. */
+G_DEFINE_TYPE(GtkBinaryParameters, gtk_binary_parameters, GTK_TYPE_GRID);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe des composants d'édition de paramètres. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_binary_parameters_class_init(GtkBinaryParametersClass *class)
+{
+ GObjectClass *object; /* Plus haut niveau équivalent */
+ GtkWidgetClass *widget; /* Classe de haut niveau */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = gtk_binary_parameters_dispose;
+ object->finalize = gtk_binary_parameters_finalize;
+
+ widget = GTK_WIDGET_CLASS(class);
+
+ gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/panels/binary-params.ui");
+
+ gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_binary_parameters_on_new_file_entry_icon_release));
+
+ gtk_widget_class_bind_template_child(widget, GtkBinaryParameters, filename);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : params = composant GTK à initialiser. *
+* *
+* Description : Initialise une instance de composant d'édition de paramètres.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_binary_parameters_init(GtkBinaryParameters *params)
+{
+ gtk_widget_init_template(GTK_WIDGET(params));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_binary_parameters_dispose(GObject *object)
+{
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_BINARY_PARAMETERS);
+
+ G_OBJECT_CLASS(gtk_binary_parameters_parent_class)->dispose(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : object = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_binary_parameters_finalize(GObject *object)
+{
+ G_OBJECT_CLASS(gtk_binary_parameters_parent_class)->finalize(object);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : entry = zone de saisie concernée par l'appel. *
+* icon_pos = position de l'icone incrustée dans la zone. *
+* params = composant d'édition des paramètres. *
+* *
+* Description : Ouvre une boîte de dialogue pour récupérer un fichier. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_binary_parameters_on_new_file_entry_icon_release(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkBinaryParameters *params)
+{
+ GtkRoot *root; /* Racine du composant */
+ GBinContent *content; /* Contenu binaire à afficher */
+ GtkTiledPanel *tiled; /* Panneau d'affichage complet */
+
+ root = gtk_widget_get_root(GTK_WIDGET(entry));
+
+ content = g_file_content_new("/bin/id");
+
+ tiled = gtk_binary_panel_new_for_content(content);
+
+ unref_object(content);
+
+ gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled);
+
+}
diff --git a/plugins/pe/section.h b/src/gui/panels/binary-params.h
index 2ac21d3..450da25 100644
--- a/plugins/pe/section.h
+++ b/src/gui/panels/binary-params.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * section.h - prototypes pour la gestion des sections d'un PE
+ * binary-params.h - prototypes pour l'édition des paramètres initiaux d'un chargement de binaire
*
- * Copyright (C) 2010-2017 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -21,18 +21,21 @@
*/
-#ifndef _PLUGINS_PE_SECTION_H
-#define _PLUGINS_PE_SECTION_H
+#ifndef _GUI_PANELS_BINARY_PARAMS_H
+#define _GUI_PANELS_BINARY_PARAMS_H
-#include "format.h"
-#include "pe_def.h"
+#include <gtk/gtk.h>
+#include "../../glibext/helpers.h"
-/* Recherche une section donnée au sein de binaire par indice. */
-image_section_header *read_all_pe_sections(const GPeFormat *, vmpa2t *);
+#define GTK_TYPE_BINARY_PARAMETERS (gtk_binary_parameters_get_type())
-#endif /* _PLUGINS_PE_SECTION_H */
+DECLARE_GTYPE(GtkBinaryParameters, gtk_binary_parameters, GTK, BINARY_PARAMETERS);
+
+
+
+#endif /* _GUI_PANELS_BINARY_PARAMS_H */
diff --git a/src/gui/panels/binary-props.ui b/src/gui/panels/binary-params.ui
index bd48ea8..dcbaf7c 100644
--- a/src/gui/panels/binary-props.ui
+++ b/src/gui/panels/binary-params.ui
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <object class="GtkGrid" id="properties">
+ <template class="GtkBinaryParameters" parent="GtkGrid">
<property name="margin-bottom">12</property>
<property name="margin-end">12</property>
<property name="margin-start">12</property>
<property name="margin-top">12</property>
<property name="column-spacing">12</property>
<property name="row-spacing">8</property>
+
<child>
<object class="GtkLabel">
<property name="label">Load and analyze a new file:</property>
@@ -18,8 +19,9 @@
</layout>
</object>
</child>
+
<child>
- <object class="GtkEntry" id="new_file">
+ <object class="GtkEntry" id="filename">
<property name="secondary-icon-name">document-open-symbolic</property>
<property name="placeholder-text">File location</property>
<property name="hexpand">TRUE</property>
@@ -31,9 +33,10 @@
<style>
<class name="background"/>
</style>
- <signal name="icon-release" handler="g_binary_panel_on_new_file_entry_icon_release"/>
+ <signal name="icon-release" handler="gtk_binary_parameters_on_new_file_entry_icon_release"/>
</object>
</child>
- </object>
+
+ </template>
</interface>
diff --git a/src/gui/panels/binary.c b/src/gui/panels/binary.c
index de0a4d0..f58c06b 100644
--- a/src/gui/panels/binary.c
+++ b/src/gui/panels/binary.c
@@ -26,8 +26,6 @@
#include "binary-int.h"
-#include "../window.h"
-#include "../../analysis/contents/file.h"
#include "../../gtkext/helpers.h"
#include "../../gtkext/hexview.h"
@@ -43,10 +41,10 @@ static void gtk_binary_panel_class_init(GtkBinaryPanelClass *);
static void gtk_binary_panel_init(GtkBinaryPanel *);
/* Supprime toutes les références externes. */
-static void gtk_binary_panel_dispose(GtkBinaryPanel *);
+static void gtk_binary_panel_dispose(GObject *);
/* Procède à la libération totale de la mémoire. */
-static void gtk_binary_panel_finalize(GtkBinaryPanel *);
+static void gtk_binary_panel_finalize(GObject *);
@@ -57,49 +55,12 @@ static void gtk_binary_panel_finalize(GtkBinaryPanel *);
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-/* Initialise la classe des panneaux graphiques pour binaires. */
-static void g_binary_panel_class_init(GBinaryPanelClass *);
-
-/* Initialise une instance de panneau graphique pour binaire. */
-static void g_binary_panel_init(GBinaryPanel *);
-
-/* Supprime toutes les références externes. */
-static void g_binary_panel_dispose(GBinaryPanel *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_binary_panel_finalize(GBinaryPanel *);
-
-
-
-/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
-
-
-/* Fournit une indication sur la personnalité du panneau. */
-static PanelItemPersonality g_binary_panel_get_personality(const GBinaryPanel *);
-
-/* Fournit un composant pour lancer l'activité d'un panneau. */
-static GtkWidget *g_binary_panel_get_launcher(GBinaryPanel *);
-
-/* Fournit un composant pour paramétrer l'activité d'un panneau. */
-static GtkWidget *g_binary_panel_get_properties(GBinaryPanel *);
-
-/* Ouvre une boîte de dialogue pour récupérer un fichier. */
-static void g_binary_panel_on_new_file_entry_icon_release(GtkEntry *, GtkEntryIconPosition, GBinaryPanel *);
-
-/* Fournit un composant représentant un panneau graphique. */
-static GtkTiledPanel *g_binary_panel_get_panel(GBinaryPanel *, GtkWidget *);
-
-
-
/* ---------------------------------------------------------------------------------- */
/* COEUR D'UN PANNEAU D'AFFICHAGE */
/* ---------------------------------------------------------------------------------- */
-/* Indique le type défini pour un panneau d'accueil. */
+/* Indique le type défini pour un panneau d'affichage de contenus d'un binaire. */
G_DEFINE_TYPE(GtkBinaryPanel, gtk_binary_panel, GTK_TYPE_TILED_PANEL);
@@ -122,8 +83,8 @@ static void gtk_binary_panel_class_init(GtkBinaryPanelClass *class)
object = G_OBJECT_CLASS(class);
- object->dispose = (GObjectFinalizeFunc/* ! */)gtk_binary_panel_dispose;
- object->finalize = (GObjectFinalizeFunc)gtk_binary_panel_finalize;
+ object->dispose = gtk_binary_panel_dispose;
+ object->finalize = gtk_binary_panel_finalize;
widget = GTK_WIDGET_CLASS(class);
@@ -155,7 +116,7 @@ static void gtk_binary_panel_init(GtkBinaryPanel *panel)
/******************************************************************************
* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
+* Paramètres : object = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
@@ -165,18 +126,18 @@ static void gtk_binary_panel_init(GtkBinaryPanel *panel)
* *
******************************************************************************/
-static void gtk_binary_panel_dispose(GtkBinaryPanel *panel)
+static void gtk_binary_panel_dispose(GObject *object)
{
- gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_BINARY_PANEL);
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_BINARY_PANEL);
- G_OBJECT_CLASS(gtk_binary_panel_parent_class)->dispose(G_OBJECT(panel));
+ G_OBJECT_CLASS(gtk_binary_panel_parent_class)->dispose(object);
}
/******************************************************************************
* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
+* Paramètres : object = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
@@ -186,9 +147,9 @@ static void gtk_binary_panel_dispose(GtkBinaryPanel *panel)
* *
******************************************************************************/
-static void gtk_binary_panel_finalize(GtkBinaryPanel *panel)
+static void gtk_binary_panel_finalize(GObject *object)
{
- G_OBJECT_CLASS(gtk_binary_panel_parent_class)->finalize(G_OBJECT(panel));
+ G_OBJECT_CLASS(gtk_binary_panel_parent_class)->finalize(object);
}
@@ -232,286 +193,3 @@ GtkTiledPanel *gtk_binary_panel_new_for_content(GBinContent *content)
-
-/* ---------------------------------------------------------------------------------- */
-/* MANIPULATIONS D'UN PANNEAU GRAPHIQUE */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour une manipulation de panneau pour binaires. */
-G_DEFINE_TYPE(GBinaryPanel, g_binary_panel, G_TYPE_PANEL_ITEM);
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à initialiser. *
-* *
-* Description : Initialise la classe des panneaux graphiques pour binaires. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_binary_panel_class_init(GBinaryPanelClass *class)
-{
- GObjectClass *object; /* Autre version de la classe */
- GPanelItemClass *panel; /* Encore une autre vision... */
-
- object = G_OBJECT_CLASS(class);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_panel_dispose;
- object->finalize = (GObjectFinalizeFunc)g_binary_panel_finalize;
-
- panel = G_PANEL_ITEM_CLASS(class);
-
- panel->get_personality = (get_panel_item_personality_cb)g_binary_panel_get_personality;
- panel->get_launcher = (get_panel_item_widget_cb)g_binary_panel_get_launcher;
- panel->get_properties = (get_panel_item_widget_cb)g_binary_panel_get_properties;
- panel->get_panel = (get_panel_item_panel_cb)g_binary_panel_get_panel;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance à initialiser. *
-* *
-* Description : Initialise une instance de panneau graphique pour binaire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_binary_panel_init(GBinaryPanel *panel)
-{
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_binary_panel_dispose(GBinaryPanel *panel)
-{
- G_OBJECT_CLASS(g_binary_panel_parent_class)->dispose(G_OBJECT(panel));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_binary_panel_finalize(GBinaryPanel *panel)
-{
- G_OBJECT_CLASS(g_binary_panel_parent_class)->finalize(G_OBJECT(panel));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Constitue une définition de manipulation de panneau. *
-* *
-* Retour : Définition de propriétés mise en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GPanelItem *g_binary_panel_new(void)
-{
- GPanelItem *result; /* Structure à retourner */
-
- result = g_object_new(G_TYPE_BINARY_PANEL, NULL);
-
- return result;
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
-/* ---------------------------------------------------------------------------------- */
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = définition de panneau à consulter. *
-* *
-* Description : Fournit une indication sur la personnalité du panneau. *
-* *
-* Retour : Identifiant lié à la nature du panneau. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PanelItemPersonality g_binary_panel_get_personality(const GBinaryPanel *panel)
-{
- PanelItemPersonality result; /* Personnalité à retourner */
-
- result = PIP_MAIN_PANEL;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = définition de panneau à manipuler. *
-* *
-* Description : Fournit un composant pour lancer l'activité d'un panneau. *
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GtkWidget *g_binary_panel_get_launcher(GBinaryPanel *panel)
-{
- GtkWidget *result; /* Composant à retourner */
- GtkBuilder *builder; /* Constructeur d'UI */
-
- builder = gtk_builder_new_from_resource("/re/chrysalide/framework/gui/panels/binary-launch.ui");
-
- result = GTK_WIDGET(gtk_builder_get_object(builder, "launcher"));
- ref_object(result);
-
- unref_object(builder);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = définition de panneau à manipuler. *
-* *
-* Description : Fournit un composant pour paramétrer l'activité d'un panneau.*
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GtkWidget *g_binary_panel_get_properties(GBinaryPanel *panel)
-{
- GtkWidget *result; /* Composant à retourner */
- GtkBuilderScope *scope; /* Fonctions pour signaux */
- GtkBuilderCScope *cscope; /* Fonctions pour signaux */
- GtkBuilder *builder; /* Constructeur d'UI */
-#ifndef NDEBUG
- gboolean status; /* Bilan d'un chargement */
-#endif
-
- scope = gtk_builder_cscope_new();
- cscope = GTK_BUILDER_CSCOPE(scope);
-
- gtk_builder_cscope_add_callback_symbol(cscope, BUILDER_CB(g_binary_panel_on_new_file_entry_icon_release));
-
- builder = gtk_builder_new();
- gtk_builder_set_scope(builder, scope);
- gtk_builder_set_current_object(builder, G_OBJECT(panel));
-
-#ifndef NDEBUG
- status = gtk_builder_add_from_resource(builder, "/re/chrysalide/framework/gui/panels/binary-props.ui", NULL);
- assert(status);
-#else
- gtk_builder_add_from_resource(builder, "/re/chrysalide/framework/gui/panels/binary-props.ui", NULL);
-#endif
-
- result = GTK_WIDGET(gtk_builder_get_object(builder, "properties"));
- ref_object(result);
-
- unref_object(builder);
- unref_object(scope);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : entry = zone de saisie concernée par l'appel. *
-* icon_pos = position de l'icone incrustée dans la zone. *
-* panel = définition de panneau à manipuler. *
-* *
-* Description : Ouvre une boîte de dialogue pour récupérer un fichier. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_binary_panel_on_new_file_entry_icon_release(GtkEntry *entry, GtkEntryIconPosition icon_pos, GBinaryPanel *panel)
-{
- GtkRoot *root; /* Racine du composant */
- GtkTiledPanel *tiled; /* Panneau d'affichage complet */
-
- root = gtk_widget_get_root(GTK_WIDGET(entry));
-
- tiled = g_binary_panel_get_panel(panel, NULL);
-
- gtk_framework_window_add(GTK_FRAMEWORK_WINDOW(root), tiled);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = définition de panneau à manipuler. *
-* props = éventuels éléments graphiques de paramétrages. *
-* *
-* Description : Fournit un composant représentant un panneau graphique. *
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GtkTiledPanel *g_binary_panel_get_panel(GBinaryPanel *panel, GtkWidget *props)
-{
- GtkTiledPanel *result; /* Composant à retourner */
- GBinContent *content; /* Contenu binaire à afficher */
-
- content = g_file_content_new("/bin/id");
-
- result = gtk_binary_panel_new_for_content(content);
-
- unref_object(content);
-
- return result;
-
-}
diff --git a/src/gui/panels/binary.h b/src/gui/panels/binary.h
index e92895b..26f8a7d 100644
--- a/src/gui/panels/binary.h
+++ b/src/gui/panels/binary.h
@@ -29,16 +29,12 @@
#include <gtk/gtk.h>
-#include "../panel.h"
#include "../../analysis/content.h"
#include "../../glibext/helpers.h"
#include "../../gtkext/panel.h"
-/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
-
-
#define GTK_TYPE_BINARY_PANEL (gtk_binary_panel_get_type())
DECLARE_GTYPE(GtkBinaryPanel, gtk_binary_panel, GTK, BINARY_PANEL);
@@ -49,17 +45,4 @@ GtkTiledPanel *gtk_binary_panel_new_for_content(GBinContent *);
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-#define G_TYPE_BINARY_PANEL (g_binary_panel_get_type())
-
-DECLARE_GTYPE(GBinaryPanel, g_binary_panel, G, BINARY_PANEL);
-
-
-/* Constitue une définition de manipulation de panneau. */
-GPanelItem *g_binary_panel_new(void);
-
-
-
#endif /* _GUI_PANELS_BINARY_H */
diff --git a/src/gui/panels/gresource.xml b/src/gui/panels/gresource.xml
index d00953d..f49a16f 100644
--- a/src/gui/panels/gresource.xml
+++ b/src/gui/panels/gresource.xml
@@ -2,8 +2,7 @@
<gresources>
<gresource prefix="/re/chrysalide/framework/gui/panels">
<file compressed="true">binary.ui</file>
- <file compressed="true">binary-launch.ui</file>
- <file compressed="true">binary-props.ui</file>
+ <file compressed="true">binary-params.ui</file>
<file compressed="true">welcome.ui</file>
<file compressed="true">welcome-hints.txt</file>
</gresource>
diff --git a/src/gui/panels/welcome-int.h b/src/gui/panels/welcome-int.h
index d99d316..206bc2c 100644
--- a/src/gui/panels/welcome-int.h
+++ b/src/gui/panels/welcome-int.h
@@ -27,20 +27,15 @@
#include "welcome.h"
-#include "../panel-int.h"
#include "../../gtkext/panel-int.h"
-/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
-
-
/* Panneau d'accueil par défaut (instance) */
struct _GtkWelcomePanel
{
GtkTiledPanel parent; /* A laisser en premier */
- GListStore *store; /* Données brutes */
GtkListBox *list; /* Liste de lanceurs */
GtkStack *properties; /* Premières propriétés */
@@ -62,28 +57,5 @@ struct _GtkWelcomePanelClass
};
-/* Met en place un nouveau panneau d'accueil. */
-bool gtk_welcome_panel_create(GtkWelcomePanel *);
-
-
-
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-/* Définition pour gestion par le framework d'un panneau graphique (instance) */
-struct _GWelcomePanel
-{
- GPanelItem parent; /* A laisser en premier */
-
-};
-
-/* Définition pour gestion par le framework d'un panneau graphique (classe) */
-struct _GWelcomePanelClass
-{
- GPanelItemClass parent; /* A laisser en premier */
-
-};
-
-
#endif /* _GUI_PANELS_WELCOME_INT_H */
diff --git a/src/gui/panels/welcome.c b/src/gui/panels/welcome.c
index 3cd349f..6e8763b 100644
--- a/src/gui/panels/welcome.c
+++ b/src/gui/panels/welcome.c
@@ -28,9 +28,6 @@
#include <assert.h>
-#include <i18n.h>
-
-
#include "welcome-int.h"
#include "../core/panels.h"
#include "../../common/shuffle.h"
@@ -48,16 +45,10 @@ static void gtk_welcome_panel_class_init(GtkWelcomePanelClass *);
static void gtk_welcome_panel_init(GtkWelcomePanel *);
/* Supprime toutes les références externes. */
-static void gtk_welcome_panel_dispose(GtkWelcomePanel *);
+static void gtk_welcome_panel_dispose(GObject *);
/* Procède à la libération totale de la mémoire. */
-static void gtk_welcome_panel_finalize(GtkWelcomePanel *);
-
-/* Intègre une définition de panneau enregistrée. */
-static bool gtk_welcome_panel_add_launcher(GPanelItem *, GListStore *);
-
-/* Prépare un composant pour représenter une définition. */
-static GtkWidget *gtk_welcome_panel_create_launcher_widget(GPanelItem *, gpointer);
+static void gtk_welcome_panel_finalize(GObject *);
/* Réagit à un changement de sélection de la liste de panneaux. */
static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *, GtkWelcomePanel *);
@@ -78,32 +69,6 @@ static void gtk_welcome_panel_on_next_hint_clicked(GtkButton *, GtkWelcomePanel
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-/* Initialise la classe des panneaux graphiques pour binaires. */
-static void g_welcome_panel_class_init(GWelcomePanelClass *);
-
-/* Initialise une instance de panneau graphique pour binaire. */
-static void g_welcome_panel_init(GWelcomePanel *);
-
-/* Supprime toutes les références externes. */
-static void g_welcome_panel_dispose(GWelcomePanel *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_welcome_panel_finalize(GWelcomePanel *);
-
-
-
-/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
-
-
-/* Fournit une indication sur la personnalité du panneau. */
-static PanelItemPersonality g_welcome_panel_get_personality(const GWelcomePanel *);
-
-/* Fournit un composant représentant un panneau graphique. */
-static GtkTiledPanel *g_welcome_panel_get_panel(GWelcomePanel *, GtkWidget *);
-
/* ---------------------------------------------------------------------------------- */
@@ -134,8 +99,8 @@ static void gtk_welcome_panel_class_init(GtkWelcomePanelClass *class)
object = G_OBJECT_CLASS(class);
- object->dispose = (GObjectFinalizeFunc/* ! */)gtk_welcome_panel_dispose;
- object->finalize = (GObjectFinalizeFunc)gtk_welcome_panel_finalize;
+ object->dispose = gtk_welcome_panel_dispose;
+ object->finalize = gtk_welcome_panel_finalize;
widget = GTK_WIDGET_CLASS(class);
@@ -169,13 +134,16 @@ static void gtk_welcome_panel_init(GtkWelcomePanel *panel)
{
GBytes *bytes; /* Données brutes de ressource */
const gchar *data; /* Données brutes natives */
+ int min; /* Taille à gauche minimale */
+ GtkConstraintLayout *layout; /* Disposition fixant la taille*/
+ GtkConstraint *constraint; /* Contrainte à considérer */
gtk_widget_init_template(GTK_WIDGET(panel));
- panel->store = g_list_store_new(G_TYPE_PANEL_ITEM);
-
panel->other_child = NULL;
+ /* Chargement des astuces */
+
bytes = g_resources_lookup_data("/re/chrysalide/framework/gui/panels/welcome-hints.txt",
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
assert(bytes != NULL);
@@ -191,105 +159,9 @@ static void gtk_welcome_panel_init(GtkWelcomePanel *panel)
panel->cur_hint = 0;
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void gtk_welcome_panel_dispose(GtkWelcomePanel *panel)
-{
- gtk_widget_dispose_template(GTK_WIDGET(panel), GTK_TYPE_WELCOME_PANEL);
-
- g_clear_object(&panel->other_child);
-
- G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->dispose(G_OBJECT(panel));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void gtk_welcome_panel_finalize(GtkWelcomePanel *panel)
-{
- G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->finalize(G_OBJECT(panel));
-
- g_strfreev(panel->raw_hints);
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Crée une nouvelle instance de panneau d'accueil. *
-* *
-* Retour : Composant GTK mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GtkTiledPanel *gtk_welcome_panel_new(void)
-{
- GtkTiledPanel *result; /* Instance à retourner */
-
- result = g_object_new(GTK_TYPE_WELCOME_PANEL, NULL);
-
- if (!gtk_welcome_panel_create(GTK_WELCOME_PANEL(result)))
- g_clear_object(&result);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = panneau d'accueil à initialiser. *
-* *
-* Description : Met en place un nouveau panneau d'accueil. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool gtk_welcome_panel_create(GtkWelcomePanel *panel)
-{
- bool result; /* Bilan à retourner */
- int min; /* Taille à gauche minimale */
- GtkConstraintLayout *layout; /* Disposition fixant la taille*/
- GtkConstraint *constraint; /* Contrainte à considérer */
-
/* Constitution de la liste des démarreurs */
- result = browse_all_item_panels(true, (handle_panel_item_fc)gtk_welcome_panel_add_launcher, panel->store);
-
- gtk_list_box_bind_model(panel->list, G_LIST_MODEL(panel->store),
- (GtkListBoxCreateWidgetFunc)gtk_welcome_panel_create_launcher_widget,
- NULL, NULL);
+ populate_framework_panel_launcher_list(panel->list);
/* Dimensionnement de la zone d'astuces */
@@ -327,57 +199,57 @@ bool gtk_welcome_panel_create(GtkWelcomePanel *panel)
gtk_label_set_markup(panel->hints, panel->raw_hints[panel->cur_hint]);
- return result;
-
}
/******************************************************************************
* *
-* Paramètres : item = définition de panneau à intégrer. *
-* store = liste à compléter. *
+* Paramètres : object = instance d'objet GLib à traiter. *
* *
-* Description : Intègre une définition de panneau enregistrée. *
+* Description : Supprime toutes les références externes. *
* *
-* Retour : true pour un parcours complet de la liste des définitions. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static bool gtk_welcome_panel_add_launcher(GPanelItem *item, GListStore *store)
+static void gtk_welcome_panel_dispose(GObject *object)
{
- bool result; /* Poursuite du parcours */
+ GtkWelcomePanel *panel; /* Version spécialisée */
+
+ gtk_widget_dispose_template(GTK_WIDGET(object), GTK_TYPE_WELCOME_PANEL);
- result = true;
+ panel = GTK_WELCOME_PANEL(object);
- g_list_store_append(store, G_OBJECT(item));
+ g_clear_object(&panel->other_child);
- return result;
+ G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->dispose(object);
}
/******************************************************************************
* *
-* Paramètres : item = définition de panneau à consulter. *
-* unused = adresse non utilisée ici. *
+* Paramètres : object = instance d'objet GLib à traiter. *
* *
-* Description : Prépare un composant pour représenter une définition. *
+* Description : Procède à la libération totale de la mémoire. *
* *
-* Retour : Composant de représentation de définition de panneau. *
+* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
-static GtkWidget *gtk_welcome_panel_create_launcher_widget(GPanelItem *item, gpointer unused)
+static void gtk_welcome_panel_finalize(GObject *object)
{
- GtkWidget *result; /* Composant GTK à retourner */
+ GtkWelcomePanel *panel; /* Version spécialisée */
+
+ panel = GTK_WELCOME_PANEL(object);
- result = g_panel_item_get_launcher(item);
+ g_strfreev(panel->raw_hints);
- return result;
+ G_OBJECT_CLASS(gtk_welcome_panel_parent_class)->finalize(object);
}
@@ -398,8 +270,6 @@ static GtkWidget *gtk_welcome_panel_create_launcher_widget(GPanelItem *item, gpo
static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelcomePanel *panel)
{
GtkListBoxRow *row; /* Ligne sélectionnée */
- int selected; /* Indice de sélection */
- GPanelItem *item; /* Elément correspondant */
GtkWidget *new; /* Nouvelles propriétés */
row = gtk_list_box_get_selected_row(box);
@@ -414,7 +284,7 @@ static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelco
gtk_stack_set_visible_child(panel->properties, panel->def_child);
gtk_stack_remove(panel->properties, panel->other_child);
- g_clear_object(&panel->other_child);
+ panel->other_child = NULL;
}
@@ -423,24 +293,16 @@ static void gtk_welcome_panel_on_selected_rows_changed(GtkListBox *box, GtkWelco
*/
else
{
- selected = gtk_list_box_row_get_index(row);
- item = g_list_model_get_item(G_LIST_MODEL(panel->store), selected);
+ new = get_framework_panel_parameters(row);
+ assert(new != NULL);
- new = g_panel_item_get_properties(item);
-
- if (new == panel->other_child)
- unref_object(new);
-
- else
+ if (new != panel->other_child)
{
gtk_stack_add_child(panel->properties, new);
gtk_stack_set_visible_child(panel->properties, new);
if (panel->other_child != NULL)
- {
gtk_stack_remove(panel->properties, panel->other_child);
- g_clear_object(&panel->other_child);
- }
panel->other_child = new;
@@ -513,178 +375,3 @@ static void gtk_welcome_panel_on_next_hint_clicked(GtkButton *button, GtkWelcome
-
-/* ---------------------------------------------------------------------------------- */
-/* MANIPULATIONS D'UN PANNEAU GRAPHIQUE */
-/* ---------------------------------------------------------------------------------- */
-
-
-/* Indique le type défini pour une manipulation de panneau de bienvenue. */
-G_DEFINE_TYPE(GWelcomePanel, g_welcome_panel, G_TYPE_PANEL_ITEM);
-
-
-/******************************************************************************
-* *
-* Paramètres : class = classe à initialiser. *
-* *
-* Description : Initialise la classe des panneaux graphiques pour binaires. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_welcome_panel_class_init(GWelcomePanelClass *class)
-{
- GObjectClass *object; /* Autre version de la classe */
- GPanelItemClass *panel; /* Encore une autre vision... */
-
- object = G_OBJECT_CLASS(class);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_welcome_panel_dispose;
- object->finalize = (GObjectFinalizeFunc)g_welcome_panel_finalize;
-
- panel = G_PANEL_ITEM_CLASS(class);
-
- panel->get_personality = (get_panel_item_personality_cb)g_welcome_panel_get_personality;
- panel->get_panel = (get_panel_item_panel_cb)g_welcome_panel_get_panel;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance à initialiser. *
-* *
-* Description : Initialise une instance de panneau graphique pour binaire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_welcome_panel_init(GWelcomePanel *panel)
-{
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_welcome_panel_dispose(GWelcomePanel *panel)
-{
- G_OBJECT_CLASS(g_welcome_panel_parent_class)->dispose(G_OBJECT(panel));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_welcome_panel_finalize(GWelcomePanel *panel)
-{
- G_OBJECT_CLASS(g_welcome_panel_parent_class)->finalize(G_OBJECT(panel));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Constitue une définition de manipulation de panneau. *
-* *
-* Retour : Définition de propriétés mise en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GPanelItem *g_welcome_panel_new(void)
-{
- GPanelItem *result; /* Structure à retourner */
-
- result = g_object_new(G_TYPE_WELCOME_PANEL, NULL);
-
- return result;
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
-/* ---------------------------------------------------------------------------------- */
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = définition de panneau à consulter. *
-* *
-* Description : Fournit une indication sur la personnalité du panneau. *
-* *
-* Retour : Identifiant lié à la nature du panneau. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static PanelItemPersonality g_welcome_panel_get_personality(const GWelcomePanel *panel)
-{
- PanelItemPersonality result; /* Personnalité à retourner */
-
- result = PIP_MAIN_PANEL | PIP_SINGLETON;
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : panel = définition de panneau à manipuler. *
-* props = éventuels éléments graphiques de paramétrages. *
-* *
-* Description : Fournit un composant représentant un panneau graphique. *
-* *
-* Retour : Composant GTK (déjà ?) mis en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GtkTiledPanel *g_welcome_panel_get_panel(GWelcomePanel *panel, GtkWidget *props)
-{
- GtkTiledPanel *result; /* Composant à retourner */
-
- /**
- * Il n'existe pas de composants de paramètrage pour ce panneau.
- */
- assert(props == NULL);
-
- result = gtk_welcome_panel_new();
-
- return result;
-
-}
diff --git a/src/gui/panels/welcome.h b/src/gui/panels/welcome.h
index d267f80..d9ea18d 100644
--- a/src/gui/panels/welcome.h
+++ b/src/gui/panels/welcome.h
@@ -29,36 +29,15 @@
#include <gtk/gtk.h>
-#include "../panel.h"
#include "../../glibext/helpers.h"
#include "../../gtkext/panel.h"
-/* ------------------------- COEUR D'UN PANNEAU D'AFFICHAGE ------------------------- */
-
-
#define GTK_TYPE_WELCOME_PANEL (gtk_welcome_panel_get_type())
DECLARE_GTYPE(GtkWelcomePanel, gtk_welcome_panel, GTK, WELCOME_PANEL);
-/* Crée une nouvelle instance de panneau d'accueil. */
-GtkTiledPanel *gtk_welcome_panel_new(void);
-
-
-
-/* ---------------------- MANIPULATIONS D'UN PANNEAU GRAPHIQUE ---------------------- */
-
-
-#define G_TYPE_WELCOME_PANEL (g_welcome_panel_get_type())
-
-DECLARE_GTYPE(GWelcomePanel, g_welcome_panel, G, WELCOME_PANEL);
-
-
-/* Constitue une définition de manipulation de panneau. */
-GPanelItem *g_welcome_panel_new(void);
-
-
#endif /* _GUI_PANELS_WELCOME_H */
diff --git a/src/gui/style.css b/src/gui/style.css
index 44161f7..9078310 100644
--- a/src/gui/style.css
+++ b/src/gui/style.css
@@ -1,4 +1,13 @@
+/* Extension de style */
+
+.dim-label {
+
+ margin-bottom: 4px;
+
+}
+
+
.boxed-widget {
border-top-left-radius: 12px;
diff --git a/src/gui/window.c b/src/gui/window.c
index 2680c89..f45cec9 100644
--- a/src/gui/window.c
+++ b/src/gui/window.c
@@ -28,6 +28,7 @@
#include "window-int.h"
#include "core/panels.h"
#include "dialogs/about.h"
+#include "dialogs/preferences.h"
#include "panels/welcome.h"
#include "../gtkext/helpers.h"
#include "../gtkext/statusstack.h"
@@ -46,6 +47,9 @@ static void gtk_framework_window_dispose(GtkFrameworkWindow *);
/* Procède à la libération totale de la mémoire. */
static void gtk_framework_window_finalize(GtkFrameworkWindow *);
+/* Réagit à une activation du menu "Préférences" de la fenetre. */
+static void gtk_framework_window_activate_preferences(GSimpleAction *, GVariant *, gpointer);
+
/* Réagit à une activation du menu "A propos de" de la fenetre. */
static void gtk_framework_window_activate_about(GSimpleAction *, GVariant *, gpointer);
@@ -88,6 +92,8 @@ static void gtk_framework_window_class_init(GtkFrameworkWindowClass *class)
/* Active une action native (cf. https://docs.gtk.org/gtk4/class.Window.html#actions) */
gtk_widget_class_add_binding_action(widget, GDK_KEY_Q, GDK_CONTROL_MASK, "window.close", NULL);
+ gtk_widget_class_add_binding_action(widget, GDK_KEY_comma, GDK_CONTROL_MASK, "win.preferences", NULL);
+
}
@@ -106,6 +112,7 @@ static void gtk_framework_window_class_init(GtkFrameworkWindowClass *class)
static void gtk_framework_window_init(GtkFrameworkWindow *window)
{
static GActionEntry app_entries[] = {
+ { "preferences", gtk_framework_window_activate_preferences, NULL, NULL, NULL },
{ "about", gtk_framework_window_activate_about, NULL, NULL, NULL },
};
@@ -208,7 +215,6 @@ GtkApplicationWindow *gtk_framework_window_new(GtkApplication *app)
bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app)
{
bool result; /* Bilan à retourner */
- GPanelItem *item; /* Définition de panneau */
GtkTiledPanel *panel; /* Panneau d'affichage */
GtkCssProvider *css; /* Feuille de style maison */
@@ -218,13 +224,10 @@ bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app
/* Inclusion d'un écran d'accueil */
- item = find_item_panel_by_type(G_TYPE_WELCOME_PANEL);
+ panel = get_framework_panel_singleton(GTK_TYPE_WELCOME_PANEL);
- panel = g_panel_item_get_panel(item);
gtk_framework_window_add(window, panel);
- unref_object(item);
-
/* Chargement des extensions de thème */
css = gtk_css_provider_new();
@@ -258,6 +261,34 @@ bool gtk_framework_window_create(GtkFrameworkWindow *window, GtkApplication *app
* unused = adresse non utilisée ici. *
* _window = instance de fenêtre principale à manipuler. *
* *
+* Description : Réagit à une activation du menu "Préférences" de la fenetre. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_framework_window_activate_preferences(GSimpleAction *action, GVariant *unused, gpointer _window)
+{
+ GtkFrameworkWindow *window; /* Fenêtre principale associée */
+ GtkWindow *dialog; /* Boîte de dialogue à afficher*/
+
+ window = _window;
+
+ dialog = gtk_preferences_dialog_new(GTK_WINDOW(window));
+
+ gtk_window_present(dialog);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : action = désignation de l'action concernée par l'appel. *
+* unused = adresse non utilisée ici. *
+* _window = instance de fenêtre principale à manipuler. *
+* *
* Description : Réagit à une activation du menu "A propos de" de la fenetre. *
* *
* Retour : - *
diff --git a/src/gui/window.ui b/src/gui/window.ui
index 59b8b2c..363ea54 100644
--- a/src/gui/window.ui
+++ b/src/gui/window.ui
@@ -5,6 +5,7 @@
<section>
<item>
<attribute name="label" translatable="yes">Preferences</attribute>
+ <attribute name="action">win.preferences</attribute>
</item>
<item>
<attribute name="label" translatable="yes">About</attribute>
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index dd191fa..fa65484 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -1,13 +1,18 @@
noinst_LTLIBRARIES = libplugins.la
-libplugins_la_SOURCES = \
- dt.h dt.c \
- pglist.h pglist.c \
- plugin-def.h \
- plugin-int.h \
- plugin.h plugin.c \
- self.h
+libplugins_la_SOURCES = \
+ manager-int.h \
+ manager.h manager.c \
+ native-int.h \
+ native.h native.c \
+ pglist.h pglist.c \
+ plugin-def.h \
+ plugin-int.h \
+ plugin.h plugin.c \
+ self.h \
+ tweakable-int.h \
+ tweakable.h tweakable.c
libplugins_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS)
diff --git a/src/plugins/dt.c b/src/plugins/dt.c
deleted file mode 100644
index 2899845..0000000
--- a/src/plugins/dt.c
+++ /dev/null
@@ -1,562 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * dt.c - possibilité de créer de nouveaux types de façon dynamique
- *
- * Copyright (C) 2019 Cyrille Bagard
- *
- * This file is part of Chrysalide.
- *
- * Chrysalide is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chrysalide is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#include "dt.h"
-
-
-#include <assert.h>
-#include <malloc.h>
-#include <string.h>
-
-
-#include "pglist.h"
-
-
-
-/* ------------------------- MODULE DE GESTION DES NOUVEAUX ------------------------- */
-
-
-#define G_TYPE_DYNAMIC_TYPES g_dynamic_types_get_type()
-#define G_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypes))
-#define G_IS_DYNAMIC_TYPES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DYNAMIC_TYPES))
-#define G_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass))
-#define G_IS_DYNAMIC_TYPES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DYNAMIC_TYPES))
-#define G_DYNAMIC_TYPES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DYNAMIC_TYPES, GDynamicTypesClass))
-
-
-/* Mémorisation des caractéristiques de type */
-typedef struct _type_dyn_info_t
-{
- GType type; /* Identifiant unique obtenu */
-
- GClassInitFunc cinit; /* Phase d'initialisation #1 */
- gconstpointer data; /* Eventuelles données utiles */
-
- GInstanceInitFunc init; /* Phase d'initialisation #2 */
-
-} type_dyn_info_t;
-
-/* Description de fichier binaire (instance) */
-typedef struct _GDynamicTypes
-{
- GObject parent; /* A laisser en premier */
-
- type_dyn_info_t **info; /* Liste d'informations utiles */
- size_t count; /* Taille de cette liste */
-
-} GDynamicTypes;
-
-/* Description de fichier binaire (classe) */
-typedef struct _GDynamicTypesClass
-{
- GObjectClass parent; /* A laisser en premier */
-
-} GDynamicTypesClass;
-
-
-/* Indique le type défini pour une gestion de types dynamique. */
-static GType g_dynamic_types_get_type(void);
-
-/* Initialise la classe de gestion de types dynamique. */
-static void g_dynamic_types_class_init(GDynamicTypesClass *);
-
-/* Initialise une gestion de types dynamique. */
-static void g_dynamic_types_init(GDynamicTypes *);
-
-/* Procède à l'initialisation de l'interface de typage nouveau. */
-static void g_dynamic_types_interface_init(GTypePluginClass *);
-
-/* Supprime toutes les références externes. */
-static void g_dynamic_types_dispose(GDynamicTypes *);
-
-/* Procède à la libération totale de la mémoire. */
-static void g_dynamic_types_finalize(GDynamicTypes *);
-
-/* Crée un nouveau gestionnaire de nouveaux types. */
-static GDynamicTypes *g_dynamic_types_new(void);
-
-/* Marque une augmentation des utilisations. */
-static void g_dynamic_types_use(GDynamicTypes *);
-
-/* Marque une diminution des utilisations. */
-static void g_dynamic_types_unuse(GDynamicTypes *);
-
-/* Complète la définition d'un type dynamiquement. */
-static void g_dynamic_types_complete_type(GDynamicTypes *, GType, GTypeInfo *, GTypeValueTable *);
-
-/* Retrouve les informations concernant un type dynamique. */
-static type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *, GType);
-
-/* Fournit un identifiant GLib pour un nouveau type. */
-static GType g_dynamic_types_register_type(GDynamicTypes *, GType, const char *, GClassInitFunc, gconstpointer, GInstanceInitFunc);
-
-
-
-/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */
-
-
-/* Encadrement des nouveaux types dérivés */
-static GDynamicTypes *_chrysalide_dtypes = NULL;
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* MODULE DE GESTION DES NOUVEAUX */
-/* ---------------------------------------------------------------------------------- */
-
-/* Indique le type défini pour une gestion de types dynamique. */
-G_DEFINE_TYPE_WITH_CODE(GDynamicTypes, g_dynamic_types, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE(G_TYPE_TYPE_PLUGIN, g_dynamic_types_interface_init));
-
-
-/******************************************************************************
-* *
-* Paramètres : klass = classe à initialiser. *
-* *
-* Description : Initialise la classe de gestion de types dynamique. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_class_init(GDynamicTypesClass *klass)
-{
- GObjectClass *object; /* Autre version de la classe */
-
- object = G_OBJECT_CLASS(klass);
-
- object->dispose = (GObjectFinalizeFunc/* ! */)g_dynamic_types_dispose;
- object->finalize = (GObjectFinalizeFunc)g_dynamic_types_finalize;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : types = instance à initialiser. *
-* *
-* Description : Initialise une gestion de types dynamique. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_init(GDynamicTypes *types)
-{
-
-}
-
-/******************************************************************************
-* *
-* Paramètres : iface = interface GLib à initialiser. *
-* *
-* Description : Procède à l'initialisation de l'interface de typage nouveau. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_interface_init(GTypePluginClass *iface)
-{
- iface->use_plugin = (GTypePluginUse)g_dynamic_types_use;
- iface->unuse_plugin = (GTypePluginUnuse)g_dynamic_types_unuse;
- iface->complete_type_info = (GTypePluginCompleteTypeInfo)g_dynamic_types_complete_type;
-
-}
-
-/******************************************************************************
-* *
-* Paramètres : types = instance d'objet GLib à traiter. *
-* *
-* Description : Supprime toutes les références externes. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_dispose(GDynamicTypes *types)
-{
- size_t i; /* Boucle de parcours */
- type_dyn_info_t *info; /* Information à exploiter */
- gpointer g_class; /* Classe à oublier */
-
- for (i = 0; i < types->count; i++)
- {
- info = types->info[i];
-
- if (info->type != G_TYPE_INVALID)
- {
- g_class = g_type_class_peek(info->type);
-
- /**
- * Constat est fait qu'un type implémenté en Python mais dont les
- * instanciations conduisent toutes à des exceptions n'est pas
- * associé à une classe à ce moment d'exécution.
- *
- * Cf. fonction testKnownFormatConstructor() de la suite de tests
- *
- * Cf. documentation de g_type_class_unref() également :
- *
- * """
- * Once the last reference count of a class has been released,
- * classes may be finalized by the type system
- * """
- */
-
- if (g_class != NULL)
- g_type_class_unref(g_class);
-
- info->type = G_TYPE_INVALID;
-
- }
-
- }
-
- G_OBJECT_CLASS(g_dynamic_types_parent_class)->dispose(G_OBJECT(types));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : types = instance d'objet GLib à traiter. *
-* *
-* Description : Procède à la libération totale de la mémoire. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_finalize(GDynamicTypes *types)
-{
- size_t i; /* Boucle de parcours */
-
- for (i = 0; i < types->count; i++)
- free(types->info[i]);
-
- if (types->info != NULL)
- free(types->info);
-
- G_OBJECT_CLASS(g_dynamic_types_parent_class)->finalize(G_OBJECT(types));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Crée un nouveau gestionnaire de nouveaux types. *
-* *
-* Retour : Instance mise en place. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GDynamicTypes *g_dynamic_types_new(void)
-{
- GDynamicTypes *result; /* Adresse à retourner */
-
- result = g_object_new(G_TYPE_DYNAMIC_TYPES, NULL);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : types = gestionnaire de types courant. *
-* *
-* Description : Marque une augmentation des utilisations. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_use(GDynamicTypes *types)
-{
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : types = gestionnaire de types courant. *
-* *
-* Description : Marque une diminution des utilisations. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_unuse(GDynamicTypes *types)
-{
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : types = gestionnaire de types courant. *
-* type = nouveau type GLib à traiter. *
-* info = information concernant ce type à constituer. [OUT] *
-* table = table de valeur à éventuellement initialiser. [OUT] *
-* *
-* Description : Complète la définition d'un type dynamiquement. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static void g_dynamic_types_complete_type(GDynamicTypes *types, GType type, GTypeInfo *info, GTypeValueTable *table)
-{
- type_dyn_info_t *nfo; /* Source d'inspiration */
- GType parent; /* Type parent du type */
- GTypeQuery query; /* Informations complémentaires*/
-
- /* Consultation */
-
- nfo = g_dynamic_types_find(types, type);
- assert(nfo != NULL);
-
- parent = g_type_parent(type);
- g_type_query(parent, &query);
-
- /* Définition */
-
- info->class_size = query.class_size;
- info->class_init = nfo->cinit;
- info->class_data = nfo->data;
-
- info->instance_size = query.instance_size;
- info->instance_init = nfo->init;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : parent = type GLib parent. *
-* type = identifiant du type GLib à considérer. *
-* *
-* Description : Retrouve les informations concernant un type dynamique. *
-* *
-* Retour : Structure contenant les informations associées au type. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static type_dyn_info_t *g_dynamic_types_find(const GDynamicTypes *types, GType target)
-{
- type_dyn_info_t *result; /* Informations à retourner */
- size_t i; /* Boucle de parcours */
-
- result = NULL;
-
- for (i = 0; i < types->count && result == NULL; i++)
- if (types->info[i]->type == target)
- result = types->info[i];
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : parent = type GLib parent. *
-* name = désignation du nouveau type. *
-* cinit = procédure d'initialisation de la classe associée. *
-* data = éventuelles données à associer à la future classe. *
-* init = procédure d'initialisation pour chaque instance. *
-* *
-* Description : Fournit un identifiant GLib pour un nouveau type. *
-* *
-* Retour : identifiant d'un nouveau type valide, ou G_TYPE_INVALID. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-static GType g_dynamic_types_register_type(GDynamicTypes *types, GType parent, const char *name, GClassInitFunc cinit, gconstpointer data, GInstanceInitFunc init)
-{
- GType result; /* Identifiant à retourner */
- type_dyn_info_t *new; /* Mémorisation de paramètres */
-
- /* Création d'un nouveau type adapté */
-
- result = g_type_register_dynamic(parent, name, G_TYPE_PLUGIN(types), 0);
-
- if (result == G_TYPE_INVALID)
- goto exit;
-
- new = malloc(sizeof(type_dyn_info_t));
-
- new->type = result;
-
- new->cinit = cinit;
- new->data = data;
-
- new->init = init;
-
- /* Inscription définitive */
-
- types->info = realloc(types->info, ++types->count * sizeof(type_dyn_info_t *));
-
- types->info[types->count - 1] = new;
-
- exit:
-
- return result;
-
-}
-
-
-
-/* ---------------------------------------------------------------------------------- */
-/* ACCOMPAGNEMENTS DES NOUVEAUX TYPES */
-/* ---------------------------------------------------------------------------------- */
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Lance le support de dérivations de types dans Chrysalide. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool init_chrysalide_dynamic_types(void)
-{
- bool result; /* Bilan à retourner */
-
- _chrysalide_dtypes = g_dynamic_types_new();
-
- result = (_chrysalide_dtypes != NULL);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : - *
-* *
-* Description : Arrête le support de dérivations de types dans Chrysalide. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void exit_chrysalide_dynamic_types(void)
-{
- g_object_unref(G_OBJECT(_chrysalide_dtypes));
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : parent = type GLib parent. *
-* name = désignation du nouveau type. *
-* cinit = procédure d'initialisation de la classe associée. *
-* data = éventuelles données à associer à la future classe. *
-* init = procédure d'initialisation pour chaque instance. *
-* *
-* Description : Fournit un identifiant GLib pour un nouveau type. *
-* *
-* Retour : Identifiant d'un nouveau type valide, ou G_TYPE_INVALID. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-GType build_dynamic_type(GType parent, const char *name, GClassInitFunc cinit, gconstpointer data, GInstanceInitFunc init)
-{
- GType result; /* Identifiant à retourner */
-
- result = g_type_from_name(name);
-
- if (result == 0)
- result = g_dynamic_types_register_type(_chrysalide_dtypes, parent, name, cinit, data, init);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
-* Paramètres : type = type d'instance à créer. *
-* *
-* Description : Crée un objet à partir d'un type, dynamique ou classique. *
-* *
-* Retour : Instance d'objet mise en place ou NULL en cas d'erreur. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-gpointer create_object_from_type(GType type)
-{
- GObject *result; /* Instance à retourner */
-
- result = NULL;
-
- if (g_dynamic_types_find(_chrysalide_dtypes, type) != NULL)
- result = build_type_instance(type);
-
- else
- result = g_object_new(type, NULL);
-
- assert(result != NULL);
-
- return result;
-
-}
diff --git a/src/plugins/dt.h b/src/plugins/dt.h
deleted file mode 100644
index 5c92593..0000000
--- a/src/plugins/dt.h
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * dt.h - prototypes pour la possibilité de créer de nouveaux types de façon dynamique
- *
- * Copyright (C) 2019 Cyrille Bagard
- *
- * This file is part of Chrysalide.
- *
- * Chrysalide is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * Chrysalide is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef _PLUGINS_DT_H
-#define _PLUGINS_DT_H
-
-
-#include <glib-object.h>
-#include <stdbool.h>
-
-
-
-/* ----------------------- ACCOMPAGNEMENTS DES NOUVEAUX TYPES ----------------------- */
-
-
-/* Lance le support de dérivations de types dans Chrysalide. */
-bool init_chrysalide_dynamic_types(void);
-
-/* Arrête le support de dérivations de types dans Chrysalide. */
-void exit_chrysalide_dynamic_types(void);
-
-/* Fournit un identifiant GLib pour un nouveau type. */
-GType build_dynamic_type(GType, const char *, GClassInitFunc, gconstpointer, GInstanceInitFunc);
-
-/* Crée un objet à partir d'un type, dynamique ou classique. */
-gpointer create_object_from_type(GType);
-
-
-
-#endif /* _PLUGINS_PYCHRYSALIDE_DT_H */
diff --git a/src/plugins/manager-int.h b/src/plugins/manager-int.h
new file mode 100644
index 0000000..dbd1d69
--- /dev/null
+++ b/src/plugins/manager-int.h
@@ -0,0 +1,54 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * manager-int.h - définitions internes propres aux interventions dans la gestion des extensions
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_MANAGER_INT_H
+#define _PLUGINS_MANAGER_INT_H
+
+
+#include "manager.h"
+
+
+
+/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */
+
+
+/* Accompagne la fin du chargement des modules natifs. */
+typedef void (* handle_native_plugins_cb) (GPluginManager *);
+
+/* Prend acte du chargement de l'ensemble des greffons. */
+typedef void (* handle_all_plugins_cb) (GPluginManager *);
+
+
+/* Accompagnant dans la gestion des extensions (interface) */
+struct _GPluginManagerInterface
+{
+ GTypeInterface base_iface; /* A laisser en premier */
+
+ handle_native_plugins_cb handle_native; /* Greffons natifs chargés */
+ handle_all_plugins_cb handle_all; /* Ensemble des greffons chargé*/
+
+};
+
+
+
+#endif /* _PLUGINS_MANAGER_INT_H */
diff --git a/src/plugins/manager.c b/src/plugins/manager.c
new file mode 100644
index 0000000..381fbc1
--- /dev/null
+++ b/src/plugins/manager.c
@@ -0,0 +1,113 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * manager.c - intervention dans la gestion des extensions
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "manager.h"
+
+
+#include "manager-int.h"
+
+
+
+/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */
+
+
+/* Procède à l'initialisation de l'interface de gestion. */
+static void g_plugin_manager_default_init(GPluginManagerInterface *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* INTERVENTION DANS LA GESTION DE GREFFONS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Détermine le type d'une interface pour l'intervention dans la gestion des greffons. */
+G_DEFINE_INTERFACE(GPluginManager, g_plugin_manager, G_TYPE_OBJECT)
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface de gestion. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_plugin_manager_default_init(GPluginManagerInterface *iface)
+{
+ iface->handle_native = NULL;
+ iface->handle_all = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : manager = interface à manipuler. *
+* *
+* Description : Accompagne la fin du chargement des modules natifs. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_plugin_manager_handle_native_plugins_loaded_event(GPluginManager *manager)
+{
+ GPluginManagerInterface *iface; /* Interface utilisée */
+
+ iface = G_PLUGIN_MANAGER_GET_IFACE(manager);
+
+ if (iface->handle_native != NULL)
+ iface->handle_native(manager);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : manager = interface à manipuler. *
+* *
+* Description : Prend acte du chargement de l'ensemble des greffons. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_plugin_manager_handle_all_plugins_loaded_event(GPluginManager *manager)
+{
+ GPluginManagerInterface *iface; /* Interface utilisée */
+
+ iface = G_PLUGIN_MANAGER_GET_IFACE(manager);
+
+ if (iface->handle_all != NULL)
+ iface->handle_all(manager);
+
+}
diff --git a/src/plugins/manager.h b/src/plugins/manager.h
new file mode 100644
index 0000000..2eb90a8
--- /dev/null
+++ b/src/plugins/manager.h
@@ -0,0 +1,61 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * manager.h - prototypes pour l'intervention dans la gestion des extensions
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_MANAGER_H
+#define _PLUGINS_MANAGER_H
+
+
+#include "../glibext/helpers.h"
+
+
+
+/* -------------------- INTERVENTION DANS LA GESTION DE GREFFONS -------------------- */
+
+
+#define G_TYPE_PLUGIN_MANAGER (g_plugin_manager_get_type())
+
+DECLARE_INTERFACE(GPluginManager, g_plugin_manager, G, PLUGIN_MANAGER);
+
+
+/* Accompagne la fin du chargement des modules natifs. */
+void g_plugin_manager_handle_native_plugins_loaded_event(GPluginManager *);
+
+/* Prend acte du chargement de l'ensemble des greffons. */
+void g_plugin_manager_handle_all_plugins_loaded_event(GPluginManager *);
+
+
+
+/* -------------------- SOLLICITATION DES FONCTIONNALITES CREEES -------------------- */
+
+
+#define notify_native_plugins_loaded() \
+ process_all_plugins_for(G_TYPE_PLUGIN_MANAGER, G_PLUGIN_MANAGER, \
+ g_plugin_manager_handle_native_plugins_loaded_event)
+
+#define notify_all_plugins_loaded() \
+ process_all_plugins_for(G_TYPE_PLUGIN_MANAGER, G_PLUGIN_MANAGER, \
+ g_plugin_manager_handle_all_plugins_loaded_event)
+
+
+
+#endif /* _PLUGINS_MANAGER_H */
diff --git a/src/plugins/native-int.h b/src/plugins/native-int.h
new file mode 100644
index 0000000..575994f
--- /dev/null
+++ b/src/plugins/native-int.h
@@ -0,0 +1,73 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * native-int.h - prototypes pour les structures internes des greffons natifs
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_NATIVE_INT_H
+#define _PLUGINS_NATIVE_INT_H
+
+
+#include "native.h"
+
+
+#include "plugin-int.h"
+
+
+
+/* Marqueur identifiable */
+#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de
+
+
+/* Greffon natif pour Chrysalide (instance) */
+struct _GNativePlugin
+{
+ GPluginModule parent; /* A laisser en premier */
+
+ /**
+ * Le module porte le code et les données en mémoire.
+ *
+ * Les fonctions *_dispose() et *_finalize() accompagnant la libération des
+ * greffons de la mémoire ne peuvent donc pas libérer ce module car elles
+ * scieraient la branche sur laquelle elles se trouvent.
+ *
+ * Par ailleurs, même s'ils sont conservés dans chaque greffon, les modules
+ * sont mis en place dans le code principal. C'est donc ce dernier qui les
+ * libère, dans la fonction on_plugin_ref_toggle().
+ */
+ GModule *module; /* Structure de chargement GLib*/
+
+};
+
+
+/* Greffon natif pour Chrysalide (classe) */
+struct _GNativePluginClass
+{
+ GPluginModuleClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un greffon natif. */
+bool g_native_plugin_create(GNativePlugin *, const char *, const char *, const char *, const char *, const char * const *, size_t, GModule *);
+
+
+
+#endif /* _PLUGINS_NATIVE_INT_H */
diff --git a/src/plugins/native.c b/src/plugins/native.c
new file mode 100644
index 0000000..de20abe
--- /dev/null
+++ b/src/plugins/native.c
@@ -0,0 +1,295 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * native.c - interactions avec un greffon natif donné
+ *
+ * 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 "native.h"
+
+
+#include "native-int.h"
+#include "../common/cpp.h"
+
+
+
+/* ------------------------- COMPOSITION DE NOUVEAU GREFFON ------------------------- */
+
+
+/* Initialise la classe des greffons natifs. */
+static void g_native_plugin_class_init(GNativePluginClass *);
+
+/* Initialise une instance de greffon natif. */
+static void g_native_plugin_init(GNativePlugin *);
+
+/* Supprime toutes les références externes. */
+static void g_native_plugin_dispose(GNativePlugin *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_native_plugin_finalize(GNativePlugin *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Pointe le fichier contenant le greffon manipulé. */
+static char *g_native_plugin_get_filename(const GNativePlugin *);
+
+/* Fournit le nom brut associé au greffon. */
+static char *g_native_plugin_get_modname(const GNativePlugin *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* COMPOSITION DE NOUVEAU GREFFON */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un greffon Python. */
+G_DEFINE_TYPE(GNativePlugin, g_native_plugin, G_TYPE_PLUGIN_MODULE);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe à initialiser. *
+* *
+* Description : Initialise la classe des greffons natifs. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_native_plugin_class_init(GNativePluginClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GPluginModuleClass *plugin; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_native_plugin_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_native_plugin_finalize;
+
+ plugin = G_PLUGIN_MODULE_CLASS(class);
+
+ plugin->get_filename = (get_plugin_filename_fc)g_native_plugin_get_filename;
+ plugin->get_modname = (get_plugin_modname_fc)g_native_plugin_get_modname;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser. *
+* *
+* Description : Initialise une instance de greffon natif. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_native_plugin_init(GNativePlugin *plugin)
+{
+ plugin->module = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_native_plugin_dispose(GNativePlugin *plugin)
+{
+ G_OBJECT_CLASS(g_native_plugin_parent_class)->dispose(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_native_plugin_finalize(GNativePlugin *plugin)
+{
+ G_OBJECT_CLASS(g_native_plugin_parent_class)->finalize(G_OBJECT(plugin));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* name = nom du greffon pour référence, principalement. *
+* desc = présentation éventuelle à destination humaine. *
+* version = indication de version éventuelle. *
+* url = référence vers une ressource en ligne. *
+* required = liste de dépendances éventuelles ou NULL. *
+* count = taille de cette liste. *
+* module = extension vue du système. *
+* *
+* Description : Met en place un greffon natif. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : Le transfert de propriétée du module est total. *
+* *
+******************************************************************************/
+
+bool g_native_plugin_create(GNativePlugin *plugin, const char *name, const char *desc, const char *version, const char *url, const char * const *required, size_t count, GModule *module)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_plugin_module_create(G_PLUGIN_MODULE(plugin), name, desc, version, url, required, count);
+
+ if (result)
+ plugin->module = module;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à consulter. *
+* *
+* Description : Renvoie la structure opaque associée au module en mémoire. *
+* *
+* Retour : Structure de chargement côté GLib. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GModule *g_native_plugin_get_module(const GNativePlugin *plugin)
+{
+ GModule *result; /* Accès au module à renvoyer */
+
+ result = plugin->module;
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à consulter. *
+* *
+* Description : Pointe le fichier contenant le greffon manipulé. *
+* *
+* Retour : Chemin d'accès au greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_native_plugin_get_filename(const GNativePlugin *plugin)
+{
+ char *result; /* Chemin d'accès à renvoyer */
+
+ result = strdup(g_module_name(plugin->module));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à valider. *
+* *
+* Description : Fournit le nom brut associé au greffon. *
+* *
+* Retour : Désignation brute du greffon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_native_plugin_get_modname(const GNativePlugin *plugin)
+{
+ char *result; /* Désignation brute à renvoyer*/
+ char *path; /* Chemin à traiter */
+ char *filename; /* Nom de bibliothèque partagée*/
+ size_t length; /* Taille du nom */
+ int ret; /* Bilan d'une comparaison */
+
+ path = g_native_plugin_get_filename(plugin);
+
+ filename = basename(path);
+
+ if (strncmp(filename, "lib", 3) == 0)
+ filename += 3;
+
+ length = strlen(filename);
+
+#ifdef _WIN32
+# define SHARED_SUFFIX ".dll"
+#else
+# define SHARED_SUFFIX ".so"
+#endif
+
+ if (length >= STATIC_STR_SIZE(SHARED_SUFFIX))
+ {
+ ret = strncmp(&filename[length - STATIC_STR_SIZE(SHARED_SUFFIX)],
+ SHARED_SUFFIX,
+ STATIC_STR_SIZE(SHARED_SUFFIX));
+
+ if (ret == 0)
+ filename[length - STATIC_STR_SIZE(SHARED_SUFFIX)] = '\0';
+
+ }
+
+ result = strdup(filename);
+
+ free(path);
+
+ return result;
+
+}
diff --git a/plugins/pychrysalide/plugins/constants.h b/src/plugins/native.h
index 56612c9..18039c8 100644
--- a/plugins/pychrysalide/plugins/constants.h
+++ b/src/plugins/native.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * constants.h - prototypes pour l'ajout des constantes principales
+ * native.h - prototypes pour les interactions avec un greffon natif donné
*
- * Copyright (C) 2020 Cyrille Bagard
+ * Copyright (C) 2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -22,20 +22,25 @@
*/
-#ifndef _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H
-#define _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H
+#ifndef _PLUGINS_NATIVE_H
+#define _PLUGINS_NATIVE_H
-#include <Python.h>
-#include <stdbool.h>
+#include <gmodule.h>
-/* Définit les constantes relatives aux greffons Python. */
-bool define_plugin_module_constants(PyTypeObject *);
+#include "../glibext/helpers.h"
-/* Tente de convertir en constante PluginAction. */
-int convert_to_plugin_action(PyObject *, void *);
+#define G_TYPE_NATIVE_PLUGIN (g_native_plugin_get_type())
-#endif /* _PLUGINS_PYCHRYSALIDE_PLUGINS_CONSTANTS_H */
+DECLARE_GTYPE(GNativePlugin, g_native_plugin, G, NATIVE_PLUGIN);
+
+
+/* Renvoie la structure opaque associée au module en mémoire. */
+GModule *g_native_plugin_get_module(const GNativePlugin *);
+
+
+
+#endif /* _PLUGINS_NATIVE_H */
diff --git a/src/plugins/pglist.c b/src/plugins/pglist.c
index e4cb825..3e107b8 100644
--- a/src/plugins/pglist.c
+++ b/src/plugins/pglist.c
@@ -30,13 +30,16 @@
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include <i18n.h>
-#include "dt.h"
+#include "manager.h"
+#include "native.h"
#include "plugin-int.h"
+#include "../common/cpp.h"
#include "../common/extstr.h"
#include "../core/logs.h"
#include "../core/nox.h"
@@ -44,6 +47,12 @@
+/**
+ * Prototype de la fonction de création, à garder synchronisé avec
+ * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h).
+ */
+typedef GPluginModule * (* get_plugin_instance_cb) (GModule *);
+
/* Liste de l'ensemble des greffons */
static GPluginModule **_pg_list = NULL;
static size_t _pg_count = 0;
@@ -61,6 +70,9 @@ static void browse_directory_for_plugins(const char *);
/* Suit les variations du compteur de références d'un greffon. */
static void on_plugin_ref_toggle(gpointer, GPluginModule *, gboolean);
+/* Fournit le greffon répondant à un nom donné. */
+static GPluginModule *_find_plugin_by_name(const char *, size_t *);
+
/******************************************************************************
@@ -83,8 +95,7 @@ bool init_all_plugins(bool load)
char *saveptr; /* Sauvegarde pour parcours */
char *udir; /* Répertoire supplémentaire ? */
- result = init_chrysalide_dynamic_types();
- if (!result) goto exit;
+ result = true;
g_rw_lock_init(&_pg_lock);
@@ -108,8 +119,6 @@ bool init_all_plugins(bool load)
if (load)
load_remaning_plugins();
- exit:
-
return result;
}
@@ -130,64 +139,22 @@ bool init_all_plugins(bool load)
void exit_all_plugins(void)
{
size_t i; /* Boucle de parcours */
- const plugin_interface *pg_iface; /* Définition du greffon */
lock_plugin_list_for_reading();
- if (_pg_list != NULL)
+ for (i = 0; i < _pg_count; i++)
{
- for (i = 0; i < _pg_count; i++)
- {
- assert(_pg_list[i] != NULL);
-
- /**
- * Si le greffon a conduit à la mise en place d'autres greffons, le
- * système de dépendances ne suffit pas pour le décompte des références :
- * le greffon voit à un instant T son compteur décroître ici ; à un
- * instant T+1, un greffon fils décrémente à son tour le compteur vers
- * le greffon principal.
- *
- * Le compteur du conteneur tombe alors à 0, et le code correspondant
- * est retiré. Lorsque que le flot d'exécution revient à la procédure
- * de sortie du second greffon, son code n'est plus en mémoire.
- *
- * On s'assure donc que les greffons qui génèrent d'autres greffons
- * sont bien traités en dernier.
- */
-
- pg_iface = g_plugin_module_get_interface(_pg_list[i]);
-
- if (pg_iface != NULL && pg_iface->container)
- g_object_ref(_pg_list[i]);
-
- g_object_unref(_pg_list[i]);
-
- }
-
- for (i = 0; i < _pg_count; i++)
- {
- if (_pg_list[i] == NULL)
- continue;
-
- pg_iface = g_plugin_module_get_interface(_pg_list[i]);
-
- if (pg_iface == NULL || !pg_iface->container)
- continue;
-
- g_object_unref(_pg_list[i]);
-
- }
+ assert(_pg_list[i] != NULL);
+ unref_object(_pg_list[i]);
+ }
+ if (_pg_list != NULL)
free(_pg_list);
- }
-
unlock_plugin_list_for_reading();
g_rw_lock_clear(&_pg_lock);
- exit_chrysalide_dynamic_types();
-
}
@@ -244,83 +211,95 @@ static int filter_dirs_or_mods(const struct dirent *entry)
* *
* Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). *
* *
-* Description : Part à la recherche de greffons sous forme de modules. *
+* Description : Indique la version (NOX/UI) associée à un nom de fichier. *
* *
-* Retour : - *
+* Retour : true si la version complémentaire existe ou false. *
* *
* Remarques : - *
* *
******************************************************************************/
-static void browse_directory_for_plugins(const char *dir)
+static bool check_for_plugin_versions(const char *dir, const char *filename, bool *is_nox, bool *is_ui)
{
- struct dirent **namelist; /* Eléments trouvés */
- int ret; /* Bilan du parcours */
- bool nox; /* Absence de support graphique*/
- char *filename; /* Elément à ausculter */
- GPluginModule *plugin; /* Greffon à intégrer ou pas */
-
- ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort);
- if (ret < 0)
- {
- LOG_ERROR_N("scandir");
- return;
- }
-
- nox = run_in_nox_mode();
-
- while (ret--)
- {
+ bool result; /* Bilan à renvoyer */
+ size_t length; /* Taille du nom de fichier */
+ char *alt_path; /* Autre chemin complet testé */
+ int ret; /* Bilan d'une impression */
- if (nox)
- {
#ifdef _WIN32
-# define UI_SHARED_SUFFIX "-ui.dll"
+# define SHARED_SUFFIX ".dll"
#else
-# define UI_SHARED_SUFFIX "-ui.so"
+# define SHARED_SUFFIX ".so"
#endif
+#define UI_SHARED_SUFFIX "ui" SHARED_SUFFIX
- if (strstr(namelist[ret]->d_name, UI_SHARED_SUFFIX) != NULL)
- {
- log_variadic_message(LMT_ERROR, _("Skipping unsuitable file: %s"), namelist[ret]->d_name);
- continue;
- }
+ result = false;
- }
+ /* Propriétés du fichier courant */
- filename = (char *)calloc(strlen(dir) + 1 + strlen(namelist[ret]->d_name) + 1, sizeof(char));
+ length = strlen(filename);
- strcpy(filename, dir);
- strcat(filename, G_DIR_SEPARATOR_S);
- strcat(filename, namelist[ret]->d_name);
+ if (length < STATIC_STR_SIZE(UI_SHARED_SUFFIX))
+ *is_ui = false;
- if (namelist[ret]->d_type == DT_DIR)
- browse_directory_for_plugins(filename);
+ else
+ *is_ui = (strcmp(filename + length - STATIC_STR_SIZE(UI_SHARED_SUFFIX), UI_SHARED_SUFFIX) == 0);
+
+ if (*is_ui)
+ *is_nox = false;
+
+ else
+ {
+ if (length < STATIC_STR_SIZE(SHARED_SUFFIX))
+ *is_nox = false;
else
- {
- plugin = g_plugin_module_new(filename);
+ *is_nox = (strcmp(filename + length - STATIC_STR_SIZE(SHARED_SUFFIX), SHARED_SUFFIX) == 0);
+
+ }
+
+ /* Recherche d'une version alternative */
- if (plugin != NULL)
- register_plugin(plugin);
+ if (*is_nox || *is_ui)
+ {
+
+ if (*is_nox)
+ ret = asprintf(&alt_path, "%s%s%.*s%s",
+ dir, G_DIR_SEPARATOR_S,
+ (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename,
+ UI_SHARED_SUFFIX);
+ else
+ ret = asprintf(&alt_path, "%s%s%.*s%s",
+ dir, G_DIR_SEPARATOR_S,
+ (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename,
+ SHARED_SUFFIX);
+ if (ret <= 0)
+ {
+ LOG_ERROR_N("asprintf");
+ goto exit;
}
- free(filename);
- free(namelist[ret]);
+ ret = access(alt_path, R_OK | X_OK);
+
+ result = (ret == 0);
+
+ free(alt_path);
}
- free(namelist);
+ exit:
+
+ return result;
}
/******************************************************************************
* *
-* Paramètres : plugin = greffon à ajouter aux autres disponibles. *
+* Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). *
* *
-* Description : Ajoute un greffon à la liste principale de greffons. *
+* Description : Part à la recherche de greffons sous forme de modules. *
* *
* Retour : - *
* *
@@ -328,52 +307,91 @@ static void browse_directory_for_plugins(const char *dir)
* *
******************************************************************************/
-void _register_plugin(GPluginModule *plugin)
+static void browse_directory_for_plugins(const char *dir)
{
- size_t i; /* Boucle de parcours */
- const plugin_interface *pg_iface; /* Informations à consulter */
- const char *name; /* Désignation du greffon */
-
- /**
- * L'appel sans verrou n'est fourni que pour les greffons
- * mettant en place des greffons en interne !
- */
-
- /* Recherche d'un éventuel doublon */
+ struct dirent **namelist; /* Eléments trouvés */
+ int ret; /* Bilan d'un appel */
+ int k; /* Boucle de parcours */
+ bool nox_mode; /* Absence de support graphique*/
+ char *filename; /* Elément à ausculter */
+ bool is_nox; /* Chemin de version basique ? */
+ bool is_ui; /* Chemin de version graphique */
+ bool has_alt; /* Existence d'une alternative */
+ GModule *module; /* Abstration de manipulation */
+ get_plugin_instance_cb get_instance; /* Point d'entrée exporté */
+ GPluginModule *plugin; /* Greffon à intégrer ou pas */
- pg_iface = g_plugin_module_get_interface(plugin);
+ ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort);
+ if (ret < 0)
+ {
+ LOG_ERROR_N("scandir");
+ return;
+ }
- name = pg_iface->name;
+ nox_mode = run_in_nox_mode();
- for (i = 0; i < _pg_count; i++)
+ for (k = ret; k--; )
{
- pg_iface = g_plugin_module_get_interface(_pg_list[i]);
-
- if (strcmp(name, pg_iface->name) == 0)
+ ret = asprintf(&filename, "%s%s%s", dir, G_DIR_SEPARATOR_S, namelist[k]->d_name);
+ if (ret <= 0)
{
- log_variadic_message(LMT_ERROR,
- _("Plugin '%s' already registered!"), name);
+ LOG_ERROR_N("asprintf");
+ continue;
+ }
- break;
+ if (namelist[k]->d_type == DT_DIR)
+ browse_directory_for_plugins(filename);
- }
+ else
+ {
+ has_alt = check_for_plugin_versions(dir, namelist[k]->d_name, &is_nox, &is_ui);
- }
+ if ((nox_mode && is_nox) || (!nox_mode && ((is_nox && !has_alt) || is_ui)))
+ {
+ module = g_module_open(filename, G_MODULE_BIND_LAZY);
+ if (module == NULL)
+ {
+ log_variadic_message(LMT_ERROR,
+ _("Error while loading the plugin candidate '%s' : %s"),
+ filename, g_module_error());
+ goto next_file;
+ }
+
+ get_instance = NULL;
+
+ if (!g_module_symbol(module, "get_chrysalide_plugin_instance", (gpointer *)&get_instance))
+ log_variadic_message(LMT_ERROR,
+ _("No '%s' entry in plugin candidate '%s'"),
+ "<sym>", filename);
+
+ if (get_instance == NULL)
+ plugin = NULL;
+ else
+ plugin = get_instance(module);
+
+ if (plugin != NULL)
+ {
+ register_plugin(plugin);
+ unref_object(plugin);
+ }
+
+ else
+ g_module_close(module);
- /* Ajout du greffon à la liste */
+ }
+ else
+ log_variadic_message(LMT_INFO, _("Skipping unsuitable file for plugin: %s"), filename);
- if (i == _pg_count)
- {
- _pg_list = (GPluginModule **)realloc(_pg_list, ++_pg_count * sizeof(GPluginModule));
+ }
- _pg_list[_pg_count - 1] = plugin;
+ next_file:
- g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL);
+ free(filename);
+ free(namelist[k]);
}
- else
- /* FIXME : leak(plugin); */;
+ free(namelist);
}
@@ -394,24 +412,55 @@ void _register_plugin(GPluginModule *plugin)
static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolean last)
{
- const plugin_interface *pg_iface; /* Vitrine d'un greffon */
+ const char *name; /* Désignation du greffon */
size_t index; /* Indice du greffon */
GPluginModule *same; /* Juste pour la récupération */
+ GModule *module; /* Structure de chargement GLib*/
if (last)
{
assert(g_rw_lock_writer_trylock(&_pg_lock) == FALSE);
- pg_iface = g_plugin_module_get_interface(plugin);
+ name = g_plugin_module_get_name(plugin);
+
+ /**
+ * Les mécanismes de g_object_unref() prennent en compte la bascule d'un
+ * compteur de références initialement à 2 avant appel pour déclencher
+ * cet appel à on_plugin_ref_toggle() mis en place par g_object_add_toggle_ref().
+ *
+ * Incrémenter ce compteur à nouveau, via get_plugin_by_name(), puis le
+ * décrémenter ensuite via unref_object() va conduire à une nouvelle
+ * bascule des statuts de suivi dans g_object_unref().
+ *
+ * Il est ainsi impératif de rechercher une instance du greffon dans
+ * la liste des extensions sans toucher au compteur de références.
+ */
+
+ same = _find_plugin_by_name(name, &index);
- same = get_plugin_by_name(pg_iface->name, &index);
assert(same != NULL);
+ assert(same == plugin);
_pg_list[index] = NULL;
- g_object_remove_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL);
+ /**
+ * Suppression de la dernière référence.
+ */
+
+ if (G_IS_NATIVE_PLUGIN(plugin))
+ module = g_native_plugin_get_module(G_NATIVE_PLUGIN(plugin));
+ else
+ module = NULL;
+
+ g_object_remove_toggle_ref(G_OBJECT(same), (GToggleNotify)on_plugin_ref_toggle, NULL);
+
+ /**
+ * Plus aucun code issu du greffon n'est désormais utile. Le module associé peut
+ * être libéré de la mémoire.
+ */
- g_object_unref(G_OBJECT(same));
+ if (module != NULL)
+ g_module_close(module);
}
@@ -432,9 +481,40 @@ static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolea
void register_plugin(GPluginModule *plugin)
{
+ size_t i; /* Boucle de parcours */
+ const char *name; /* Désignation du greffon */
+ const char *existing; /* Nom d'un greffon en place */
+
g_rw_lock_writer_lock(&_pg_lock);
- _register_plugin(plugin);
+ /* Recherche d'un éventuel doublon */
+
+ name = g_plugin_module_get_name(plugin);
+
+ for (i = 0; i < _pg_count; i++)
+ {
+ existing = g_plugin_module_get_name(_pg_list[i]);
+
+ if (strcmp(name, existing) == 0)
+ {
+ log_variadic_message(LMT_ERROR, _("Plugin '%s' already registered!"), name);
+ break;
+ }
+
+ }
+
+ /* Ajout du greffon à la liste */
+
+ if (i == _pg_count)
+ {
+ _pg_list = realloc(_pg_list, ++_pg_count * sizeof(GPluginModule));
+
+ _pg_list[_pg_count - 1] = plugin;
+ ref_object(plugin);
+
+ g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL);
+
+ }
g_rw_lock_writer_unlock(&_pg_lock);
@@ -502,13 +582,18 @@ void load_remaning_plugins(void)
/* Supprime les greffons non chargés */
- for (i = 0; i < _pg_count; i++)
+ for (i = 0; i < _pg_count;)
{
flags = g_plugin_module_get_flags(_pg_list[i]);
- if ((flags & PSF_LOADED) == 0)
+ if (flags & PSF_LOADED)
+ i++;
+
+ else
{
- g_object_unref(G_OBJECT(_pg_list[i]));
+ unref_object(_pg_list[i]);
+
+ assert(_pg_list[i] == NULL);
memmove(&_pg_list[i], &_pg_list[i + 1], (_pg_count - i - 1) * sizeof(GPluginModule *));
_pg_count--;
@@ -535,15 +620,16 @@ void load_remaning_plugins(void)
* *
* Retour : Instance du greffon trouvé ou NULL si aucun. *
* *
-* Remarques : - *
+* Remarques : Le compteur de référence d'un greffon trouvé n'est pas *
+* modifié. *
* *
******************************************************************************/
-GPluginModule *get_plugin_by_name(const char *name, size_t *index)
+static GPluginModule *_find_plugin_by_name(const char *name, size_t *index)
{
GPluginModule *result; /* Greffon trouvé à renvoyer */
size_t i; /* Boucle de parcours */
- const plugin_interface *pg_iface; /* Vitrine d'un greffon */
+ const char *current; /* Nom du greffon courant */
result = NULL;
@@ -557,9 +643,9 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index)
/* Si on est en train de procéder à un nettoyage... */
if (_pg_list[i] == NULL) continue;
- pg_iface = g_plugin_module_get_interface(_pg_list[i]);
+ current = g_plugin_module_get_name(_pg_list[i]);
- if (strcmp(pg_iface->name, name) == 0)
+ if (strcmp(current, name) == 0)
{
result = _pg_list[i];
@@ -570,9 +656,6 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index)
}
- if (result != NULL)
- g_object_ref(G_OBJECT(result));
-
return result;
}
@@ -580,33 +663,25 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index)
/******************************************************************************
* *
-* Paramètres : count = nombre de greffons trouvés. [OUT] *
+* Paramètres : name = désignation du greffon recherché. *
+* index = indice du greffon trouvé. [OUT] *
* *
-* Description : Fournit la liste de l'ensemble des greffons. *
+* Description : Fournit le greffon répondant à un nom donné. *
* *
-* Retour : Liste de tous les greffons chargés. *
+* Retour : Instance du greffon trouvé ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
-GPluginModule **get_all_plugins(size_t *count)
+GPluginModule *get_plugin_by_name(const char *name, size_t *index)
{
- GPluginModule **result; /* Liste à retourner */
- size_t i; /* Boucle de parcours */
-
- g_rw_lock_reader_lock(&_pg_lock);
-
- result = malloc(_pg_count * sizeof(GPluginModule *));
- *count = _pg_count;
+ GPluginModule *result; /* Greffon trouvé à renvoyer */
- for (i = 0; i < _pg_count; i++)
- {
- result[i] = _pg_list[i];
- g_object_ref(G_OBJECT(_pg_list[i]));
- }
+ result = _find_plugin_by_name(name, index);
- g_rw_lock_reader_unlock(&_pg_lock);
+ if (result != NULL)
+ ref_object(result);
return result;
@@ -615,48 +690,30 @@ GPluginModule **get_all_plugins(size_t *count)
/******************************************************************************
* *
-* Paramètres : action = fonctionnalité recherchée. *
-* count = nombre de greffons trouvés. [OUT] *
+* Paramètres : count = nombre de greffons trouvés. [OUT] *
* *
-* Description : Fournit les greffons offrant le service demandé. *
+* Description : Fournit la liste de l'ensemble des greffons. *
* *
-* Retour : Liste de greffons correspondants issue d'un tri interne. *
+* Retour : Liste de tous les greffons chargés. *
* *
* Remarques : - *
* *
******************************************************************************/
-GPluginModule **get_all_plugins_for_action(PluginAction action, size_t *count)
+GPluginModule **get_all_plugins(size_t *count)
{
GPluginModule **result; /* Liste à retourner */
- size_t i; /* Boucle de parcours #1 */
- const plugin_interface *pg_iface; /* Informations à consulter */
- size_t j; /* Boucle de parcours #2 */
-
- result = NULL;
- *count = 0;
+ size_t i; /* Boucle de parcours */
g_rw_lock_reader_lock(&_pg_lock);
+ result = malloc(_pg_count * sizeof(GPluginModule *));
+ *count = _pg_count;
+
for (i = 0; i < _pg_count; i++)
{
- pg_iface = g_plugin_module_get_interface(_pg_list[i]);
-
- for (j = 0; j < pg_iface->actions_count; j++)
- {
- if (pg_iface->actions[j] == action)
- {
- result = realloc(result, ++(*count) * sizeof(GPluginModule *));
-
- result[*count - 1] = _pg_list[i];
- g_object_ref(G_OBJECT(_pg_list[i]));
-
- break;
-
- }
-
- }
-
+ result[i] = _pg_list[i];
+ ref_object(result[i]);
}
g_rw_lock_reader_unlock(&_pg_lock);
diff --git a/src/plugins/pglist.h b/src/plugins/pglist.h
index 83e9091..777b19c 100644
--- a/src/plugins/pglist.h
+++ b/src/plugins/pglist.h
@@ -52,9 +52,6 @@ void _lock_unlock_plugin_list_for_reading(bool lock);
#define unlock_plugin_list_for_reading() _lock_unlock_plugin_list_for_reading(false)
/* Ajoute un greffon à la liste principale de greffons. */
-void _register_plugin(GPluginModule *);
-
-/* Ajoute un greffon à la liste principale de greffons. */
void register_plugin(GPluginModule *);
/* Charge tous les greffons restant à charger. */
@@ -66,16 +63,74 @@ GPluginModule *get_plugin_by_name(const char *, size_t *);
/* Fournit la liste de l'ensemble des greffons. */
GPluginModule **get_all_plugins(size_t *);
-/* Fournit les greffons offrant le service demandé. */
-GPluginModule **get_all_plugins_for_action(PluginAction, size_t *);
-
/**
* Définitions des opérations appliquables à une catégories de greffons.
*/
-#define process_all_plugins_for(a, f, ...) \
+#define process_all_plugins_for(tp, cst, fc) \
+ do \
+ { \
+ size_t __count; \
+ GPluginModule **__list; \
+ size_t __i; \
+ GPluginModule *__pg; \
+ __list = get_all_plugins(&__count); \
+ for (__i = 0; __i < __count; __i++) \
+ { \
+ __pg = __list[__i]; \
+ if (G_TYPE_CHECK_INSTANCE_TYPE(__pg, tp)) \
+ fc(cst(__pg)); \
+ unref_object(__pg); \
+ } \
+ if (__list != NULL) \
+ free(__list); \
+ } \
+ while (0)
+
+#define accumulate_from_all_plugins(tp, cst, fc, atp, cnt) \
+ ({ \
+ atp *__acc_list; \
+ size_t __count; \
+ GPluginModule **__list; \
+ size_t __i; \
+ GPluginModule *__pg; \
+ size_t __tmp_count; \
+ atp *__tmp_list; \
+ *cnt = 0; \
+ __acc_list = NULL; \
+ __list = get_all_plugins(&__count); \
+ for (__i = 0; __i < __count; __i++) \
+ { \
+ __pg = __list[__i]; \
+ if (G_TYPE_CHECK_INSTANCE_TYPE(__pg, tp)) \
+ { \
+ __tmp_list = fc(cst(__pg), &__tmp_count); \
+ if (__tmp_list != NULL) \
+ { \
+ __acc_list = realloc(__acc_list, \
+ (*cnt + __tmp_count) * sizeof(atp)); \
+ memcpy(&__acc_list[*cnt], __tmp_list, \
+ __tmp_count * sizeof(atp)); \
+ *cnt += __tmp_count; \
+ free(__tmp_list); \
+ } \
+ } \
+ unref_object(__pg); \
+ } \
+ if (__list != NULL) \
+ free(__list); \
+ __acc_list; \
+ })
+
+
+
+#if 0
+
+// TODO : REMME
+
+#define process_all_plugins_for_old__(a, f, ...) \
do \
{ \
size_t __count; \
@@ -114,11 +169,13 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *);
/* DPS_PG_MANAGEMENT */
+/*
#define notify_native_plugins_loaded() \
- process_all_plugins_for(PGA_NATIVE_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL)
+ process_all_plugins_for_old__(PGA_NATIVE_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL)
#define notify_all_plugins_loaded() \
- process_all_plugins_for(PGA_ALL_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL)
+ process_all_plugins_for_old__(PGA_ALL_PLUGINS_LOADED, g_plugin_module_notify_plugins_loaded, NULL)
+*/
#define build_type_instance(t) \
process_plugins_while_null(PGA_TYPE_BUILDING, g_plugin_module_build_type_instance, t)
@@ -126,44 +183,47 @@ GPluginModule **get_all_plugins_for_action(PluginAction, size_t *);
/* DPS_SETUP */
#define include_plugin_theme(d, r, c) \
- process_all_plugins_for(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c)
+ process_all_plugins_for_old__(PGA_GUI_THEME, g_plugin_module_include_theme, d, r, c)
/* DPS_RUNNING */
#define notify_panel_creation(i) \
- process_all_plugins_for(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i)
+ process_all_plugins_for_old__(PGA_PANEL_CREATION, g_plugin_module_notify_panel_creation, i)
#define notify_panel_docking(i, d) \
- process_all_plugins_for(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d)
+ process_all_plugins_for_old__(PGA_PANEL_DOCKING, g_plugin_module_notify_panel_docking, i, d)
/* DPS_CONTENT */
#define handle_binary_content(a, c, i, s) \
- process_all_plugins_for(a, g_plugin_module_handle_binary_content, c, i, s)
+ process_all_plugins_for_old__(a, g_plugin_module_handle_binary_content, c, i, s)
#define handle_loaded_content(a, c, i, s) \
- process_all_plugins_for(a, g_plugin_module_handle_loaded_content, c, i, s)
+ process_all_plugins_for_old__(a, g_plugin_module_handle_loaded_content, c, i, s)
/* DPS_FORMAT */
#define handle_known_format_analysis(a, f, g, s) \
- process_all_plugins_for(a, g_plugin_module_handle_known_format_analysis, f, g, s)
+ process_all_plugins_for_old__(a, g_plugin_module_handle_known_format_analysis, f, g, s)
#define preload_binary_format(a, f, i, s) \
- process_all_plugins_for(a, g_plugin_module_preload_binary_format, f, i, s)
+ process_all_plugins_for_old__(a, g_plugin_module_preload_binary_format, f, i, s)
#define attach_debug_format(f) \
- process_all_plugins_for(PGA_FORMAT_ATTACH_DEBUG, g_plugin_module_attach_debug_format, f)
+ process_all_plugins_for_old__(PGA_FORMAT_ATTACH_DEBUG, g_plugin_module_attach_debug_format, f)
/* DPS_DISASSEMBLY */
#define process_disassembly_event(a, b, s, c) \
- process_all_plugins_for(a, g_plugin_module_process_disassembly_event, b, s, c)
+ process_all_plugins_for_old__(a, g_plugin_module_process_disassembly_event, b, s, c)
/* DPS_DETECTION */
#define detect_external_tools(a, cnt, v, n, c) \
- process_all_plugins_for(a, g_plugin_module_detect_external_tools, cnt, v, n, c)
+ process_all_plugins_for_old__(a, g_plugin_module_detect_external_tools, cnt, v, n, c)
+
+
+#endif
diff --git a/src/plugins/plugin-def.h b/src/plugins/plugin-def.h
index 1118140..b5e0b51 100644
--- a/src/plugins/plugin-def.h
+++ b/src/plugins/plugin-def.h
@@ -35,18 +35,6 @@
/* ------------------------ IDENTIFICATION DE COMPATIBILITES ------------------------ */
-/* Version identifiant les définitions courantes */
-typedef uint32_t plugin_abi_version_t;
-
-#define DEFINE_PLUGIN_ABI_VERSION(maj, min, rev) \
- (((maj & 0xff) << 24) | ((min & 0xff) << 16) | (rev & 0xffff))
-
-#define GET_ABI_MAJ_VERSION(vs) ((vs >> 24) & 0xff)
-#define GET_ABI_MIN_VERSION(vs) ((vs >> 16) & 0xff)
-#define GET_ABI_REV_VERSION(vs) (vs & 0xffff)
-
-#define CURRENT_ABI_VERSION DEFINE_PLUGIN_ABI_VERSION(0, 3, 0)
-
/* ------------------------- DEFINITION D'UN PROJET INTERNE ------------------------- */
@@ -232,14 +220,14 @@ typedef enum _PluginAction
/* ------------------------ PREMIER INTERFACAGE PROTOCOLAIRE ------------------------ */
-#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de1234abcdull
+//#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de1234abcdull
/* Définition d'un greffon */
typedef struct _plugin_interface
{
uint64_t magic; /* Vérification a minima */
- plugin_abi_version_t abi_version; /* Version du protocole utilisé*/
+ uint32_t /*plugin_abi_version_t*/ abi_version; /* Version du protocole utilisé*/
/**
* Les champs suivants ne sont généralement pas alloués dynamiquement,
diff --git a/src/plugins/plugin-int.h b/src/plugins/plugin-int.h
index 3ba19dc..07b455a 100644
--- a/src/plugins/plugin-int.h
+++ b/src/plugins/plugin-int.h
@@ -37,21 +37,28 @@
#include "../common/bits.h"
-/* Transfert de la conscience de soi. */
-typedef void (* pg_set_self_fc) (GPluginModule *);
-/* Prend acte du [dé]chargement du greffon. */
+/* Pointe le fichier contenant le greffon manipulé. */
+typedef char * (* get_plugin_filename_fc) (const GPluginModule *);
+
+/* Fournit le nom brut associé au greffon. */
+typedef char * (* get_plugin_modname_fc) (const GPluginModule *);
+
+/* Prend acte de (l'|la dés)activation du greffon. */
typedef bool (* pg_management_fc) (GPluginModule *);
+
+
+
+/* Transfert de la conscience de soi. */
+typedef void (* pg_set_self_fc) (GPluginModule *);
+
/* Accompagne la fin du chargement des modules natifs. */
typedef void (* pg_plugins_loaded_fc) (GPluginModule *, PluginAction);
/* Crée une instance à partir d'un type dynamique externe. */
typedef gpointer (* pg_build_instance_fc) (GPluginModule *, PluginAction, GType);
-/* Fournit le nom brut associé au greffon. */
-typedef char * (* pg_get_modname_fc) (const GPluginModule *);
-
#if 0
/* Procède à une opération liée à un contenu binaire. */
@@ -90,22 +97,47 @@ typedef void (* pg_detect_tools_fc) (const GPluginModule *, PluginAction, const
#endif
+/* Marqueur identifiable */
+#define CHRYSALIDE_PLUGIN_MAGIC 0xdeadc0de
+
+
+/* Version identifiant les définitions courantes */
+typedef uint32_t plugin_abi_version_t;
+
+#define DEFINE_PLUGIN_ABI_VERSION(maj, min, rev) \
+ (((maj & 0xff) << 24) | ((min & 0xff) << 16) | (rev & 0xffff))
+
+#define GET_ABI_MAJ_VERSION(vs) ((vs >> 24) & 0xff)
+#define GET_ABI_MIN_VERSION(vs) ((vs >> 16) & 0xff)
+#define GET_ABI_REV_VERSION(vs) (vs & 0xffff)
+
+/**
+ * 0.3.0 : dernière version avec actions et fonctions associées
+ * 1.0.0 (04/01/25) : bascule en chargement d'objet et interfaces
+ */
+#define CURRENT_ABI_VERSION DEFINE_PLUGIN_ABI_VERSION(1, 0, 0)
+
+
/* Greffon pour Chrysalide (instance) */
struct _GPluginModule
{
GObject parent; /* A laisser en premier */
- char *filename; /* Fichier associé au greffon */
- GModule *module; /* Abstration de manipulation */
+ uint32_t magic; /* Vérification a minima */
+ plugin_abi_version_t abi_version; /* Version du protocole utilisé*/
+
+ char *name; /* Désignation humaine courte */
+ char *desc; /* Description plus loquace */
+ char *version; /* Version du greffon */
+ char *url; /* Site Web associé */
- const plugin_interface *interface; /* Déclaration d'interfaçage */
+ char **required; /* Pré-chargements requis */
+ size_t required_count; /* Quantité de ces dépendances */
PluginStatusFlags flags; /* Fanion pour l'état courant */
bitfield_t *dependencies; /* Cartographie des dépendances*/
- //GGenConfig *config; /* Configuration dédiée */
-
};
@@ -114,6 +146,18 @@ struct _GPluginModuleClass
{
GObjectClass parent; /* A laisser en premier */
+ get_plugin_filename_fc get_filename; /* Obtention du chemin */
+ get_plugin_modname_fc get_modname; /* Fourniture du nom brut */
+
+ pg_management_fc enable; /* Procédure d'activation */
+ pg_management_fc disable; /* Procédure d'extinction */
+
+
+
+ /////////////////////////////////////////////
+
+#if 0
+
pg_management_fc init; /* Procédure d'initialisation */
pg_management_fc manage; /* Etape dans la vie du greffon*/
pg_management_fc exit; /* Procédure d'extinction */
@@ -121,8 +165,6 @@ struct _GPluginModuleClass
pg_plugins_loaded_fc plugins_loaded; /* Fin des chargements */
pg_build_instance_fc build_instance; /* Création d'objets */
- pg_get_modname_fc get_modname; /* Fourniture du nom brut */
-
#if 0
#ifdef INCLUDE_GTK_SUPPORT
pg_include_theme_fc include_theme; /* Extension d'un thème */
@@ -141,12 +183,28 @@ struct _GPluginModuleClass
pg_detect_tools_fc detect; /* Lancement de détections */
#endif
+#endif
+
+ /////////////////////////////////////////////
+
};
-/* Met en place la configuration dédiée au greffon. */
-void g_plugin_module_create_config(GPluginModule *);
+
+#define STORE_PLUGIN_ABI(p) \
+ do \
+ { \
+ GPluginModule *_p; \
+ _p = G_PLUGIN_MODULE(p); \
+ _p->magic = CHRYSALIDE_PLUGIN_MAGIC; \
+ _p->abi_version = CURRENT_ABI_VERSION; \
+ } \
+ while (0);
+
+
+/* Met en place un greffon. */
+bool g_plugin_module_create(GPluginModule *, const char *, const char *, const char *, const char *, const char * const *, size_t);
diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c
index 75cf4e0..dfdf3ed 100644
--- a/src/plugins/plugin.c
+++ b/src/plugins/plugin.c
@@ -30,14 +30,13 @@
#include <libgen.h>
#include <malloc.h>
#include <stdarg.h>
-#include <stdbool.h>
#include <stdio.h>
#include <string.h>
-#include "dt.h"
#include "pglist.h"
#include "plugin-int.h"
+#include "../common/compiler.h"
#include "../common/extstr.h"
#include "../common/pathname.h"
#include "../common/xdg.h"
@@ -56,19 +55,12 @@ static void g_plugin_module_dispose(GPluginModule *);
/* Procède à la libération totale de la mémoire. */
static void g_plugin_module_finalize(GPluginModule *);
-/* Initialise la classe des greffons d'extension. */
-static void g_plugin_module_init_gclass(GPluginModuleClass *, GModule *);
-
-/* Fournit le nom brut associé au greffon. */
-static char *_g_plugin_module_get_modname(const GPluginModule *);
-
/* Indique le type défini pour un greffon. */
G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT);
-
/******************************************************************************
* *
* Paramètres : class = classe à initialiser. *
@@ -84,16 +76,17 @@ G_DEFINE_TYPE(GPluginModule, g_plugin_module, G_TYPE_OBJECT);
static void g_plugin_module_class_init(GPluginModuleClass *class)
{
GObjectClass *object; /* Autre version de la classe */
- GPluginModuleClass *plugin; /* Version parente de la classe*/
object = G_OBJECT_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)g_plugin_module_dispose;
object->finalize = (GObjectFinalizeFunc)g_plugin_module_finalize;
- plugin = G_PLUGIN_MODULE_CLASS(class);
+ class->get_filename = NULL;
+ class->get_modname = NULL;
- plugin->get_modname = (pg_get_modname_fc)_g_plugin_module_get_modname;
+ class->enable = NULL;
+ class->disable = NULL;
}
@@ -112,7 +105,17 @@ static void g_plugin_module_class_init(GPluginModuleClass *class)
static void g_plugin_module_init(GPluginModule *plugin)
{
- //plugin->config = NULL;
+ plugin->name = NULL;
+ plugin->desc = NULL;
+ plugin->version = NULL;
+ plugin->url = NULL;
+
+ plugin->required = NULL;
+ plugin->required_count = 0;
+
+ plugin->flags = PSF_NONE;
+
+ plugin->dependencies = NULL;
}
@@ -131,29 +134,29 @@ static void g_plugin_module_init(GPluginModule *plugin)
static void g_plugin_module_dispose(GPluginModule *plugin)
{
- const plugin_interface *pg_iface; /* Définition du greffon */
size_t i; /* Boucle de parcours */
+ size_t index; /* Indice de greffon visé */
GPluginModule *dependency; /* Module nécessaire */
GPluginModuleClass *class; /* Classe de l'instance active */
- pg_iface = g_plugin_module_get_interface(plugin);
-
- if (pg_iface != NULL)
+ if (plugin->dependencies != NULL)
{
lock_plugin_list_for_reading();
- for (i = 0; i < pg_iface->required_count; i++)
+ for (i = 0; i < plugin->required_count; i++)
{
- dependency = get_plugin_by_name(pg_iface->required[i], NULL);
+ dependency = get_plugin_by_name(plugin->required[i], &index);
- /* Si le chargement a bien été complet avant la sortie... */
- if (dependency != NULL)
+ /* Si la dépendance a bien été pris en compte... */
+ if (test_in_bit_field(plugin->dependencies, index))
{
+ assert(dependency != NULL);
+
/* Un coup pour l'appel à get_plugin_by_name(). */
- g_object_unref(G_OBJECT(dependency));
+ unref_object(dependency);
/* Un coup pour la dépendance */
- g_object_unref(G_OBJECT(dependency));
+ unref_object(dependency);
}
@@ -163,25 +166,13 @@ static void g_plugin_module_dispose(GPluginModule *plugin)
}
- class = G_PLUGIN_MODULE_GET_CLASS(plugin);
-
- if (class->exit != NULL)
- class->exit(plugin);
-
- /*
- if (plugin->config != NULL)
+ if (plugin->flags & PSF_LOADED)
{
- g_generic_config_write(plugin->config);
-
- g_clear_object(&plugin->config);
+ class = G_PLUGIN_MODULE_GET_CLASS(plugin);
- }
- */
+ if (class->disable != NULL)
+ class->disable(plugin);
- if (plugin->module != NULL)
- {
- g_module_close(plugin->module);
- plugin->module = NULL;
}
G_OBJECT_CLASS(g_plugin_module_parent_class)->dispose(G_OBJECT(plugin));
@@ -203,7 +194,25 @@ static void g_plugin_module_dispose(GPluginModule *plugin)
static void g_plugin_module_finalize(GPluginModule *plugin)
{
- free(plugin->filename);
+ size_t i; /* Boucle de parcours */
+
+ if (plugin->name != NULL)
+ free(plugin->name);
+
+ if (plugin->desc != NULL)
+ free(plugin->desc);
+
+ if (plugin->version != NULL)
+ free(plugin->version);
+
+ if (plugin->url != NULL)
+ free(plugin->url);
+
+ for (i = 0; i < plugin->required_count; i++)
+ free(plugin->required[i]);
+
+ if (plugin->required != NULL)
+ free(plugin->required);
if (plugin->dependencies != NULL)
delete_bit_field(plugin->dependencies);
@@ -215,627 +224,173 @@ static void g_plugin_module_finalize(GPluginModule *plugin)
/******************************************************************************
* *
-* Paramètres : filename = nom du fichier à charger. *
+* Paramètres : plugin = instance à initialiser pleinement. *
+* name = nom du greffon pour référence, principalement. *
+* desc = présentation éventuelle à destination humaine. *
+* version = indication de version éventuelle. *
+* url = référence vers une ressource en ligne. *
+* required = liste de dépendances éventuelles ou NULL. *
+* count = taille de cette liste. *
* *
-* Description : Crée un module pour un greffon donné. *
+* Description : Met en place un greffon. *
* *
-* Retour : Adresse de la structure mise en place. *
+* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
-GPluginModule *g_plugin_module_new(const gchar *filename)
+bool g_plugin_module_create(GPluginModule *plugin, const char *name, const char *desc, const char *version, const char *url, const char * const *required, size_t count)
{
- GPluginModule *result; /* Structure à retourner */
- GModule *module; /* Abstration de manipulation */
- pg_set_self_fc set_self; /* Copie du greffon */
- const plugin_interface *interface; /* Déclaration d'interfaçage */
- plugin_abi_version_t current; /* Version de l'ABI actuelle */
- bool valid; /* Statut de validité */
- size_t i; /* Boucle de parcours */
- uint32_t action; /* Identifiant d'une action */
- uint32_t category; /* Catégorie principale */
- uint32_t sub; /* Sous-catégorie visée */
- GType gtype; /* Nouveau type de greffon */
-
- module = g_module_open(filename, G_MODULE_BIND_LAZY);
- if (module == NULL)
- {
- log_variadic_message(LMT_ERROR,
- _("Error while loading the plugin candidate '%s' : %s"),
- filename, g_module_error());
- goto bad_module;
- }
-
-
-#define load_plugin_symbol(mod, sym, dest) \
- ({ \
- bool __result; \
- if (!g_module_symbol(mod, sym, (gpointer *)dest)) \
- { \
- log_variadic_message(LMT_ERROR, \
- _("No '%s' entry in plugin candidate '%s'"), \
- sym, filename); \
- __result = false; \
- } \
- else __result = true; \
- __result; \
- })
-
-
- /* Récupération de la version d'ABI */
-
- if (!load_plugin_symbol(module, "chrysalide_plugin_set_self", &set_self))
- goto no_self_setter;
-
- if (!load_plugin_symbol(module, "_chrysalide_plugin", &interface))
- goto no_interface;
-
- current = CURRENT_ABI_VERSION;
-
- if (current != interface->abi_version)
- goto wrong_abi;
-
- /* Localisation des différents points d'entrée déclarés */
-
+ bool result; /* Bilan à retourner */
+ size_t i; /* Boucle de parcours #1 */
+ size_t k; /* Boucle de parcours #2 */
-#define check_plugin_symbol(mod, sym) \
- ({ \
- bool __result; \
- __result = g_module_symbol(mod, sym, (gpointer []) { NULL }); \
- if (!__result) \
- log_variadic_message(LMT_ERROR, \
- _("No '%s' entry in plugin candidate '%s'"), \
- sym, filename); \
- __result; \
- })
+ /* Validations préalables */
+ assert(name != NULL);
- valid = true;
+ result = (name != NULL);
- for (i = 0; i < interface->actions_count && valid; i++)
+ if (result && plugin->abi_version != CURRENT_ABI_VERSION)
{
- action = interface->actions[i];
- category = MASK_PLUGIN_CATEGORY(action);
- sub = MASK_PLUGIN_SUB_CATEGORY(action);
-
- switch (category)
- {
- case DPC_BASIC:
-
- switch (sub)
- {
- case DPS_NONE:
- break;
-
- case DPS_PG_MANAGEMENT:
-
- switch (action)
- {
- case PGA_PLUGIN_INIT:
- valid = check_plugin_symbol(module, "chrysalide_plugin_init");
- break;
-
- case PGA_PLUGIN_LOADED:
- valid = check_plugin_symbol(module, "chrysalide_plugin_manage");
- break;
-
- case PGA_PLUGIN_EXIT:
- valid = check_plugin_symbol(module, "chrysalide_plugin_exit");
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown action '0x%02x' in plugin '%s'..."),
- interface->actions[i], filename);
- break;
-
- }
-
- break;
-
- case DPS_CORE_MANAGEMENT:
-
- switch (action)
- {
- case PGA_NATIVE_PLUGINS_LOADED:
- case PGA_ALL_PLUGINS_LOADED:
- valid = check_plugin_symbol(module, "chrysalide_plugin_on_plugins_loaded");
- break;
-
- case PGA_TYPE_BUILDING:
- valid = check_plugin_symbol(module, "chrysalide_plugin_build_type_instance");
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown action '0x%02x' in plugin '%s'..."),
- interface->actions[i], filename);
- break;
-
- }
-
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename);
- break;
-
- }
-
- break;
-
-#if 0
-
- case DPC_GUI:
-
- switch (sub)
- {
- case DPS_SETUP:
-
- switch (action)
- {
- case PGA_GUI_THEME:
- valid = check_plugin_symbol(module, "chrysalide_plugin_include_theme");
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown action '0x%02x' in plugin '%s'..."),
- interface->actions[i], filename);
- break;
-
- }
-
- break;
-
- case DPS_RUNNING:
-
- switch (action)
- {
- case PGA_PANEL_CREATION:
- valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_creation");
- break;
-
- case PGA_PANEL_DOCKING:
- valid = check_plugin_symbol(module, "chrysalide_plugin_on_panel_docking");
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown action '0x%02x' in plugin '%s'..."),
- interface->actions[i], filename);
- break;
-
- }
-
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename);
- break;
-
- }
-
- break;
-
- case DPC_BINARY_PROCESSING:
-
- switch (sub)
- {
- case DPS_CONTENT:
-
- switch (action)
- {
- case PGA_CONTENT_EXPLORER:
- case PGA_CONTENT_RESOLVER:
- valid = check_plugin_symbol(module, "chrysalide_plugin_handle_binary_content");
- break;
-
- case PGA_CONTENT_ANALYZED:
- valid = check_plugin_symbol(module, "chrysalide_plugin_handle_loaded_content");
- break;
-
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown action '0x%02x' in plugin '%s'..."),
- interface->actions[i], filename);
- break;
-
- }
+ result = false;
- break;
-
- case DPS_FORMAT:
+ log_variadic_message(LMT_ERROR, _("ABI mismatch detected: %08x (plugin) vs %08x (core)"),
+ plugin->abi_version, CURRENT_ABI_VERSION);
- switch (action)
- {
- case PGA_FORMAT_ANALYSIS_STARTED:
- case PGA_FORMAT_ANALYSIS_ENDED:
- case PGA_FORMAT_POST_ANALYSIS_STARTED:
- case PGA_FORMAT_POST_ANALYSIS_ENDED:
- valid = check_plugin_symbol(module, "chrysalide_plugin_handle_binary_format_analysis");
- break;
+ }
- case PGA_FORMAT_PRELOAD:
- valid = check_plugin_symbol(module, "chrysalide_plugin_preload_binary_format");
- break;
+ /* Mémorisation des informations */
- case PGA_FORMAT_ATTACH_DEBUG:
- valid = check_plugin_symbol(module, "chrysalide_plugin_attach_debug");
- break;
+ if (result)
+ {
+ plugin->name = strdup(name);
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown action '0x%02x' in plugin '%s'..."),
- interface->actions[i], filename);
- break;
+ if (desc != NULL)
+ plugin->desc = strdup(desc);
- }
+ if (version != NULL)
+ plugin->version = strdup(version);
- break;
+ if (url != NULL)
+ plugin->url = strdup(url);
- case DPS_DISASSEMBLY:
- valid = check_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event");
- break;
-
- case DPS_DETECTION:
- valid = check_plugin_symbol(module, "chrysalide_plugin_detect_external_tools");
- break;
+ if (count > 0)
+ {
+ plugin->required = malloc(count * sizeof(char *));
+ plugin->required_count = 0;
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown sub-category '0x%02x' in plugin '%s'..."), sub, filename);
+ for (i = 0; i < count; i++)
+ {
+ for (k = 0; k < plugin->required_count; k++)
+ if (strcmp(required[i], plugin->required[k]) == 0)
break;
- }
+ if (k < plugin->required_count)
+ continue;
-#endif
+ plugin->required[plugin->required_count++] = strdup(required[i]);
- break;
+ }
- default:
- log_variadic_message(LMT_WARNING,
- _("Unknown category '0x%02x' in plugin '%s'..."), category, filename);
- break;
+ plugin->required = realloc(plugin->required, plugin->required_count * sizeof(char *));
}
}
- if (!valid)
- goto missing_feature;
-
- gtype = build_dynamic_type(G_TYPE_PLUGIN_MODULE, interface->gtp_name,
- (GClassInitFunc)g_plugin_module_init_gclass, module, NULL);
-
- if (gtype == G_TYPE_INVALID)
- goto no_instance;
-
- result = g_object_new(gtype, NULL);
-
- result->filename = strdup(filename);
- result->module = module;
-
- result->interface = interface;
-
- set_self(result);
-
return result;
- no_self_setter:
-
- log_variadic_message(LMT_ERROR, _("Self pointer setter is missing for plugin '%s'"), filename);
- goto bad_plugin;
-
- no_interface:
-
- log_variadic_message(LMT_ERROR, _("Main interface is missing for plugin '%s'"), filename);
- goto bad_plugin;
-
- wrong_abi:
-
- log_variadic_message(LMT_ERROR, _("ABI mismatch detected! Plugin '%s' rejected"), filename);
- goto bad_plugin;
-
- missing_feature:
-
- log_variadic_message(LMT_ERROR, _("An expected feature is missing for plugin '%s'"), filename);
- goto bad_plugin;
-
- no_instance:
-
- log_variadic_message(LMT_ERROR, _("Unabled to create an instance of plugin '%s'"), filename);
-
- bad_plugin:
-
- g_module_close(module);
-
- bad_module:
-
- return NULL;
-
}
/******************************************************************************
* *
-* Paramètres : class = classe à initialiser. *
-* module = module représentant le greffon chargé en mémoire. *
+* Paramètres : plugin = greffon à consulter. *
* *
-* Description : Initialise la classe des greffons d'extension. *
+* Description : Indique le nom associé à un greffon. *
* *
-* Retour : - *
+* Retour : Désignation interne de l'extension, pour référence(s). *
* *
* Remarques : - *
* *
******************************************************************************/
-static void g_plugin_module_init_gclass(GPluginModuleClass *class, GModule *module)
+const char *g_plugin_module_get_name(const GPluginModule *plugin)
{
- const plugin_interface *interface; /* Déclaration d'interfaçage */
- size_t i; /* Boucle de parcours */
- uint32_t action; /* Identifiant d'une action */
- uint32_t category; /* Catégorie principale */
- uint32_t sub; /* Sous-catégorie visée */
-
-
-#undef load_plugin_symbol
-
-#define load_plugin_symbol(mod, sym, dest) \
- ({ \
- bool __result; \
- __result = g_module_symbol(mod, sym, (gpointer *)dest); \
- assert(__result); \
- __result; \
- })
-
-
- load_plugin_symbol(module, "_chrysalide_plugin", &interface);
-
- for (i = 0; i < interface->actions_count; i++)
- {
- action = interface->actions[i];
- category = MASK_PLUGIN_CATEGORY(action);
- sub = MASK_PLUGIN_SUB_CATEGORY(action);
-
- switch (category)
- {
- case DPC_BASIC:
-
- switch (sub)
- {
- case DPS_NONE:
- break;
-
- case DPS_PG_MANAGEMENT:
-
- switch (action)
- {
- case PGA_PLUGIN_INIT:
- load_plugin_symbol(module, "chrysalide_plugin_init", &class->init);
- break;
-
- case PGA_PLUGIN_LOADED:
- load_plugin_symbol(module, "chrysalide_plugin_manage", &class->manage);
- break;
-
- case PGA_PLUGIN_EXIT:
- load_plugin_symbol(module, "chrysalide_plugin_exit", &class->exit);
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- break;
-
- case DPS_CORE_MANAGEMENT:
-
- switch (action)
- {
- case PGA_NATIVE_PLUGINS_LOADED:
- case PGA_ALL_PLUGINS_LOADED:
- load_plugin_symbol(module, "chrysalide_plugin_on_plugins_loaded",
- &class->plugins_loaded);
- break;
-
- case PGA_TYPE_BUILDING:
- load_plugin_symbol(module, "chrysalide_plugin_build_type_instance",
- &class->build_instance);
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- break;
-#if 0
- case DPC_GUI:
-
- switch (sub)
- {
- case DPS_SETUP:
-
- switch (action)
- {
- case PGA_GUI_THEME:
-#ifdef INCLUDE_GTK_SUPPORT
- load_plugin_symbol(module, "chrysalide_plugin_include_theme",
- &class->include_theme);
-#endif
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- break;
-
- case DPS_RUNNING:
-
- switch (action)
- {
- case PGA_PANEL_CREATION:
-#ifdef INCLUDE_GTK_SUPPORT
- load_plugin_symbol(module, "chrysalide_plugin_on_panel_creation",
- &class->notify_panel);
-#endif
- break;
-
- case PGA_PANEL_DOCKING:
-#ifdef INCLUDE_GTK_SUPPORT
- load_plugin_symbol(module, "chrysalide_plugin_on_panel_docking",
- &class->notify_docking);
-#endif
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- break;
-
- default:
- assert(false);
- break;
+ const char *result; /* Valeur finale à renvoyer */
- }
+ result = plugin->name;
- break;
-
- case DPC_BINARY_PROCESSING:
-
- switch (sub)
- {
- case DPS_CONTENT:
-
- switch (action)
- {
- case PGA_CONTENT_EXPLORER:
- case PGA_CONTENT_RESOLVER:
- load_plugin_symbol(module, "chrysalide_plugin_handle_binary_content",
- &class->handle_content);
- break;
-
- case PGA_CONTENT_ANALYZED:
- load_plugin_symbol(module, "chrysalide_plugin_handle_loaded_content",
- &class->handle_loaded);
- break;
-
- default:
- assert(false);
- break;
-
- }
-
- break;
-
- case DPS_FORMAT:
-
- switch (action)
- {
- case PGA_FORMAT_ANALYSIS_STARTED:
- case PGA_FORMAT_ANALYSIS_ENDED:
- case PGA_FORMAT_POST_ANALYSIS_STARTED:
- case PGA_FORMAT_POST_ANALYSIS_ENDED:
- load_plugin_symbol(module, "chrysalide_plugin_handle_binary_format_analysis",
- &class->handle_fmt_analysis);
- break;
-
- case PGA_FORMAT_PRELOAD:
- load_plugin_symbol(module, "chrysalide_plugin_preload_binary_format", &class->preload_format);
- break;
-
- case PGA_FORMAT_ATTACH_DEBUG:
- load_plugin_symbol(module, "chrysalide_plugin_attach_debug", &class->attach_debug);
- break;
+ return result;
- default:
- assert(false);
- break;
+}
- }
- break;
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à consulter. *
+* *
+* Description : Fournit une description fonctionnelle d'un greffon. *
+* *
+* Retour : Description textuelle associée à une extension ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- case DPS_DISASSEMBLY:
- load_plugin_symbol(module, "chrysalide_plugin_process_disassembly_event", &class->process_disass);
- break;
+const char *g_plugin_module_get_desc(const GPluginModule *plugin)
+{
+ const char *result; /* Valeur finale à renvoyer */
- case DPS_DETECTION:
- load_plugin_symbol(module, "chrysalide_plugin_detect_external_tools", &class->detect);
- break;
+ result = plugin->desc;
- default:
- assert(false);
- break;
+ return result;
- }
+}
-#endif
- break;
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à consulter. *
+* *
+* Description : Fournit la version d'un greffon et de ses fonctionnalités. *
+* *
+* Retour : Version sous forme de chaîne de caractères ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- default:
- assert(false);
- break;
+const char *g_plugin_module_get_version(const GPluginModule *plugin)
+{
+ const char *result; /* Valeur finale à renvoyer */
- }
+ result = plugin->version;
- }
+ return result;
}
/******************************************************************************
* *
-* Paramètres : plugin = greffon à valider. *
+* Paramètres : plugin = greffon à consulter. *
* *
-* Description : Fournit le nom brut associé au greffon. *
+* Description : Fournit l'URL des ressources en ligne liées à un greffon. *
* *
-* Retour : Désignation brute du greffon. *
+* Retour : URL de renvoi associée à une extension ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
-char *g_plugin_module_get_modname(const GPluginModule *plugin)
+const char *g_plugin_module_get_url(const GPluginModule *plugin)
{
- char *result; /* Désignation brute à renvoyer*/
- GPluginModuleClass *class; /* Classe de l'instance active */
-
- class = G_PLUGIN_MODULE_GET_CLASS(plugin);
-
- result = class->get_modname(plugin);
+ const char *result; /* Valeur finale à renvoyer */
- /**
- * Tente une opération de la dernière chance.
- *
- * Dans le cas d'un module Python, la fonction de classe peut ne pas
- * trouver de support si l'extension Python n'est pas au point.
- */
- if (result == NULL && class->get_modname != _g_plugin_module_get_modname)
- result = _g_plugin_module_get_modname(plugin);
+ result = plugin->url;
return result;
@@ -844,41 +399,24 @@ char *g_plugin_module_get_modname(const GPluginModule *plugin)
/******************************************************************************
* *
-* Paramètres : plugin = greffon à valider. *
+* Paramètres : plugin = greffon à consulter. *
+* count = taille de la liste fournie. [OUT] *
* *
-* Description : Fournit le nom brut associé au greffon. *
+* Description : Fournit la liste des dépendances d'un greffon donné. *
* *
-* Retour : Désignation brute du greffon. *
+* Retour : Liste des noms d'extensions requises pour une extension. *
* *
* Remarques : - *
* *
******************************************************************************/
-static char *_g_plugin_module_get_modname(const GPluginModule *plugin)
+const char * const *g_plugin_module_get_requirements(const GPluginModule *plugin, size_t *count)
{
- char *result; /* Désignation brute à renvoyer*/
- char *path; /* Chemin à traiter */
- char *filename; /* Nom de bibliothèque partagée*/
- size_t length; /* Taille du nom */
-
- path = strdup(g_plugin_module_get_filename(G_PLUGIN_MODULE(plugin)));
-
- filename = basename(path);
-
- if (strncmp(filename, "lib", 3) == 0)
- filename += 3;
-
- length = strlen(filename);
-
- if (length >= 3)
- {
- if (strncmp(&filename[length - 3], ".so", 3) == 0)
- filename[length - 3] = '\0';
- }
+ const char * const *result; /* Valeur finale à renvoyer */
- result = strdup(filename);
+ result = CONST_ARRAY_CAST(plugin->required, char);
- free(path);
+ *count = plugin->required_count;
return result;
@@ -889,7 +427,7 @@ static char *_g_plugin_module_get_modname(const GPluginModule *plugin)
* *
* Paramètres : plugin = greffon à consulter. *
* *
-* Description : Indique le fichier contenant le greffon manipulé. *
+* Description : Pointe le fichier contenant le greffon manipulé. *
* *
* Retour : Chemin d'accès au greffon. *
* *
@@ -897,28 +435,42 @@ static char *_g_plugin_module_get_modname(const GPluginModule *plugin)
* *
******************************************************************************/
-const char *g_plugin_module_get_filename(const GPluginModule *plugin)
+char *g_plugin_module_get_filename(const GPluginModule *plugin)
{
- return plugin->filename;
+ char *result; /* Chemin d'accès à renvoyer */
+ GPluginModuleClass *class; /* Classe de l'instance active */
+
+ class = G_PLUGIN_MODULE_GET_CLASS(plugin);
+
+ result = class->get_filename(plugin);
+
+ return result;
}
/******************************************************************************
* *
-* Paramètres : plugin = greffon à consulter. *
+* Paramètres : plugin = greffon à valider. *
* *
-* Description : Fournit la description du greffon dans son intégralité. *
+* Description : Fournit le nom brut associé au greffon. *
* *
-* Retour : Interfaçage renseigné. *
+* Retour : Désignation brute du greffon. *
* *
* Remarques : - *
* *
******************************************************************************/
-const plugin_interface *g_plugin_module_get_interface(const GPluginModule *plugin)
+char *g_plugin_module_get_modname(const GPluginModule *plugin)
{
- return plugin->interface;
+ char *result; /* Désignation brute à renvoyer*/
+ GPluginModuleClass *class; /* Classe de l'instance active */
+
+ class = G_PLUGIN_MODULE_GET_CLASS(plugin);
+
+ result = class->get_modname(plugin);
+
+ return result;
}
@@ -979,7 +531,6 @@ void g_plugin_module_override_flags(GPluginModule *plugin, PluginStatusFlags fla
bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule **list, size_t count)
{
bool result; /* Bilan à faire remonter */
- const plugin_interface *pg_iface; /* Définition du greffon */
bitfield_t *new; /* Nouvelle définition */
size_t i; /* Boucle de parcours */
GPluginModule *dependency; /* Module nécessaire */
@@ -997,15 +548,13 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule *
if ((plugin->flags & (PSF_UNKNOW_DEP | PSF_DEP_LOOP)) == 0)
{
- pg_iface = g_plugin_module_get_interface(plugin);
-
/* Collecte des dépendances */
new = dup_bit_field(plugin->dependencies);
- for (i = 0; i < pg_iface->required_count; i++)
+ for (i = 0; i < plugin->required_count; i++)
{
- dependency = get_plugin_by_name(pg_iface->required[i], &index);
+ dependency = get_plugin_by_name(plugin->required[i], &index);
if (dependency == NULL)
plugin->flags |= PSF_UNKNOW_DEP;
@@ -1023,7 +572,7 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule *
*/
if (test_in_bit_field(plugin->dependencies, index))
- g_object_unref(G_OBJECT(dependency));
+ unref_object(dependency);
}
@@ -1041,13 +590,14 @@ bool g_plugin_module_resolve_dependencies(GPluginModule *plugin, GPluginModule *
/* Vérification sanitaire */
- dependency = get_plugin_by_name(pg_iface->name, &index);
+ dependency = get_plugin_by_name(plugin->name, &index);
assert(dependency != NULL);
+ assert(dependency == plugin);
if (test_in_bit_field(plugin->dependencies, index))
plugin->flags |= PSF_DEP_LOOP;
- g_object_unref(G_OBJECT(dependency));
+ unref_object(dependency);
}
@@ -1075,12 +625,12 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co
{
bool result; /* Bilan à retourner */
PluginStatusFlags flags; /* Fanions de greffon */
- const plugin_interface *pg_iface; /* Définition du greffon */
size_t i; /* Boucle de parcours */
GPluginModule *dependency; /* Module nécessaire */
+ char *filename; /* Chemin d'accès au greffon */
GPluginModuleClass *class; /* Classe de l'instance active */
- //GGenConfig *config; /* Configuration à charger */
- char *dir; /* Répertoire modifiable */
+ char *tmp; /* Chaîne modifiable */
+ char *dir; /* Pointeur vers répertoire */
/* Si un essai précédent a déjà échoué ou réussi... */
@@ -1092,40 +642,41 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co
/* Chargement des dépendances */
- pg_iface = g_plugin_module_get_interface(plugin);
-
result = true;
- for (i = 0; i < pg_iface->required_count && result; i++)
+ filename = g_plugin_module_get_filename(plugin);
+
+ for (i = 0; i < plugin->required_count && result; i++)
{
- dependency = get_plugin_by_name(pg_iface->required[i], NULL);
+ dependency = get_plugin_by_name(plugin->required[i], NULL);
assert(dependency != NULL);
result = g_plugin_module_load(dependency, list, count);
- g_object_unref(G_OBJECT(dependency));
+ unref_object(dependency);
}
if (!result)
{
- log_variadic_message(LMT_ERROR,
- _("Some dependencies failed to load for plugin '%s'"), plugin->filename);
+ log_variadic_message(LMT_ERROR, _("Some dependencies failed to load for plugin '%s'"), filename);
+
+ plugin->flags |= PSF_FAILURE;
goto failure;
+
}
/* Chargement du greffon courant */
class = G_PLUGIN_MODULE_GET_CLASS(plugin);
- if (class->init != NULL)
+ if (class->enable != NULL)
{
- result = class->init(plugin);
+ result = class->enable(plugin);
if (!result)
{
- log_variadic_message(LMT_ERROR,
- _("Plugin '%s' failed to load itself..."), plugin->filename);
+ log_variadic_message(LMT_ERROR, _("Plugin '%s' failed to load itself..."), filename);
plugin->flags |= PSF_FAILURE;
goto failure;
@@ -1134,39 +685,23 @@ bool g_plugin_module_load(GPluginModule *plugin, GPluginModule **list, size_t co
}
- g_plugin_module_create_config(plugin);
-
- result = g_plugin_module_manage(plugin, PGA_PLUGIN_LOADED);
-
- if (!result)
- {
- log_variadic_message(LMT_ERROR,
- _("Plugin '%s' failed to complete loading..."), plugin->filename);
-
- plugin->flags |= PSF_FAILURE;
- goto failure;
-
- }
-
- /*
- config = g_plugin_module_get_config(plugin);
- g_generic_config_read(config);
- g_object_unref(G_OBJECT(config));
- */
+ /* Message de bilan */
- dir = strdup(plugin->filename);
- dir = dirname(dir);
+ tmp = strdup(filename);
+ dir = dirname(tmp);
log_variadic_message(LMT_PROCESS,
_("Loaded the '<b>%s</b>' file as plugin from the '<b>%s</b>' directory"),
- strrchr(plugin->filename, G_DIR_SEPARATOR) + 1, dir);
+ strrchr(filename, G_DIR_SEPARATOR) + 1, dir);
- free(dir);
+ free(tmp);
plugin->flags |= PSF_LOADED;
failure:
+ free(filename);
+
return result;
}
@@ -1227,31 +762,6 @@ char *g_plugin_module_build_config_filename(const GPluginModule *plugin, const c
/******************************************************************************
* *
-* Paramètres : plugin = greffon à compléter. *
-* *
-* Description : Met en place la configuration dédiée au greffon. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-void g_plugin_module_create_config(GPluginModule *plugin)
-{
- char *filename; /* Chemin d'accès particulier */
-
- filename = g_plugin_module_build_config_filename(plugin, "config.xml", false);
-
- //plugin->config = g_generic_config_new_from_file(filename);
-
- free(filename);
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : plugin = greffon à consulter. *
* *
* Description : Fournit la configuration mise en place pour le greffon. *
@@ -1295,11 +805,11 @@ void g_plugin_module_log_simple_message(const GPluginModule *plugin, LogMessageT
size_t len; /* Taille tampon disponible */
char *buffer; /* Tampon du msg reconstitué */
- len = 4 + strlen(plugin->interface->name) + 6 + strlen(msg) + 1;
+ len = 4 + strlen(plugin->name) + 6 + strlen(msg) + 1;
buffer = calloc(len, sizeof(char));
strcpy(buffer, "<i>[");
- strcat(buffer, plugin->interface->name);
+ strcat(buffer, plugin->name);
strcat(buffer, "]</i> ");
strcat(buffer, msg);
@@ -1345,80 +855,12 @@ void g_plugin_module_log_variadic_message(const GPluginModule *plugin, LogMessag
}
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
-* *
-* Description : Encadre une étape de la vie d'un greffon. *
-* *
-* Retour : Bilan de l'opération. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool g_plugin_module_manage(GPluginModule *plugin, PluginAction action)
-{
- bool result; /* Bilan à faire remonter */
- GPluginModuleClass *class; /* Classe de l'instance active */
- const plugin_interface *pg_iface; /* Informations à consulter */
- size_t i; /* Boucle de parcours */
- bool handle_action; /* Action supportée ? */
-
- class = G_PLUGIN_MODULE_GET_CLASS(plugin);
-
- if (class->manage == NULL)
- result = true;
-
- else
- {
- handle_action = false;
-
- pg_iface = g_plugin_module_get_interface(plugin);
-
- for (i = 0; i < pg_iface->actions_count; i++)
- if (pg_iface->actions[i] == PGA_PLUGIN_LOADED)
- {
- handle_action = true;
- break;
- }
-
- if (handle_action)
- result = class->manage(plugin/*, action*/);
- else
- result = true;
-
- }
-
- return result;
-
-}
-
-/******************************************************************************
-* *
-* Paramètres : plugin = greffon à manipuler. *
-* action = type d'action attendue. *
-* unused = variable non utilisé pour l'usage de __VA_ARGS__. *
-* *
-* Description : Accompagne la fin du chargement des modules. *
-* *
-* Retour : - *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-void g_plugin_module_notify_plugins_loaded(GPluginModule *plugin, PluginAction action, void *unused)
-{
- GPluginModuleClass *class; /* Classe de l'instance active */
- class = G_PLUGIN_MODULE_GET_CLASS(plugin);
- class->plugins_loaded(plugin, action);
-}
+#if 0
/******************************************************************************
@@ -1450,8 +892,6 @@ gpointer g_plugin_module_build_type_instance(GPluginModule *plugin, PluginAction
-#if 0
-
#ifdef INCLUDE_GTK_SUPPORT
diff --git a/src/plugins/plugin.h b/src/plugins/plugin.h
index 5c473b2..a4cc388 100644
--- a/src/plugins/plugin.h
+++ b/src/plugins/plugin.h
@@ -52,6 +52,27 @@
DECLARE_GTYPE(GPluginModule, g_plugin_module, G, PLUGIN_MODULE);
+/* Indique le nom associé à un greffon. */
+const char *g_plugin_module_get_name(const GPluginModule *);
+
+/* Fournit une description fonctionnelle d'un greffon. */
+const char *g_plugin_module_get_desc(const GPluginModule *);
+
+/* Fournit la version d'un greffon et de ses fonctionnalités. */
+const char *g_plugin_module_get_version(const GPluginModule *);
+
+/* Fournit l'URL des ressources en ligne liées à un greffon. */
+const char *g_plugin_module_get_url(const GPluginModule *);
+
+/* Fournit la liste des dépendances d'un greffon donné. */
+const char * const *g_plugin_module_get_requirements(const GPluginModule *, size_t *);
+
+/* Pointe le fichier contenant le greffon manipulé. */
+char *g_plugin_module_get_filename(const GPluginModule *);
+
+/* Fournit le nom brut associé au greffon. */
+char *g_plugin_module_get_modname(const GPluginModule *);
+
/* Fanions indiquant le statut du greffon */
typedef enum _PluginStatusFlags
{
@@ -66,19 +87,6 @@ typedef enum _PluginStatusFlags
#define BROKEN_PLUGIN_STATUS (PSF_UNKNOW_DEP | PSF_DEP_LOOP | PSF_FAILURE)
-
-/* Crée un module pour un greffon donné. */
-GPluginModule *g_plugin_module_new(const gchar *);
-
-/* Fournit le nom brut associé au greffon. */
-char *g_plugin_module_get_modname(const GPluginModule *);
-
-/* Indique le fichier contenant le greffon manipulé. */
-const char *g_plugin_module_get_filename(const GPluginModule *);
-
-/* Fournit la description du greffon dans son intégralité. */
-const plugin_interface *g_plugin_module_get_interface(const GPluginModule *);
-
/* Fournit des indications sur l'état du greffon. */
PluginStatusFlags g_plugin_module_get_flags(const GPluginModule *);
@@ -103,17 +111,12 @@ void g_plugin_module_log_simple_message(const GPluginModule *, LogMessageType, c
/* Présente dans le journal un message complexe. */
void g_plugin_module_log_variadic_message(const GPluginModule *, LogMessageType, const char *, ...);
-/* Encadre une étape de la vie d'un greffon. */
-bool g_plugin_module_manage(GPluginModule *, PluginAction);
-/* Accompagne la fin du chargement des modules natifs. */
-void g_plugin_module_notify_plugins_loaded(GPluginModule *, PluginAction, void *);
+#if 0
/* Crée une instance à partir d'un type dynamique externe. */
gpointer g_plugin_module_build_type_instance(GPluginModule *, PluginAction, GType);
-#if 0
-
#ifdef INCLUDE_GTK_SUPPORT
/* Complète une liste de resources pour thème. */
diff --git a/src/plugins/self.h b/src/plugins/self.h
index 4d5ddb0..78a3fe6 100644
--- a/src/plugins/self.h
+++ b/src/plugins/self.h
@@ -1,8 +1,8 @@
/* Chrysalide - Outil d'analyse de fichiers binaires
- * self.h - définitions pour inclusion dans les différents greffons
+ * self.h - définitions de fonctionnalités facilitant la mise en place d'extensions natives
*
- * Copyright (C) 2020 Cyrille Bagard
+ * Copyright (C) 2020-2025 Cyrille Bagard
*
* This file is part of Chrysalide.
*
@@ -26,72 +26,55 @@
#define _PLUGINS_SELF_H
-#ifndef _PLUGINS_PLUGIN_H
-# include "plugin.h"
-#endif
+#include <assert.h>
+#include <malloc.h>
+
+
+#include "plugin.h"
#include "../common/compiler.h"
-/* Facilitations de déclarations */
+/* Symboles principaux */
+
+#define PLUGIN_CORE_SELF \
+static GPluginModule *_this_plugin = NULL; \
+static void chrysalide_plugin_set_self(GPluginModule *); \
+static void chrysalide_plugin_set_self(GPluginModule *plugin) \
+{ \
+ assert(_this_plugin == NULL); \
+ _this_plugin = plugin; \
+}; \
+__private GPluginModule *_chrysalide_plugin_get_self(void); \
+__private GPluginModule *_chrysalide_plugin_get_self(void) \
+{ \
+ return _this_plugin; \
+};
+
+#define NATIVE_PLUGIN_ENTRYPOINT(fc) \
+PLUGIN_CORE_SELF; \
+G_MODULE_EXPORT GPluginModule *get_chrysalide_plugin_instance(GModule *); \
+G_MODULE_EXPORT GPluginModule *get_chrysalide_plugin_instance(GModule *module) \
+{ \
+ GPluginModule *result; /* Instance à retourner */ \
+ result = fc(module); \
+ chrysalide_plugin_set_self(result); \
+ return result; \
+}
+
+
+/* Spécifications */
#define CHRYSALIDE_WEBSITE(p) "https://www.chrysalide.re/" p
-#define EMPTY_PG_LIST(name) \
- name = NULL, \
- name ## _count = 0 \
-
-#define BUILD_PG_LIST(name, lst) \
- name = lst, \
- name ## _count = sizeof(lst) / sizeof(lst[0]) \
-
-#define AL(...) BUILD_PG_LIST(.actions, ((plugin_action_t []){ __VA_ARGS__ }))
-
-#define RL(...) BUILD_PG_LIST(.required, ((char *[]){ __VA_ARGS__ }))
-
-#define NO_REQ EMPTY_PG_LIST(.required)
-
-
-/* Composants d'interface */
-
-#define PLUGIN_CORE_SELF \
-static GPluginModule *_this_plugin = NULL; \
-G_MODULE_EXPORT void chrysalide_plugin_set_self(GPluginModule *p); \
-G_MODULE_EXPORT void chrysalide_plugin_set_self(GPluginModule *p) { _this_plugin = p; }; \
-__private GPluginModule *_chrysalide_plugin_get_self(void); \
-__private GPluginModule *_chrysalide_plugin_get_self(void) { return _this_plugin; };
-
-#define PLUGIN_CORE_PROPS(n, d, v, u, c) \
- \
- .magic = CHRYSALIDE_PLUGIN_MAGIC, \
- .abi_version = CURRENT_ABI_VERSION, \
- \
- .gtp_name = "G" n "Plugin", \
- .name = n, \
- .desc = d, \
- .version = v, \
- .url = u, \
- \
- .container = c
-
-#define DEFINE_CHRYSALIDE_PLUGIN(n, d, v, u, r, a) \
-PLUGIN_CORE_SELF \
-G_MODULE_EXPORT const plugin_interface _chrysalide_plugin = { \
- PLUGIN_CORE_PROPS(n, d, v, u, false), \
- r, \
- a, \
-}
+#define NO_REQ NULL, 0
-#define DEFINE_CHRYSALIDE_CONTAINER_PLUGIN(n, d, v, u, r, a) \
-PLUGIN_CORE_SELF \
-G_MODULE_EXPORT const plugin_interface _chrysalide_plugin = { \
- PLUGIN_CORE_PROPS(n, d, v, u, true), \
- r, \
- a, \
-}
+#define BUILD_PG_LIST(lst) lst, sizeof(lst) / sizeof(lst[0])
+
+#define REQ_LIST(...) BUILD_PG_LIST((const char *[]){ __VA_ARGS__ })
-/* Manipulations accélérées */
+/* Journalisation */
__private GPluginModule *_chrysalide_plugin_get_self(void);
diff --git a/src/plugins/tweakable-int.h b/src/plugins/tweakable-int.h
new file mode 100644
index 0000000..776626f
--- /dev/null
+++ b/src/plugins/tweakable-int.h
@@ -0,0 +1,50 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweakable-int.h - définitions internes propres aux participations aux mécanismes de configuration
+ *
+ * Copyright (C) 2025 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_TWEAKABLE_INT_H
+#define _PLUGINS_TWEAKABLE_INT_H
+
+
+#include "tweakable.h"
+
+
+
+/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */
+
+
+/* Fournit une liste de sections de configuration. */
+typedef tweak_info_t * (* get_tweakable_plugin_info) (const GTweakablePlugin *, size_t *);
+
+
+/* Greffon avec des compléments pour l'interface de configuration (interface) */
+struct _GTweakablePluginInterface
+{
+ GTypeInterface base_iface; /* A laisser en premier */
+
+ get_tweakable_plugin_info get_info; /* Récupération de section(s) */
+
+};
+
+
+
+#endif /* _PLUGINS_TWEAKABLE_INT_H */
diff --git a/src/plugins/tweakable.c b/src/plugins/tweakable.c
new file mode 100644
index 0000000..517c4a3
--- /dev/null
+++ b/src/plugins/tweakable.c
@@ -0,0 +1,98 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweakable.c - participation aux mécanismes de configuration
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "tweakable.h"
+
+
+#include "tweakable-int.h"
+
+
+
+/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */
+
+
+/* Procède à l'initialisation de l'interface d'intervention. */
+static void g_tweakable_plugin_default_init(GTweakablePluginInterface *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* INTEGRATION DANS L'EDITION DES PREFERENCES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Détermine le type d'une interface pour l'intervention dans la gestion des greffons. */
+G_DEFINE_INTERFACE(GTweakablePlugin, g_tweakable_plugin, G_TYPE_OBJECT)
+
+
+/******************************************************************************
+* *
+* Paramètres : iface = interface GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation de l'interface d'intervention. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_tweakable_plugin_default_init(GTweakablePluginInterface *iface)
+{
+ iface->get_info = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = interface à manipuler. *
+* count = taille de la liste renvoyée. [OUT] *
+* *
+* Description : Fournit une liste de sections de configuration. *
+* *
+* Retour : Définition(s) de section de configuration ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+tweak_info_t *g_tweakable_plugin_get_tweak_info(const GTweakablePlugin *plugin, size_t *count)
+{
+ tweak_info_t *result; /* Liste à renvoyer */
+ GTweakablePluginInterface *iface; /* Interface utilisée */
+
+ iface = G_TWEAKABLE_PLUGIN_GET_IFACE(plugin);
+
+ if (iface->get_info != NULL)
+ result = iface->get_info(plugin, count);
+
+ else
+ {
+ *count = 0;
+ result = NULL;
+ }
+
+ return result;
+
+}
diff --git a/src/plugins/tweakable.h b/src/plugins/tweakable.h
new file mode 100644
index 0000000..aea70b4
--- /dev/null
+++ b/src/plugins/tweakable.h
@@ -0,0 +1,62 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tweakable.h - prototypes pour la participation aux mécanismes de configuration
+ *
+ * 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 Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_TWEAKABLE_H
+#define _PLUGINS_TWEAKABLE_H
+
+
+#include "../glibext/helpers.h"
+#include "../gtkext/tweak.h"
+
+
+
+/* ------------------- INTEGRATION DANS L'EDITION DES PREFERENCES ------------------- */
+
+
+#define G_TYPE_TWEAKABLE_PLUGIN (g_tweakable_plugin_get_type())
+
+DECLARE_INTERFACE(GTweakablePlugin, g_tweakable_plugin, G, TWEAKABLE_PLUGIN);
+
+
+/* Fournit une liste de sections de configuration. */
+tweak_info_t *g_tweakable_plugin_get_tweak_info(const GTweakablePlugin *, size_t *);
+
+
+
+/* -------------------- SOLLICITATION DES FONCTIONNALITES CREEES -------------------- */
+
+
+#define get_tweakable_plugins_info(c) \
+ ({ \
+ tweak_info_t *__all_info; \
+ __all_info = accumulate_from_all_plugins(G_TYPE_TWEAKABLE_PLUGIN, \
+ G_TWEAKABLE_PLUGIN, \
+ g_tweakable_plugin_get_tweak_info, \
+ tweak_info_t, \
+ c); \
+ __all_info; \
+ })
+
+
+
+#endif /* _PLUGINS_TWEAKABLE_H */
diff --git a/src/schemas/re.chrysalide.framework.gschema.xml b/src/schemas/re.chrysalide.framework.gschema.xml
index e8331ff..87088f7 100644
--- a/src/schemas/re.chrysalide.framework.gschema.xml
+++ b/src/schemas/re.chrysalide.framework.gschema.xml
@@ -1,19 +1,29 @@
<schemalist gettext-domain="chrysalide">
- <schema id="re.chrysalide.framework" path="/re/chrysalide/framework/">
- <child schema="re.chrysalide.framework.gui" name="gui"/>
- </schema>
+ <schema id="re.chrysalide.framework" path="/re/chrysalide/framework/">
+ <child schema="re.chrysalide.framework.gui" name="gui"/>
+ <child schema="re.chrysalide.framework.secstorage" name="secstorage"/>
+ </schema>
- <schema id="re.chrysalide.framework.gui" path="/re/chrysalide/framework/gui/">
- <key name="window-width" type="i">
- <default>600</default>
- </key>
- <key name="window-height" type="i">
- <default>400</default>
- </key>
- <key name="window-maximized" type="b">
- <default>false</default>
- </key>
- </schema>
+ <schema id="re.chrysalide.framework.gui" path="/re/chrysalide/framework/gui/">
+ <key name="window-width" type="i">
+ <default>600</default>
+ </key>
+ <key name="window-height" type="i">
+ <default>400</default>
+ </key>
+ <key name="window-maximized" type="b">
+ <default>false</default>
+ </key>
+ </schema>
+
+ <schema id="re.chrysalide.framework.secstorage" path="/re/chrysalide/framework/secstorage/">
+ <key name="salt" type="ay">
+ <default>[]</default>
+ </key>
+ <key name="master" type="ay">
+ <default>[]</default>
+ </key>
+ </schema>
</schemalist>
diff --git a/tests/arch/immediate.py b/tests/arch/operands/immediate.py
index 74b8069..74b8069 100644
--- a/tests/arch/immediate.py
+++ b/tests/arch/operands/immediate.py
diff --git a/tests/constval.py b/tests/constval.py
deleted file mode 100644
index eafb8d3..0000000
--- a/tests/constval.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/python3-dbg
-# -*- coding: utf-8 -*-
-
-
-from chrysacase import ChrysalideTestCase
-from pychrysalide import PyConstvalObject
-from pychrysalide.arch import ArchInstruction
-import pickle
-
-
-class TestConstVal(ChrysalideTestCase):
- """TestCase for PyConstvalObject."""
-
-
- def testCreation(self):
- """Validate PyConstvalObject creation from Python."""
-
- cst = PyConstvalObject(123, 'XXX')
-
- self.assertEqual(cst, 123)
-
- self.assertEqual(str(cst), 'XXX')
-
-
- def testString(self):
- """Validate the PyConstvalObject implementation."""
-
- self.assertEqual(ArchInstruction.ILT_JUMP, 1)
-
- self.assertEqual(str(ArchInstruction.ILT_JUMP), 'ILT_JUMP')
-
-
- def testStorage(self):
- """Ensure PyConstvalObject instances are storable."""
-
- cst = ArchInstruction.ILT_JUMP
-
- data = pickle.dumps(cst)
-
- cst = pickle.loads(data)
-
- self.assertEqual(cst, ArchInstruction.ILT_JUMP)
-
- self.assertEqual(str(cst), 'ILT_JUMP')
diff --git a/tests/format/program.py b/tests/format/program.py
index 7a649b8..7027cdf 100644
--- a/tests/format/program.py
+++ b/tests/format/program.py
@@ -1,6 +1,7 @@
from chrysacase import ChrysalideTestCase
from pychrysalide import SourceEndian
+from pychrysalide.analysis.contents import MemoryContent
#from pychrysalide.arch import vmpa, mrange
from pychrysalide.format import ProgramFormat
#from pychrysalide.format import BinSymbol
@@ -17,12 +18,16 @@ class TestProgramFormat(ChrysalideTestCase):
def testCustomInstance(self):
"""Validate a full custom ProgramFormat implementation."""
+ data = b'\x00\x00\x00\xef'
+ cnt = MemoryContent(data)
+
+
class CustomFormat(ProgramFormat):
def _get_endianness(self):
return SourceEndian.BIG
- cf = CustomFormat()
+ cf = CustomFormat(cnt)
self.assertEqual(cf.endianness, SourceEndian.BIG)
@@ -30,7 +35,7 @@ class TestProgramFormat(ChrysalideTestCase):
class EmptyCustomFormat(ProgramFormat):
pass
- cf = EmptyCustomFormat()
+ cf = EmptyCustomFormat(cnt)
self.assertEqual(cf.endianness, SourceEndian.LITTLE)
diff --git a/tests/glibext/comparable.py b/tests/glibext/comparable.py
new file mode 100644
index 0000000..48291ca
--- /dev/null
+++ b/tests/glibext/comparable.py
@@ -0,0 +1,132 @@
+
+import gi
+
+from chrysacase import ChrysalideTestCase
+from gi.repository import GObject
+from pychrysalide.glibext import ComparableObject
+
+
+class TestStringBuilder(ChrysalideTestCase):
+ """Test cases for pychrysalide.glibext.ComparableObject."""
+
+
+ def testComparableObjectCreation(self):
+ """Create objects with ComparableObject interface."""
+
+ with self.assertRaisesRegex(NotImplementedError, 'ComparableObject can not be constructed'):
+
+ co = ComparableObject()
+
+
+ class NewComparableObjectImplem(gi._gi.GObject, ComparableObject):
+ pass
+
+ nco = NewComparableObjectImplem()
+
+ self.assertIsNotNone(nco)
+
+
+ class NewComparableObjectImplem2(GObject.Object, ComparableObject):
+ pass
+
+ nco2 = NewComparableObjectImplem()
+
+ self.assertIsNotNone(nco2)
+
+
+ def testComparableObjectMethods(self):
+ """Test the ComparableObject methods."""
+
+ class BasicComparableObjectImplem(GObject.Object, ComparableObject):
+
+ def __init__(self, val):
+ super().__init__()
+ self._val = val
+
+ def _compare(self, other):
+ if self._val < other._val:
+ status = -1
+ elif self._val > other._val:
+ status = 1
+ else:
+ status = 0
+ return status
+
+
+ a = BasicComparableObjectImplem(123)
+ b = BasicComparableObjectImplem(456)
+
+ self.assertTrue(a <= b)
+
+ # Sans l'action de inherit_interface_slots(), c'est pygobject_richcompare() qui est appelée,
+ # laquelle compare simplement les adresses des pointeurs
+
+ c = BasicComparableObjectImplem(789)
+ d = BasicComparableObjectImplem(234)
+
+ self.assertTrue(c > d)
+
+
+ def testComparableObjectExceptions(self):
+ """Raise exceptions from the ComparableObject interface as expected."""
+
+
+ class OtherComparableObject(GObject.Object, ComparableObject):
+ pass
+
+ other = OtherComparableObject()
+
+
+ class BadComparableObjectImplem(GObject.Object, ComparableObject):
+ pass
+
+ obj = BadComparableObjectImplem()
+
+
+ with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_compare'"):
+
+ s = obj < other
+
+
+ class BadComparableObjectImplem2(GObject.Object, ComparableObject):
+
+ def _compare(self, other):
+ return 'AAA'
+
+ obj2 = BadComparableObjectImplem2()
+
+
+ with self.assertRaisesRegex(TypeError, 'comparison status has to be a signed integer'):
+
+ s = obj2 < other
+
+
+ class BadComparableObjectImplem3a(GObject.Object, ComparableObject):
+
+ def _compare(self, other):
+ return 123
+
+ class BadComparableObjectImplem3b(BadComparableObjectImplem3a, ComparableObject):
+
+ def _compare(self, other):
+ return self.parent_compare()
+
+ obj3 = BadComparableObjectImplem3b()
+
+
+ with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'):
+
+ s = obj3 < other
+
+
+ class BadComparableObjectImplem4(GObject.Object, ComparableObject):
+
+ def _compare(self, other):
+ raise Exception('error')
+
+ obj4 = BadComparableObjectImplem4()
+
+
+ with self.assertRaisesRegex(Exception, 'error'):
+
+ s = obj4 < other
diff --git a/tests/glibext/configuration.py b/tests/glibext/configuration.py
deleted file mode 100644
index 880b445..0000000
--- a/tests/glibext/configuration.py
+++ /dev/null
@@ -1,106 +0,0 @@
-
-import gi
-gi.require_version('Gdk', '3.0')
-from gi.repository import Gdk
-
-from chrysacase import ChrysalideTestCase
-from pychrysalide.glibext import ConfigParam, GenConfig
-
-
-class TestConfiguration(ChrysalideTestCase):
- """TestCase for configuration related items.*"""
-
-
- def testCfgParamValues(self):
- """Set and unset configuration parameter values."""
-
- color = Gdk.RGBA()
- color.parse('#3465A4')
-
- param = ConfigParam('config.color', ConfigParam.ConfigParamType.COLOR, color)
-
- self.assertEqual(param.value, color)
-
- param.make_empty()
-
- void = Gdk.RGBA(red=0, green=0, blue=0, alpha=0)
- self.assertEqual(param.value, void)
-
- param.value = color
-
- self.assertEqual(param.value, color)
-
-
- def testCfgParamStates(self):
- """Validate all states of an evolving parameter."""
-
- param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER)
-
- self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT)
-
- param.make_empty()
-
- self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT)
-
- param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER, 0x123)
-
- self.assertEqual(param.value, 0x123)
-
- self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT)
-
- param.make_empty()
-
- self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.CHANGED)
-
- param.value = 0x1
-
- self.assertEqual(param.state, ConfigParam.ConfigParamState.CHANGED)
-
- param.reset()
-
- self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT)
-
-
- def testCfgParamDesc(self):
- """Export types and states as strings when needed."""
-
- param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER)
-
- self.assertTrue('|' in str(param.state))
-
- self.assertTrue('.' in str(param.type))
-
-
- def testConfiguration(self):
- """Feed and browse a basic configuration."""
-
- config = GenConfig()
- self.assertIsNotNone(config)
- self.assertIsNone(config.filename)
-
- for i in range(5):
- param = ConfigParam('config.int.%u' % i, ConfigParam.ConfigParamType.INTEGER, i)
- config.add(param)
-
- chain = ''
-
- for p in config.params:
- chain += '%u' % p.value
-
- self.assertTrue(chain == ''.join([ '%u' % i for i in range(5) ]))
-
- found = config.search('config.int.3')
- self.assertTrue(found.value == 3)
-
- found = config.search('config.int.33')
- self.assertIsNone(found)
-
- for p in config.params:
- p.value *= 10
-
- chain = ''
-
- for p in config.params:
- chain += '%u' % p.value
-
- self.assertTrue(chain == ''.join([ '%u' % (i * 10) for i in range(5) ]))
diff --git a/tests/glibext/hashable.py b/tests/glibext/hashable.py
new file mode 100644
index 0000000..07f22e3
--- /dev/null
+++ b/tests/glibext/hashable.py
@@ -0,0 +1,190 @@
+
+import gi
+
+from chrysacase import ChrysalideTestCase
+from gi.repository import GObject
+from pychrysalide.glibext import HashableObject
+
+
+class TestStringBuilder(ChrysalideTestCase):
+ """Test cases for pychrysalide.glibext.HashableObject."""
+
+
+ def testHashableObjectCreation(self):
+ """Create objects with HashableObject interface."""
+
+ with self.assertRaisesRegex(NotImplementedError, 'HashableObject can not be constructed'):
+
+ ho = HashableObject()
+
+
+ class NewHashableObjectImplem(gi._gi.GObject, HashableObject):
+ pass
+
+ nho = NewHashableObjectImplem()
+
+ self.assertIsNotNone(nho)
+
+
+ class NewHashableObjectImplem2(GObject.Object, HashableObject):
+ pass
+
+ nho2 = NewHashableObjectImplem()
+
+ self.assertIsNotNone(nho2)
+
+
+ def testHashableObjectMethods(self):
+ """Test the HashableObject methods."""
+
+ class BasicHashableObjectImplem(gi._gi.GObject, HashableObject):
+
+ def __init__(self, val):
+ super().__init__()
+ self._val = val
+
+ def _hash(self):
+ return self._val
+
+ value = 1234
+
+ ho = BasicHashableObjectImplem(value)
+
+ self.assertEqual(hash(ho), value)
+
+
+ class BasicHashableObjectImplem2(GObject.Object, HashableObject):
+
+ def __init__(self, val):
+ super().__init__()
+ self._val = val
+
+ def _hash(self):
+ return self._val
+
+ value = 5678
+
+ ho2 = BasicHashableObjectImplem2(value)
+
+ self.assertEqual(hash(ho2), value)
+
+
+ def testCascadingHashableObjectImplementations(self):
+ """Request the hash from the object parent for a full computing."""
+
+
+ class CascadingHashableObjectFullImplemA(GObject.Object, HashableObject):
+
+ def _hash(self):
+ return 1
+
+ class CascadingHashableObjectFullImplemB(CascadingHashableObjectFullImplemA, HashableObject):
+
+ def _hash(self):
+ return super()._hash() * 2
+
+ class CascadingHashableObjectFullImplemC(CascadingHashableObjectFullImplemB, HashableObject):
+
+ def _hash(self):
+ return super()._hash() * 3
+
+
+ obj_a = CascadingHashableObjectFullImplemA()
+
+ obj_b = CascadingHashableObjectFullImplemB()
+
+ obj_c = CascadingHashableObjectFullImplemC()
+
+
+ self.assertEqual(hash(obj_a), 1)
+ self.assertEqual(hash(obj_b), 2)
+ self.assertEqual(hash(obj_c), 6)
+
+
+ class CascadingHashableObjectPartialImplemA(GObject.Object, HashableObject):
+
+ def _hash(self):
+ return 1
+
+ class CascadingHashableObjectPartialImplemB(CascadingHashableObjectPartialImplemA):
+ pass
+
+ class CascadingHashableObjectPartialImplemC(CascadingHashableObjectPartialImplemB, HashableObject):
+
+ def _hash(self):
+ return super()._hash() * 3
+
+
+ obj_a = CascadingHashableObjectPartialImplemA()
+
+ obj_b = CascadingHashableObjectPartialImplemB()
+
+ obj_c = CascadingHashableObjectPartialImplemC()
+
+
+ self.assertEqual(hash(obj_a), 1)
+ self.assertEqual(hash(obj_b), 1)
+ self.assertEqual(hash(obj_c), 3)
+
+
+ def testHashableObjectExceptions(self):
+ """Raise exceptions from the HashableObject interface as expected."""
+
+ class BadHashableObjectImplem(GObject.Object, HashableObject):
+ pass
+
+ obj = BadHashableObjectImplem()
+
+
+ with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_hash'"):
+
+ h = hash(obj)
+
+
+ with self.assertRaisesRegex(TypeError, 'object parent does not implement the HashableObject interface'):
+
+ h = obj.parent_hash()
+
+
+ class BadHashableObjectImplem2(GObject.Object, HashableObject):
+
+ def _hash(self):
+ return 'AAA'
+
+ obj2 = BadHashableObjectImplem2()
+
+
+ with self.assertRaisesRegex(TypeError, 'computed hash value has to be an unsigned integer'):
+
+ h = hash(obj2)
+
+
+ class BadHashableObjectImplem3a(GObject.Object, HashableObject):
+
+ def _hash(self):
+ return 123
+
+ class BadHashableObjectImplem3b(BadHashableObjectImplem3a, HashableObject):
+
+ def _hash(self):
+ return self.parent_hash()
+
+ obj3 = BadHashableObjectImplem3b()
+
+
+ with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'):
+
+ h = hash(obj3)
+
+
+ class BadHashableObjectImplem4(GObject.Object, HashableObject):
+
+ def _hash(self):
+ raise Exception('error')
+
+ obj4 = BadHashableObjectImplem4()
+
+
+ with self.assertRaisesRegex(Exception, 'error'):
+
+ h = hash(obj4)
diff --git a/tests/glibext/objhole.py b/tests/glibext/objhole.py
new file mode 100644
index 0000000..6a7c2e8
--- /dev/null
+++ b/tests/glibext/objhole.py
@@ -0,0 +1,31 @@
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.glibext import ThickObject
+
+
+class TestWorks(ChrysalideTestCase):
+ """TestCase for pychrysalide.glibext.BinaryPortion"""
+
+ def testExtraAccess(self):
+ """Access to various definitions of the extra data for ThickObject."""
+
+ obj = ThickObject()
+
+ self.assertEqual(obj.extra, 0)
+
+ obj.extra = 0xffffffe0
+
+ self.assertEqual(obj.extra, 0xffffffe0)
+
+ obj.extra = 0x00123000
+
+ self.assertEqual(obj.extra, 0x00123000)
+
+
+ def testRservedBits(self):
+ """Check space leaved as available by the GLib."""
+
+ obj = ThickObject()
+
+ self.assertTrue(obj._GOBJECT_RESERVED_EXTRA_BITS > 0)
+ self.assertTrue(obj._GOBJECT_RESERVED_EXTRA_BITS < 32)
diff --git a/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml
new file mode 100644
index 0000000..6afa96b
--- /dev/null
+++ b/tests/glibext/re.chrysalide.tests.secstorage.gschema.xml
@@ -0,0 +1,15 @@
+<schemalist>
+
+ <schema id="re.chrysalide.tests.secstorage" path="/re/chrysalide/tests/secstorage/">
+
+ <key name="salt" type="ay">
+ <default>[]</default>
+ </key>
+
+ <key name="master" type="ay">
+ <default>[]</default>
+ </key>
+
+ </schema>
+
+</schemalist>
diff --git a/tests/glibext/secstorage.py b/tests/glibext/secstorage.py
new file mode 100644
index 0000000..5b8f680
--- /dev/null
+++ b/tests/glibext/secstorage.py
@@ -0,0 +1,153 @@
+
+import gi
+import os
+import subprocess
+
+from chrysacase import ChrysalideTestCase
+from gi.repository import Gio, GLib
+from pychrysalide.glibext import SecretStorage
+
+
+class TestSecretStorage(ChrysalideTestCase):
+ """TestCase for secret storage features."""
+
+ @classmethod
+ def setUpClass(cls):
+
+ super(TestSecretStorage, cls).setUpClass()
+
+ cls.log('Creating GSettings schema...')
+
+ path = os.path.dirname(os.path.realpath(__file__))
+
+ subprocess.run([ 'glib-compile-schemas', path ])
+
+
+ @classmethod
+ def tearDownClass(cls):
+
+ super(TestSecretStorage, cls).tearDownClass()
+
+ cls.log('Removing compiled GSettings schema...')
+
+ path = os.path.dirname(os.path.realpath(__file__))
+
+ filename = os.path.join(path, 'gschemas.compiled')
+
+ if os.path.exists(filename):
+ os.remove(filename)
+
+
+ def _get_settings(self, sid):
+ """Provide local GSettings instance."""
+
+ path = os.path.dirname(os.path.realpath(__file__))
+
+ source = Gio.SettingsSchemaSource.new_from_directory(path, None, True)
+
+ schema = Gio.SettingsSchemaSource.lookup(source, sid, False)
+
+ settings = Gio.Settings.new_full(schema, None, None)
+
+ return settings
+
+
+ def testMasterKeyDefinition(self):
+ """Check for cryptographic parameters for secret storage."""
+
+ settings = self._get_settings('re.chrysalide.tests.secstorage')
+
+ storage = SecretStorage(settings)
+
+ settings.reset('master')
+
+ self.assertEqual(len(settings.get_value('master').unpack()), 0)
+
+ self.assertFalse(storage.has_key)
+
+ settings.set_value('master', GLib.Variant('ay', b'ABC'))
+
+ self.assertFalse(storage.has_key)
+
+ settings.set_value('master', GLib.Variant('ay', b'A' * 23))
+
+ self.assertTrue(storage.has_key)
+
+
+ def testMasterKeyCreation(self):
+ """Create and update cryptographic parameters for secret storage."""
+
+ settings = self._get_settings('re.chrysalide.tests.secstorage')
+
+ storage = SecretStorage(settings)
+
+ settings.reset('salt')
+ settings.reset('master')
+
+ self.assertFalse(storage.has_key)
+
+ status = storage.set_password('')
+
+ self.assertTrue(status);
+
+ self.assertTrue(storage.has_key)
+ self.assertTrue(storage.is_locked)
+
+ status = storage.unlock('')
+
+ self.assertTrue(status)
+
+ self.assertFalse(storage.is_locked)
+
+ storage.lock()
+
+ self.assertTrue(storage.is_locked)
+
+ status = storage.unlock('XXX')
+
+ self.assertFalse(status)
+
+ self.assertTrue(storage.is_locked)
+
+
+ def testDataEncryption(self):
+ """Encrypt and decrypt data with the secret storage."""
+
+ settings = self._get_settings('re.chrysalide.tests.secstorage')
+
+ storage = SecretStorage(settings)
+
+ settings.reset('salt')
+ settings.reset('master')
+
+ status = storage.set_password('<s3cUre>')
+
+ self.assertTrue(status);
+
+ status = storage.unlock('<s3cUre>')
+
+ self.assertTrue(status)
+
+
+ original = b'ABC'
+
+ encrypted = storage.encrypt_data(original)
+
+ self.assertIsNotNone(encrypted)
+
+ plain = storage.decrypt_data(encrypted)
+
+ self.assertIsNotNone(plain)
+ self.assertEqual(original, plain)
+
+
+ original = b'A' * 136
+
+ encrypted = storage.encrypt_data(original)
+
+ self.assertIsNotNone(encrypted)
+
+ plain = storage.decrypt_data(encrypted)
+
+ self.assertIsNotNone(plain)
+ self.assertEqual(original, plain)
diff --git a/tests/glibext/singleton.py b/tests/glibext/singleton.py
index 4588ae5..712aece 100644
--- a/tests/glibext/singleton.py
+++ b/tests/glibext/singleton.py
@@ -1,21 +1,24 @@
+import gi
+
from chrysacase import ChrysalideTestCase
from gi.repository import GObject
-from pychrysalide.glibext import SingletonCandidate, SingletonFactory
+from pychrysalide.glibext import ComparableObject, HashableObject, SingletonCandidate, SingletonFactory
class TestSingleton(ChrysalideTestCase):
"""Test cases for pychrysalide.glibext.SingletonFactory."""
- def testSingletonCreation(self):
- """Create singleton objects."""
+ def testSingletonCandidateCreation(self):
+ """Create objects with SingletonCandidate interface."""
with self.assertRaisesRegex(NotImplementedError, 'SingletonCandidate can not be constructed'):
sc = SingletonCandidate()
- class NewSingletonImplem(GObject.Object, SingletonCandidate):
+
+ class NewSingletonImplem(gi._gi.GObject, HashableObject, ComparableObject, SingletonCandidate):
pass
nsi = NewSingletonImplem()
@@ -23,121 +26,132 @@ class TestSingleton(ChrysalideTestCase):
self.assertIsNotNone(nsi)
- def testFactoryCreation(self):
- """Create singleton factories."""
+ class NewSingletonImplem2(GObject.Object, HashableObject, ComparableObject, SingletonCandidate):
+ pass
+
+ nsi2 = NewSingletonImplem2()
- sf = SingletonFactory()
+ self.assertIsNotNone(nsi2)
- self.assertIsNotNone(sf)
- class MyFactory(SingletonFactory):
- pass
+ # def testFactoryCreation(self):
+ # """Create singleton factories."""
+
+ # sf = SingletonFactory()
+
+ # self.assertIsNotNone(sf)
+
+ # class MyFactory(SingletonFactory):
+ # pass
+
+ # msf = MyFactory()
+
+ # self.assertIsNotNone(msf)
+
- msf = MyFactory()
- self.assertIsNotNone(msf)
- def testSingletonMethods(self):
- """Test the singleton methods."""
+ # def testSingletonMethods(self):
+ # """Test the singleton methods."""
- class IntegerCacheImplem(GObject.Object, SingletonCandidate):
+ # class IntegerCacheImplem(GObject.Object, SingletonCandidate):
- def __init__(self, val):
- super().__init__()
- self._val = val
+ # def __init__(self, val):
+ # super().__init__()
+ # self._val = val
- def _list_inner_instances(self):
- return ()
+ # def _list_inner_instances(self):
+ # return ()
- def __hash__(self):
- return hash('common-key')
+ # def __hash__(self):
+ # return hash('common-key')
- def __eq__(self, other):
- return self._val == other._val
+ # def __eq__(self, other):
+ # return self._val == other._val
- val_0 = IntegerCacheImplem(0)
- val_0_bis = IntegerCacheImplem(0)
- val_1 = IntegerCacheImplem(1)
+ # val_0 = IntegerCacheImplem(0)
+ # val_0_bis = IntegerCacheImplem(0)
+ # val_1 = IntegerCacheImplem(1)
- self.assertEqual(hash(val_0), hash(val_0_bis))
- self.assertEqual(hash(val_0), hash(val_1))
+ # self.assertEqual(hash(val_0), hash(val_0_bis))
+ # self.assertEqual(hash(val_0), hash(val_1))
- self.assertEqual(val_0.hash(), val_0_bis.hash())
- self.assertEqual(val_0.hash(), val_1.hash())
+ # self.assertEqual(val_0.hash(), val_0_bis.hash())
+ # self.assertEqual(val_0.hash(), val_1.hash())
- self.assertTrue(val_0 == val_0_bis)
- self.assertFalse(val_0 == val_1)
+ # self.assertTrue(val_0 == val_0_bis)
+ # self.assertFalse(val_0 == val_1)
- def testSingletonFootprint(self):
- """Check for singleton memory footprint."""
+ # def testSingletonFootprint(self):
+ # """Check for singleton memory footprint."""
- sf = SingletonFactory()
+ # sf = SingletonFactory()
- class IntegerCacheImplem(GObject.Object, SingletonCandidate):
+ # class IntegerCacheImplem(GObject.Object, SingletonCandidate):
- def __init__(self, val):
- super().__init__()
- self._val = val
+ # def __init__(self, val):
+ # super().__init__()
+ # self._val = val
- def _list_inner_instances(self):
- return ()
+ # def _list_inner_instances(self):
+ # return ()
- def __hash__(self):
- return hash('common-key')
+ # def __hash__(self):
+ # return hash('common-key')
- def __eq__(self, other):
- return self._val == other._val
+ # def __eq__(self, other):
+ # return self._val == other._val
- def _set_read_only(self):
- pass
+ # def _set_read_only(self):
+ # pass
- val_0 = IntegerCacheImplem(0)
- val_0_bis = IntegerCacheImplem(0)
- val_1 = IntegerCacheImplem(1)
+ # val_0 = IntegerCacheImplem(0)
+ # val_0_bis = IntegerCacheImplem(0)
+ # val_1 = IntegerCacheImplem(1)
- obj = sf.get_instance(val_0)
+ # obj = sf.get_instance(val_0)
- self.assertTrue(obj is val_0)
+ # self.assertTrue(obj is val_0)
- obj = sf.get_instance(val_0_bis)
+ # obj = sf.get_instance(val_0_bis)
- self.assertTrue(obj is val_0)
+ # self.assertTrue(obj is val_0)
- obj = sf.get_instance(val_1)
+ # obj = sf.get_instance(val_1)
- self.assertTrue(obj is val_1)
+ # self.assertTrue(obj is val_1)
- self.assertEqual(len(obj.inner_instances), 0)
+ # self.assertEqual(len(obj.inner_instances), 0)
- class MasterCacheImplem(GObject.Object, SingletonCandidate):
+ # class MasterCacheImplem(GObject.Object, SingletonCandidate):
- def __init__(self, children):
- super().__init__()
- self._children = children
+ # def __init__(self, children):
+ # super().__init__()
+ # self._children = children
- def _list_inner_instances(self):
- return self._children
+ # def _list_inner_instances(self):
+ # return self._children
- def _update_inner_instances(self, instances):
- self._children = instances
+ # def _update_inner_instances(self, instances):
+ # self._children = instances
- def __hash__(self):
- return hash('master-key')
+ # def __hash__(self):
+ # return hash('master-key')
- def __eq__(self, other):
- return False
+ # def __eq__(self, other):
+ # return False
- def _set_read_only(self):
- pass
+ # def _set_read_only(self):
+ # pass
- master = MasterCacheImplem(( val_0_bis, val_1 ))
+ # master = MasterCacheImplem(( val_0_bis, val_1 ))
- obj = sf.get_instance(master)
+ # obj = sf.get_instance(master)
- self.assertTrue(obj.inner_instances[0] is val_0)
+ # self.assertTrue(obj.inner_instances[0] is val_0)
- self.assertTrue(obj.inner_instances[1] is val_1)
+ # self.assertTrue(obj.inner_instances[1] is val_1)
diff --git a/tests/glibext/strbuilder.py b/tests/glibext/strbuilder.py
new file mode 100644
index 0000000..72fd5c2
--- /dev/null
+++ b/tests/glibext/strbuilder.py
@@ -0,0 +1,114 @@
+
+import gi
+
+from chrysacase import ChrysalideTestCase
+from gi.repository import GObject
+from pychrysalide.glibext import StringBuilder
+
+
+class TestStringBuilder(ChrysalideTestCase):
+ """Test cases for pychrysalide.glibext.StringBuilder."""
+
+
+ def ZZZtestStringBuilderCreation(self):
+ """Create objects with StringBuilder interface."""
+
+ with self.assertRaisesRegex(NotImplementedError, 'StringBuilder can not be constructed'):
+
+ sb = StringBuilder()
+
+
+ class NewStringBuilderImplem(gi._gi.GObject, StringBuilder):
+ pass
+
+ nsb = NewStringBuilderImplem()
+
+ self.assertIsNotNone(nsb)
+
+
+ class NewStringBuilderImplem2(GObject.Object, StringBuilder):
+ pass
+
+ nsb2 = NewStringBuilderImplem()
+
+ self.assertIsNotNone(nsb2)
+
+
+ def ZZZtestStringBuilderMethods(self):
+ """Test the StringBuilder methods."""
+
+ class BasicStringBuilderImplem(GObject.Object, StringBuilder):
+
+ def __init__(self, desc):
+ super().__init__()
+ self._desc = desc
+
+ def _to_string(self, flags=0):
+ return self._desc
+
+ desc = 'simple_desc'
+
+ sb = BasicStringBuilderImplem(desc)
+
+ self.assertEqual(sb.to_string(), desc)
+ self.assertEqual(str(sb), desc)
+ self.assertEqual(f'{sb}', desc)
+
+
+ def testStringBuilderExceptions(self):
+ """Raise exceptions from the StringBuilder interface as expected."""
+
+
+ class BadStringBuilderImplem(GObject.Object, StringBuilder):
+ pass
+
+ obj = BadStringBuilderImplem()
+
+
+ with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_to_string'"):
+
+ s = str(obj)
+
+
+ class BadStringBuilderImplem2(GObject.Object, StringBuilder):
+
+ def _to_string(self, flags=0):
+ return 0xcafe
+
+ obj2 = BadStringBuilderImplem2()
+
+
+ with self.assertRaisesRegex(TypeError, 'object description has to get provided as an UTF-8 string value'):
+
+ s = str(obj2)
+
+
+ class BadStringBuilderImplem3a(GObject.Object, StringBuilder):
+
+ def _to_string(self, flags=0):
+ return 'desc'
+
+ class BadStringBuilderImplem3b(BadStringBuilderImplem3a, StringBuilder):
+
+ def _to_string(self, flags=0):
+ return self.parent_to_string()
+
+ obj3 = BadStringBuilderImplem3b()
+
+
+ with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'):
+
+ s = str(obj3)
+
+
+ class BadStringBuilderImplem4(GObject.Object, StringBuilder):
+
+ def _to_string(self, flags=0):
+ raise Exception('error')
+
+ obj4 = BadStringBuilderImplem4()
+
+
+ with self.assertRaisesRegex(Exception, 'error'):
+
+ s = str(obj4)
diff --git a/tests/plugins/plugin.py b/tests/plugins/plugin.py
index 6409975..9015409 100644
--- a/tests/plugins/plugin.py
+++ b/tests/plugins/plugin.py
@@ -1,223 +1,96 @@
-#!/usr/bin/python3-dbg
-# -*- coding: utf-8 -*-
-
from chrysacase import ChrysalideTestCase
-from pychrysalide import PluginModule
-import gc
+from pychrysalide.plugins import PluginModule
class TestPlugin(ChrysalideTestCase):
"""TestCase for GPluginModule."""
- def testGarbageCollecting(self):
- """Ensure the garbarge collector is working for plugin modules."""
-
+ def testPluginInfoImplementations(self):
+ """Retrieve plugin basic information provided by __init__()."""
- class MyPG_1(PluginModule):
+ class MyPlugin(PluginModule):
def __init__(self):
+ super().__init__('custom-name', desc='custom-desc', url='custom-url', version='0.0.1')
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
+ my = MyPlugin()
- super(MyPG_1, self).__init__(**interface)
+ self.assertEqual(my.name, 'custom-name')
+ self.assertEqual(my.desc, 'custom-desc')
+ self.assertEqual(my.version, '0.0.1')
+ self.assertEqual(my.url, 'custom-url')
- pg = MyPG_1()
- self.assertIsNotNone(pg)
+ def testPluginMethodImplementations(self):
+ """Ensure required plugins abstract methods can be implemented."""
-
- class MyPG_2(PluginModule):
+ class MyWrongPlugin(PluginModule):
def __init__(self):
+ super().__init__('pg-name')
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
-
- super(MyPG_2, self).__init__(**interface)
+ my = MyWrongPlugin()
+ with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_filename'"):
+ print(my.filename)
- pg = MyPG_2()
- self.assertIsNotNone(pg)
+ with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_get_modname'"):
+ print(my.modname)
- class MyPG_3(PluginModule):
+ class MyPlugin(PluginModule):
def __init__(self):
+ super().__init__('pg-name')
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
+ def _get_filename(self):
+ return 'file-name'
- super(MyPG_3, self).__init__(**interface)
+ def _get_modname(self):
+ return 'mod-name'
+ my = MyPlugin()
- pg = MyPG_3()
- self.assertIsNotNone(pg)
+ self.assertEqual(my.filename, 'file-name')
+ self.assertEqual(my.modname, 'mod-name')
- class MyPG_4(PluginModule):
-
- def __init__(self):
-
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
- super(MyPG_4, self).__init__(**interface)
+ def testPluginRequirementIncludePython(self):
+ """Ensure that plugin requirements include the Python bindings."""
-
- pg = MyPG_4()
- self.assertIsNotNone(pg)
-
-
- class MyPG_5(PluginModule):
+ class MyPlugin(PluginModule):
def __init__(self):
+ super().__init__('pg-name')
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
-
- super(MyPG_5, self).__init__(**interface)
-
-
- pg = MyPG_5()
- self.assertIsNotNone(pg)
+ my = MyPlugin()
+ self.assertEqual(len(my.requirements), 1)
+ self.assertEqual(my.requirements[0], 'PyChrysalide')
- gc.collect()
+ def testPluginRequirementUniqueness(self):
+ """Ensure that plugin requirements are unique."""
- def testCreation(self):
- """Validate PluginModule creation from Python."""
-
-
- class MyPG_0(PluginModule):
- pass
-
-
- # TypeError: Required argument 'name' (pos 1) not found
- with self.assertRaises(TypeError):
- pg = MyPG_0()
-
-
- class MyPG_1(PluginModule):
+ class MyPlugin(PluginModule):
def __init__(self):
+ super().__init__('pg-name', required=[ 'PyChrysalide' ])
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
-
- super(MyPG_1, self).__init__(**interface)
+ my = MyPlugin()
+ self.assertEqual(my.requirements.count('PyChrysalide'), 1)
- pg = MyPG_1()
- self.assertIsNotNone(pg)
-
- class MyPG_2(PluginModule):
+ class MyPlugin2(PluginModule):
def __init__(self):
+ super().__init__('pg-name2', required=[ 'AA', 'BB', 'AA' ])
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( 'ABC', )
- }
-
- super(MyPG_2, self).__init__(**interface)
-
-
- # TypeError: Invalid type for plugin action.
- with self.assertRaises(TypeError):
- pg = MyPG_2()
-
-
- class MyPG_3(PluginModule):
-
- def __init__(self):
-
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, )
- }
-
- super(MyPG_3, self).__init__(**interface)
-
-
- # TypeError: missing features for the declared actions.
- with self.assertRaises(TypeError):
- pg = MyPG_3()
-
-
- class MyPG_4(PluginModule):
-
- def __init__(self):
-
- interface = {
- 'name' : 'some_name4',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( PluginModule.PGA_CONTENT_EXPLORER, )
- }
-
- super(MyPG_4, self).__init__(**interface)
-
- def handle_binary_content(self, action, content, wid, status):
- pass
-
-
- pg = MyPG_4()
- self.assertIsNotNone(pg)
-
-
- def testDoubleUsage(self):
- """Validate PluginModule double usage in Python."""
-
-
- class MyPG(PluginModule):
-
- def __init__(self):
-
- interface = {
- 'name' : 'some_name',
- 'desc' : 'Provide some information about the useless plugin',
- 'version' : '0.1',
- 'actions' : ( )
- }
-
- super(MyPG, self).__init__(**interface)
-
-
- pg1 = MyPG()
- self.assertIsNotNone(pg1)
+ my = MyPlugin2()
- pg2 = MyPG()
- self.assertIsNotNone(pg2)
+ self.assertEqual(my.requirements.count('AA'), 1)
+ self.assertEqual(my.requirements.count('PyChrysalide'), 1)
diff --git a/tests/plugins/python.py b/tests/plugins/python.py
new file mode 100644
index 0000000..1a3dd97
--- /dev/null
+++ b/tests/plugins/python.py
@@ -0,0 +1,27 @@
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.plugins import PythonPlugin
+
+
+class TestPlugin(ChrysalideTestCase):
+ """TestCase for GPythonPlugin."""
+
+
+ def testPluginInfoImplementations(self):
+ """Retrieve plugin basic information provided by __init__()."""
+
+ class MyPlugin(PythonPlugin):
+ """Custom description."""
+
+ def __init__(self):
+ super().__init__(__file__, '0.0.1', 'custom-url')
+
+ my = MyPlugin()
+
+ self.assertEqual(my.name, 'MyPlugin')
+ self.assertEqual(my.desc, 'Custom description.')
+ self.assertEqual(my.version, '0.0.1')
+ self.assertEqual(my.url, 'custom-url')
+
+ self.assertEqual(my.filename, __file__)
+ self.assertEqual(my.modname, 'python')
diff --git a/tools/maint/exportall.inc b/tools/maint/exportall.inc
new file mode 100644
index 0000000..f03be3c
--- /dev/null
+++ b/tools/maint/exportall.inc
@@ -0,0 +1,8 @@
+
+# source tools/maint/exportall.inc
+
+# unset PYTHONPATH GSETTINGS_SCHEMA_DIR LD_LIBRARY_PATH
+
+source tools/maint/pypath.inc
+source tools/maint/gsettings.inc
+source tools/maint/libpath.inc
diff --git a/tools/maint/libpath.inc b/tools/maint/libpath.inc
new file mode 100644
index 0000000..19e68d7
--- /dev/null
+++ b/tools/maint/libpath.inc
@@ -0,0 +1,10 @@
+
+# Look for all path containing binaries for local execution
+
+# source tools/maint/libpath.inc
+
+LD_LIBRARY_PATH="$PWD/src/.libs:$LD_LIBRARY_PATH"
+
+find . -type d -name schemas -exec glib-compile-schemas {} \;
+
+export LD_LIBRARY_PATH=$( find plugins/ -maxdepth 2 -type d -name '.libs' -printf "$PWD/plugins/%P:" | sed 's/:$//' ):"$LD_LIBRARY_PATH"