[ghex/expand-search-options: 4/14] Add new HexDocument API and refactor existing to expand search options.




commit e8922c3c678ba76f47271dfe5ce2c55c5414b338
Author: Logan Rathbone <poprocks gmail com>
Date:   Tue Apr 12 22:27:46 2022 -0400

    Add new HexDocument API and refactor existing to expand search options.
    
    - *_full and *_full_async functions now take FindData structs to
      simplify signatures and to make it cleaner to pass and change values
    (mostly needed to change ->len value)
    
    - find_data->len can be changed by compare_data_full after a search to
      show the length of the *result* - this is a bit confusing upon first
    blush but it does allow for easy communication of length of the match
    which may not be the same as the length of the search string.
    
    - autohighlighting does not work except for exact matches. This is
      because hex_widget_insert_autohighlight() takes a string and
    highlights exact matches. Need to decide how to handle that.
    
    - this is still unstable API, but once stabilized, will need to
      gtk-doc-ify it.

 src/find-options.ui |  11 +-
 src/findreplace.c   |  44 ++++---
 src/hex-document.c  | 343 +++++++++++++++++++++++++++++++++++++++-------------
 src/hex-document.h  |  21 ++++
 4 files changed, 319 insertions(+), 100 deletions(-)
---
diff --git a/src/find-options.ui b/src/find-options.ui
index ad9b6e9..7e3a152 100644
--- a/src/find-options.ui
+++ b/src/find-options.ui
@@ -36,7 +36,16 @@
                                                </layout>
                                        </object>
                                </child>
-                       </object>
+                               <child>
+                                       <object class="GtkCheckButton" id="find_options_ignore_case">
+                                               <property name="label">Ignore case</property>
+                                               <layout>
+                                                       <property name="column">1</property>
+                                                       <property name="row">0</property>
+                                               </layout>
+                                       </object>
+                               </child>
+                       </object> <!-- grid -->
                </child>
        </object>
 </interface>
diff --git a/src/findreplace.c b/src/findreplace.c
index c2d184c..c9b14d6 100644
--- a/src/findreplace.c
+++ b/src/findreplace.c
@@ -73,6 +73,7 @@ typedef struct {
        GtkWidget *options_popover;
        GtkWidget *options_regex;
        gboolean found;
+       size_t last_found_len;
        GCancellable *cancellable;
 
 } FindDialogPrivate;
