Re: GIOChannel missing binary mode?



Ron Steinke <rsteinke w-link net> writes:

> > From: Michael Natterer <mitch gimp org>
> >
> > Hi,
> >
> > while porting GIMP <-> plug-in communication to
> > g_io_channel_[read|write]_chars() i noticed that there
> > are two GIOChannel attributes, "do_encode" and "encoding".
> >
> > My impression from looking at the code is that if
> > "do_encode" == FALSE, "encoding" is assumed to be UTF-8.
> 
> Actually, it's assumed to be either "UTF-8" or NULL. Use
> NULL for binary encoding. The default just happens to
> be "UTF-8".
> 
> > So either I missed something or there seems to be no way
> > to use a GIOChannel in binary mode, because if I say
> > g_io_channel_set_encoding (channel, NULL, NULL),
> > "do_encoding" is set to FALSE automatically.
> 
> The do_encode flag is set when we are calling g_iconv to do conversion.
> It just happens this is true for neither UTF-8 or binary encoding.
> 
> > A subsequent call to read_chars() or write_chars() however
> > looks at "do_encode", assumes UTF-8 if it is FALSE and
> > calls g_utf8_validate() which of course fails on binary
> > data.
> 
> In write_chars(), a binary encoding hits the earlier !channel->encoding
> test and completely avoids checking do_encode. In read_chars(),
> the test for binary encoding is hidden in the USE_BUF() macro.
> 
> 
> > Please point me to the right direction if I made a mistake :-)
> >
> > (FYI: right now, as a workaround, i use channel->funcs->io_foo())
> 
> Just call g_io_channel_set_encoding() with a NULL argument before
> you use the channel. If this isn't in the docs, it should be.

You're right, it was not g_io_channel_set_encoding()'s fault.

In fact, I used g_io_channel_set_encoding() and it didn't work, so my
first (and wrong) glance at giochannel.c made me think I found the bug.

However I additionally have to say g_io_channel_set_buffered (channel, FALSE);
to make it work.

I tried to write a small test program to trigger the failing reads and
writes (attached) but was *not* able to reproduce it, no matter if
reading/writing is done on a tty, file of pipe. Resizing the random_crap
array in the test program also doesn't trigger anything.

Maybe GIMP's IO channel events are a bit more complex and trigger
something completely different :-)  If you want to test it, remove
the g_io_channel_set_buffered() from CVS gimp in app/plug_in.c and
libgimp/gimp.c and see what happens.

(Maybe it's just an explicit flush() or something that's missing
to make it work in buffered mode...).

ciao,
--Mitch

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

#include <glib.h>

gint
main (gint   argc,
      gchar *argv[])
{
  GIOChannel *channel0 = NULL;
  GIOChannel *channel1;
  gchar       random_crap[5];
  gint        i;
  GIOStatus   status;
  GError     *error = NULL;
  gsize       written;

  if (! isatty (0))
    {
      channel0 = g_io_channel_unix_new (0);
      g_io_channel_set_encoding (channel0, NULL, NULL);
      g_io_channel_set_close_on_unref (channel0, TRUE);
    }

  channel1 = g_io_channel_unix_new (1);
  g_io_channel_set_encoding (channel1, NULL, NULL);
  g_io_channel_set_close_on_unref (channel1, TRUE);

  if (channel0)
    {
      g_message ("%s: reading from STDIN", argv[0]);

      do
	{
	  status = g_io_channel_read_chars (channel0,
					    random_crap, G_N_ELEMENTS (random_crap),
					    &written,
					    &error);
	}
      while (status == G_IO_STATUS_AGAIN);

      if (status != G_IO_STATUS_NORMAL)
	{
	  if (error)
	    {
	      g_warning ("%s: read EEK: %s", argv[0], error->message);
	      g_error_free (error);
	    }
	  else
	    {
	      g_warning ("%s: read EEK", argv[0]);
	    }

	  return -1;
	}
    }
  else
    {
      g_message ("%s: producing random_crap", argv[0]);

      for (i = 0; i < G_N_ELEMENTS (random_crap); i++)
	random_crap[i] = i % 127 + 128;
    }

  do
    {
      status = g_io_channel_write_chars (channel1,
					 random_crap, G_N_ELEMENTS (random_crap),
					 &written,
					 &error);
    }
  while (status == G_IO_STATUS_AGAIN);

  if (status != G_IO_STATUS_NORMAL)
    {
      if (error)
	{
	  g_warning ("%s: write EEK: %s", argv[0], error->message);
	  g_error_free (error);
	}
      else
	{
	  g_warning ("%s: write EEK", argv[0]);
	}

      return -1;
    }

  if (channel0)
    g_io_channel_unref (channel0);

  g_io_channel_unref (channel1);

  return 0;
}


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