2

This is a bare bones save_as() function:

gint save_as(GtkWidget *parent, struct buffers B)
{
    GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
    gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");

    gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
    switch(response)
    {
        case GTK_RESPONSE_OK:
                GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
                GtkTextIter *start;
                gtk_text_buffer_get_start(B.buffer0, &start);
                GtkTextIter *end;
                gtk_text_buffer_get_end(B.buffer0, &end);
                // program abnormally terminates on the following line
                gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
                g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
                g_free(contents);
                gtk_widget_destroy(file_chooser);
                return GTK_RESPONSE_OK;
                break;
        case GTK_RESPONSE_CANCEL:
                gtk_widget_destroy(file_chooser);
                return GTK_RESPONSE_CANCEL;
    }
    // user pressed X
    gtk_widget_destroy(file_chooser);
    return GTK_RESPONSE_CANCEL;
}

This is pretty much all we need to diagnose and fix the problem.

Here is the full message I get when I try to click on File -> Save As... and then on Save:

(test:4478): Gtk-WARNING **: 11:56:20.184: Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixbufs/widgets in the buffer have been modified since the iterator was created.
You must use marks, character numbers, or line numbers to preserve a position across buffer modifications.
You can apply tags and insert marks without invalidating your iterators, but any mutation that affects 'indexable' buffer contents (contents that can be referred to by character offset) will invalidate all outstanding iterators


(test:4478): Gtk-CRITICAL **: 11:56:20.184: gtk_text_buffer_get_text: assertion 'gtk_text_iter_get_buffer (start) == buffer' failed
Segmentation fault (core dumped)

For some reason, it seems that the iterators start and end do not belong to B.buffer0. Apart from GTK+3 documentation, I also followed answers to this question as a guide.

Why is this happening and how it can be fixed?


I also tried changing the following lines:

gtk_text_buffer_get_start_iter(B.buffer0, &start); to gtk_text_buffer_get_start_iter(B.buffer0, start);

gtk_text_buffer_get_start_iter(B.buffer0, &end); to gtk_text_buffer_get_start_iter(B.buffer0, end);

gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE); to gchar *contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE);

The only error I get is:

Segmentation fault (core dumped)

This is also supposed to be the correct way to send arguments according to the documentation.

Also, I tried replacing contents = gtk_text_buffer_get_text(B.buffer0, start, end, FALSE); with contents = gtk_text_iter_get_text(start, end); but I get the same error.

I also noticed that I get two warnings during compiling after applying those changes:

src/save.c:96:5: warning: ‘start’ may be used uninitialized in this function [-Wmaybe-uninitialized]
     gtk_text_buffer_get_start_iter(B.buffer0, start);
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/save.c:98:5: warning: ‘end’ may be used uninitialized in this function [-Wmaybe-uninitialized]
     gtk_text_buffer_get_end_iter(B.buffer0, end);
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This obviously means that gtk_text_buffer_get_start/end_iter() fails to associate start and end with B.buffer0, but why? It says that start and end are passed uninitialized, but isn't that normal since I'm initializing them now? Or I should do something before that?


Definition of struct buffer is in a file called buffer.h. Here it is (not the whole file, just the struct definition):
struct buffers
{
    GtkTextBuffer *buffer0;
    GtkTextBuffer *buffer1;
    GtkTextBuffer *buffer2;
};

The instance I use is:

struct buffers Buffer;

This is a global variable, so buffer.h is included in every file that uses Buffer. And for the same purpose (globality), of course it is declared extern struct buffers Buffer; in the header file associated with the source file in which it is defined.

This buffer is passed to a function save_as() like this:

gint response = save_as(main_window, Buffer);

In case you're wondering why I just don't use global variable instead of passing it as a parameter, it is because I can just pass any variable of type struct buffers to a save_file() and let it do the job, instead of having another function for each struct buffers variable.


I tried initializing iterators upon their definition:
GtkTextIter *start = NULL;
GtkTextIter *end = NULL;

Everything compiles without any warnings. Unfortunately, this doesn't solve the problem. Upon clicking File -> Save As and then Save, the program exits and I get messages:

(test:4081): Gtk-CRITICAL **: 19:48:29.843: gtk_text_buffer_get_start_iter: assertion 'iter != NULL' failed

(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_end_iter: assertion 'iter != NULL' failed

(test:4081): Gtk-CRITICAL **: 19:48:29.844: gtk_text_buffer_get_text: assertion 'start != NULL' failed

Why is it even important that my start and end iterators are NULL at least in the first two cases? Aren't those two functions supposed to set start and end iterators to the start and the end of a file, respectively? Therefore, why does it matter to what iterators are set prior to that?

Hanlon
  • 432
  • 4
  • 13
  • In gtk2, the correct form ar `gtk_text_buffer_get_start_iter(buf, start);`, `gtk_text_buffer_get_end_iter(buf, start);` and `gtk_text_buffer_get_text([...], buf, start, [...]);` (no `&`). Have you checked that `contents` is not `NULL`? – Mathieu Sep 05 '18 at 13:13
  • @purplepsycho As for the forms, yes I know (this is gtk3 btw). I changed that but I still get an error `Segmentation fault (core dumped)`. As for the `contents`, I can't check that because `gtk_text_buffer_get_text()` never returns. – Hanlon Sep 05 '18 at 13:21
  • Well, how is `struct buffer` defined, and are you sure that `B.buffer0` is a valid `GtkTextBuffer*`? – Mathieu Sep 05 '18 at 14:16
  • @purplepsycho I'll edit my answer to include that information. – Hanlon Sep 05 '18 at 14:41
  • @purplepsycho Done. – Hanlon Sep 05 '18 at 14:58

1 Answers1

1

Finally solved it. It looks like that GtkTextIter is a little bit different than most GTK types.

Here is how it should be done:

gint save_as(GtkWidget *parent, struct buffers B)
{
    GtkWidget *file_chooser = gtk_file_chooser_dialog_new("Save As", GTK_WINDOW(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Save", GTK_RESPONSE_OK, NULL);
    gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(file_chooser), "Untitled");

    gint response = gtk_dialog_run(GTK_DIALOG(file_chooser));
    switch(response)
    {
        case GTK_RESPONSE_OK:
                GFile *file = g_file_new_for_path(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_chooser)));
                GtkTextIter start;
                gtk_text_buffer_get_start(B.buffer0, &start);
                GtkTextIter end;
                gtk_text_buffer_get_end(B.buffer0, &end);
                // program abnormally terminates on the following line
                gchar *contents = gtk_text_buffer_get_text(B.buffer0, &start, &end, FALSE);
                g_file_replace_contents(file, contents, strlen(contents), NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, NULL);
                g_free(contents);
                gtk_widget_destroy(file_chooser);
                return GTK_RESPONSE_OK;
                break;
        case GTK_RESPONSE_CANCEL:
                gtk_widget_destroy(file_chooser);
                return GTK_RESPONSE_CANCEL;
    }
    // user pressed X
    gtk_widget_destroy(file_chooser);
    return GTK_RESPONSE_CANCEL;
}

As you can see, the only difference in this code and the code from my question are the following two lines:

Wrong

GtkTextIter *start;
GtkTextIter *end;

Right

GtkTextIter start;
GtkTextIter end;

Most of the GTK types want you to make them pointers, but this one is an exception.

Hanlon
  • 432
  • 4
  • 13