[ghex] hex-buffer-direct: Initial commit



commit c750c81c5f1b1843bb26895123e1624b41da7943
Author: Logan Rathbone <poprocks gmail com>
Date:   Tue Jun 7 13:54:46 2022 -0400

    hex-buffer-direct: Initial commit
    
    Written primarily to support reading/writing block devices.
    
    At this stage, there is no special mechanism for privilege escalation.
    That is, a normal user will likely need to be added to appropriate
    groups.

 meson.build             |  25 +-
 meson_options.txt       |   3 +-
 src/hex-buffer-direct.c | 646 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/hex-buffer-direct.h |  47 ++++
 src/hex-document.c      |   2 +
 src/meson.build         |  10 +
 6 files changed, 730 insertions(+), 3 deletions(-)
---
diff --git a/meson.build b/meson.build
index ed846da..8fc22ea 100644
--- a/meson.build
+++ b/meson.build
@@ -17,6 +17,7 @@ endif
 
 # Backend plugins: (nb: only mmap exists at this time along with malloc, which is baked in)
 mmap_backend = get_option('mmap-buffer-backend')
+direct_backend = get_option('direct-buffer-backend')
 
 gir = find_program('g-ir-scanner', required : get_option('introspection'))
 generate_gir = gir.found() and (not meson.is_cross_build() or get_option('introspection').enabled())
@@ -90,8 +91,10 @@ config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR')
 config_h.set('CONFIG_H_SHADED_BOX_MAX', shaded_box_max)
 
 # nb: this config.h flag will likely be removed in a future release
-if mmap_backend
-  config_h.set('BACKEND_MMAP', true)
+#if mmap_backend
+#  config_h.set('BACKEND_MMAP', true)
+if direct_backend
+  config_h.set('BACKEND_DIRECT', true)
 endif
 
 config_h.set_quoted('LIBGTKHEX_RELEASE_STRING', 'gtkhex-@0@.0'.format(libgtkhex_api_version))
@@ -130,6 +133,23 @@ if mmap_backend
   message('...DONE')
 endif
 
+# direct buffer backend: Check for required headers
+if direct_backend
+  message('Checking dependencies for `direct` buffer backend...')
+
+  check_headers_direct = [
+    'unistd.h',
+    'fcntl.h',
+    'sys/ioctl.h',
+    'linux/fs.h',
+    ]
+  foreach h : check_headers_direct
+    cc.has_header(h, required: true)
+  endforeach
+
+  message('...DONE')
+endif
+
 glib_ver = '>= 2.68.0'
 gmodule_dep = dependency('gmodule-2.0')
 glib_dep = dependency('glib-2.0', version: glib_ver)
