From 0aea964ab880a972e8a4d54b36f7eee340f49d5b Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Fri, 8 Oct 2021 08:52:02 +0200
Subject: Extract filenames when creating content attributes.

---
 plugins/pychrysalide/analysis/cattribs.c | 79 +++++++++++---------------------
 src/analysis/cattribs.c                  | 45 ++++++------------
 src/analysis/cattribs.h                  |  5 +-
 src/analysis/contents/memory.c           |  2 +-
 src/main.c                               | 14 ++++--
 tests/analysis/cattribs.py               | 42 ++++++++---------
 6 files changed, 72 insertions(+), 115 deletions(-)

diff --git a/plugins/pychrysalide/analysis/cattribs.c b/plugins/pychrysalide/analysis/cattribs.c
index be5c5b1..895fed8 100644
--- a/plugins/pychrysalide/analysis/cattribs.c
+++ b/plugins/pychrysalide/analysis/cattribs.c
@@ -45,9 +45,6 @@ static PyObject *py_content_attributes_new(PyTypeObject *, PyObject *, PyObject
 /* Fournit l'ensemble des clefs d'un ensemble d'attributs. */
 static PyObject *py_content_attributes_subscript(PyObject *, PyObject *);
 
-/* Fournit le fichier de base compris dans le chemin initial. */
-static PyObject *py_content_attributes_get_filename(PyObject *, void *);
-
 /* Fournit l'ensemble des clefs d'un ensemble d'attributs. */
 static PyObject *py_content_attributes_get_keys(PyObject *, void *);
 
@@ -72,7 +69,10 @@ static PyObject *py_content_attributes_new(PyTypeObject *type, PyObject *args, P
     PyObject *result;                       /* Instance à retourner        */
     const char *path;                       /* Chemin d'accès à traiter    */
     int ret;                                /* Bilan de lecture des args.  */
+    char *filename;                         /* Nom de fichier embarqué     */
     GContentAttributes *attribs;            /* Création GLib à transmettre */
+    PyObject *obj;                          /* Objet Python à retourner    */
+    PyObject *str;                          /* Chaîne à retourner          */
 
 #define CONTENT_ATTRIBUTES_DOC                                                  \
     "ContentAttributes is a set of values used at binary content loading.\n"    \
@@ -87,25 +87,43 @@ static PyObject *py_content_attributes_new(PyTypeObject *type, PyObject *args, P
     "\n"                                                                        \
     "    ContentAttributes(path)\n"                                             \
     "\n"                                                                        \
-    "Where path is a list of parameters: '[...]&key0=value0&key1=value1...'"
+    "Where path is a list of parameters: '[...]&key0=value0&key1=value1...'"    \
+    "\n"                                                                        \
+    "The constructor returns a tuple containing a ContentAttributes instance"   \
+    " and the original targot filename."
 
     ret = PyArg_ParseTuple(args, "s", &path);
     if (!ret) return NULL;
 
-    attribs = g_content_attributes_new(path);
+    attribs = g_content_attributes_new(path, &filename);
 
     if (attribs != NULL)
     {
         g_object_ref_sink(G_OBJECT(attribs));
-        result = pygobject_new(G_OBJECT(attribs));
+        obj = pygobject_new(G_OBJECT(attribs));
         g_object_unref(attribs);
     }
     else
     {
-        result = Py_None;
-        Py_INCREF(result);
+        obj = Py_None;
+        Py_INCREF(obj);
     }
 
+    if (filename != NULL)
+    {
+        str = PyUnicode_FromString(filename);
+        free(filename);
+    }
+    else
+    {
+        str = Py_None;
+        Py_INCREF(str);
+    }
+
+    result = PyTuple_New(2);
+    PyTuple_SetItem(result, 0, obj);
+    PyTuple_SetItem(result, 1, str);
+
     return result;
 
 }
@@ -200,50 +218,6 @@ static PyObject *py_content_attributes_get_keys(PyObject *self, void *closure)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : self    = objet Python concerné par l'appel.                 *
-*                closure = non utilisé ici.                                   *
-*                                                                             *
-*  Description : Fournit le fichier de base compris dans le chemin initial.   *
-*                                                                             *
-*  Retour      : Nom de fichier renvoyant vers un contenu à charger ou None.  *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static PyObject *py_content_attributes_get_filename(PyObject *self, void *closure)
-{
-    PyObject *result;                       /* Valeur à retourner          */
-    GContentAttributes *cattribs;           /* Version native              */
-    const char *filename;                   /* Nom de fichier natif        */
-
-#define CONTENT_ATTRIBUTES_FILENAME_ATTRIB PYTHON_GET_DEF_FULL              \
-(                                                                           \
-    filename, py_content_attributes,                                        \
-    "Filename extracted from the path provided to the attribute set,"       \
-    " constructor, or None if no filename was defined."                     \
-)
-
-    cattribs = G_CONTENT_ATTRIBUTES(pygobject_get(self));
-
-    filename = g_content_attributes_get_filename(cattribs);
-
-    if (filename != NULL)
-        result = PyUnicode_FromString(filename);
-
-    else
-    {
-        result = Py_None;
-        Py_INCREF(result);
-    }
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Fournit un accès à une définition de type à diffuser.        *
@@ -267,7 +241,6 @@ PyTypeObject *get_python_content_attributes_type(void)
     };
 
     static PyGetSetDef py_content_attributes_getseters[] = {
-        CONTENT_ATTRIBUTES_FILENAME_ATTRIB,
         CONTENT_ATTRIBUTES_KEYS_ATTRIB,
         { NULL }
     };
diff --git a/src/analysis/cattribs.c b/src/analysis/cattribs.c
index 704e665..1299991 100644
--- a/src/analysis/cattribs.c
+++ b/src/analysis/cattribs.c
@@ -37,8 +37,6 @@ struct _GContentAttributes
 {
     GObject parent;                         /* A laisser en premier        */
 
-    char *filename;                         /* Fichier de base du chemin   */
-
     GGenConfig **configs;                   /* Paramètres par niveaux      */
     size_t count;                           /* Quantité de ces niveaux     */
 
@@ -108,8 +106,6 @@ static void g_content_attributes_class_init(GContentAttributesClass *klass)
 
 static void g_content_attributes_init(GContentAttributes *attribs)
 {
-    attribs->filename = NULL;
-
     attribs->configs = malloc(sizeof(GGenConfig *));
     attribs->count = 1;
 
@@ -165,7 +161,8 @@ static void g_content_attributes_finalize(GContentAttributes *attribs)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : path = chemin d'accès à un contenu à charger.                *
+*  Paramètres  : path     = chemin d'accès à un contenu à charger.            *
+*                filename = nom de fichier embarqué.                          *
 *                                                                             *
 *  Description : Construit un ensemble d'attribut pour contenu binaire.       *
 *                                                                             *
@@ -175,7 +172,7 @@ static void g_content_attributes_finalize(GContentAttributes *attribs)
 *                                                                             *
 ******************************************************************************/
 
-GContentAttributes *g_content_attributes_new(const char *path)
+GContentAttributes *g_content_attributes_new(const char *path, char **filename)
 {
     GContentAttributes *result;             /* Adresse à retourner         */
     GGenConfig *config;                     /* Niveau de config. courant   */
@@ -184,17 +181,26 @@ GContentAttributes *g_content_attributes_new(const char *path)
     char *part;                             /* Clef et sa valeur           */
     char *eq;                               /* Signe '=' rencontré         */
 
+    if (filename != NULL)
+        *filename = NULL;
+
     result = g_object_new(G_TYPE_CONTENT_ATTRIBUTES, NULL);
 
     iter = strchr(path, '&');
 
     if (iter == NULL)
-        result->filename = strdup(path);
+    {
+        if (strlen(path) && filename != NULL)
+            *filename = strdup(path);
+    }
 
     else
     {
         if (iter != path)
-            result->filename = strndup(path, iter - path);
+        {
+            if (filename != NULL)
+                *filename = strndup(path, iter - path);
+        }
 
         config = result->configs[0];
 
@@ -253,29 +259,6 @@ GContentAttributes *g_content_attributes_new(const char *path)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : attribs = ensemble d'attributs de contenu à consulter.       *
-*                                                                             *
-*  Description : Fournit le fichier de base compris dans le chemin initial.   *
-*                                                                             *
-*  Retour      : Nom de fichier renvoyant vers un contenu à charger ou NULL.  *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-const char *g_content_attributes_get_filename(const GContentAttributes *attribs)
-{
-    char *result;                           /* Nom de fichier à retourner  */
-
-    result = attribs->filename;
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : attribs = ensemble d'attributs de contenu à consulter.       *
 *                count   = taille de la liste de clefs renvoyées. [OUT]       *
 *                                                                             *
 *  Description : Fournit l'ensemble des clefs d'un ensemble d'attributs.      *
diff --git a/src/analysis/cattribs.h b/src/analysis/cattribs.h
index c2e8a2e..2745fde 100644
--- a/src/analysis/cattribs.h
+++ b/src/analysis/cattribs.h
@@ -47,10 +47,7 @@ typedef struct _GContentAttributesClass GContentAttributesClass;
 GType g_content_attributes_get_type(void);
 
 /* Construit un ensemble d'attribut pour contenu binaire. */
-GContentAttributes *g_content_attributes_new(const char *);
-
-/* Fournit le fichier de base compris dans le chemin initial. */
-const char *g_content_attributes_get_filename(const GContentAttributes *);
+GContentAttributes *g_content_attributes_new(const char *, char **);
 
 /* Fournit l'ensemble des clefs d'un ensemble d'attributs. */
 const char **g_content_attributes_get_keys(const GContentAttributes *, size_t *);
diff --git a/src/analysis/contents/memory.c b/src/analysis/contents/memory.c
index 6a020e8..50647f6 100644
--- a/src/analysis/contents/memory.c
+++ b/src/analysis/contents/memory.c
@@ -170,7 +170,7 @@ static void g_memory_content_init(GMemoryContent *content)
 
     content->attribs = NULL;
 
-    empty = g_content_attributes_new("");
+    empty = g_content_attributes_new("", NULL);
 
     g_binary_content_set_attributes(G_BIN_CONTENT(content), empty);
 
diff --git a/src/main.c b/src/main.c
index 7d49ba6..434c861 100644
--- a/src/main.c
+++ b/src/main.c
@@ -511,7 +511,7 @@ static int open_binaries(char **files, int count)
     GStudyProject *project;                 /* Projet courant à compléter  */
     int i;                                  /* Boucle de parcours          */
     GContentAttributes *attribs;            /* Attributs à lier au contenu */
-    const char *filename;                   /* Chemin d'accès au contenu   */
+    char *filename;                         /* Chemin d'accès au contenu   */
     GBinContent *content;                   /* Contenu binaire à charger   */
 
     result = EXIT_SUCCESS;
@@ -520,11 +520,15 @@ static int open_binaries(char **files, int count)
 
     for (i = 0; i < count && result == EXIT_SUCCESS; i++)
     {
-        attribs = g_content_attributes_new(files[i]);
+        attribs = g_content_attributes_new(files[i], &filename);
 
-        filename = g_content_attributes_get_filename(attribs);
-
-        content = g_file_content_new(filename);
+        if (filename == NULL)
+            content = NULL;
+        else
+        {
+            content = g_file_content_new(filename);
+            free(filename);
+        }
 
         if (content != NULL)
         {
diff --git a/tests/analysis/cattribs.py b/tests/analysis/cattribs.py
index 1a7f7da..e388afc 100644
--- a/tests/analysis/cattribs.py
+++ b/tests/analysis/cattribs.py
@@ -15,10 +15,10 @@ class TestProjectFeatures(ChrysalideTestCase):
     def testEmptyContentAttributeSet(self):
         """Check properties of empty content attribute set."""
 
-        attribs = ContentAttributes('')
-        self.assertIsNotNone(attribs)
+        attribs, filename = ContentAttributes('')
 
-        self.assertIsNone(attribs.filename)
+        self.assertIsNotNone(attribs)
+        self.assertIsNone(filename)
 
         self.assertEqual(len(attribs.keys), 0)
 
@@ -33,16 +33,16 @@ class TestProjectFeatures(ChrysalideTestCase):
             'dddd': '3',
         }
 
-        filename = 'filename'
-        path = filename
+        orig_filename = 'filename'
+        path = orig_filename
 
         for k in model.keys():
             path += '&%s=%s' % (k, model[k])
 
-        attribs = ContentAttributes(path)
-        self.assertIsNotNone(attribs)
+        attribs, filename = ContentAttributes(path)
 
-        self.assertEqual(attribs.filename, filename)
+        self.assertIsNotNone(attribs)
+        self.assertEqual(orig_filename, filename)
 
         kcount = 0
 
@@ -73,10 +73,10 @@ class TestProjectFeatures(ChrysalideTestCase):
         for k in model.keys():
             path += '&e%s=%s' % (k, model[k])
 
-        attribs = ContentAttributes(path)
-        self.assertIsNotNone(attribs)
+        attribs, filename = ContentAttributes(path)
 
-        self.assertIsNone(attribs.filename)
+        self.assertIsNotNone(attribs)
+        self.assertIsNone(filename)
 
         kcount = 0
 
@@ -92,31 +92,31 @@ class TestProjectFeatures(ChrysalideTestCase):
 
         path = '&&'
 
-        attribs = ContentAttributes(path)
-        self.assertIsNotNone(attribs)
+        attribs, filename = ContentAttributes(path)
 
-        self.assertIsNone(attribs.filename)
+        self.assertIsNotNone(attribs)
+        self.assertIsNone(filename)
 
         self.assertEqual(len(attribs.keys), 0)
 
         path = '&&&'
 
-        attribs = ContentAttributes(path)
-        self.assertIsNotNone(attribs)
+        attribs, filename = ContentAttributes(path)
 
-        self.assertIsNone(attribs.filename)
+        self.assertIsNotNone(attribs)
+        self.assertIsNone(filename)
 
         self.assertEqual(len(attribs.keys), 0)
 
         path = 'filename'
 
-        attribs = ContentAttributes(path)
+        attribs, filename = ContentAttributes(path)
+
         self.assertIsNotNone(attribs)
+        self.assertEqual(filename, path)
 
         self.assertEqual(len(attribs.keys), 0)
 
-        self.assertEqual(attribs.filename, path)
-
 
     def testContentAttributesKeyAccess(self):
         """Test some access keys for content attributes."""
@@ -130,7 +130,7 @@ class TestProjectFeatures(ChrysalideTestCase):
         for k in model.keys():
             path += '&%s=%s' % (k, model[k])
 
-        attribs = ContentAttributes(path)
+        attribs, _ = ContentAttributes(path)
         self.assertIsNotNone(attribs)
 
         with self.assertRaisesRegex(Exception, 'key must be a string value'):
-- 
cgit v0.11.2-87-g4458