/* Chrysalide - Outil d'analyse de fichiers binaires
 * comment.c - gestion des commentaires dans du texte
 *
 * Copyright (C) 2014-2017 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 "comment.h"


#include <assert.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/socket.h>


#include <i18n.h>


#include "../collection-int.h"
#include "../item-int.h"
#include "../../human/asm/lang.h"
#include "../../../common/array.h"
#include "../../../common/extstr.h"
#include "../../../glibext/gbinarycursor.h"
#include "../../../glibext/linegen-int.h"



/* --------------------- ELABORATION D'UN ELEMENT DE COLLECTION --------------------- */


/* Commentaire à placer dans du texte quelconque (instance) */
struct _GDbComment
{
    GDbItem parent;                         /* A laisser en premier        */

    vmpa2t addr;                            /* Adresse du commentaire      */
    BufferLineFlags flags;                  /* Identification de l'accroche*/

    flat_array_t *text;                     /* Contenu du commentaire      */
    size_t count;                           /* Quantité de lignes affichées*/

    bool inlined;                           /* Intégration dans une ligne ?*/

    union
    {
        bool repeatable;                    /* Répétition aux lignes liées */
        bool before;                        /* Zone dédiée au dessus ?     */
    };

    GLineGenerator *previous;               /* Commentaire remplacé        */

    GLineGenerator **old_inlined;           /* Commentaires d'origine ?    */
    size_t old_count;                       /* Nombre de places à restaurer*/

};

/* Commentaire à placer dans du texte quelconque (classe) */
struct _GDbCommentClass
{
    GDbItemClass parent;                    /* A laisser en premier        */

};


/* Initialise la classe des commentaires dans une zone de texte. */
static void g_db_comment_class_init(GDbCommentClass *);

/* Initialise un commentaire dans une zone de texte. */
static void g_db_comment_init(GDbComment *);

/* Supprime toutes les références externes. */
static void g_db_comment_dispose(GDbComment *);

/* Procède à la libération totale de la mémoire. */
static void g_db_comment_finalize(GDbComment *);

/* Effectue la comparaison entre deux commentaires enregistrés. */
static gint g_db_comment_cmp(GDbComment *, GDbComment *, bool);

/* Importe la définition d'un commentaire dans un flux réseau. */
static bool g_db_comment_unpack(GDbComment *, packed_buffer *);

/* Exporte la définition d'un commentaire dans un flux réseau. */
static bool g_db_comment_pack(GDbComment *, packed_buffer *);

/* Construit la description humaine d'un commentaire. */
static char *g_db_comment_build_label(GDbComment *);

/* Exécute l'impression de commentaire dans du code de binaire. */
static bool g_db_comment_run(GDbComment *, GLoadedBinary *, bool);

/* Réalise l'impression de commentaire dans du code de binaire. */
static bool g_db_comment_apply(GDbComment *, GLoadedBinary *);

/* Annule l'impression d'un commentaire dans du code de binaire. */
static bool g_db_comment_cancel(GDbComment *, GLoadedBinary *);

/* Charge les valeurs utiles pour un commentaire. */
static bool g_db_comment_load(GDbComment *, const bound_value *, size_t);

/* Constitue les champs destinés à une insertion / modification. */
static bool g_db_comment_store(GDbComment *, bound_value **, size_t *);

/* Associe un contenu formaté supplémentaire à un commentaire. */
static void g_db_comment_add_rle_string(GDbComment *, const rle_string *);



/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */


/* Calcule le nombre de lignes suite à un changement de contenu. */
static void g_db_comment_update_count_lines(GDbComment *);

/* Indique le nombre de ligne prêtes à être générées. */
static size_t g_db_comment_count_lines(const GDbComment *);

/* Retrouve l'emplacement correspondant à une position donnée. */
static void g_db_comment_compute_cursor(const GDbComment *, gint, size_t, size_t, GLineCursor **);

/* Détermine si le conteneur s'inscrit dans une plage donnée. */
static int g_db_comment_contains_cursor(const GDbComment *, size_t, size_t, const GLineCursor *);

/* Renseigne sur les propriétés liées à un générateur. */
static BufferLineFlags g_db_comment_get_flags(const GDbComment *, size_t, size_t);

/* Imprime dans une ligne de rendu le contenu représenté. */
static void g_db_comment_print(GDbComment *, GBufferLine *, size_t, size_t, const GBinContent *);



/* ---------------------- DEFINITION DE LA COLLECTION ASSOCIEE ---------------------- */


/* Collection dédiée aux commentaires textuels (instance) */
struct _GCommentCollection
{
    GDbCollection parent;                   /* A laisser en premier        */

};

/* Collection dédiée aux commentaires textuels (classe) */
struct _GCommentCollectionClass
{
    GDbCollectionClass parent;              /* A laisser en premier        */

};


/* Initialise la classe des commentaires sous forme de texte. */
static void g_comment_collection_class_init(GCommentCollectionClass *);

/* Initialise un commentaire sous forme de zone de texte. */
static void g_comment_collection_init(GCommentCollection *);

/* Procède à l'initialisation de l'interface de génération. */
static void g_db_comment_interface_init(GLineGeneratorInterface *);

/* Supprime toutes les références externes. */
static void g_comment_collection_dispose(GCommentCollection *);

/* Procède à la libération totale de la mémoire. */
static void g_comment_collection_finalize(GCommentCollection *);