@@ -165,6 +185,7 @@ summary({'prefix': ghex_prefix,
 
 summary({'Development build': get_option('development'),
          '`mmap` buffer backend': mmap_backend,
+         '`direct` buffer backend': direct_backend,
          'Static HTML help': get_option('static-html-help'),
          'API Documentation': build_gtk_doc,
         }, section: 'Configuration')
diff --git a/meson_options.txt b/meson_options.txt
index a5795fc..e7276c6 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -3,7 +3,8 @@
 option('docdir', type: 'string', value: 'share/doc', description: 'docdir (defaults to: share/doc)')
 
 option('development', type: 'boolean', value: false, description: 'If this is a development build')
-option('mmap-buffer-backend', type : 'boolean', value: true, description: 'Build mmap buffer backend')
+option('mmap-buffer-backend', type : 'boolean', value: true, description: 'Build `mmap` buffer backend')
+option('direct-buffer-backend', type : 'boolean', value: true, description: 'Build `direct` buffer backend')
 option('introspection', type: 'feature', value: 'auto', description: 'Generate gir data (requires 
gobject-introspection)')
 option('gtk_doc', type: 'boolean', value: false, description: 'Build reference manual (requires gtk-doc)')
 option('static-html-help', type: 'boolean', value: false, description: 'Install static HTML help files (for 
systems without Yelp)')
diff --git a/src/hex-buffer-direct.c b/src/hex-buffer-direct.c
new file mode 100644
index 0000000..23a4ccc
--- /dev/null
+++ b/src/hex-buffer-direct.c
@@ -0,0 +1,646 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-document-direct.c - `direct` implementation of the HexBuffer iface.
+ *
+ * Copyright © 2022 Logan Rathbone
+ *
+ * GHex 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GHex 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 GHex; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "hex-buffer-direct.h"
+
+#define HEX_BUFFER_DIRECT_ERROR hex_buffer_direct_error_quark ()
+GQuark
+hex_buffer_direct_error_quark (void)
+{
+  return g_quark_from_static_string ("hex-buffer-direct-error-quark");
+}
+
+/* PROPERTIES */
+
+enum
+{
+       PROP_FILE = 1,
+       N_PROPERTIES
+};
+static GParamSpec *properties[N_PROPERTIES];
+
+static char *invalid_path_msg = N_("The file appears to have an invalid path.");
+
+/**
+ * HexBufferDirect:
+ * 
+ * #HexBufferDirect is an object implementing the [iface@Hex.Buffer] interface,
+ * allowing it to be used as a #HexBuffer backend to be used with
+ * [class@Hex.Document].
+ *
+ * #HexBufferDirect allows for direct reading/writing of files, and is
+ * primarily intended to be used with block devices.
+ *
+ * #HexBufferDirect depends upon UNIX/POSIX system calls; as such, it will
+ * be unlikely to be available under WIN32.
+ */
+struct _HexBufferDirect
+{
+       GObject parent_instance;
+
+       GFile *file;
+       GError *error;          /* no custom codes; use codes from errno */
+       int last_errno;         /* cache in case we need to re-report errno error. */
+
+       int fd;                         /* file descriptor for direct read/write */
+       gint64 payload;         /* size of the payload */
+
+       gint64 clean_bytes;     /* 'clean' size of the file (no additions or deletions */
+       GHashTable *changes;
+};
+
+static void hex_buffer_direct_iface_init (HexBufferInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (HexBufferDirect, hex_buffer_direct, G_TYPE_OBJECT,
+               G_IMPLEMENT_INTERFACE (HEX_TYPE_BUFFER, hex_buffer_direct_iface_init))
+
+/* FORWARD DECLARATIONS */
+       
+static gboolean        hex_buffer_direct_set_file (HexBuffer *buf, GFile *file);
+static GFile * hex_buffer_direct_get_file (HexBuffer *buf);
+
+/* PROPERTIES - GETTERS AND SETTERS */
+
+static void
+hex_buffer_direct_set_property (GObject *object,
+               guint property_id,
+               const GValue *value,
+               GParamSpec *pspec)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT(object);
+       HexBuffer *buf = HEX_BUFFER(object);
+
+       switch (property_id)
+       {
+               case PROP_FILE:
+                       hex_buffer_direct_set_file (buf, g_value_get_object (value));
+                       break;
+
+               default:
+                       /* We don't have any other property... */
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                       break;
+       }
+}
+
+static void
+hex_buffer_direct_get_property (GObject *object,
+               guint property_id,
+               GValue *value,
+               GParamSpec *pspec)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT(object);
+       HexBuffer *buf = HEX_BUFFER(object);
+
+       switch (property_id)
+       {
+               case PROP_FILE:
+                       g_value_set_object (value, self->file);
+                       break;
+
+               default:
+                       /* We don't have any other property... */
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+                       break;
+       }
+}
+
+/* PRIVATE FUNCTIONS AND HELPERS */
+
+/* Helper wrapper for g_set_error and to cache errno */
+static void
+set_error (HexBufferDirect *self, const char *blurb)
+{
+       char *message = NULL;
+
+       if (errno) {
+       /* Translators:  the first '%s' is the blurb indicating some kind of an
+        * error has occurred (eg, 'An error has occurred', and the the 2nd '%s'
+        * is the standard error message that will be reported from the system
+        * (eg, 'No such file or directory').
+        */
+               message = g_strdup_printf (_("%s: %s"), blurb, g_strerror (errno));
+       }
+       else
+       {
+               message = g_strdup (blurb);
+       }
+       g_debug ("%s: %s", __func__, message);
+
+       g_clear_error (&self->error);
+
+       g_set_error (&self->error,
+                       HEX_BUFFER_DIRECT_ERROR,
+                       errno,
+                       "%s",
+                       message);
+
+       if (errno)
+               self->last_errno = errno;
+
+       g_free (message);
+}
+
+/* mostly a helper for _get_data and _set_data */
+static char *
+get_file_data (HexBufferDirect *self,
+               gint64 offset,
+               size_t len)
+{
+       char *data = NULL;
+       off_t new_offset;
+       ssize_t nread;
+
+       data = g_malloc (len);
+       new_offset = lseek (self->fd, offset, SEEK_SET);
+
+       g_assert (offset == new_offset);
+
+       errno = 0;
+       nread = read (self->fd, data, len);
+
+       /* FIXME/TODO - test that if nread is less than amount requested, that it
+        * marries up with amount left in payload */
+       if (nread == -1)
+       {
+               set_error (self, _("Failed to read data from file."));
+               g_clear_pointer (&data, g_free);
+       }
+
+       return data;
+}
+
+static int
+create_fd_from_path (HexBufferDirect *self, const char *path)
+{
+       int fd = -1;
+       struct stat statbuf;
+
+       errno = 0;
+
+       if (stat (path, &statbuf) != 0)
+       {
+               if (errno != ENOENT) {
+                       set_error (self,
+                               _("Unable to retrieve file or directory information"));
+                       return -1;
+               }
+
+               errno = 0;
+               fd = open(path, O_CREAT|O_TRUNC|O_RDWR,
+                               S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+
+               if (fd < 0) {
+                       set_error (self, _("Unable to create file"));
+                       return -1;
+               }
+       } 
+       else
+       {
+               /*      We only support regular files and block devices. */
+               if (! S_ISREG(statbuf.st_mode) && ! S_ISBLK(statbuf.st_mode))
+               {
+                       set_error (self, _("Not a regular file or block device"));
+                       return -1;
+               }
+
+               fd = open (path, O_RDWR);
+
+               if (fd < 0) {
+                       errno = 0;
+                       fd = open (path, O_RDONLY);
+                       if (fd < 0) {
+                               set_error (self, _("Unable to open file for reading"));
+                               return -1;
+                       }
+               }
+       }
+       return fd;
+}
+
+/* CONSTRUCTORS AND DESTRUCTORS */
+
+static void
+hex_buffer_direct_init (HexBufferDirect *self)
+{
+       self->fd = -1;
+       self->changes = g_hash_table_new_full (g_int64_hash, g_int64_equal,
+                       g_free, g_free);
+}
+
+static void
+hex_buffer_direct_dispose (GObject *gobject)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT(gobject);
+
+       /* chain up */
+       G_OBJECT_CLASS(hex_buffer_direct_parent_class)->dispose (gobject);
+}
+
+static void
+hex_buffer_direct_finalize (GObject *gobject)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT(gobject);
+
+       if (self->fd >= 0)
+       {
+               close (self->fd);
+       }
+
+       /* chain up */
+       G_OBJECT_CLASS(hex_buffer_direct_parent_class)->finalize (gobject);
+}
+
+static void
+hex_buffer_direct_class_init (HexBufferDirectClass *klass)
+{
+       GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+       
+       gobject_class->finalize = hex_buffer_direct_finalize;
+       gobject_class->dispose = hex_buffer_direct_dispose;
+
+       gobject_class->set_property = hex_buffer_direct_set_property;
+       gobject_class->get_property = hex_buffer_direct_get_property;
+
+       g_object_class_override_property (gobject_class, PROP_FILE, "file");
+}
+
+/* INTERFACE - VFUNC IMPLEMENTATIONS */
+
+static gint64
+hex_buffer_direct_get_payload_size (HexBuffer *buf)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+
+       return self->payload;
+}
+
+static char *
+hex_buffer_direct_get_data (HexBuffer *buf,
+               gint64 offset,
+               size_t len)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       char *data;
+
+       g_return_val_if_fail (self->fd != -1, NULL);
+
+       data = get_file_data (self, offset, len);
+
+       if (! data)
+       {
+               return NULL;
+       }
+
+       for (size_t i = 0; i < len; ++i)
+       {
+               char *cp;
+               gint64 loc = offset + i;
+
+               cp = g_hash_table_lookup (self->changes, &loc);
+               if (cp)
+               {
+                       g_debug ("found change - swapping byte at: %ld", loc);
+                       data[i] = *cp;
+               }
+       }
+
+       return data;
+}
+
+static char
+hex_buffer_direct_get_byte (HexBuffer *buf,
+               gint64 offset)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       char *cp = NULL;
+
+       cp = hex_buffer_direct_get_data (buf, offset, 1);
+
+       if (cp)
+               return cp[0];
+       else
+               return 0;
+}
+
+/* transfer: none */
+static GFile *
+hex_buffer_direct_get_file (HexBuffer *buf)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+
+       return self->file;
+}
+
+
+static gboolean
+hex_buffer_direct_set_file (HexBuffer *buf, GFile *file)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       const char *file_path;
+
+       g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+       file_path = g_file_peek_path (file);
+       if (! file_path)
+       {
+               set_error (self, _(invalid_path_msg));
+               return FALSE;
+       }
+
+       self->file = file;
+       g_object_notify (G_OBJECT(self), "file");
+       return TRUE;
+}
+
+static gboolean
+hex_buffer_direct_read (HexBuffer *buf)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       gint64 bytes = 0;
+       int tmp_fd;
+
+       g_return_val_if_fail (G_IS_FILE (self->file), FALSE);
+
+       const char *file_path = g_file_peek_path (self->file);
+
+       if (! file_path)
+       {
+               set_error (self, _(invalid_path_msg));
+               return FALSE;
+       }
+
+       tmp_fd = create_fd_from_path (self, file_path);
+       if (tmp_fd < 0)
+       {
+               set_error (self, _("Unable to read file"));
+               return FALSE;
+       }
+
+       /* will only return > 0 for a regular file. */
+       bytes = hex_buffer_util_get_file_size (self->file);
+
+       if (! bytes)    /* block device */
+       {
+               gint64 block_file_size;
+
+               if (ioctl (tmp_fd, BLKGETSIZE64, &block_file_size) != 0)
+               {
+                       set_error (self, _("Error attempting to read block device"));
+                       return FALSE;
+               }
+               bytes = block_file_size;
+       }
+
+       self->payload = self->clean_bytes = bytes;
+
+       self->fd = tmp_fd;
+
+       return TRUE;
+}
+
+static gboolean
+hex_buffer_direct_read_finish (HexBuffer *buf,
+               GAsyncResult *result,
+               GError **error)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+
+       g_return_val_if_fail (g_task_is_valid (result, G_OBJECT(self)), FALSE);
+
+       return g_task_propagate_boolean (G_TASK(result), error);
+}
+
+static void
+hex_buffer_direct_read_thread (GTask *task,
+               gpointer source_object,
+               gpointer task_data,
+               GCancellable *cancellable)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (source_object);
+       gboolean success;
+
+       success = hex_buffer_direct_read (HEX_BUFFER(self));
+       if (success)
+               g_task_return_boolean (task, TRUE);
+       else
+               g_task_return_error (task, self->error);
+}
+
+static void
+hex_buffer_direct_read_async (HexBuffer *buf,
+               GCancellable *cancellable,
+               GAsyncReadyCallback callback,
+               gpointer user_data)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       GTask *task;
+
+       task = g_task_new (self, cancellable, callback, user_data);
+       g_task_run_in_thread (task, hex_buffer_direct_read_thread);
+       g_object_unref (task);  /* _run_in_thread takes a ref */
+}
+
+static gboolean
+hex_buffer_direct_set_data (HexBuffer *buf,
+               gint64 offset,
+               size_t len,
+               size_t rep_len,
+               char *data)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+
+       if (rep_len != len)
+       {
+               g_debug ("%s: rep_len != len; returning false", __func__);
+               return FALSE;
+       }
+
+       for (size_t i = 0; i < len; ++i)
+       {
+               gboolean retval;
+               gint64 *ip = g_new (gint64, 1);
+               char *cp = g_new (char, 1);
+
+               *ip = offset + i;
+               *cp = data[i];
+
+               retval = g_hash_table_replace (self->changes, ip, cp);
+
+               // TEST
+               if (retval) /* key did not exist yet */
+               {
+                       g_debug ("key did not exist yet");
+               }
+               else
+               {
+                       char *tmp = NULL;
+
+                       g_debug ("key already existed; replaced");
+
+                       tmp = get_file_data (self, offset, 1);
+
+                       if (*tmp == *cp)
+                       {
+                               g_debug ("key value back to what O.G. file was. Removing hash entry.");
+                               g_hash_table_remove (self->changes, ip);
+                       }
+                       g_free (tmp);
+               }
+       }
+
+       return TRUE;
+}
+
+static gboolean
+hex_buffer_direct_write_to_file (HexBuffer *buf,
+               GFile *file)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       guint len;
+       gint64 **keys;
+
+       g_return_val_if_fail (self->fd != -1, FALSE);
+       g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+       keys = (gint64 **)g_hash_table_get_keys_as_array (self->changes, &len);
+
+       /* FIXME - very inefficient - we should at least implement a sorter
+        * function that puts the changes in order, and merges changes that
+        * are adjacent to one another.
+        */
+       for (guint i = 0; i < len; ++i)
+       {
+               ssize_t nwritten;
+               char *cp = g_hash_table_lookup (self->changes, keys[i]);
+               off_t offset, new_offset;
+
+               offset = *keys[i];
+               new_offset = lseek (self->fd, offset, SEEK_SET);
+               g_assert (offset == new_offset);
+               g_debug ("%u: offset %ld: switch with: %c", i, offset, *cp);
+
+               errno = 0;
+               nwritten = write (self->fd, cp, 1);
+               if (nwritten != 1)
+               {
+                       set_error (self, _("Error writing changes to file"));
+                       return FALSE;
+               }
+       }
+       g_hash_table_remove_all (self->changes);
+       return TRUE;
+}
+
+static gboolean
+hex_buffer_direct_write_to_file_finish (HexBuffer *buf,
+               GAsyncResult *result,
+               GError **error)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+
+       g_return_val_if_fail (g_task_is_valid (result, G_OBJECT(self)), FALSE);
+
+       return g_task_propagate_boolean (G_TASK(result), error);
+}
+
+static void
+hex_buffer_direct_write_thread (GTask *task,
+               gpointer source_object,
+               gpointer task_data,
+               GCancellable *cancellable)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (source_object);
+       GFile *file = G_FILE (task_data);
+       gboolean success;
+
+       success = hex_buffer_direct_write_to_file (HEX_BUFFER(self), file);
+       if (success)
+               g_task_return_boolean (task, TRUE);
+       else
+               g_task_return_error (task, self->error);
+}
+
+static void
+hex_buffer_direct_write_to_file_async (HexBuffer *buf,
+               GFile *file,
+               GCancellable *cancellable,
+               GAsyncReadyCallback callback,
+               gpointer user_data)
+{
+       HexBufferDirect *self = HEX_BUFFER_DIRECT (buf);
+       GTask *task;
+
+       task = g_task_new (self, cancellable, callback, user_data);
+       g_task_set_task_data (task, file, NULL);
+       g_task_run_in_thread (task, hex_buffer_direct_write_thread);
+       g_object_unref (task);  /* _run_in_thread takes a ref */
+}
+
+/* PUBLIC FUNCTIONS */
+
+/**
+ * hex_buffer_direct_new:
+ * @file: a #GFile pointing to a valid file on the system
+ *
+ * Create a new #HexBufferDirect object.
+ *
+ * Returns: a new #HexBufferDirect object, automatically cast to a #HexBuffer
+ * type, or %NULL if the operation failed.
+ */
+HexBuffer *
+hex_buffer_direct_new (GFile *file)
+{
+       HexBufferDirect *self = g_object_new (HEX_TYPE_BUFFER_DIRECT, NULL);
+
+       if (file)
+       {
+               /* If a path is provided but it can't be set, nullify the object */
+               if (! hex_buffer_direct_set_file (HEX_BUFFER(self), file))
+                       g_clear_object (&self);
+       }
+
+       if (self)
+               return HEX_BUFFER(self);
+       else
+               return NULL;
+}
+
+/* INTERFACE IMPLEMENTATION FUNCTIONS */
+
+static void
+hex_buffer_direct_iface_init (HexBufferInterface *iface)
+{
+       iface->get_data = hex_buffer_direct_get_data;
+       iface->get_byte = hex_buffer_direct_get_byte;
+       iface->set_data = hex_buffer_direct_set_data;
+       iface->get_file = hex_buffer_direct_get_file;
+       iface->set_file = hex_buffer_direct_set_file;
+       iface->read = hex_buffer_direct_read;
+       iface->read_async = hex_buffer_direct_read_async;
+       iface->read_finish = hex_buffer_direct_read_finish;
+       iface->write_to_file = hex_buffer_direct_write_to_file;
+       iface->write_to_file_async = hex_buffer_direct_write_to_file_async;
+       iface->write_to_file_finish = hex_buffer_direct_write_to_file_finish;
+       iface->get_payload_size = hex_buffer_direct_get_payload_size;
+}
diff --git a/src/hex-buffer-direct.h b/src/hex-buffer-direct.h
new file mode 100644
index 0000000..e59e876
--- /dev/null
+++ b/src/hex-buffer-direct.h
@@ -0,0 +1,47 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-document-direct.h - `direct` implementation of the HexBuffer iface.
+ *
+ * Copyright © 2022 Logan Rathbone
+ *
+ * GHex 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * GHex 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 GHex; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef HEX_DOCUMENT_DIRECT_H
+#define HEX_DOCUMENT_DIRECT_H
+
+#include <hex-buffer-iface.h>
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+G_BEGIN_DECLS
+
+#define HEX_TYPE_BUFFER_DIRECT hex_buffer_direct_get_type ()
+G_DECLARE_FINAL_TYPE (HexBufferDirect, hex_buffer_direct, HEX, BUFFER_DIRECT, GObject)
+
+/* HexBufferNewFunc declaration */
+HexBuffer *hex_buffer_direct_new (GFile *file);
+
+G_END_DECLS
+#endif
diff --git a/src/hex-document.c b/src/hex-document.c
index 83e1235..2d3afa1 100644
--- a/src/hex-document.c
+++ b/src/hex-document.c
@@ -358,6 +358,8 @@ hex_document_init (HexDocument *doc)
 {
 #ifdef BACKEND_MMAP
        doc->buffer = hex_buffer_util_new ("mmap", NULL);
+#elif defined BACKEND_DIRECT
+       doc->buffer = hex_buffer_util_new ("direct", NULL);
 #else
        doc->buffer = hex_buffer_util_new (NULL, NULL);
 #endif
diff --git a/src/meson.build b/src/meson.build
index 4091859..80a37b4 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -102,6 +102,16 @@ if mmap_backend
   )
 endif
 
+if direct_backend
+    shared_module('hex-buffer-direct',
+    sources: 'hex-buffer-direct.c',
+    c_args: libgtkhex_c_args,
+    dependencies: libgtkhex_dep,
+    install_dir: ghex_plugindir,
+    install: true,
+  )
+endif
+
 ghex_sources = [
   'chartable.c',
   'common-ui.c',


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