From c5631dd09fa9981e914ec9082c6270b69a719ca4 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 18 Aug 2020 22:08:39 +0200
Subject: Refreshed the rendering for newly added bookmarks.

---
 configure.ac                               |  17 +++++
 plugins/pychrysalide/glibext/buffercache.c | 108 +++++++++++++++++++++++++++++
 plugins/pychrysalide/glibext/bufferline.c  |  92 +-----------------------
 src/analysis/db/collection.c               |   2 +-
 src/analysis/db/items/bookmark.c           |  13 ++--
 src/glibext/buffercache-int.h              |   2 +
 src/glibext/buffercache.c                  |  88 +++++++++++++++++++++++
 src/glibext/buffercache.h                  |   8 ++-
 src/glibext/bufferline.c                   |   2 +-
 src/glibext/bufferview.c                   |  25 +++++++
 10 files changed, 255 insertions(+), 102 deletions(-)

diff --git a/configure.ac b/configure.ac
index f467f66..855091f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,6 +60,23 @@ AC_CHECK_HEADERS([unistd.h])
 
 AC_TYPE_SIZE_T
 
+AC_MSG_CHECKING([for suitable size_t])
+AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[#include <stddef.h>]],
+                                    [[char dummy[sizeof(size_t) == sizeof(unsigned long) ? 1 : -1];]])],
+                   [
+                      AC_MSG_RESULT([yes])
+                      suitable_size_t=yes
+                    ],
+                    [
+                      AC_MSG_RESULT([no])
+                      suitable_size_t=no
+                    ]
+                 )
+
+if test "x$suitable_size_t" = "xno"; then
+   AC_MSG_FAILURE([Current size of size_t is not supported])
+fi
+
 
 #--- Checks for structures
 
diff --git a/plugins/pychrysalide/glibext/buffercache.c b/plugins/pychrysalide/glibext/buffercache.c
index 019d981..d3bee45 100644
--- a/plugins/pychrysalide/glibext/buffercache.c
+++ b/plugins/pychrysalide/glibext/buffercache.c
@@ -73,9 +73,15 @@ static PyObject *py_buffer_cache_extend_with(PyObject *, PyObject *);
 /* Réduit le tampon à une quantité de lignes précise. */
 static PyObject *py_buffer_cache_truncate(PyObject *, PyObject *);
 
+/* Ajoute une propriété particulière à une ligne. */
+static PyObject *py_buffer_cache_add_line_flag(PyObject *, PyObject *);
+
 /* Détermine l'ensemble des propriétés attachées à une ligne. */
 static PyObject *py_buffer_cache_get_line_flags(PyObject *, PyObject *);
 
+/* Retire une propriété particulière attachée à une ligne. */
+static PyObject *py_buffer_cache_remove_line_flag(PyObject *, PyObject *);
+
 /* Retrouve une ligne au sein d'un tampon avec un indice. */
 static PyObject *py_buffer_cache_find_line_by_index(PyObject *, PyObject *);
 