/* Crée la table des commentaires dans une base de données. */
static bool g_comment_collection_create_db_table(const GCommentCollection *, sqlite3 *);

/* Décrit les colonnes utiles à un chargement de données. */
static bool g_comment_collection_setup_load(GCommentCollection *, bound_value **, size_t *);

/* Détermine si un élément est déjà présent ou non. */
static GDbItem *g_comment_collection_has_key(GCommentCollection *, va_list);



/* ---------------------------------------------------------------------------------- */
/*                       ELABORATION D'UN ELEMENT DE COLLECTION                       */
/* ---------------------------------------------------------------------------------- */


/* Indique le type défini pour un commentaire à l'intérieur d'une zone de texte. */
G_DEFINE_TYPE_WITH_CODE(GDbComment, g_db_comment, G_TYPE_DB_ITEM,
                        G_IMPLEMENT_INTERFACE(G_TYPE_LINE_GENERATOR, g_db_comment_interface_init));


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des commentaires dans une zone de texte.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_class_init(GDbCommentClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GDbItemClass *item;                     /* Encore une autre vision...  */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_db_comment_dispose;
    object->finalize = (GObjectFinalizeFunc)g_db_comment_finalize;

    item = G_DB_ITEM_CLASS(klass);

    item->feature = DBF_COMMENTS;

    item->cmp = (cmp_db_item_fc)g_db_comment_cmp;

    item->unpack = (unpack_db_item_fc)g_db_comment_unpack;
    item->pack = (pack_db_item_fc)g_db_comment_pack;

    item->build_label = (build_item_label_fc)g_db_comment_build_label;
    item->apply = (run_item_fc)g_db_comment_apply;
    item->cancel = (run_item_fc)g_db_comment_cancel;

    item->load = (load_db_item_fc)g_db_comment_load;
    item->store = (store_db_item_fc)g_db_comment_store;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = instance à initialiser.                            *
*                                                                             *
*  Description : Initialise un commentaire dans une zone de texte.            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_init(GDbComment *comment)
{
    comment->text = NULL;
    comment->count = 0;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : iface = interface GLib à initialiser.                        *
*                                                                             *
*  Description : Procède à l'initialisation de l'interface de génération.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_interface_init(GLineGeneratorInterface *iface)
{
    iface->count = (linegen_count_lines_fc)g_db_comment_count_lines;
    iface->compute = (linegen_compute_fc)g_db_comment_compute_cursor;
    iface->contains = (linegen_contains_fc)g_db_comment_contains_cursor;
    iface->get_flags = (linegen_get_flags_fc)g_db_comment_get_flags;
    iface->print = (linegen_print_fc)g_db_comment_print;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = instance d'objet GLib à traiter.                   *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_dispose(GDbComment *comment)
{
    size_t i;                               /* Boucle de parcours          */

    g_clear_object(&comment->previous);

    for (i = 0; i < comment->old_count; i++)
        g_clear_object(&comment->old_inlined[i]);

    G_OBJECT_CLASS(g_db_comment_parent_class)->dispose(G_OBJECT(comment));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = instance d'objet GLib à traiter.                   *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_finalize(GDbComment *comment)
{
    size_t count;                           /* Nombre d'éléments textuels  */
    size_t i;                               /* Boucle de parcours          */
    rle_string *string;                     /* Chaîne à traiter            */

    lock_flat_array(&comment->text);

    count = count_flat_array_items(comment->text);

    for (i = 0; i < count; i++)
    {
        string = get_flat_array_item(comment->text, 0, sizeof(rle_string));

        exit_rle_string(string);

        rem_item_from_flat_array(&comment->text, 0, sizeof(rle_string));

    }

    unlock_flat_array(&comment->text);

    if (comment->old_inlined != NULL)
        free(comment->old_inlined);

    G_OBJECT_CLASS(g_db_comment_parent_class)->finalize(G_OBJECT(comment));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : addr       = adresse inamovible localisant une position.     *
*                flags      = indentifiants supplémentaires de ligne visée.   *
*                repeatable = repétition aux instructions liées ?             *
*                                                                             *
*  Description : Crée une définition de commentaire dans une zone de texte.   *
*                                                                             *
*  Retour      : Commentaire mis en place ou NULL en cas d'erreur.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDbComment *g_db_comment_new_inlined(const vmpa2t *addr, BufferLineFlags flags, bool repeatable)
{
    GDbComment *result;                    /* Instance à retourner        */

    result = g_object_new(G_TYPE_DB_COMMENT, NULL);

    copy_vmpa(&result->addr, addr);

    result->flags = flags;

    result->inlined = true;

    result->repeatable = repeatable;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : addr   = adresse inamovible localisant une position.         *
*                flags  = indentifiants supplémentaires de ligne visée.       *
*                text   = commentaire construit ou NULL.                      *
*                before = précision quant à la position de la zone à définir. *
*                                                                             *
*  Description : Crée une définition de commentaire dans une zone de texte.   *
*                                                                             *
*  Retour      : Commentaire mis en place ou NULL en cas d'erreur.            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDbComment *g_db_comment_new_area(const vmpa2t *addr, BufferLineFlags flags, const char *text, bool before)
{
    GDbComment *result;                    /* Instance à retourner        */

    result = g_object_new(G_TYPE_DB_COMMENT, NULL);

    copy_vmpa(&result->addr, addr);

    result->flags = flags;

    g_db_comment_add_dynamic_text(result, strdup(text));

    result->inlined = false;

    result->before = before;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : a    = premier élément à analyser.                           *
*                b    = second élément à analyser.                            *
*                with = précise les horodatages à prendre en compte.          *
*                                                                             *
*  Description : Effectue la comparaison entre deux commentaires enregistrés. *
*                                                                             *
*  Retour      : Bilan de la comparaison : -1, 0 ou 1.                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static gint g_db_comment_cmp(GDbComment *a, GDbComment *b, bool with)
{
    gint result;                            /* Bilan de la comparaison     */
    char *string_a;                         /* Texte du commentaire A      */
    char *string_b;                         /* Texte du commentaire B      */

    result = cmp_vmpa_by_phy(&a->addr, &b->addr);

    if (result == 0)
    {
        string_a = g_db_comment_get_text(a);
        string_b = g_db_comment_get_text(b);

        if (string_a == NULL && string_b == NULL)
            result = 0;

        else if (string_a != NULL && string_b == NULL)
            result = 1;

        else if (string_a == NULL && string_b != NULL)
            result = -1;

        else
            result = strcmp(string_a, string_b);

        if (string_a != NULL) free(string_a);
        if (string_b != NULL) free(string_b);

    }

    if (result == 0)
        result = G_DB_ITEM_CLASS(g_db_comment_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b), with);

    return 0;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = commentaire avec informations sont à charger. [OUT]*
*                pbuf    = paquet de données où venir inscrire les infos.     *
*                                                                             *
*  Description : Importe la définition d'un commentaire dans un flux réseau.  *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_unpack(GDbComment *comment, packed_buffer *pbuf)
{
    bool result;                            /* Bilan à retourner           */
    uint32_t tmp32;                         /* Valeur sur 32 bits          */
    rle_string string;                      /* Texte brut récupéré         */
    uint8_t tmp8;                           /* Valeur sur 8 bits           */

    result = G_DB_ITEM_CLASS(g_db_comment_parent_class)->unpack(G_DB_ITEM(comment), pbuf);

    if (result)
        result = unpack_vmpa(&comment->addr, pbuf);

    if (result)
    {
        result = extract_packed_buffer(pbuf, &tmp32, sizeof(uint32_t), true);
        comment->flags = tmp32;
    }

    if (result)
    {
        result = unpack_rle_string(&string, pbuf);

        if (!is_rle_string_empty(&string) > 0)
            g_db_comment_add_rle_string(comment, &string);

    }

    if (result)
    {
        result = extract_packed_buffer(pbuf, &tmp8, sizeof(uint8_t), true);
        comment->inlined = tmp8;
    }

    if (result)
    {
        result = extract_packed_buffer(pbuf, &tmp8, sizeof(uint8_t), true);
        comment->repeatable = tmp8;
    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à sauvegarder.                        *
*                pbuf    = paquet de données où venir inscrire les infos.     *
*                                                                             *
*  Description : Exporte la définition d'un commentaire dans un flux réseau.  *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_pack(GDbComment *comment, packed_buffer *pbuf)
{
    bool result;                            /* Bilan à retourner           */
    rle_string text;                        /* Texte brut récupéré         */

    result = G_DB_ITEM_CLASS(g_db_comment_parent_class)->pack(G_DB_ITEM(comment), pbuf);

    if (result)
        result = pack_vmpa(&comment->addr, pbuf);

    if (result)
        result = extend_packed_buffer(pbuf, (uint32_t []) { comment->flags }, sizeof(uint32_t), true);

    if (result)
    {
        init_dynamic_rle_string(&text, g_db_comment_get_text(comment));
        result = pack_rle_string(&text, pbuf);
        exit_rle_string(&text);
    }

    if (result)
        result = extend_packed_buffer(pbuf, (uint8_t []) { comment->inlined }, sizeof(uint8_t), true);

    if (result)
        result = extend_packed_buffer(pbuf, (uint8_t []) { comment->repeatable }, sizeof(uint8_t), true);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = définition de commentaire à manipuler.             *
*                                                                             *
*  Description : Construit la description humaine d'un commentaire.           *
*                                                                             *
*  Retour      : Chaîne de caractère correspondante.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static char *g_db_comment_build_label(GDbComment *comment)
{
#if 0
    VMPA_BUFFER(loc);                       /* Indication de position      */
    size_t count;                           /* Nombre d'éléments textuels  */

    vmpa2_to_string(&comment->addr, MDS_UNDEFINED, loc, NULL);

    lock_flat_array(&comment->text);
    count = count_flat_array_items(comment->text);
    unlock_flat_array(&comment->text);

    if (count == 0)
        asprintf(&G_DB_ITEM(comment)->label, _("Delete comment at %s"), loc);

    else
    {
        if (comment->inlined)
            asprintf(&G_DB_ITEM(comment)->label, _("Enter inlined comment at %s"), loc);
        else
            asprintf(&G_DB_ITEM(comment)->label, _("Enter comment area at %s"), loc);
    }
#endif
    return NULL;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = définition de commentaire à manipuler.             *
*                binary  = binaire chargé en mémoire à modifier.              *
*                apply   = indique s'il faut appliquer la définition ou non.  *
*                                                                             *
*  Description : Exécute l'impression de commentaire dans du code de binaire. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_run(GDbComment *comment, GLoadedBinary *binary, bool apply)
{
    bool result;                            /* Bilan à faire remonter      */
    GBufferCache *cache;                    /* Ensemble de lignes à traiter*/
    GLineCursor *cursor;                    /* Emplacement dans un tampon  */
    size_t index;                           /* Point d'insertion           */
    GArchProcessor *proc;                   /* Propriétaire d'instructions */
    GArchInstruction *instr;                /* Instruction à traiter       */
    size_t scount;                          /* Nbre de sources affichées   */
    size_t i;                               /* Boucle de parcours          */
    const instr_link_t *source;             /* Instruction diverse liée    */
    const mrange_t *range;                  /* Emplacement d'instruction   */
    size_t linked;                          /* Indice lié à traiter        */

    result = true;

    cache = g_loaded_binary_get_disassembled_cache(binary);

    cursor = g_binary_cursor_new();
    g_binary_cursor_update(G_BINARY_CURSOR(cursor), &comment->addr);

    index = g_buffer_cache_find_index_by_cursor(cache, cursor, true);

    g_object_unref(G_OBJECT(cursor));

    index = g_buffer_cache_look_for_flag(cache, index, BLF_HAS_CODE);

    if (comment->inlined)
    {

#define RUN_INLINED_COMMENT(idx, new, old)                                                          \
        if (apply)                                                                                  \
        {                                                                                           \
            old = g_buffer_cache_delete_type_at(cache, idx, G_TYPE_DB_COMMENT, false, false);       \
                                                                                                    \
            g_buffer_cache_insert_at(cache, idx, G_LINE_GENERATOR(new), BLF_NONE, false, false);    \
                                                                                                    \
        }                                                                                           \
        else                                                                                        \
        {                                                                                           \
            g_buffer_cache_delete_type_at(cache, idx, G_TYPE_DB_COMMENT, false, false);             \
                                                                                                    \
            if (old != NULL)                                                                        \
            {                                                                                       \
                g_buffer_cache_insert_at(cache, idx, old, BLF_NONE, false, false);                  \
                g_object_unref(G_OBJECT(old));                                                      \
            }                                                                                       \
                                                                                                    \
        }

        /* Commentaire principal */

        RUN_INLINED_COMMENT(index, comment, comment->previous);

        /* Renvois répétés */

        if (comment->repeatable)
        {
            proc = g_loaded_binary_get_processor(binary);

            instr = g_arch_processor_find_instr_by_address(proc, &comment->addr);
            assert(instr != NULL);

            scount = g_arch_instruction_count_sources(instr);

            if (apply)
            {
                comment->old_count = scount;
                comment->old_inlined = realloc(comment->old_inlined, comment->old_count * sizeof(GLineGenerator *));
            }

            for (i = 0; i < scount && result; i++)
            {
                source = g_arch_instruction_get_source(instr, i);

                range = g_arch_instruction_get_range(source->linked);

                cursor = g_binary_cursor_new();
                g_binary_cursor_update(G_BINARY_CURSOR(cursor), get_mrange_addr(range));

                linked = g_buffer_cache_find_index_by_cursor(cache, cursor, true);
                assert(linked != g_buffer_cache_count_lines(cache));

                g_object_unref(G_OBJECT(cursor));

                /**
                 * On recherche ici une ligne potentiellement BLF_HAS_CODE ou BLF_IS_LABEL.
                 * Comme on ne peut pas traiter les deux cas, on prend la première qui vient
                 * avec BLF_NONE.
                 */

                linked = g_buffer_cache_look_for_flag(cache, linked, BLF_HAS_CODE | BLF_IS_LABEL);

                RUN_INLINED_COMMENT(linked, comment, comment->old_inlined[i]);

                unref_instr_link(source);

            }

            if (!apply)
            {
                free(comment->old_inlined);

                comment->old_inlined = NULL;
                comment->old_count = 0;

            }

            g_object_unref(G_OBJECT(proc));

        }



    }

    else
    {









    }


    //void g_buffer_cache_insert_at(GBufferCache *cache, size_t index, GLineGenerator *generator, bool before, bool after)

    //GLineGenerator *g_buffer_cache_delete_type_at(GBufferCache *cache, size_t index, GType type, bool before, bool after)




    g_object_unref(G_OBJECT(cache));

    return result;




#if 0

    bool result;                            /* Bilan à faire remonter      */
    GCodeBuffer *buffer;                    /* Tampon de lignes à traiter  */
    GBufferLine *line;                      /* Ligne de tampon à marquer   */
    GArchProcessor *proc;                   /* Propriétaire d'instructions */
    GArchInstruction *instr;                /* Instruction à traiter       */
    instr_link_t *sources;                  /* Instructions diverses liées */
    size_t scount;                          /* Nbre de sources affichées   */
    size_t i;                               /* Boucle de parcours          */
    const mrange_t *range;                  /* Emplacement d'instruction   */
    GBufferLine *linked;                    /* Ligne liée à traiter        */
    GDbComment *old;                        /* Ancien élément à restaurer  */

    result = true;

    /* Recherche de la ligne visée */

    buffer = g_loaded_binary_get_disassembled_buffer(binary);

    line = g_code_buffer_find_line_by_addr(buffer, &comment->addr, comment->flags, NULL);
    if (line == NULL)
    {
        result = false;
        goto exit_gui;
    }

    /* Détermination de l'imprimeur précédent */

    if (apply)
    {
        assert(comment->old_count == 0);

        comment->old_count = 1;
        comment->oldies = calloc(comment->old_count, sizeof(GDbComment *));

    }

    /* Applications globales finales */

#define g_code_buffer_update_inlined_comment_dummy(b, l, c, d, o)   \
    g_code_buffer_update_inlined_comment(b, l, c, o)

    /**
     * Note : on part du principe qu'un commentaire intégré ne peut remplacer qu'un
     * commentaire intégré. Et pareil pour les commentaires en blocs.
     */

#define RUN_COMMENT_ON(ln, idx, func)                                                               \
    if (apply)                                                                                      \
    {                                                                                               \
        comment->oldies[idx] = (GDbComment *)g_code_buffer_get_comment_creator(buffer, ln);         \
        assert(comment->oldies[idx] == NULL || G_IS_DB_COMMENT(comment->oldies[idx]));              \
        assert(comment->oldies[idx] == NULL || comment->oldies[idx]->inlined == comment->inlined);  \
                                                                                                    \
        if (is_rle_string_empty(&comment->text))                                                    \
            result = g_code_buffer_delete_lines_comment(buffer, ln);                                \
        else                                                                                        \
            result = func(buffer, ln, get_rle_string(&comment->text),                               \
                          comment->before, G_OBJECT(comment));                                      \
    }                                                                                               \
    else                                                                                            \
    {                                                                                               \
        old = comment->oldies[idx];                                                                 \
                                                                                                    \
        if (old == NULL)                                                                            \
        {                                                                                           \
            if (!is_rle_string_empty(&comment->text))                                               \
                result = g_code_buffer_delete_lines_comment(buffer, ln);                            \
            else                                                                                    \
                result = true;                                                                      \
        }                                                                                           \
        else                                                                                        \
        {                                                                                           \
            if (is_rle_string_empty(&old->text))                                                    \
                result = g_code_buffer_delete_lines_comment(buffer, ln);                            \
            else                                                                                    \
                result = func(buffer, ln, get_rle_string(&old->text),                               \
                              old->before, G_OBJECT(old));                                          \
        }                                                                                           \
                                                                                                    \
    }

    if (comment->inlined)
    {
        /* Traitement d'un commentaire inscrusté */

        RUN_COMMENT_ON(line, 0, g_code_buffer_update_inlined_comment_dummy);

        /* Traitement des répétitions ? */

        if (comment->repeatable/* && result*/)
        {
            proc = g_loaded_binary_get_processor(binary);

            range = g_buffer_line_get_range(line);

            instr = g_arch_processor_find_instr_by_address(proc, get_mrange_addr(range));
            assert(instr != NULL);

            scount = g_arch_instruction_get_sources(instr, &sources);

            if (apply)
            {
                comment->old_count += scount;
                comment->oldies = realloc(comment->oldies, comment->old_count * sizeof(GDbComment *));
            }

            for (i = 0; i < scount && result; i++)
            {
                range = g_arch_instruction_get_range(sources[i].linked);

                /**
                 * On recherche ici une ligne potentiellement BLF_HAS_CODE ou BLF_IS_LABEL.
                 * Comme on ne peut pas traiter les deux cas, on prend la première qui vient
                 * avec BLF_NONE.
                 */
                linked = g_code_buffer_find_line_by_addr(buffer, get_mrange_addr(range), BLF_NONE, NULL);
                assert(linked != NULL);

                RUN_COMMENT_ON(linked, 1 + i, g_code_buffer_update_inlined_comment_dummy);

            }

            g_object_unref(G_OBJECT(instr));

            g_object_unref(G_OBJECT(proc));

        }

    }

    else
    {
        RUN_COMMENT_ON(line, 0, g_code_buffer_update_comment_area);
    }

    g_object_unref(G_OBJECT(line));

 exit_gui:

    /* TODO g_object_unref(G_OBJECT(buffer));*/

    return result;
#endif
}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = définition de commentaire à manipuler.             *
*                binary  = binaire chargé en mémoire à modifier.              *
*                                                                             *
*  Description : Réalise l'impression de commentaire dans du code de binaire. *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_apply(GDbComment *comment, GLoadedBinary *binary)
{
    bool result;                            /* Bilan à faire remonter      */

    result = g_db_comment_run(comment, binary, true);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = définition de commentaire à manipuler.             *
*                binary  = binaire chargé en mémoire à modifier.              *
*                                                                             *
*  Description : Annule l'impression d'un commentaire dans du code de binaire.*
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_cancel(GDbComment *comment, GLoadedBinary *binary)
{
    bool result;                            /* Bilan à faire remonter      */

    result = g_db_comment_run(comment, binary, false);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = commentaire textuel à charger depuis les réponses. *
*                values  = tableau d'éléments à consulter.                    *
*                count   = nombre de descriptions renseignées.                *
*                                                                             *
*  Description : Charge les valeurs utiles pour un commentaire.               *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_load(GDbComment *comment, const bound_value *values, size_t count)
{
    bool result;                            /* Bilan à faire remonter      */
    const bound_value *value;               /* Valeur à éditer / définir   */
    rle_string string;                      /* Texte brut récupéré         */

    result = G_DB_ITEM_CLASS(g_db_comment_parent_class)->load(G_DB_ITEM(comment), values, count);

    result &= load_vmpa(&comment->addr, NULL, values, count);

    if (result)
    {
        value = find_bound_value(values, count, "flags");
        result = (value != NULL && value->type == SQLITE_INTEGER);

        if (result)
            comment->flags = value->integer;

    }

    result &= load_rle_string(&string, "text", values, count);

    if (result)
        g_db_comment_add_rle_string(comment, &string);

    if (result)
    {
        value = find_bound_value(values, count, "inlined");
        result = (value != NULL && value->type == SQLITE_BOOLEAN);

        if (result)
            comment->inlined = value->boolean;

    }

    if (result)
    {
        value = find_bound_value(values, count, "repeatable");
        result = (value != NULL && value->type == SQLITE_BOOLEAN);

        if (result)
            comment->repeatable = value->boolean;

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = base d'éléments sur laquelle s'appuyer.            *
*                values   = couples de champs et de valeurs à lier. [OUT]     *
*                count    = nombre de ces couples. [OUT]                      *
*                                                                             *
*  Description : Constitue les champs destinés à une insertion / modification.*
*                                                                             *
*  Retour      : Etat du besoin en sauvegarde.                                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_db_comment_store(GDbComment *comment, bound_value **values, size_t *count)
{
    bool status;                            /* Bilan d'opération initiale  */
    bound_value *value;                     /* Valeur à éditer / définir   */
    rle_string text;                        /* Texte brut récupéré         */

    if (comment == NULL)
        status = G_DB_ITEM_CLASS(g_db_comment_parent_class)->store(NULL, values, count);
    else
        status = G_DB_ITEM_CLASS(g_db_comment_parent_class)->store(G_DB_ITEM(comment), values, count);

    if (!status) return false;

    if (comment == NULL)
        status = store_vmpa(NULL, NULL, values, count);
    else
        status = store_vmpa(&comment->addr, NULL, values, count);

    if (!status) return false;

    *count += 1;
    *values = realloc(*values, *count * sizeof(bound_value));

    value = &(*values)[*count - 1];

    value->cname = "flags";
    value->built_name = false;
    value->type = SQLITE_INTEGER;
    value->integer = comment->flags;
    value->delete = NULL;

    value->has_value = (comment != NULL);

    if (value->has_value)
    {
        init_dynamic_rle_string(&text, g_db_comment_get_text(comment));
        status = store_rle_string(&text, "text", values, count);
        exit_rle_string(&text);
    }

    if (!status) return false;

    *count += 2;
    *values = realloc(*values, *count * sizeof(bound_value));

    value = &(*values)[*count - 2];

    value->cname = "inlined";
    value->built_name = false;
    value->type = SQLITE_BOOLEAN;

    value->has_value = (comment != NULL);

    if (value->has_value)
    {
        value->boolean = comment->inlined;
        value->delete = NULL;
    }

    value = &(*values)[*count - 1];

    value->cname = "repeatable";
    value->built_name = false;
    value->type = SQLITE_BOOLEAN;

    value->has_value = (comment != NULL);

    if (value->has_value)
    {
        value->boolean = comment->repeatable;
        value->delete = NULL;
    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à consulter.                          *
*                                                                             *
*  Description : Fournit l'adresse associée à un commentaire.                 *
*                                                                             *
*  Retour      : Adresse mémoire.                                             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const vmpa2t *g_db_comment_get_address(const GDbComment *comment)
{
    return &comment->addr;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à consulter.                          *
*                                                                             *
*  Description : Fournit le commentaire associé à un commentaire.             *
*                                                                             *
*  Retour      : Commentaire existant à libérer après usage ou NULL.          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

char *g_db_comment_get_text(GDbComment *comment)
{
    char *result;                           /* Chaîne constituée à renvoyer*/
    size_t count;                           /* Nombre d'éléments textuels  */
    size_t i;                               /* Boucle de parcours          */
    rle_string *string;                     /* Chaîne à consulter          */

    result = NULL;

    lock_flat_array(&comment->text);

    count = count_flat_array_items(comment->text);

    for (i = 0; i < count; i++)
    {
        string = get_flat_array_item(comment->text, i, sizeof(rle_string));

        assert(!is_rle_string_empty(string));

        result = stradd(result, get_rle_string(string));

    }

    unlock_flat_array(&comment->text);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à consulter.                          *
*                text    = commentaire construit ou NULL.                     *
*                                                                             *
*  Description : Associe un contenu supplémentaire à un commentaire.          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_db_comment_add_dynamic_text(GDbComment *comment, char *text)
{
    rle_string string;                      /* Fragment de texte à ajouter */

    if (text != NULL)
    {
        init_dynamic_rle_string(&string, text);

        g_db_comment_add_rle_string(comment, &string);

    }

    else
        g_db_comment_update_count_lines(comment);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à consulter.                          *
*                text    = commentaire construit ou NULL.                     *
*                                                                             *
*  Description : Associe un contenu statique supplémentaire à un commentaire. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_db_comment_add_static_text(GDbComment *comment, const char *text)
{
    rle_string string;                      /* Fragment de texte à ajouter */

    if (text != NULL)
    {
        init_static_rle_string(&string, text);

        g_db_comment_add_rle_string(comment, &string);

    }

    else
        g_db_comment_update_count_lines(comment);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à consulter.                          *
*                string  = commentaire établi à inétgrer.                     *
*                                                                             *
*  Description : Associe un contenu formaté supplémentaire à un commentaire.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_add_rle_string(GDbComment *comment, const rle_string *string)
{
    /* Extension du contenu */

    lock_flat_array(&comment->text);

    add_item_to_flat_array(&comment->text, string, sizeof(rle_string));

    unlock_flat_array(&comment->text);

    /* Mise à jour de la taille de rendu */

    g_db_comment_update_count_lines(comment);

}



/* ---------------------------------------------------------------------------------- */
/*                          OFFRE DE CAPACITES DE GENERATION                          */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = informations à réactualiser.                       *
*                                                                             *
*  Description : Calcule le nombre de lignes suite à un changement de contenu.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_update_count_lines(GDbComment *comment)
{
    char *full;                             /* Contenu textuel complet     */
    char **lines;                           /* Lignes brutes à représenter */
    GCodingLanguage *lang;                  /* Langage de sortie préféré   */
    size_t i;                               /* Boucle de parcours          */

    full = g_db_comment_get_text(comment);

    if (full == NULL)
        comment->count = 0;

    else
    {
        lines = strtoka(full, "\n", &comment->count);

        lang = g_asm_language_new();
        g_coding_language_encapsulate_comments(lang, &lines, &comment->count);
        g_object_unref(G_OBJECT(lang));

        for (i = 0; i < comment->count; i++)
            free(lines[i]);

        if (lines != NULL)
            free(lines);

        free(full);

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = générateur à consulter.                            *
*                                                                             *
*  Description : Indique le nombre de ligne prêtes à être générées.           *
*                                                                             *
*  Retour      : Nombre de lignes devant apparaître au final.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static size_t g_db_comment_count_lines(const GDbComment *comment)
{
    return comment->count;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = générateur à consulter.                            *
*                x       = position géographique sur la ligne concernée.      *
*                index   = indice de cette même ligne dans le tampon global.  *
*                repeat  = indice d'utilisations successives du générateur.   *
*                cursor  = emplacement à constituer. [OUT]                    *
*                                                                             *
*  Description : Retrouve l'emplacement correspondant à une position donnée.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_compute_cursor(const GDbComment *comment, gint x, size_t index, size_t repeat, GLineCursor **cursor)
{
    *cursor = g_binary_cursor_new();

    g_binary_cursor_update(G_BINARY_CURSOR(*cursor), &comment->addr);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = générateur à consulter.                            *
*                index   = indice de cette même ligne dans le tampon global.  *
*                repeat  = indice d'utilisations successives du générateur.   *
*                cursor  = emplacement à analyser.                            *
*                                                                             *
*  Description : Détermine si le conteneur s'inscrit dans une plage donnée.   *
*                                                                             *
*  Retour      : Bilan de la détermination, utilisable en comparaisons.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static int g_db_comment_contains_cursor(const GDbComment *comment, size_t index, size_t repeat, const GLineCursor *cursor)
{
    int result;                             /* Conclusion à retourner      */
    vmpa2t addr;                            /* Autre emplacement à comparer*/

    assert(G_IS_BINARY_CURSOR(cursor));

    g_binary_cursor_get_info(G_BINARY_CURSOR(cursor), &addr);

    result = cmp_vmpa(&addr, &comment->addr);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = générateur à consulter.                            *
*                index   = indice de cette même ligne dans le tampon global.  *
*                repeat  = indice d'utilisations successives du générateur.   *
*                                                                             *
*  Description : Renseigne sur les propriétés liées à un générateur.          *
*                                                                             *
*  Retour      : Propriétés particulières associées.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static BufferLineFlags g_db_comment_get_flags(const GDbComment *comment, size_t index, size_t repeat)
{
    return BLF_NONE;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : comment = générateur à utiliser pour l'impression.           *
*                line    = ligne de rendu à compléter.                        *
*                index   = indice de cette même ligne dans le tampon global.  *
*                repeat  = indice d'utilisations successives du générateur.   *
*                content = éventuel contenu binaire brut à imprimer.          *
*                                                                             *
*  Description : Imprime dans une ligne de rendu le contenu représenté.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_db_comment_print(GDbComment *comment, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content)
{
    char *full;                             /* Contenu textuel complet     */
    size_t count;                           /* Quantité de ces lignes      */
    char **lines;                           /* Lignes brutes à représenter */
    GCodingLanguage *lang;                  /* Langage de sortie préféré   */
    size_t i;                               /* Boucle de parcours          */

    full = g_db_comment_get_text(comment);

    if (full != NULL)
    {
        lines = strtoka(full, "\n", &count);

        lang = g_asm_language_new();
        g_coding_language_encapsulate_comments(lang, &lines, &count);
        g_object_unref(G_OBJECT(lang));

        if (count != comment->count)
            printf("full=%s\n", full);

        assert(count == comment->count);

        g_buffer_line_append_text(line, BLC_COMMENTS, SL(lines[repeat]), RTT_COMMENT, NULL);

        for (i = 0; i < count; i++)
            free(lines[i]);

        if (lines != NULL)
            free(lines);

        free(full);

    }

}



/* ---------------------------------------------------------------------------------- */
/*                        DEFINITION DE LA COLLECTION ASSOCIEE                        */
/* ---------------------------------------------------------------------------------- */


/* Indique le type défini pour une collection de commentaires. */
G_DEFINE_TYPE(GCommentCollection, g_comment_collection, G_TYPE_DB_COLLECTION);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des commentaires sous forme de texte.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_comment_collection_class_init(GCommentCollectionClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GDbCollectionClass *collec;             /* Encore une autre vision...  */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_comment_collection_dispose;
    object->finalize = (GObjectFinalizeFunc)g_comment_collection_finalize;

    collec = G_DB_COLLECTION_CLASS(klass);

    collec->create_table = (collec_create_db_table_fc)g_comment_collection_create_db_table;
    collec->setup_load = (collec_setup_load_fc)g_comment_collection_setup_load;
    collec->has_key = (collec_has_key_fc)g_comment_collection_has_key;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise un commentaire sous forme de zone de texte.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_comment_collection_init(GCommentCollection *collec)
{
    G_DB_COLLECTION(collec)->featuring = DBF_COMMENTS;
    G_DB_COLLECTION(collec)->type = G_TYPE_DB_COMMENT;
    G_DB_COLLECTION(collec)->name = "Comments";

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_comment_collection_dispose(GCommentCollection *collec)
{
    G_OBJECT_CLASS(g_comment_collection_parent_class)->dispose(G_OBJECT(collec));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_comment_collection_finalize(GCommentCollection *collec)
{
    G_OBJECT_CLASS(g_comment_collection_parent_class)->finalize(G_OBJECT(collec));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée une collection dédiée aux commentaires.                 *
*                                                                             *
*  Retour      : Collection mise en place.                                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GCommentCollection *g_comment_collection_new(void)
{
    GCommentCollection *result;            /* Instance à retourner        */

    result = g_object_new(G_TYPE_COMMENT_COLLECTION, NULL);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec = ensemble d'éléments spectateur des opérations.      *
*                db     = accès à la base de données.                         *
*                                                                             *
*  Description : Crée la table des commentaires dans une base de données.     *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_comment_collection_create_db_table(const GCommentCollection *collec, sqlite3 *db)
{
    const char *sql;                        /* Patron de requête SQL       */
    char *addr_fields;                      /* Champs pour l'adresse       */
    char *request;                          /* Requête à exécuter          */
    char *msg;                              /* Message d'erreur            */
    int ret;                                /* Bilan de la création        */

    sql = "CREATE TABLE Comments ("             \
             SQLITE_DB_ITEM_CREATE ", "         \
             "%s, "                             \
             "flags INTEGER, "                  \
             SQLITE_RLESTR_CREATE("text") ", "  \
             "inlined INTEGER, "                \
             "repeatable INTEGER"               \
          ");";

    addr_fields = create_vmpa_db_table(NULL);

    asprintf(&request, sql, addr_fields);

    ret = sqlite3_exec(db, request, NULL, NULL, &msg);

    free(addr_fields);
    free(request);

    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3_exec(): %s\n", msg);
        sqlite3_free(msg);
    }

    return (ret == SQLITE_OK);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
*                values = tableau d'éléments à compléter. [OUT]               *
*                count  = nombre de descriptions renseignées. [OUT]           *
*                                                                             *
*  Description : Décrit les colonnes utiles à un chargement de données.       *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool g_comment_collection_setup_load(GCommentCollection *collec, bound_value **values, size_t *count)
{
    bool status;                            /* Bilan d'une préparation     */
    bound_value *value;                     /* Valeur à éditer / définir   */

    status = G_DB_COLLECTION_CLASS(g_comment_collection_parent_class)->setup_load(G_DB_COLLECTION(collec), \
                                                                                  values, count);
    if (!status) return false;

    if (!store_vmpa(NULL, NULL, values, count))
        return false;

    *count += 1;
    *values = realloc(*values, *count * sizeof(bound_value));

    value = &(*values)[*count - 1];

    value->cname = "flags";
    value->built_name = false;
    value->type = SQLITE_INTEGER;

    if (!store_rle_string(NULL, "text", values, count))
        return false;

    *count += 2;
    *values = realloc(*values, *count * sizeof(bound_value));

    value = &(*values)[*count - 2];

    value->cname = "inlined";
    value->built_name = false;
    value->type = SQLITE_BOOLEAN;

    value = &(*values)[*count - 1];

    value->cname = "repeatable";
    value->built_name = false;
    value->type = SQLITE_BOOLEAN;

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
*                ap     = clef identifiant de manière unique un élément.      *
*                                                                             *
*  Description : Détermine si un élément est déjà présent ou non.             *
*                                                                             *
*  Retour      : Elément trouvé ou NULL si aucun.                             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GDbItem *g_comment_collection_has_key(GCommentCollection *collec, va_list ap)
{
    GDbItem *result;                        /* Bilan à retourner           */
    vmpa2t *ref;                            /* Adresse de référence        */
    GList *items;                           /* Eléments déjà en place      */
    GList *iter;                            /* Boucle de parcours          */
    GDbComment *bm;                        /* Signet à consulter          */

    result = NULL;

    ref = va_arg(ap, vmpa2t *);

    items = g_db_collection_list_items(G_DB_COLLECTION(collec));

    for (iter = g_list_first(items); iter != NULL && result == NULL; iter = g_list_next(iter))
    {
        bm = G_DB_COMMENT(iter->data);

        if (cmp_vmpa(&bm->addr, ref) != 0)

            /**
             * Un verrou est sensé être posé, donc il n'y a pas besoin
             * de toucher au décompte des références car l'élément trouvé
             * ne peut pas être supprimé.
             */
            result = G_DB_ITEM(bm);

    }

    return result;

}