@@ -255,6 +256,8 @@ find_ready_cb (GObject *source_object,
        if (find_data->found)
        {
                f_priv->found = TRUE;
+               f_priv->last_found_len = find_data->len;
+
                hex_widget_set_cursor (priv->gh, find_data->offset);
 
                /* If string found, insert auto-highlights of search string */
@@ -294,6 +297,7 @@ find_common (FindDialog *self, enum FindDirection direction,
        gint64 str_len;
        gint64 offset;
        char *str;
+       HexDocumentFindData *find_data;
        
        g_return_if_fail (FIND_IS_DIALOG(self));
 
@@ -315,31 +319,39 @@ find_common (FindDialog *self, enum FindDirection direction,
        }
 
        /* Search for requested string */
+
+       find_data = g_new0 (HexDocumentFindData, 1);
+       find_data->what = str;
+       find_data->len = str_len;
+       find_data->found_msg = found_msg;
+       find_data->not_found_msg = not_found_msg;
        
+       g_cancellable_reset (f_priv->cancellable);
+
        if (direction == FIND_FORWARD)
        {
-               g_cancellable_reset (f_priv->cancellable);
-               hex_document_find_forward_async (doc,
-                               f_priv->found == FALSE ? cursor_pos : cursor_pos + 1,
-                               str,
-                               str_len,
-                               &offset,
-                               found_msg,
-                               not_found_msg,
+               // TEST
+
+               find_data->start = f_priv->found == FALSE ?
+                                                               cursor_pos :
+                                                               cursor_pos + f_priv->last_found_len;
+
+               hex_document_find_forward_full_async (doc,
+                               find_data,
+                               HEX_SEARCH_REGEX,       // TEST
                                f_priv->cancellable,
                                find_ready_cb,
                                self);
        }
        else    /* FIND_BACKWARD */
        {
-               hex_document_find_backward_async (doc,
-                               cursor_pos,
-                               str,
-                               str_len,
-                               &offset,
-                               found_msg,
-                               not_found_msg,
-                               NULL,
+               // TEST
+               find_data->start = cursor_pos;
+
+               hex_document_find_backward_full_async (doc,
+                               find_data,
+                               HEX_SEARCH_REGEX,       // TEST
+                               f_priv->cancellable,
                                find_ready_cb,
                                self);
        }
diff --git a/src/hex-document.c b/src/hex-document.c
index 8b7cb91..cbc6130 100644
--- a/src/hex-document.c
+++ b/src/hex-document.c
@@ -52,6 +52,7 @@ static void undo_stack_ascend           (HexDocument *doc);
 static void undo_stack_free             (HexDocument *doc);
 
 #define DEFAULT_UNDO_DEPTH 1024
+#define REGEX_SEARCH_LEN 1024  /* FIXME/TODO: This is kind of lazy. Stopgap? */
 
 enum {
        DOCUMENT_CHANGED,
@@ -83,6 +84,19 @@ hex_document_find_data_copy (HexDocumentFindData *data)
 G_DEFINE_BOXED_TYPE (HexDocumentFindData, hex_document_find_data,
                hex_document_find_data_copy, g_free)
 
+/* HexDocumentFindFullData (private) */
+typedef struct
+{
+       HexDocumentFindData *find_data;
+       HexSearchFlags flags;
+} HexDocumentFindFullData;
+
+static void
+hex_document_find_full_data_free (HexDocumentFindFullData *full_data)
+{
+       g_clear_pointer (&full_data->find_data, g_free);
+       g_free (full_data);
+}
 
 /* HexChangeData GType Definitions */
 
@@ -1095,6 +1109,93 @@ hex_document_export_html (HexDocument *doc,
        return TRUE;
 }
 
+static int
+hex_document_compare_data_full (HexDocument *doc,
+               HexDocumentFindData *find_data,
+               HexSearchFlags flags,
+               gint64 pos)
+{
+       char *cp = 0;
+       GError *local_error = NULL;
+       int retval = 1;         /* match will make it zero, so set it non-zero now. */
+
+       g_return_val_if_fail (find_data, 0);
+       g_return_val_if_fail (find_data->what, 0);
+
+       if (flags & HEX_SEARCH_REGEX)
+       {
+               GRegex *regex;
+               GMatchInfo *match_info;
+               char *regex_search_str;
+
+               /* GRegex doesn't let you specify the length of the search string, so
+                * it needs to be NULL-terminated.
+                */
+               regex_search_str = g_malloc (find_data->len+1);
+
+               memcpy (regex_search_str, find_data->what, find_data->len);
+               regex_search_str[find_data->len] = 0;
+
+               cp = hex_buffer_get_data (doc->buffer, pos, REGEX_SEARCH_LEN);
+
+               regex = g_regex_new (regex_search_str,
+                               G_REGEX_RAW,
+                               G_REGEX_MATCH_ANCHORED,
+                               &local_error);
+
+               g_free (regex_search_str);
+
+               /* sanity check */
+               if (!regex || local_error)
+               {
+                       g_debug ("%s: error: %s", __func__, local_error->message);
+                       goto out;
+               }
+
+               if (g_regex_match_full (regex, cp,
+                                       REGEX_SEARCH_LEN,       /* length of string being searched */
+                                       0,                                      /* start pos */
+                                       0,                                      /* addl match_options */
+                                       &match_info,
+                                       &local_error))
+               {
+                       char *word = g_match_info_fetch (match_info, 0);
+
+                       g_debug ("Found: %s", word);
+                       find_data->len = strlen (word);
+                       g_free (word);
+                       retval = 0;
+               }
+               else
+               {
+                       if (local_error)
+                       {
+                               g_debug ("%s: error: %s",
+                                               __func__,
+                                               local_error ? local_error->message : NULL);
+                       }
+                       retval = 1;
+               }
+       }
+       else
+       {
+               cp = hex_buffer_get_data (doc->buffer, pos, find_data->len);
+
+               if (flags & HEX_SEARCH_IGNORE_CASE)
+               {
+                       retval = g_ascii_strncasecmp (cp, find_data->what, find_data->len);
+               }
+               else
+               {
+                       retval = memcmp (cp, find_data->what, find_data->len);
+               }
+       }
+out:
+       g_clear_error (&local_error);
+       g_free (cp);
+       return retval;
+}
+
 /**
  * hex_document_compare_data:
  * @doc: a [class@Hex.Document] object
@@ -1110,19 +1211,42 @@ int
 hex_document_compare_data (HexDocument *doc,
                const char *what, gint64 pos, size_t len)
 {
-       char c;
+       int retval;
+       HexDocumentFindData *find_data = g_new0 (HexDocumentFindData, 1);
 
-       g_return_val_if_fail (what, 0);
+       find_data->what = what;
+       find_data->len = len;
 
-       for (size_t i = 0; i < len; i++, what++)
-       {
-               c = hex_buffer_get_byte (doc->buffer, pos + i);
+       retval = hex_document_compare_data_full (doc, find_data, HEX_SEARCH_NONE,
+                       pos);
+       g_free (find_data);
+
+       return retval;
+}
 
-               if (c != *what)
-                       return (c - *what);
+gboolean
+hex_document_find_forward_full (HexDocument *doc,
+               HexDocumentFindData *find_data,
+               HexSearchFlags flags)
+{
+       gint64 pos;
+       gint64 payload = hex_buffer_get_payload_size (
+                       hex_document_get_buffer (doc));
+
+       g_return_val_if_fail (find_data != NULL, FALSE);
+
+       pos = find_data->start;
+       while (pos < payload)
+       {
+               if (hex_document_compare_data_full (doc, find_data, flags, pos) == 0)
+               {
+                       find_data->offset = pos;
+                       return TRUE;
+               }
+               pos++;
        }
-       
-       return 0;
+
+       return FALSE;
 }
 
 /**
@@ -1148,22 +1272,19 @@ gboolean
 hex_document_find_forward (HexDocument *doc, gint64 start, const char *what,
                                                  size_t len, gint64 *offset)
 {
-       gint64 pos;
-       gint64 payload = hex_buffer_get_payload_size (
-                       hex_document_get_buffer (doc));
+       gboolean retval;
+       HexDocumentFindData *find_data = g_new0 (HexDocumentFindData, 1);
 
-       pos = start;
-       while (pos < payload)
-       {
-               if (hex_document_compare_data (doc, what, pos, len) == 0)
-               {
-                       *offset = pos;
-                       return TRUE;
-               }
-               pos++;
-       }
+       find_data->start = start;
+       find_data->what = what;
+       find_data->len = len;
 
-       return FALSE;
+       retval = hex_document_find_forward_full (doc, find_data, HEX_SEARCH_NONE);
+       *offset = find_data->offset;
+
+       g_free (find_data);
+
+       return retval;
 }
 
 /**
@@ -1185,6 +1306,27 @@ hex_document_find_finish (HexDocument *doc,
        return g_task_propagate_pointer (G_TASK(result), NULL);
 }
 
+#define FIND_FULL_THREAD_TEMPLATE(FUNC_NAME, FUNC_TO_CALL) \
+static void \
+FUNC_NAME (GTask *task, \
+               gpointer source_object, \
+               gpointer task_data, \
+               GCancellable *cancellable) \
+{ \
+       HexDocument *doc = HEX_DOCUMENT (source_object); \
+       HexDocumentFindFullData *full_data = task_data; \
+       HexDocumentFindData *find_data = full_data->find_data; \
+ \
+       g_return_if_fail (find_data); \
+ \
+       find_data->found = FUNC_TO_CALL (doc, find_data, full_data->flags); \
+ \
+       g_task_return_pointer (task, find_data, g_free); \
+}
+
+FIND_FULL_THREAD_TEMPLATE(hex_document_find_forward_full_thread,
+               hex_document_find_forward_full)
+
 static void
 hex_document_find_forward_thread (GTask *task,
                gpointer source_object,
@@ -1201,6 +1343,34 @@ hex_document_find_forward_thread (GTask *task,
        g_task_return_pointer (task, find_data, g_free);
 }
 
+#define FIND_FULL_ASYNC_TEMPLATE(FUNC_NAME, FUNC_TO_CALL) \
+void \
+FUNC_NAME (HexDocument *doc, \
+               HexDocumentFindData *find_data, \
+               HexSearchFlags flags, \
+               GCancellable *cancellable, \
+               GAsyncReadyCallback callback, \
+               gpointer user_data) \
+{ \
+       GTask *task; \
+       HexDocumentFindFullData *full_data = g_new0 (HexDocumentFindFullData, 1); \
+ \
+       /* This is kind of gross, but we can't really do any better without \
+        * breaking libgtkhex-4.0 ABI \
+        */ \
+       full_data->find_data = find_data; \
+       full_data->flags = flags; \
+ \
+       task = g_task_new (doc, cancellable, callback, user_data); \
+       g_task_set_return_on_cancel (task, TRUE); \
+       g_task_set_task_data (task, full_data, \
+                       (GDestroyNotify)hex_document_find_full_data_free); \
+       g_task_run_in_thread (task, FUNC_TO_CALL); \
+}
+
+FIND_FULL_ASYNC_TEMPLATE(hex_document_find_forward_full_async, 
+               hex_document_find_forward_full_thread)
+
 /* CROSSREF: hex-document.h - HexDocumentFindData */
 /**
  * hex_document_find_forward_async:
@@ -1221,31 +1391,57 @@ hex_document_find_forward_thread (GTask *task,
  * function that should generally be used by a GUI client to find a string
  * forwards in a #HexDocument.
  */
-void
-hex_document_find_forward_async (HexDocument *doc,
-               gint64 start,
-               const char *what,
-               size_t len,
-               gint64 *offset,
-               const char *found_msg,
-               const char *not_found_msg,
-               GCancellable *cancellable,
-               GAsyncReadyCallback callback,
-               gpointer user_data)
+
+#define FIND_ASYNC_TEMPLATE(FUNC_NAME, FUNC_TO_CALL) \
+void \
+FUNC_NAME (HexDocument *doc, \
+               gint64 start, \
+               const char *what, \
+               size_t len, \
+               gint64 *offset, \
+               const char *found_msg, \
+               const char *not_found_msg, \
+               GCancellable *cancellable, \
+               GAsyncReadyCallback callback, \
+               gpointer user_data) \
+{ \
+       GTask *task; \
+       HexDocumentFindData *find_data = g_new0 (HexDocumentFindData, 1); \
+ \
+       find_data->start = start; \
+       find_data->what = what; \
+       find_data->len = len; \
+       find_data->found_msg = found_msg; \
+       find_data->not_found_msg = not_found_msg; \
+ \
+       task = g_task_new (doc, cancellable, callback, user_data); \
+       g_task_set_return_on_cancel (task, TRUE); \
+       g_task_set_task_data (task, find_data, g_free); \
+       g_task_run_in_thread (task, FUNC_TO_CALL); \
+}
+
+FIND_ASYNC_TEMPLATE(hex_document_find_forward_async,
+               hex_document_find_forward_thread)
+
+gboolean
+hex_document_find_backward_full (HexDocument *doc,
+               HexDocumentFindData *find_data,
+               HexSearchFlags flags)
 {
-       GTask *task;
-       HexDocumentFindData *find_data = g_new0 (HexDocumentFindData, 1);
+       gint64 pos = find_data->start;
+       
+       if (pos == 0)
+               return FALSE;
 
-       find_data->start = start;
-       find_data->what = what;
-       find_data->len = len;
-       find_data->found_msg = found_msg;
-       find_data->not_found_msg = not_found_msg;
+       do {
+               pos--;
+               if (hex_document_compare_data_full (doc, find_data, flags, pos) == 0) {
+                       find_data->offset = pos;
+                       return TRUE;
+               }
+       } while (pos > 0);
 
-       task = g_task_new (doc, cancellable, callback, user_data);
-       g_task_set_return_on_cancel (task, TRUE);
-       g_task_set_task_data (task, find_data, g_free);
-       g_task_run_in_thread (task, hex_document_find_forward_thread);
+       return FALSE;
 }
 
 /**
@@ -1271,22 +1467,24 @@ gboolean
 hex_document_find_backward (HexDocument *doc, gint64 start, const char *what,
                                                   size_t len, gint64 *offset)
 {
-       gint64 pos = start;
-       
-       if (pos == 0)
-               return FALSE;
+       gboolean retval;
+       HexDocumentFindData *find_data = g_new0 (HexDocumentFindData, 1);
 
-       do {
-               pos--;
-               if (hex_document_compare_data (doc, what, pos, len) == 0) {
-                       *offset = pos;
-                       return TRUE;
-               }
-       } while (pos > 0);
+       find_data->start = start;
+       find_data->what = what;
+       find_data->len = len;
 
-       return FALSE;
+       retval = hex_document_find_backward_full (doc, find_data, HEX_SEARCH_NONE);
+       *offset = find_data->offset;
+
+       g_free (find_data);
+
+       return retval;
 }
 
+FIND_FULL_THREAD_TEMPLATE(hex_document_find_backward_full_thread,
+               hex_document_find_backward_full)
+
 static void
 hex_document_find_backward_thread (GTask *task,
                gpointer source_object,
@@ -1303,6 +1501,9 @@ hex_document_find_backward_thread (GTask *task,
        g_task_return_pointer (task, find_data, g_free);
 }
 
+FIND_FULL_ASYNC_TEMPLATE(hex_document_find_backward_full_async,
+               hex_document_find_backward_full_thread)
+
 /**
  * hex_document_find_backward_async:
  * @doc: a [class@Hex.Document] object
@@ -1322,32 +1523,8 @@ hex_document_find_backward_thread (GTask *task,
  * function that should generally be used by a GUI client to find a string
  * backwards in a #HexDocument.
  */
-void
-hex_document_find_backward_async (HexDocument *doc,
-               gint64 start,
-               const char *what,
-               size_t len,
-               gint64 *offset,
-               const char *found_msg,
-               const char *not_found_msg,
-               GCancellable *cancellable,
-               GAsyncReadyCallback callback,
-               gpointer user_data)
-{
-       GTask *task;
-       HexDocumentFindData *find_data = g_new0 (HexDocumentFindData, 1);
-
-       find_data->start = start;
-       find_data->what = what;
-       find_data->len = len;
-       find_data->found_msg = found_msg;
-       find_data->not_found_msg = not_found_msg;
-
-       task = g_task_new (doc, cancellable, callback, user_data);
-       g_task_set_return_on_cancel (task, TRUE);
-       g_task_set_task_data (task, find_data, g_free);
-       g_task_run_in_thread (task, hex_document_find_backward_thread);
-}
+FIND_ASYNC_TEMPLATE(hex_document_find_backward_async,
+               hex_document_find_backward_thread)
 
 /**
  * hex_document_undo:
diff --git a/src/hex-document.h b/src/hex-document.h
index 41f09f4..20ec536 100644
--- a/src/hex-document.h
+++ b/src/hex-document.h
@@ -55,6 +55,13 @@ typedef enum
        HEX_CHANGE_BYTE
 } HexChangeType;
 
+typedef enum
+{
+       HEX_SEARCH_NONE                         = 0,
+       HEX_SEARCH_REGEX                        = 1 << 0,
+       HEX_SEARCH_IGNORE_CASE          = 1 << 1,
+} HexSearchFlags;
+
 /**
  * HexDocumentFindData:
  * @found: whether the string was found
@@ -161,18 +168,32 @@ int                       hex_document_compare_data (HexDocument *doc, const char *what,
                gint64 pos, size_t len);
 gboolean       hex_document_find_forward (HexDocument *doc, gint64 start,
                const char *what, size_t len, gint64 *offset);
+gboolean hex_document_find_forward_full (HexDocument *doc,
+               HexDocumentFindData *find_data,
+               HexSearchFlags flags);
 
 void   hex_document_find_forward_async (HexDocument *doc, gint64 start,
                const char *what, size_t len, gint64 *offset, const char *found_msg,
                const char *not_found_msg, GCancellable *cancellable,
                GAsyncReadyCallback callback, gpointer user_data);
 
+void   hex_document_find_forward_full_async (HexDocument *doc,
+               HexDocumentFindData *find_data, HexSearchFlags flags,
+               GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
+
 gboolean       hex_document_find_backward (HexDocument *doc, gint64 start,
                const char *what, size_t len, gint64 *offset);
+
+gboolean hex_document_find_backward_full (HexDocument *doc,
+               HexDocumentFindData *find_data,
+               HexSearchFlags flags);
 void           hex_document_find_backward_async (HexDocument *doc, gint64 start,
                const char *what, size_t len, gint64 *offset, const char *found_msg,
                const char *not_found_msg, GCancellable *cancellable,
                GAsyncReadyCallback callback, gpointer user_data);
+void   hex_document_find_backward_full_async (HexDocument *doc,
+               HexDocumentFindData *find_data, HexSearchFlags flags,
+               GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);
 
 HexDocumentFindData *
 hex_document_find_finish (HexDocument *doc, GAsyncResult *result);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]