@@ -568,6 +574,56 @@ static PyObject *py_buffer_cache_truncate(PyObject *self, PyObject *args)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : self = tampon de lignes à venir consulter.                   *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Ajoute une propriété particulière à une ligne.               *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_add_line_flag(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de la ligne visée    */
+    BufferLineFlags flag;                   /* Drapeau à considérer        */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_ADD_LINE_FLAG_METHOD PYTHON_METHOD_DEF         \
+(                                                                   \
+    add_line_flag, "$self, index, flag, /",                         \
+    METH_VARARGS, py_buffer_cache,                                  \
+    "Add one optional flag to those assigned to a given buffer"     \
+    " line. The line is located using the *index* argument.\n"      \
+    "\n"                                                            \
+    "The *index* has to be a simple integer, and the *flag* a"      \
+    " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n"     \
+    "\n"                                                            \
+    "An access lock has to be held for the cache; see the"          \
+    " pychrysalide.glibext.BufferCache.lock() function."            \
+)
+
+    ret = PyArg_ParseTuple(args, "nO&", &index, convert_to_buffer_line_flags, &flag);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_add_line_flag(cache, index, flag);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = classe représentant un tampon de code.                *
 *                args = arguments fournis à l'appel.                          *
 *                                                                             *
@@ -617,6 +673,56 @@ static PyObject *py_buffer_cache_get_line_flags(PyObject *self, PyObject *args)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : self = tampon de lignes à venir consulter.                   *
+*                args = arguments fournis à l'appel.                          *
+*                                                                             *
+*  Description : Retire une propriété particulière attachée à une ligne.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_buffer_cache_remove_line_flag(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Trouvailles à retourner     */
+    size_t index;                           /* Indice de la ligne visée    */
+    BufferLineFlags flag;                   /* Drapeau à considérer        */
+    int ret;                                /* Bilan de lecture des args.  */
+    GBufferCache *cache;                    /* Tampon natif à consulter    */
+
+#define BUFFER_CACHE_REMOVE_LINE_FLAG_METHOD PYTHON_METHOD_DEF          \
+(                                                                       \
+    remove_line_flag, "$self, index, flag, /",                          \
+    METH_VARARGS, py_buffer_cache,                                      \
+    "Remove one optional flag from those assigned to a given buffer"    \
+    " line. The line is located using the *index* argument.\n"          \
+    "\n"                                                                \
+    "The *index* has to be a simple integer, and the *flag* a"          \
+    " pychrysalide.glibext.BufferLine.BufferLineFlags value.\n"         \
+    "\n"                                                                \
+    "An access lock has to be held for the cache; see the"              \
+    " pychrysalide.glibext.BufferCache.lock() function."                \
+)
+
+    ret = PyArg_ParseTuple(args, "nO&", &index, convert_to_buffer_line_flags, &flag);
+    if (!ret) return NULL;
+
+    cache = G_BUFFER_CACHE(pygobject_get(self));
+
+    g_buffer_cache_remove_line_flag(cache, index, flag);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : self = classe représentant un tampon de code.                *
 *                args = arguments fournis à l'appel.                          *
 *                                                                             *
@@ -942,7 +1048,9 @@ PyTypeObject *get_python_buffer_cache_type(void)
         BUFFER_CACHE_APPEND_METHOD,
         BUFFER_CACHE_EXTEND_WITH_METHOD,
         BUFFER_CACHE_TRUNCATE_METHOD,
+        BUFFER_CACHE_ADD_LINE_FLAG_METHOD,
         BUFFER_CACHE_GET_LINE_FLAGS_METHOD,
+        BUFFER_CACHE_REMOVE_LINE_FLAG_METHOD,
         BUFFER_CACHE_FIND_LINE_BY_INDEX_METHOD,
         BUFFER_CACHE_LOOK_FOR_FLAG_METHOD,
         { NULL }
diff --git a/plugins/pychrysalide/glibext/bufferline.c b/plugins/pychrysalide/glibext/bufferline.c
index ea84567..c88fe7f 100644
--- a/plugins/pychrysalide/glibext/bufferline.c
+++ b/plugins/pychrysalide/glibext/bufferline.c
@@ -51,12 +51,6 @@ static PyObject *py_buffer_line_append_text(PyObject *, PyObject *);
 /* Reconstruit et fournit le texte présent sur une ligne tampon. */
 static PyObject *py_buffer_line_get_text(PyObject *, PyObject *);
 
-/* Ajoute une propriété particulière à une ligne donnée. */
-static PyObject *py_buffer_line_add_flag(PyObject *, PyObject *);
-
-/* Retire une propriété particulière à une ligne donnée. */
-static PyObject *py_buffer_line_remove_flag(PyObject *, PyObject *);
-
 /* Renseigne sur les propriétés particulières liées à une ligne. */
 static PyObject *py_buffer_line_get_flags(PyObject *, void *);
 
@@ -93,7 +87,9 @@ static PyObject *py_buffer_line_new(PyTypeObject *type, PyObject *args, PyObject
     "    BufferLine()"                                                      \
     "\n"                                                                    \
     "Such objets aim to be created from the Chrysalide core only, and"      \
-    " then get populated by plugins on demand."
+    " then get populated on demand. Thus, these lines can be viewed as"     \
+    " cached lines and their properties have to be set through the"         \
+    " pychrysalide.glibext.BufferCache instance which contains them."
 
     /* Validations diverses */
 
@@ -235,78 +231,6 @@ static PyObject *py_buffer_line_get_text(PyObject *self, PyObject *args)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : self = classe représentant une ligne de tampon.              *
-*                args = arguments fournis à l'appel.                          *
-*                                                                             *
-*  Description : Ajoute une propriété particulière à une ligne donnée.        *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static PyObject *py_buffer_line_add_flag(PyObject *self, PyObject *args)
-{
-    BufferLineFlags flag;                   /* Drapeau à considérer        */
-    int ret;                                /* Bilan de lecture des args.  */
-    GBufferLine *line;                      /* Version native              */
-
-    ret = PyArg_ParseTuple(args, "I", &flag);
-    if (!ret) return NULL;
-
-    if ((flag & ~BLF_ALL) != 0)
-    {
-        PyErr_SetString(PyExc_ValueError, _("Invalid flag"));
-        return NULL;
-    }
-
-    line = G_BUFFER_LINE(pygobject_get(self));
-    g_buffer_line_add_flag(line, flag);
-
-    Py_RETURN_NONE;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : self = classe représentant une ligne de tampon.              *
-*                args = arguments fournis à l'appel.                          *
-*                                                                             *
-*  Description : Retire une propriété particulière à une ligne donnée.        *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static PyObject *py_buffer_line_remove_flag(PyObject *self, PyObject *args)
-{
-    BufferLineFlags flag;                   /* Drapeau à considérer        */
-    int ret;                                /* Bilan de lecture des args.  */
-    GBufferLine *line;                      /* Version native              */
-
-    ret = PyArg_ParseTuple(args, "I", &flag);
-    if (!ret) return NULL;
-
-    if ((flag & ~BLF_ALL) != 0)
-    {
-        PyErr_SetString(PyExc_ValueError, _("Invalid flag"));
-        return NULL;
-    }
-
-    line = G_BUFFER_LINE(pygobject_get(self));
-    g_buffer_line_remove_flag(line, flag);
-
-    Py_RETURN_NONE;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : self    = objet Python concerné par l'appel.                 *
 *                closure = non utilisé ici.                                   *
 *                                                                             *
@@ -355,16 +279,6 @@ PyTypeObject *get_python_buffer_line_type(void)
             METH_VARARGS,
             "get_text($self, first_col, last_col, markup, /)\n--\n\nProvide the text of a buffer line."
         },
-        {
-            "add_flag", py_buffer_line_add_flag,
-            METH_VARARGS,
-            "add_flag($self, flag, /)\n--\n\nAdd a flag to the buffer line."
-        },
-        {
-            "remove_flag", py_buffer_line_remove_flag,
-            METH_VARARGS,
-            "remove_flag($self, flag, /)\n--\n\nRemove a flag from the buffer line."
-        },
         { NULL }
     };
 
diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c
index 57e675b..4c7a496 100644
--- a/src/analysis/db/collection.c
+++ b/src/analysis/db/collection.c
@@ -652,7 +652,7 @@ static void g_db_collection_set_last_item(GDbCollection *collec, GDbItem *item,
 
     else
     {
-        if (g_db_item_get_flags(item) & DIF_ERASER)
+        if ((g_db_item_get_flags(item) & DIF_ERASER) == 0)
         {
             g_object_ref(G_OBJECT(item));
             g_object_ref(G_OBJECT(item));
diff --git a/src/analysis/db/items/bookmark.c b/src/analysis/db/items/bookmark.c
index f493559..2afbbc0 100644
--- a/src/analysis/db/items/bookmark.c
+++ b/src/analysis/db/items/bookmark.c
@@ -526,7 +526,6 @@ static bool g_db_bookmark_run(GDbBookmark *bookmark, GLoadedBinary *binary, bool
     GBufferCache *cache;                    /* Tampon d'impression colorée */
     GLineCursor *cursor;                    /* Emplacement dans un tampon  */
     size_t index;                           /* Indice de ligne à traiter   */
-    GBufferLine *line;                      /* Ligne de tampon à marquer   */
 
     result = false;
 
@@ -544,22 +543,18 @@ static bool g_db_bookmark_run(GDbBookmark *bookmark, GLoadedBinary *binary, bool
 
     g_object_unref(G_OBJECT(cursor));
 
-    line = g_buffer_cache_find_line_by_index(cache, index);
+    index = g_buffer_cache_look_for_flag(cache, index, BLF_HAS_CODE);
 
     /* Application du changement */
 
-    result = (line != NULL);
+    result = (index < g_buffer_cache_count_lines(cache));
 
     if (result)
     {
         if (set)
-            g_buffer_line_add_flag(line, BLF_BOOKMARK);
+            g_buffer_cache_add_line_flag(cache, index, BLF_BOOKMARK);
         else
-            g_buffer_line_remove_flag(line, BLF_BOOKMARK);
-
-        g_object_unref(G_OBJECT(line));
-
-        g_buffer_cache_throw_update_at_index(cache, index);
+            g_buffer_cache_remove_line_flag(cache, index, BLF_BOOKMARK);
 
     }
 
diff --git a/src/glibext/buffercache-int.h b/src/glibext/buffercache-int.h
index 3dfd5e2..2ffa8ec 100644
--- a/src/glibext/buffercache-int.h
+++ b/src/glibext/buffercache-int.h
@@ -89,6 +89,8 @@ struct _GBufferCacheClass
 
     void (* size_changed) (GBufferCache *, bool, size_t, size_t);
 
+    void (* line_updated) (GBufferCache *, size_t);
+
 };
 
 
diff --git a/src/glibext/buffercache.c b/src/glibext/buffercache.c
index 6f52956..169c75e 100644
--- a/src/glibext/buffercache.c
+++ b/src/glibext/buffercache.c
@@ -397,6 +397,8 @@ static GBufferLine *get_cache_info_line(cache_info *info, const GWidthTracker *t
     {
         result = g_buffer_line_new(g_width_tracker_count_columns(tracker));
 
+        g_buffer_line_add_flag(result, info->extra_flags);
+
         g_object_add_toggle_ref(G_OBJECT(result), (GToggleNotify)on_line_ref_toggle, info);
 
         if (info->count == 1)
@@ -515,6 +517,14 @@ static void g_buffer_cache_class_init(GBufferCacheClass *class)
                  g_cclosure_user_marshal_VOID__BOOLEAN_ULONG_ULONG,
                  G_TYPE_NONE, 3, G_TYPE_BOOLEAN, G_TYPE_ULONG, G_TYPE_ULONG);
 
+    g_signal_new("line-updated",
+                 G_TYPE_BUFFER_CACHE,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET(GBufferCacheClass, line_updated),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__ULONG,
+                 G_TYPE_NONE, 1, G_TYPE_ULONG);
+
 }
 
 
@@ -761,6 +771,8 @@ gint g_buffer_cache_get_text_position(const GBufferCache *cache)
 
 size_t g_buffer_cache_count_lines(const GBufferCache *cache)
 {
+    // TODO check lock
+
     return cache->used;
 
 }
@@ -1337,6 +1349,44 @@ void g_buffer_cache_get_line_cursor(const GBufferCache *cache, size_t index, gin
 *                                                                             *
 *  Paramètres  : cache = tampon de lignes à venir consulter.                  *
 *                index = indice de la ligne visée par la consultation.        *
+*                flag  = propriété à intégrer.                                *
+*                                                                             *
+*  Description : Ajoute une propriété particulière à une ligne.               *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_buffer_cache_add_line_flag(GBufferCache *cache, size_t index, BufferLineFlags flag)
+{
+    cache_info *info;                       /* Accès direct à une ligne    */
+
+    // TODO : check lock
+
+    assert(index < cache->used);
+
+    info = &cache->lines[index];
+
+    if ((info->extra_flags & flag) == 0)
+    {
+        info->extra_flags |= flag;
+
+        if (info->line != NULL)
+            g_buffer_line_add_flag(info->line, flag);
+
+        g_signal_emit_by_name(cache, "line-updated", index);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cache = tampon de lignes à venir consulter.                  *
+*                index = indice de la ligne visée par la consultation.        *
 *                                                                             *
 *  Description : Détermine l'ensemble des propriétés attachées à une ligne.   *
 *                                                                             *
@@ -1381,6 +1431,44 @@ BufferLineFlags g_buffer_cache_get_line_flags(const GBufferCache *cache, size_t
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : cache = tampon de lignes à venir consulter.                  *
+*                index = indice de la ligne visée par la consultation.        *
+*                flag   = propriété à supprimer.                              *
+*                                                                             *
+*  Description : Retire une propriété particulière attachée à une ligne.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_buffer_cache_remove_line_flag(GBufferCache *cache, size_t index, BufferLineFlags flag)
+{
+    cache_info *info;                       /* Accès direct à une ligne    */
+
+    // TODO : check lock
+
+    assert(index < cache->used);
+
+    info = &cache->lines[index];
+
+    if ((info->extra_flags & flag) != 0)
+    {
+        info->extra_flags &= ~flag;
+
+        if (info->line != NULL)
+            g_buffer_line_remove_flag(info->line, flag);
+
+        g_signal_emit_by_name(cache, "line-updated", index);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : cache = tampon de lignes à consulter.                        *
 *                index = indice de la ligne recherchée.                       *
 *                                                                             *
diff --git a/src/glibext/buffercache.h b/src/glibext/buffercache.h
index 61114b0..7422c76 100644
--- a/src/glibext/buffercache.h
+++ b/src/glibext/buffercache.h
@@ -99,14 +99,18 @@ void g_buffer_cache_truncate(GBufferCache *, size_t);
 /* Retrouve l'emplacement correspondant à une position de ligne. */
 void g_buffer_cache_get_line_cursor(const GBufferCache *, size_t, gint, GLineCursor **);
 
+/* Ajoute une propriété particulière à une ligne. */
+void g_buffer_cache_add_line_flag(GBufferCache *, size_t, BufferLineFlags);
+
 /* Détermine l'ensemble des propriétés attachées à une ligne. */
 BufferLineFlags g_buffer_cache_get_line_flags(const GBufferCache *, size_t);
 
+/* Retire une propriété particulière attachée à une ligne. */
+void g_buffer_cache_remove_line_flag(GBufferCache *, size_t, BufferLineFlags);
+
 #define g_buffer_cache_lock(c)
 #define g_buffer_cache_unlock(c)
 
-#define g_buffer_cache_throw_update_at_index(c, i) // check locked
-
 /* Retrouve une ligne au sein d'un tampon avec un indice. */
 GBufferLine *g_buffer_cache_find_line_by_index(const GBufferCache *, size_t);
 
diff --git a/src/glibext/bufferline.c b/src/glibext/bufferline.c
index dd6bc19..85fe027 100644
--- a/src/glibext/bufferline.c
+++ b/src/glibext/bufferline.c
@@ -1430,7 +1430,7 @@ void g_buffer_line_draw(GBufferLine *line, size_t index, cairo_t *cairo, gint x_
     size_t i;                               /* Boucle de parcours          */
     gint max_width;                         /* Largeur maximale de colonne */
 
-    if (line->flags != BLF_NONE && line->flags != BLF_HAS_CODE)
+    if (line->flags != BLF_NONE)
     {
         class = G_BUFFER_LINE_GET_CLASS(line);
 
diff --git a/src/glibext/bufferview.c b/src/glibext/bufferview.c
index 769cd8b..7ac6718 100644
--- a/src/glibext/bufferview.c
+++ b/src/glibext/bufferview.c
@@ -75,6 +75,9 @@ static void g_buffer_view_finalize(GBufferView *);
 /* Accompagne une variation de la quantité de lignes du tampon. */
 static void on_buffer_cache_size_changed(const GBufferCache *, bool, size_t, size_t, GBufferView *);
 
+/* Réagit à la modification d'une ligne du tampon. */
+static void on_buffer_cache_line_updated(const GBufferCache *, size_t, GBufferView *);
+
 /* Calcule la position idéale de curseur pour un point donné. */
 static bool _g_buffer_view_compute_caret_full(GBufferView *, gint, GBufferLine *, size_t, const GDisplayOptions *, cairo_rectangle_int_t *, GLineCursor **);
 
@@ -236,6 +239,7 @@ GBufferView *g_buffer_view_new(GBufferCache *cache, segcnt_list *highlighted)
     g_buffer_view_restrict(result, NULL, NULL);
 
     g_signal_connect(cache, "size-changed", G_CALLBACK(on_buffer_cache_size_changed), result);
+    g_signal_connect(cache, "line-updated", G_CALLBACK(on_buffer_cache_line_updated), result);
 
     if (highlighted != NULL)
     {
@@ -253,6 +257,27 @@ GBufferView *g_buffer_view_new(GBufferCache *cache, segcnt_list *highlighted)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : cache = tampon de lignes cohérentes à manipuler.             *
+*                index = indice de la première ligne actualisée.              *
+*                                                                             *
+*  Description : Réagit à la modification d'une ligne du tampon.              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_buffer_cache_line_updated(const GBufferCache *cache, size_t index, GBufferView *view)
+{
+    if (view->first <= index && index <= view->last)
+        g_signal_emit_by_name(view, "need-redraw");
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : cache = tampon de lignes cohérentes à manipuler.             *
 *                added = indication sur la variation de la taille du tampon.  *
 *                index = indice de la première ligne à traiter.               *
 *                count = nombre de lignes à traiter.                          *
-- 
cgit v0.11.2-87-g4458