aboutsummaryrefslogtreecommitdiff
path: root/src_c
diff options
context:
space:
mode:
authorbozo.kopic <bozo@kopic.xyz>2022-05-22 03:02:28 +0200
committerbozo.kopic <bozo@kopic.xyz>2022-05-23 00:15:13 +0200
commitd94ce591f20f0a561a68b239ece14f7f8fd487d9 (patch)
treeab187d9d6f146cf5da84f65b0806b034829cf705 /src_c
parent128ded012ed9a1853f4ef73bd020156d0613446c (diff)
gtk4 rewrite
Diffstat (limited to 'src_c')
-rw-r--r--src_c/main.c389
-rw-r--r--src_c/mblaze.c511
-rw-r--r--src_c/mblaze.h59
3 files changed, 959 insertions, 0 deletions
diff --git a/src_c/main.c b/src_c/main.c
new file mode 100644
index 0000000..ec30a13
--- /dev/null
+++ b/src_c/main.c
@@ -0,0 +1,389 @@
+#include <gtk/gtk.h>
+#include "mblaze.h"
+
+
+typedef struct {
+ GtkTreeStore *directories_store;
+ GtkTreeSelection *directories_selection;
+ GtkTreeStore *messages_store;
+ GtkTreeSelection *messages_selection;
+ GtkTextBuffer *message_buffer;
+} app_data_t;
+
+typedef struct {
+ grefcount rc;
+ GtkTreeStore *store;
+ GtkTreeIter iter;
+} tree_store_iter_data_t;
+
+
+static gchar *get_message_status_icon(mbgui_message_status_t status) {
+ switch (status) {
+ case MBGUI_MSG_STATUS_SEEN:
+ return "mail-read";
+ case MBGUI_MSG_STATUS_FLAGGED:
+ return "starred";
+ case MBGUI_MSG_STATUS_UNSEEN:
+ return "mail-unread";
+ case MBGUI_MSG_STATUS_TRASHED:
+ return "user-trash";
+ case MBGUI_MSG_STATUS_VIRTUAL:
+ return NULL;
+ }
+ return NULL;
+}
+
+
+static gchar *get_selected_directory(app_data_t *data) {
+ GtkTreeIter iter;
+
+ if (!gtk_tree_selection_get_selected(
+ data->directories_selection,
+ (GtkTreeModel **)&(data->directories_store), &iter))
+ return NULL;
+
+ gchar *result;
+ gtk_tree_model_get(GTK_TREE_MODEL(data->directories_store), &iter, 0,
+ &result, -1);
+ return result;
+}
+
+
+static gchar *get_selected_message(app_data_t *data) {
+ GtkTreeIter iter;
+
+ if (!gtk_tree_selection_get_selected(
+ data->messages_selection, (GtkTreeModel **)&(data->messages_store),
+ &iter))
+ return NULL;
+
+ gchar *result;
+ gtk_tree_model_get(GTK_TREE_MODEL(data->messages_store), &iter, 0, &result,
+ -1);
+ return result;
+}
+
+
+static void on_get_message(gchar *path, gchar *message, gpointer user_data) {
+ app_data_t *data = user_data;
+
+ if (!message || !message[0])
+ return;
+
+ gchar *selected_message = get_selected_message(data);
+ if (!selected_message)
+ return;
+
+ int not_selected = g_strcmp0(path, selected_message);
+ g_free(selected_message);
+ if (not_selected)
+ return;
+
+ gtk_text_buffer_set_text(data->message_buffer, message, -1);
+}
+
+
+static void on_messages_selection_changed(GtkTreeSelection *self,
+ gpointer user_data) {
+ app_data_t *data = user_data;
+
+ gtk_text_buffer_set_text(data->message_buffer, "", 0);
+
+ gchar *message = get_selected_message(data);
+ if (!message)
+ return;
+
+ // TODO chech virtual
+
+ mbgui_get_message(message, on_get_message, data);
+ g_free(message);
+}
+
+
+static void add_message(GtkTreeStore *store, mbgui_message_t *message,
+ GtkTreeIter *parent) {
+ GtkTreeIter iter;
+ gtk_tree_store_append(store, &iter, parent);
+ gtk_tree_store_set(store, &iter, 0, message->path->str, 1,
+ get_message_status_icon(message->status), 2,
+ message->subject->str, 3, message->sender->str, 4,
+ message->date->str, -1);
+
+ for (mbgui_message_t *child = message->children; child; child = child->next)
+ add_message(store, child, &iter);
+}
+
+
+static void on_get_messages(gchar *directory, mbgui_message_t *messages,
+ gpointer user_data) {
+ app_data_t *data = user_data;
+
+ gchar *selected_directory = get_selected_directory(data);
+ if (!selected_directory)
+ return;
+
+ int not_selected = g_strcmp0(directory, selected_directory);
+ g_free(selected_directory);
+ if (not_selected)
+ return;
+
+ for (mbgui_message_t *message = messages; message; message = message->next)
+ add_message(data->messages_store, message, NULL);
+}
+
+
+static void on_directories_selection_changed(GtkTreeSelection *self,
+ gpointer user_data) {
+ app_data_t *data = user_data;
+
+ gtk_tree_store_clear(data->messages_store);
+
+ gchar *directory = get_selected_directory(data);
+ if (!directory)
+ return;
+
+ mbgui_get_messages(directory, on_get_messages, data);
+ g_free(directory);
+}
+
+
+static GtkWidget *create_directories(app_data_t *data) {
+ data->directories_store =
+ gtk_tree_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ GtkCellRenderer *icon_renderer = gtk_cell_renderer_pixbuf_new();
+ g_object_set(icon_renderer, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
+
+ GtkCellRenderer *left_renderer = gtk_cell_renderer_text_new();
+ g_object_set(left_renderer, "xalign", 0.0, "xpad", 5, "mode",
+ GTK_CELL_RENDERER_MODE_INERT, NULL);
+
+ GtkCellRenderer *right_renderer = gtk_cell_renderer_text_new();
+ g_object_set(right_renderer, "xalign", 1.0, "mode",
+ GTK_CELL_RENDERER_MODE_INERT, NULL);
+
+ GtkWidget *scrolled_window = gtk_scrolled_window_new();
+
+ GtkWidget *directories =
+ gtk_tree_view_new_with_model(GTK_TREE_MODEL(data->directories_store));
+ gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window),
+ directories);
+
+ GtkTreeViewColumn *col_directory = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(col_directory, "Directory");
+ gtk_tree_view_column_set_expand(col_directory, TRUE);
+ gtk_tree_view_column_pack_start(col_directory, icon_renderer, FALSE);
+ gtk_tree_view_column_set_attributes(col_directory, icon_renderer,
+ "icon-name", 1, NULL);
+ gtk_tree_view_column_pack_start(col_directory, left_renderer, TRUE);
+ gtk_tree_view_column_set_attributes(col_directory, left_renderer, "text", 2,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(directories), col_directory);
+
+ GtkTreeViewColumn *col_unseen = gtk_tree_view_column_new_with_attributes(
+ "Unseen", right_renderer, "text", 3, NULL);
+ gtk_tree_view_column_set_sizing(col_unseen, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(directories), col_unseen);
+
+ GtkTreeViewColumn *col_total = gtk_tree_view_column_new_with_attributes(
+ "Total", right_renderer, "text", 4, NULL);
+ gtk_tree_view_column_set_sizing(col_total, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(directories), col_total);
+
+ data->directories_selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(directories));
+ gtk_tree_selection_set_mode(data->directories_selection,
+ GTK_SELECTION_SINGLE);
+ g_signal_connect(data->directories_selection, "changed",
+ G_CALLBACK(on_directories_selection_changed), data);
+
+ return scrolled_window;
+}
+
+
+static GtkWidget *create_messages(app_data_t *data) {
+ data->messages_store =
+ gtk_tree_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ GtkCellRenderer *icon_renderer = gtk_cell_renderer_pixbuf_new();
+ g_object_set(icon_renderer, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
+
+ GtkCellRenderer *left_renderer = gtk_cell_renderer_text_new();
+ g_object_set(left_renderer, "xalign", 0.0, "xpad", 5, "mode",
+ GTK_CELL_RENDERER_MODE_INERT, NULL);
+
+ GtkWidget *scrolled_window = gtk_scrolled_window_new();
+
+ GtkWidget *messages =
+ gtk_tree_view_new_with_model(GTK_TREE_MODEL(data->messages_store));
+ gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window),
+ messages);
+
+ GtkTreeViewColumn *col_subject = gtk_tree_view_column_new();
+ gtk_tree_view_column_set_title(col_subject, "Subject");
+ gtk_tree_view_column_set_expand(col_subject, TRUE);
+ gtk_tree_view_column_pack_start(col_subject, icon_renderer, FALSE);
+ gtk_tree_view_column_set_attributes(col_subject, icon_renderer, "icon-name",
+ 1, NULL);
+ gtk_tree_view_column_pack_start(col_subject, left_renderer, TRUE);
+ gtk_tree_view_column_set_attributes(col_subject, left_renderer, "text", 2,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(messages), col_subject);
+
+ GtkTreeViewColumn *col_sender = gtk_tree_view_column_new_with_attributes(
+ "Sender", left_renderer, "text", 3, NULL);
+ gtk_tree_view_column_set_sizing(col_sender, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(messages), col_sender);
+
+ GtkTreeViewColumn *col_date = gtk_tree_view_column_new_with_attributes(
+ "Date", left_renderer, "text", 4, NULL);
+ gtk_tree_view_column_set_sizing(col_date, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(messages), col_date);
+
+ data->messages_selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(messages));
+ gtk_tree_selection_set_mode(data->messages_selection, GTK_SELECTION_SINGLE);
+ g_signal_connect(data->messages_selection, "changed",
+ G_CALLBACK(on_messages_selection_changed), data);
+
+ return scrolled_window;
+}
+
+
+static GtkWidget *create_message(app_data_t *data) {
+ data->message_buffer = gtk_text_buffer_new(NULL);
+
+ GtkWidget *scrolled_window = gtk_scrolled_window_new();
+
+ GtkWidget *message = gtk_text_view_new_with_buffer(data->message_buffer);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(message), FALSE);
+ gtk_text_view_set_monospace(GTK_TEXT_VIEW(message), TRUE);
+ gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window),
+ message);
+
+ return scrolled_window;
+}
+
+
+static GtkWidget *create_window(GtkApplication *app, app_data_t *data) {
+ GtkWidget *window = gtk_application_window_new(app);
+ gtk_window_set_title(GTK_WINDOW(window), "mbgui");
+ gtk_window_set_default_size(GTK_WINDOW(window), 600, 800);
+
+ GtkWidget *hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+ gtk_paned_set_position(GTK_PANED(hpaned), 400);
+ gtk_window_set_child(GTK_WINDOW(window), hpaned);
+
+ GtkWidget *directories = create_directories(data);
+ gtk_paned_set_start_child(GTK_PANED(hpaned), directories);
+ gtk_paned_set_resize_start_child(GTK_PANED(hpaned), TRUE);
+
+ GtkWidget *vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
+ gtk_paned_set_position(GTK_PANED(vpaned), 400);
+ gtk_paned_set_end_child(GTK_PANED(hpaned), vpaned);
+ gtk_paned_set_resize_end_child(GTK_PANED(hpaned), TRUE);
+
+ GtkWidget *messages = create_messages(data);
+ gtk_paned_set_start_child(GTK_PANED(vpaned), messages);
+ gtk_paned_set_resize_start_child(GTK_PANED(vpaned), TRUE);
+
+ GtkWidget *message = create_message(data);
+ gtk_paned_set_end_child(GTK_PANED(vpaned), message);
+ gtk_paned_set_resize_end_child(GTK_PANED(vpaned), TRUE);
+
+ return window;
+}
+
+
+static void on_get_directory_unseen(gchar *directory, gsize unseen,
+ gpointer user_data) {
+ tree_store_iter_data_t *data = user_data;
+
+ GString *unseen_str = g_string_sized_new(8);
+ g_string_printf(unseen_str, "%lu", unseen);
+
+ gtk_tree_store_set(data->store, &(data->iter), 3, unseen_str->str, -1);
+
+ g_string_free(unseen_str, TRUE);
+ if (g_ref_count_dec((grefcount *)data))
+ g_free(data);
+}
+
+
+static void on_get_directory_total(gchar *directory, gsize total,
+ gpointer user_data) {
+ tree_store_iter_data_t *data = user_data;
+
+ GString *total_str = g_string_sized_new(8);
+ g_string_printf(total_str, "%lu", total);
+
+ gtk_tree_store_set(data->store, &(data->iter), 4, total_str->str, -1);
+
+ g_string_free(total_str, TRUE);
+ if (g_ref_count_dec((grefcount *)data))
+ g_free(data);
+}
+
+
+static void add_directory(GtkTreeStore *store, mbgui_directory_t *directory,
+ GtkTreeIter *parent) {
+ GtkTreeIter iter;
+ gtk_tree_store_append(store, &iter, parent);
+ gtk_tree_store_set(store, &iter, 0,
+ (directory->path ? directory->path->str : NULL), 1,
+ (directory->path ? "folder-documents" : "folder"), 2,
+ directory->name->str, -1);
+
+ for (mbgui_directory_t *child = directory->children; child;
+ child = child->next)
+ add_directory(store, child, &iter);
+
+ if (!directory->path)
+ return;
+
+ tree_store_iter_data_t *data = g_malloc(sizeof(tree_store_iter_data_t));
+ data->store = store;
+ data->iter = iter;
+
+ g_ref_count_init((grefcount *)data);
+ mbgui_get_directory_unseen(directory->path->str, on_get_directory_unseen,
+ data);
+
+ g_ref_count_inc((grefcount *)data);
+ mbgui_get_directory_total(directory->path->str, on_get_directory_total,
+ data);
+}
+
+
+static void on_get_directories(mbgui_directory_t *directories,
+ gpointer user_data) {
+ app_data_t *data = user_data;
+
+ for (mbgui_directory_t *directory = directories; directory;
+ directory = directory->next)
+ add_directory(data->directories_store, directory, NULL);
+}
+
+
+static void on_command_line(GtkApplication *app,
+ GApplicationCommandLine *command_line,
+ gpointer user_data) {
+ app_data_t *data = g_malloc(sizeof(app_data_t));
+ GtkWidget *window = create_window(app, data);
+ gtk_window_present(GTK_WINDOW(window));
+
+ gchar **argv = g_application_command_line_get_arguments(command_line, NULL);
+ mbgui_get_directories(argv, on_get_directories, data);
+ g_strfreev(argv);
+}
+
+
+int main(int argc, char **argv) {
+ GtkApplication *app =
+ gtk_application_new(NULL, G_APPLICATION_HANDLES_COMMAND_LINE);
+ g_signal_connect(app, "command-line", G_CALLBACK(on_command_line), NULL);
+
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/src_c/mblaze.c b/src_c/mblaze.c
new file mode 100644
index 0000000..f6ee2ce
--- /dev/null
+++ b/src_c/mblaze.c
@@ -0,0 +1,511 @@
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <gio/gunixinputstream.h>
+#include "mblaze.h"
+
+
+typedef struct {
+ mbgui_get_directories_cb_t cb;
+ gpointer user_data;
+ mbgui_directory_t *directories;
+ GSubprocess *process;
+ GDataInputStream *stream;
+} get_directories_data_t;
+
+typedef struct {
+ GString *directory;
+ mbgui_get_directory_total_cb_t cb;
+ gpointer user_data;
+ gsize total;
+ GSubprocess *process;
+ GDataInputStream *stream;
+} get_directory_total_data_t;
+
+typedef struct {
+ GString *directory;
+ mbgui_get_directory_unseen_cb_t cb;
+ gpointer user_data;
+ gsize unseen;
+ GSubprocess *process;
+ GDataInputStream *stream;
+} get_directory_unseen_data_t;
+
+typedef struct {
+ GString *directory;
+ mbgui_get_messages_cb_t cb;
+ gpointer user_data;
+ mbgui_message_t *messages;
+ gchar *line_buff[6];
+ gsize line_buff_count;
+ gint mlist_stdout_fd;
+ gint mthread_stdout_fd;
+ gint mscan_stdout_fd;
+ GInputStream *mscan_stdout;
+ GDataInputStream *stream;
+} get_messages_data_t;
+
+typedef struct {
+ GString *path;
+ mbgui_get_message_cb_t cb;
+ gpointer user_data;
+ gchar buff[1024];
+ GString *message;
+ GSubprocess *process;
+} get_message_data_t;
+
+
+static void free_directories(mbgui_directory_t *directories) {
+ if (!directories)
+ return;
+ if (directories->path)
+ g_string_free(directories->path, TRUE);
+ if (directories->name)
+ g_string_free(directories->name, TRUE);
+ free_directories(directories->children);
+ free_directories(directories->next);
+ g_free(directories);
+}
+
+
+static void free_messages(mbgui_message_t *messages) {
+ if (!messages)
+ return;
+ if (messages->path)
+ g_string_free(messages->path, TRUE);
+ if (messages->subject)
+ g_string_free(messages->subject, TRUE);
+ if (messages->sender)
+ g_string_free(messages->sender, TRUE);
+ if (messages->date)
+ g_string_free(messages->date, TRUE);
+ free_messages(messages->children);
+ free_messages(messages->next);
+ g_free(messages);
+}
+
+
+static void free_get_directories_data(get_directories_data_t *data) {
+ free_directories(data->directories);
+ g_object_unref(data->stream);
+ g_object_unref(data->process);
+ g_free(data);
+}
+
+
+static void free_get_directory_total_data(get_directory_total_data_t *data) {
+ g_string_free(data->directory, TRUE);
+ g_object_unref(data->stream);
+ g_object_unref(data->process);
+ g_free(data);
+}
+
+
+static void free_get_directory_unseen_data(get_directory_unseen_data_t *data) {
+ g_string_free(data->directory, TRUE);
+ g_object_unref(data->stream);
+ g_object_unref(data->process);
+ g_free(data);
+}
+
+
+static void free_get_messages_data(get_messages_data_t *data) {
+ g_string_free(data->directory, TRUE);
+ free_messages(data->messages);
+ for (gsize i = 0; i < data->line_buff_count; ++i)
+ g_free(data->line_buff[i]);
+ if (data->stream)
+ g_object_unref(data->stream);
+ if (data->mscan_stdout)
+ g_object_unref(data->mscan_stdout);
+ if (data->mscan_stdout_fd >= 0)
+ g_close(data->mscan_stdout_fd, NULL);
+ if (data->mthread_stdout_fd >= 0)
+ g_close(data->mthread_stdout_fd, NULL);
+ if (data->mlist_stdout_fd >= 0)
+ g_close(data->mlist_stdout_fd, NULL);
+ free(data);
+}
+
+
+static void free_get_message_data(get_message_data_t *data) {
+ g_string_free(data->path, TRUE);
+ g_string_free(data->message, TRUE);
+ g_object_unref(data->process);
+ g_free(data);
+}
+
+
+static void reduce_directory(mbgui_directory_t *directory) {
+ for (mbgui_directory_t *child = directory->children;
+ child && !child->next && !directory->path;
+ child = directory->children) {
+ g_string_append_printf(directory->name, "/%s", child->name->str);
+ directory->path = child->path;
+ child->path = NULL;
+ directory->children = child->children;
+ child->children = NULL;
+ free_directories(child);
+ }
+
+ for (mbgui_directory_t *child = directory->children; child;
+ child = child->next)
+ reduce_directory(child);
+}
+
+
+static mbgui_directory_t *reverse_directories(mbgui_directory_t *directories) {
+ mbgui_directory_t *reversed = NULL;
+ while (directories) {
+ mbgui_directory_t *next = directories->next;
+ directories->children = reverse_directories(directories->children);
+ directories->next = reversed;
+ reversed = directories;
+ directories = next;
+ }
+ return reversed;
+}
+
+
+static mbgui_directory_t *add_directory(mbgui_directory_t *directories,
+ gchar *path, gchar *name) {
+ if (*name != '/')
+ return directories;
+
+ gsize index = 1;
+ while (name[index] && name[index] != '/')
+ ++index;
+
+ if (name[index]) {
+ GString *subname = g_string_new_len(name + 1, index - 1);
+
+ mbgui_directory_t *directory = directories;
+ while (directory && !g_string_equal(directory->name, subname))
+ directory = directory->next;
+
+ if (directory) {
+ g_string_free(subname, TRUE);
+
+ } else {
+ directory = g_malloc(sizeof(mbgui_directory_t));
+ directory->path = NULL;
+ directory->name = subname;
+ directory->children = NULL;
+ directory->next = directories;
+ }
+
+ directory->children =
+ add_directory(directory->children, path, name + index);
+ return directory;
+ }
+
+ mbgui_directory_t *directory = g_malloc(sizeof(mbgui_directory_t));
+ directory->path = g_string_new(path);
+ directory->name = g_string_new(name + 1);
+ directory->children = NULL;
+ directory->next = directories;
+ return directory;
+}
+
+
+static gsize get_message_depth(gchar *line) {
+ gsize depth = 0;
+
+ if (line[0] == '.' && line[1] == '.') {
+ for (gchar *i = line + 2; g_ascii_isdigit(*i); ++i)
+ depth = depth * 10 + (*i - '0');
+
+ } else {
+ for (gchar *i = line; *i; ++i) {
+ if (*i == ' ')
+ depth += 1;
+ }
+ }
+
+ return depth;
+}
+
+
+static mbgui_message_t *reverse_messages(mbgui_message_t *messages) {
+ mbgui_message_t *reversed = NULL;
+ while (messages) {
+ mbgui_message_t *next = messages->next;
+ messages->children = reverse_messages(messages->children);
+ messages->next = reversed;
+ reversed = messages;
+ messages = next;
+ }
+ return reversed;
+}
+
+
+static mbgui_message_t *add_message(mbgui_message_t *messages, gsize depth,
+ gchar **lines) {
+ if (depth && messages) {
+ messages->children = add_message(messages->children, depth - 1, lines);
+ return messages;
+ }
+
+ mbgui_message_t *message = g_malloc(sizeof(mbgui_message_t));
+ message->path = g_string_new(lines[0]);
+ message->status = lines[1][0];
+ message->subject = g_string_new(lines[2]);
+ message->sender = g_string_new(lines[3]);
+ message->date = g_string_new(lines[4]);
+ message->children = NULL;
+ message->next = messages;
+
+ return message;
+}
+
+
+static void on_get_directories_read_line(GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data) {
+ get_directories_data_t *data = user_data;
+
+ gchar *line =
+ g_data_input_stream_read_line_finish(data->stream, result, NULL, NULL);
+
+ if (!line) {
+ data->directories = reverse_directories(data->directories);
+ for (mbgui_directory_t *directory = data->directories; directory;
+ directory = directory->next) {
+ g_string_prepend_c(directory->name, '/');
+ reduce_directory(directory);
+ }
+ data->cb(data->directories, data->user_data);
+ free_get_directories_data(data);
+ return;
+ }
+
+ data->directories = add_directory(data->directories, line, line);
+ g_free(line);
+
+ g_data_input_stream_read_line_async(data->stream, 0, NULL,
+ on_get_directories_read_line, data);
+}
+
+
+static void on_get_directory_total_read_line(GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data) {
+ get_directory_total_data_t *data = user_data;
+
+ gchar *line =
+ g_data_input_stream_read_line_finish(data->stream, result, NULL, NULL);
+
+ if (!line) {
+ data->cb(data->directory->str, data->total, data->user_data);
+ free_get_directory_total_data(data);
+ return;
+ }
+
+ data->total += 1;
+ g_free(line);
+
+ g_data_input_stream_read_line_async(data->stream, 0, NULL,
+ on_get_directory_total_read_line, data);
+}
+
+
+static void on_get_directory_unseen_read_line(GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data) {
+ get_directory_unseen_data_t *data = user_data;
+
+ gchar *line =
+ g_data_input_stream_read_line_finish(data->stream, result, NULL, NULL);
+
+ if (!line) {
+ data->cb(data->directory->str, data->unseen, data->user_data);
+ free_get_directory_unseen_data(data);
+ return;
+ }
+
+ data->unseen += 1;
+ g_free(line);
+
+ g_data_input_stream_read_line_async(
+ data->stream, 0, NULL, on_get_directory_unseen_read_line, data);
+}
+
+
+static void on_get_messages_read_line(GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data) {
+ get_messages_data_t *data = user_data;
+
+ gchar *line =
+ g_data_input_stream_read_line_finish(data->stream, result, NULL, NULL);
+
+ if (!line) {
+ data->messages = reverse_messages(data->messages);
+ data->cb(data->directory->str, data->messages, data->user_data);
+ free_get_messages_data(data);
+ return;
+ }
+
+ data->line_buff[data->line_buff_count++] = line;
+
+ if (data->line_buff_count >= 6) {
+ gsize depth = get_message_depth(data->line_buff[0]);
+ data->messages =
+ add_message(data->messages, depth, data->line_buff + 1);
+
+ for (gsize i = 0; i < data->line_buff_count; ++i)
+ g_free(data->line_buff[i]);
+ data->line_buff_count = 0;
+ }
+
+ g_data_input_stream_read_line_async(data->stream, 0, NULL,
+ on_get_messages_read_line, data);
+}
+
+
+static void on_get_message_read_all(GObject *source_object,
+ GAsyncResult *result, gpointer user_data) {
+ get_message_data_t *data = user_data;
+ GInputStream *stream = g_subprocess_get_stdout_pipe(data->process);
+
+ gsize count = 0;
+ g_input_stream_read_all_finish(stream, result, &count, NULL);
+ g_string_append_len(data->message, data->buff, count);
+
+ if (count < sizeof(data->buff)) {
+ data->cb(data->path->str, data->message->str, data->user_data);
+ free_get_message_data(data);
+ return;
+ }
+
+ g_input_stream_read_all_async(stream, data->buff, sizeof(data->buff), 0,
+ NULL, on_get_message_read_all, data);
+}
+
+
+void mbgui_get_directories(gchar **argv, mbgui_get_directories_cb_t cb,
+ gpointer user_data) {
+ GStrvBuilder *new_argv_builder = g_strv_builder_new();
+ g_strv_builder_add_many(new_argv_builder, "mdirs", "-a", NULL);
+ g_strv_builder_addv(new_argv_builder, (const gchar **)argv);
+
+ gchar **new_argv = g_strv_builder_end(new_argv_builder);
+ g_strv_builder_unref(new_argv_builder);
+
+ get_directories_data_t *data = g_malloc(sizeof(get_directories_data_t));
+ data->cb = cb;
+ data->user_data = user_data;
+ data->directories = NULL;
+ data->process = g_subprocess_newv((const gchar **)new_argv,
+ G_SUBPROCESS_FLAGS_STDOUT_PIPE, NULL);
+ data->stream =
+ g_data_input_stream_new(g_subprocess_get_stdout_pipe(data->process));
+
+ g_data_input_stream_read_line_async(data->stream, 0, NULL,
+ on_get_directories_read_line, data);
+
+ g_strfreev(new_argv);
+}
+
+
+void mbgui_get_directory_total(gchar *directory,
+ mbgui_get_directory_total_cb_t cb,
+ gpointer user_data) {
+ get_directory_total_data_t *data =
+ g_malloc(sizeof(get_directory_total_data_t));
+ data->directory = g_string_new(directory);
+ data->cb = cb;
+ data->user_data = user_data;
+ data->total = 0;
+ data->process = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, NULL,
+ "mlist", directory, NULL);
+ data->stream =
+ g_data_input_stream_new(g_subprocess_get_stdout_pipe(data->process));
+
+ g_data_input_stream_read_line_async(data->stream, 0, NULL,
+ on_get_directory_total_read_line, data);
+}
+
+
+void mbgui_get_directory_unseen(gchar *directory,
+ mbgui_get_directory_unseen_cb_t cb,
+ gpointer user_data) {
+ get_directory_unseen_data_t *data =
+ g_malloc(sizeof(get_directory_unseen_data_t));
+ data->directory = g_string_new(directory);
+ data->cb = cb;
+ data->user_data = user_data;
+ data->unseen = 0;
+ data->process = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, NULL,
+ "mlist", "-s", directory, NULL);
+ data->stream =
+ g_data_input_stream_new(g_subprocess_get_stdout_pipe(data->process));
+
+ g_data_input_stream_read_line_async(
+ data->stream, 0, NULL, on_get_directory_unseen_read_line, data);
+}
+
+
+void mbgui_get_messages(gchar *directory, mbgui_get_messages_cb_t cb,
+ gpointer user_data) {
+ get_messages_data_t *data = g_malloc(sizeof(get_messages_data_t));
+ data->directory = g_string_new(directory), data->cb = cb;
+ data->user_data = user_data;
+ data->messages = NULL;
+ data->line_buff_count = 0;
+ data->mlist_stdout_fd = -1;
+ data->mthread_stdout_fd = -1;
+ data->mscan_stdout_fd = -1;
+ data->mscan_stdout = NULL;
+ data->stream = NULL;
+
+ const gchar *mlist_argv[] = {"mlist", directory, NULL};
+ if (!g_spawn_async_with_pipes_and_fds(
+ NULL, mlist_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, -1, -1, -1,
+ NULL, NULL, 0, NULL, NULL, &(data->mlist_stdout_fd), NULL, NULL)) {
+ g_printerr(">> mlist err");
+ free_get_messages_data(data);
+ return;
+ }
+
+ const gchar *mthread_argv[] = {"mthread", "-r", NULL};
+ if (!g_spawn_async_with_pipes_and_fds(
+ NULL, mthread_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ data->mlist_stdout_fd, -1, -1, NULL, NULL, 0, NULL, NULL,
+ &(data->mthread_stdout_fd), NULL, NULL)) {
+ g_printerr(">> mthread err");
+ free_get_messages_data(data);
+ return;
+ }
+
+ const gchar *mscan_argv[] = {"mscan", "-f", "%i\n%R\n%u\n%s\n%f\n%D", NULL};
+ if (!g_spawn_async_with_pipes_and_fds(
+ NULL, mscan_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ data->mthread_stdout_fd, -1, -1, NULL, NULL, 0, NULL, NULL,
+ &(data->mscan_stdout_fd), NULL, NULL)) {
+ g_printerr(">> mscan err");
+ free_get_messages_data(data);
+ return;
+ }
+
+ data->mscan_stdout = g_unix_input_stream_new(data->mscan_stdout_fd, FALSE);
+ data->stream = g_data_input_stream_new(data->mscan_stdout);
+
+ g_data_input_stream_read_line_async(data->stream, 0, NULL,
+ on_get_messages_read_line, data);
+}
+
+
+void mbgui_get_message(gchar *path, mbgui_get_message_cb_t cb,
+ gpointer user_data) {
+ get_message_data_t *data = g_malloc(sizeof(get_message_data_t));
+ data->path = g_string_new(path);
+ data->cb = cb;
+ data->user_data = user_data;
+ data->message = g_string_new("");
+ data->process = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE, NULL,
+ "mshow", path, NULL);
+
+ GInputStream *stream = g_subprocess_get_stdout_pipe(data->process);
+ g_input_stream_read_all_async(stream, data->buff, sizeof(data->buff), 0,
+ NULL, on_get_message_read_all, data);
+}
diff --git a/src_c/mblaze.h b/src_c/mblaze.h
new file mode 100644
index 0000000..e49abc5
--- /dev/null
+++ b/src_c/mblaze.h
@@ -0,0 +1,59 @@
+#ifndef MBGUI_MBLAZE_H
+#define MBGUI_MBLAZE_H
+
+#include <glib.h>
+
+
+typedef enum {
+ MBGUI_MSG_STATUS_SEEN = ' ',
+ MBGUI_MSG_STATUS_FLAGGED = '*',
+ MBGUI_MSG_STATUS_UNSEEN = '.',
+ MBGUI_MSG_STATUS_TRASHED = 'x',
+ MBGUI_MSG_STATUS_VIRTUAL = 'v'
+} mbgui_message_status_t;
+
+typedef struct mbgui_directory_t {
+ GString *path;
+ GString *name;
+ struct mbgui_directory_t *children;
+ struct mbgui_directory_t *next;
+} mbgui_directory_t;
+
+typedef struct mbgui_message_t {
+ GString *path;
+ mbgui_message_status_t status;
+ GString *subject;
+ GString *sender;
+ GString *date;
+ struct mbgui_message_t *children;
+ struct mbgui_message_t *next;
+} mbgui_message_t;
+
+
+typedef void (*mbgui_get_directories_cb_t)(mbgui_directory_t *directories,
+ gpointer user_data);
+typedef void (*mbgui_get_directory_total_cb_t)(gchar *directory, gsize total,
+ gpointer user_data);
+typedef void (*mbgui_get_directory_unseen_cb_t)(gchar *directory, gsize unseen,
+ gpointer user_data);
+typedef void (*mbgui_get_messages_cb_t)(gchar *directory,
+ mbgui_message_t *messages,
+ gpointer user_data);
+typedef void (*mbgui_get_message_cb_t)(gchar *path, gchar *message,
+ gpointer user_data);
+
+
+void mbgui_get_directories(gchar **argv, mbgui_get_directories_cb_t cb,
+ gpointer user_data);
+void mbgui_get_directory_total(gchar *directory,
+ mbgui_get_directory_total_cb_t cb,
+ gpointer user_data);
+void mbgui_get_directory_unseen(gchar *directory,
+ mbgui_get_directory_unseen_cb_t cb,
+ gpointer user_data);
+void mbgui_get_messages(gchar *directory, mbgui_get_messages_cb_t cb,
+ gpointer user_data);
+void mbgui_get_message(gchar *path, mbgui_get_message_cb_t cb,
+ gpointer user_data);
+
+#endif