Modified Files: glib/ChangeLog glib/glib/giochannel.c glib/glib/giounix.c
authorRon Steinke <rsteinke@src.gnome.org>
Sun, 5 Aug 2001 16:48:42 +0000 (16:48 +0000)
committerRon Steinke <rsteinke@src.gnome.org>
Sun, 5 Aug 2001 16:48:42 +0000 (16:48 +0000)
Modified Files:
glib/ChangeLog glib/glib/giochannel.c glib/glib/giounix.c

        * glib/giochannel.c: Replaced the local use_buf variable with a macro
        in most places. This allows us to check some things without worrying
        whether we have allocated the read buffers yet, and allows us to allocate
        the buffers later in some cases.

        * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
        supposed to be greater than or equal to the length in bytes
        of the longest character in any encoding. This is necessary
        to get the minimum buffer size for successful writing.

        * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
        that it just prints a warning if partial_write_buf isn't
        empty instead of failing.

        * glib/giochannel.c: Fixed several functions so they can accept
        NULL parameters for pointers to return values.

        * glib/giochannel.c: Altered the error handling for
        g_io_channel_read_chars () to only return an error if
        it doesn't have any buffered data.

        * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
        to fix the error handling and remove duplicate sections
        of code.

        * glib/giounix.c: Fixed g_io_channel_new_file () to
        call fstat () to set the is_seekable flag, in case someone
        uses it on a FIFO.

ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-12
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
glib/giochannel.c
glib/giounix.c

index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 880c11d9176b9685e57b1632cce03e590cb67c88..9810b9307e875ab58f58f89f8ffbf4ddbf6b2078 100644 (file)
@@ -1,3 +1,34 @@
+2001-08-05  Ron Steinke  <rsteinke@w-link.net>
+
+       * glib/giochannel.c: Replaced the local use_buf variable with a macro
+       in most places. This allows us to check some things without worrying
+       whether we have allocated the read buffers yet, and allows us to allocate
+       the buffers later in some cases.
+
+       * glib/giochannel.c: Introduced a MAX_CHAR_SIZE macro, which is
+       supposed to be greater than or equal to the length in bytes
+       of the longest character in any encoding. This is necessary
+       to get the minimum buffer size for successful writing.
+
+       * glib/giochannel.c: Fixed g_io_channel_set_encoding () so
+       that it just prints a warning if partial_write_buf isn't
+       empty instead of failing.
+
+       * glib/giochannel.c: Fixed several functions so they can accept
+       NULL parameters for pointers to return values.
+
+       * glib/giochannel.c: Altered the error handling for
+       g_io_channel_read_chars () to only return an error if
+       it doesn't have any buffered data.
+
+       * glib/giochannel.c: Rewrote g_io_channel_write_chars ()
+       to fix the error handling and remove duplicate sections
+       of code.
+
+       * glib/giounix.c: Fixed g_io_channel_new_file () to
+       call fstat () to set the is_seekable flag, in case someone
+       uses it on a FIFO.
+
 Sun Aug  5 08:25:30 2001  Owen Taylor  <otaylor@redhat.com>
 
        * glib/gmacros.h: Include stddef.h so that we use
index 7f4c5c2dfcdc979ef4bbffa28f706faabbe31a57..4c40bbde8a7bd7cb0ac93d3ead7243c525b81322 100644 (file)
 
 #define G_IO_NICE_BUF_SIZE     1024
 
+/* This needs to be as wide as the largest character in any possible encoding */
+#define MAX_CHAR_SIZE          10
+
+/* Some simplifying macros, which reduce the need to worry whether the
+ * buffers have been allocated. These also make USE_BUF () an lvalue,
+ * which is used in g_io_channel_read_to_end ().
+ */
+#define USE_BUF(channel)       ((channel)->encoding ? (channel)->encoded_read_buf \
+                                : (channel)->read_buf)
+#define BUF_LEN(string)                ((string) ? (string)->len : 0)
+
 static GIOError                g_io_error_get_from_g_error     (GIOStatus    status,
                                                         GError      *err);
 static void            g_io_channel_purge              (GIOChannel  *channel);
@@ -587,8 +598,8 @@ g_io_channel_set_buffer_size (GIOChannel    *channel,
   if (size == 0)
     size = G_IO_NICE_BUF_SIZE;
 
-  if (size < 10) /* Needs to be larger than the widest char in any encoding */
-    size = 10;
+  if (size < MAX_CHAR_SIZE)
+    size = MAX_CHAR_SIZE;
 
   channel->buf_size = size;
 }
@@ -933,7 +944,6 @@ g_io_channel_set_encoding (GIOChannel       *channel,
 
   g_return_val_if_fail (!channel->do_encode || !channel->encoded_read_buf ||
                        channel->encoded_read_buf->len == 0, G_IO_STATUS_ERROR);
-  g_return_val_if_fail (channel->partial_write_buf[0] == '\0', G_IO_STATUS_ERROR);
 
   if (!channel->use_buffer)
     {
@@ -943,6 +953,12 @@ g_io_channel_set_encoding (GIOChannel      *channel,
       channel->use_buffer = TRUE;
     }
 
+  if (channel->partial_write_buf[0] != '\0')
+    {
+      g_warning ("Partial character at end of write buffer not flushed.\n");
+      channel->partial_write_buf[0] = '\0';
+    }
+
   did_encode = channel->do_encode;
 
   if (!encoding || strcmp (encoding, "UTF8") == 0 || strcmp (encoding, "UTF-8") == 0)
@@ -1102,7 +1118,8 @@ g_io_channel_fill_buffer (GIOChannel *channel,
   else
     {
       oldlen = 0;
-      channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
+      if (channel->encoding)
+        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
     }
 
   if (channel->do_encode)
@@ -1110,6 +1127,8 @@ g_io_channel_fill_buffer (GIOChannel *channel,
       size_t errnum, inbytes_left, outbytes_left;
       gchar *inbuf, *outbuf;
 
+      g_assert (channel->encoded_read_buf);
+
 reencode:
 
       inbytes_left = channel->read_buf->len;
@@ -1169,6 +1188,8 @@ reencode:
     {
       gchar *nextchar, *lastchar;
 
+      g_assert (channel->encoded_read_buf);
+
       nextchar = channel->read_buf->str;
       lastchar = channel->read_buf->str + channel->read_buf->len;
 
@@ -1206,9 +1227,6 @@ reencode:
         {
           gint copy_len = lastchar - channel->read_buf->str;
 
-          if (!channel->encoded_read_buf)
-            channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
-
           g_string_append_len (channel->encoded_read_buf, channel->read_buf->str,
                                copy_len);
           g_string_erase (channel->read_buf, 0, copy_len);
@@ -1247,6 +1265,7 @@ g_io_channel_read_line (GIOChannel *channel,
                        GError    **error)
 {
   GIOStatus status;
+  gsize got_length;
   
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail (str_return != NULL, G_IO_STATUS_ERROR);
@@ -1254,21 +1273,16 @@ g_io_channel_read_line (GIOChannel *channel,
                        G_IO_STATUS_ERROR);
   g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
 
-  status = g_io_channel_read_line_backend (channel, length, terminator_pos, error);
+  status = g_io_channel_read_line_backend (channel, &got_length, terminator_pos, error);
+
+  if (length)
+    *length = got_length;
 
   if (status == G_IO_STATUS_NORMAL)
     {
-      GString *use_buf;
-
-      if (channel->encoding)
-        use_buf = channel->encoded_read_buf;
-      else
-        use_buf = channel->read_buf;
-
-      g_assert (use_buf);
-
-      *str_return = g_strndup (use_buf->str, *length);
-      g_string_erase (use_buf, 0, *length);
+      g_assert (USE_BUF (channel));
+      *str_return = g_strndup (USE_BUF (channel)->str, got_length);
+      g_string_erase (USE_BUF (channel), 0, got_length);
     }
   else
     *str_return = NULL;
@@ -1312,17 +1326,9 @@ g_io_channel_read_line_string (GIOChannel *channel,
 
   if (status == G_IO_STATUS_NORMAL)
     {
-      GString *use_buf;
-
-      if (channel->encoding)
-        use_buf = channel->encoded_read_buf;
-      else
-        use_buf = channel->read_buf;
-
-      g_assert (use_buf);
-
-      g_string_append_len (buffer, use_buf->str, length);
-      g_string_erase (use_buf, 0, length);
+      g_assert (USE_BUF (channel));
+      g_string_append_len (buffer, USE_BUF (channel)->str, length);
+      g_string_erase (USE_BUF (channel), 0, length);
     }
 
   return status;
@@ -1337,7 +1343,6 @@ g_io_channel_read_line_backend    (GIOChannel *channel,
 {
   GIOStatus status;
   gsize checked_to, line_term_len, line_length, got_term_len;
-  GString *use_buf;
   gboolean first_time = TRUE;
 
   if (!channel->use_buffer)
@@ -1348,19 +1353,6 @@ g_io_channel_read_line_backend   (GIOChannel *channel,
       return G_IO_STATUS_ERROR;
     }
 
-  if (channel->encoding)
-    {
-      if (!channel->encoded_read_buf)
-        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
-      use_buf = channel->encoded_read_buf;
-    }
-  else
-    {
-      if (!channel->read_buf)
-        channel->read_buf = g_string_sized_new (channel->buf_size);
-      use_buf = channel->read_buf;
-    }
-
   status = G_IO_STATUS_NORMAL;
 
   if (channel->line_term)
@@ -1376,15 +1368,16 @@ g_io_channel_read_line_backend  (GIOChannel *channel,
   while (TRUE)
     {
       gchar *nextchar, *lastchar;
+      GString *use_buf;
 
-      if (!first_time || (use_buf->len == 0))
+      if (!first_time || (BUF_LEN (USE_BUF (channel)) == 0))
         {
 read_again:
           status = g_io_channel_fill_buffer (channel, error);
           switch (status)
             {
               case G_IO_STATUS_NORMAL:
-                if (use_buf->len == 0)
+                if (BUF_LEN (USE_BUF (channel)) == 0)
                   /* Can happen when using conversion and only read
                    * part of a character
                    */
@@ -1394,9 +1387,10 @@ read_again:
                   }
                 break;
               case G_IO_STATUS_EOF:
-                if (use_buf->len == 0)
+                if (BUF_LEN (USE_BUF (channel)) == 0)
                   {
-                    *length = 0;
+                    if (length)
+                      *length = 0;
 
                     if (channel->encoding && channel->read_buf->len != 0)
                       {
@@ -1410,12 +1404,15 @@ read_again:
                   }
                 break;
               default:
-                *length = 0;
+                if (length)
+                  *length = 0;
                 return status;
             }
         }
 
-      g_assert (use_buf->len > 0);
+      g_assert (BUF_LEN (USE_BUF (channel)) != 0);
+
+      use_buf = USE_BUF (channel); /* The buffer has been created by this point */
 
       first_time = FALSE;
 
@@ -1502,7 +1499,8 @@ done:
   if (terminator_pos)
     *terminator_pos = line_length;
 
-  *length = line_length + got_term_len;
+  if (length)
+    *length = line_length + got_term_len;
 
   return G_IO_STATUS_NORMAL;
 }
@@ -1532,17 +1530,16 @@ g_io_channel_read_to_end (GIOChannel    *channel,
                           GError       **error)
 {
   GIOStatus status;
-  GString **use_buf;
     
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
-  g_return_val_if_fail (str_return != NULL, G_IO_STATUS_ERROR);
-  g_return_val_if_fail (length != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail ((error == NULL) || (*error == NULL),
     G_IO_STATUS_ERROR);
   g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
 
-  *str_return = NULL;
-  *length = 0;
+  if (str_return)
+    *str_return = NULL;
+  if (length)
+    *length = 0;
 
   if (!channel->use_buffer)
     {
@@ -1558,39 +1555,32 @@ g_io_channel_read_to_end (GIOChannel    *channel,
   if (status != G_IO_STATUS_EOF)
     return status;
 
-  if (channel->encoding)
+  if (channel->encoding && channel->read_buf->len > 0)
     {
-      if (channel->read_buf->len > 0)
-        {
-          g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
-                       "Channel terminates in a partial character");
-          return G_IO_STATUS_ERROR;
-        }
-
-      if (!channel->encoded_read_buf)
-        {
-          if (length)
-            *length = 0;
-          if (str_return)
-            *str_return = g_strdup ("");
-        }
+      g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_PARTIAL_INPUT,
+                   "Channel terminates in a partial character");
+      return G_IO_STATUS_ERROR;
+    }
 
-      use_buf = &channel->encoded_read_buf;
+  if (USE_BUF (channel) == NULL)
+    {
+      /* length is already set to zero */
+      if (str_return)
+        *str_return = g_strdup ("");
     }
   else
-    use_buf = &channel->read_buf;
-
-  g_assert (*use_buf); /* Created by fill_buffer if it didn't previously exist */
+    {
+      if (length)
+        *length = USE_BUF (channel)->len;
 
-  if (length)
-    *length = (*use_buf)->len;
+      if (str_return)
+        *str_return = g_string_free (USE_BUF (channel), FALSE);
+      else
+        g_string_free (USE_BUF (channel), TRUE);
 
-  if (str_return)
-    *str_return = g_string_free (*use_buf, FALSE);
-  else
-    g_string_free (*use_buf, TRUE);
-  
-  *use_buf = NULL;
+      /* This only works because USE_BUF () is a macro */
+      USE_BUF (channel) = NULL;
+    }
 
   return G_IO_STATUS_NORMAL;
 }
@@ -1619,12 +1609,11 @@ g_io_channel_read_chars (GIOChannel     *channel,
                          GError        **error)
 {
   GIOStatus status;
-  GString *use_buf;
+  gsize got_bytes;
 
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail ((error == NULL) || (*error == NULL),
                        G_IO_STATUS_ERROR);
-  g_return_val_if_fail (bytes_read != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail (channel->is_readable, G_IO_STATUS_ERROR);
 
   if (count == 0)
@@ -1641,84 +1630,66 @@ g_io_channel_read_chars (GIOChannel     *channel,
       return channel->funcs->io_read (channel, buf, count, bytes_read, error);
     }
 
-  if (channel->encoding)
-    {
-      if (!channel->encoded_read_buf)
-        channel->encoded_read_buf = g_string_sized_new (channel->buf_size);
-      use_buf = channel->encoded_read_buf;
-    }
-  else
-    {
-      if (!channel->read_buf)
-        channel->read_buf = g_string_sized_new (channel->buf_size);
-      use_buf = channel->read_buf;
-    }
-
   status = G_IO_STATUS_NORMAL;
 
-  while (use_buf->len < MAX (count, 6) && status == G_IO_STATUS_NORMAL)
-    /* Be sure to read in at least one full UTF-8 character
-     * (max length 6)
-     */
+  while (BUF_LEN (USE_BUF (channel)) < count && status == G_IO_STATUS_NORMAL)
     status = g_io_channel_fill_buffer (channel, error);
 
-  switch (status)
+  /* Only return an error if we have no data */
+
+  if (BUF_LEN (USE_BUF (channel)) == 0)
     {
-    case G_IO_STATUS_NORMAL:
-      g_assert (use_buf->len > 0);
-      break;
-    case G_IO_STATUS_EOF:
-      if (use_buf->len == 0)
-       {
-         *bytes_read = 0;
-
-          if (channel->encoding && channel->read_buf->len != 0)
-            {
-              g_set_error (error, G_CONVERT_ERROR,
-                           G_CONVERT_ERROR_PARTIAL_INPUT,
-                           "Leftover unconverted data in read buffer");
-              return G_IO_STATUS_ERROR;
-            }
-          else
-            return G_IO_STATUS_EOF;
-       }
-      break;
-    case G_IO_STATUS_AGAIN:
-      if (use_buf->len > 0)
-        break; /* return what we have */
-      else
+      g_assert (status != G_IO_STATUS_NORMAL);
+
+      if (status == G_IO_STATUS_EOF && channel->encoding
+          && BUF_LEN (channel->read_buf) > 0)
         {
-          *bytes_read = 0;
-          return G_IO_STATUS_AGAIN;
+          g_set_error (error, G_CONVERT_ERROR,
+                       G_CONVERT_ERROR_PARTIAL_INPUT,
+                       "Leftover unconverted data in read buffer");
+          status = G_IO_STATUS_ERROR;
         }
-    default:
-      *bytes_read = 0;
+
+      if (bytes_read)
+        *bytes_read = 0;
+
       return status;
     }
 
-  *bytes_read = MIN (count, use_buf->len);
+  if (status == G_IO_STATUS_ERROR)
+    g_clear_error (error);
+
+  got_bytes = MIN (count, BUF_LEN (USE_BUF (channel)));
+
+  g_assert (got_bytes > 0);
 
-  if (channel->encoding && *bytes_read > 0)
+  if (channel->encoding)
     /* Don't validate for NULL encoding, binary safe */
     {
-      gchar *nextchar, *prevchar = NULL;
+      gchar *nextchar, *prevchar;
+
+      g_assert (USE_BUF (channel) == channel->encoded_read_buf);
 
-      nextchar = use_buf->str;
+      nextchar = channel->encoded_read_buf->str;
 
-      while (nextchar < use_buf->str + *bytes_read)
+      do
         {
           prevchar = nextchar;
           nextchar = g_utf8_next_char (nextchar);
         }
+      while (nextchar < channel->encoded_read_buf->str + got_bytes);
 
-      if (nextchar > use_buf->str + *bytes_read)
-        *bytes_read = prevchar - use_buf->str;
+      if (nextchar > channel->encoded_read_buf->str + got_bytes)
+        got_bytes = prevchar - channel->encoded_read_buf->str;
 
-      g_assert (*bytes_read > 0 || count < 6);
+      g_assert (got_bytes > 0 || count < 6);
     }
 
-  memcpy (buf, use_buf->str, *bytes_read);
-  g_string_erase (use_buf, 0, *bytes_read);
+  memcpy (buf, USE_BUF (channel)->str, got_bytes);
+  g_string_erase (USE_BUF (channel), 0, got_bytes);
+
+  if (bytes_read)
+    *bytes_read = got_bytes;
 
   return G_IO_STATUS_NORMAL;
 }
@@ -1746,6 +1717,7 @@ g_io_channel_write_chars (GIOChannel      *channel,
                           GError       **error)
 {
   GIOStatus status;
+  gssize wrote_bytes = 0;
 
   g_return_val_if_fail (channel != NULL, G_IO_STATUS_ERROR);
   g_return_val_if_fail (bytes_written != NULL, G_IO_STATUS_ERROR);
@@ -1773,10 +1745,10 @@ g_io_channel_write_chars (GIOChannel    *channel,
 
   /* General case */
 
-  if (channel->is_seekable && ((channel->read_buf && channel->read_buf->len > 0)
-    || (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)))
+  if (channel->is_seekable && (( BUF_LEN (channel->read_buf) > 0)
+    || (BUF_LEN (channel->encoded_read_buf) > 0)))
     {
-      if (channel->encoded_read_buf && channel->encoded_read_buf->len > 0)
+      if (channel->do_encode && BUF_LEN (channel->encoded_read_buf) > 0)
         {
           g_warning("Mixed reading and writing not allowed on encoded files");
           return G_IO_STATUS_ERROR;
@@ -1789,299 +1761,239 @@ g_io_channel_write_chars (GIOChannel  *channel,
   if (!channel->write_buf)
     channel->write_buf = g_string_sized_new (channel->buf_size);
 
-  if (!channel->do_encode)
+  while (wrote_bytes < count)
     {
-      guint copy_bytes;
-
-      if (channel->encoding)
-        { 
-          const gchar *badchar;
-
-          if (channel->partial_write_buf[0] != '\0')
-            {
-              guint partial_chars = strlen (channel->partial_write_buf);
-              guint chars_in_buf = MIN (6, partial_chars + count);
-              gint skip;
-
-              memcpy (channel->partial_write_buf + partial_chars, buf,
-                chars_in_buf - partial_chars);
-
-              g_utf8_validate (channel->partial_write_buf, chars_in_buf, &badchar);
+      gsize space_in_buf;
 
-              if (badchar < channel->partial_write_buf + partial_chars)
-                {
-                  gunichar try_char;
-                  gsize left_len = chars_in_buf;
-
-                  g_assert (badchar == channel->partial_write_buf);
-
-                  try_char = g_utf8_get_char_validated (badchar, left_len);
+      /* If the buffer is full, try a write immediately. In
+       * the nonblocking case, this prevents the user from
+       * writing just a little bit to the buffer every time
+       * and never receiving an EAGAIN.
+       */
 
-                  switch (try_char)
-                    {
-                      case -2:
-                        g_assert (chars_in_buf < 6);
-                        channel->partial_write_buf[chars_in_buf] = '\0';
-                        *bytes_written = count;
-                        return G_IO_STATUS_NORMAL;
-                      case -1:
-                        g_set_error (error, G_CONVERT_ERROR,
-                                     G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
-                                     "Illegal UTF-8 sequence");
-                        *bytes_written = 0;
-                        return G_IO_STATUS_ERROR;
-                      default:
-                        g_assert_not_reached ();
-                        return G_IO_STATUS_ERROR; /* keep the compiler happy */
-                    }
-                }
+      if (channel->write_buf->len >= channel->buf_size)
+        {
+          gsize did_write = 0, this_time;
 
-              skip = badchar - (channel->partial_write_buf + partial_chars);
+          do
+            {
+              status = channel->funcs->io_write (channel, channel->write_buf->str
+                                                 + did_write, channel->write_buf->len
+                                                 - did_write, &this_time, error);
+              did_write += this_time;
+            }
+          while (status == G_IO_STATUS_NORMAL &&
+                 did_write < MIN (channel->write_buf->len,
+                             MAX (MAX_CHAR_SIZE, channel->buf_size / 4)));
 
-              buf += skip;
-              copy_bytes = count - skip;
+          g_string_erase (channel->write_buf, 0, did_write);
 
-              g_string_append_len (channel->write_buf, channel->partial_write_buf,
-                                  badchar - channel->partial_write_buf);
-              channel->partial_write_buf[0] = '\0';
+          if (status != G_IO_STATUS_NORMAL)
+            {
+              if (status == G_IO_STATUS_AGAIN && wrote_bytes > 0)
+                status = G_IO_STATUS_NORMAL;
+              if (bytes_written)
+                *bytes_written = wrote_bytes;
+              return status;
             }
-          else
-            copy_bytes = count;
+        }
 
-          if (!g_utf8_validate (buf, copy_bytes, &badchar))
-            {
-              gunichar try_char;
-              gsize left_len = buf + copy_bytes - badchar;
+      space_in_buf = MAX (channel->buf_size, channel->write_buf->allocated_len)
+                     - channel->write_buf->len;
 
-              try_char = g_utf8_get_char_validated (badchar, left_len);
+      /* This is only true because g_io_channel_set_buffer_size ()
+       * ensures that channel->buf_size >= MAX_CHAR_SIZE.
+       */
+      g_assert (space_in_buf >= MAX_CHAR_SIZE);
 
-              switch (try_char)
-                {
-                  case -2:
-                    g_assert (left_len < 6);
-                    break;
-                  case -1:
-                    g_set_error (error, G_CONVERT_ERROR,
-                                 G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
-                                 "Illegal UTF-8 sequence");
-                    *bytes_written = count - copy_bytes;
-                    return G_IO_STATUS_ERROR;
-                  default:
-                    g_assert_not_reached ();
-                    return G_IO_STATUS_ERROR; /* Don't confunse the compiler */
-                }
+      if (!channel->encoding)
+        {
+          gssize write_this = MIN (space_in_buf, count - wrote_bytes);
 
-              g_string_append (channel->write_buf, channel->partial_write_buf);
-              memcpy (channel->partial_write_buf, badchar, left_len);
-              channel->partial_write_buf[left_len] = '\0';
-              copy_bytes = count - left_len;
-            }
-          else
-            {
-              g_string_append (channel->write_buf, channel->partial_write_buf);
-              channel->partial_write_buf[0] = '\0';
-              copy_bytes = count;
-            }
+          g_string_append_len (channel->write_buf, buf, write_this);
+          buf += write_this;
+          wrote_bytes += write_this;
         }
       else
         {
-          copy_bytes = count;
-          g_assert (channel->partial_write_buf[0] == '\0');
-        }
-
-      while (copy_bytes > channel->write_buf->allocated_len - channel->write_buf->len)
-        {
-          gsize wrote_bytes;
+          const gchar *from_buf;
+          gsize from_buf_len, from_buf_old_len, left_len;
+          size_t err;
+          gint errnum;
 
-          if (channel->write_buf->len > 0)
+          if (channel->partial_write_buf[0] != '\0')
             {
-              status = channel->funcs->io_write (channel, channel->write_buf->str,
-                                                 channel->write_buf->len, &wrote_bytes,
-                                                 error);
-              g_string_erase (channel->write_buf, 0, wrote_bytes);
+              g_assert (wrote_bytes == 0);
+
+              from_buf = channel->partial_write_buf;
+              from_buf_old_len = strlen (channel->partial_write_buf);
+              g_assert (from_buf_old_len > 0);
+              from_buf_len = MIN (6, from_buf_old_len + count);
+
+              memcpy (channel->partial_write_buf + from_buf_old_len, buf,
+                      from_buf_len - from_buf_old_len);
             }
           else
             {
-              status = channel->funcs->io_write (channel, buf, copy_bytes, &wrote_bytes,
-                                                 error);
-              copy_bytes -= wrote_bytes;
-              buf += wrote_bytes;
-            }
-
-          if (status != G_IO_STATUS_NORMAL)
-            {
-              *bytes_written = count - copy_bytes;
-              if (channel->partial_write_buf[0] != '\0')
-                {
-                  *bytes_written -= strlen (channel->partial_write_buf);
-                  channel->partial_write_buf[0] = '\0';
-                }
-              return status;
+              from_buf = buf;
+              from_buf_len = count - wrote_bytes;
+              from_buf_old_len = 0;
             }
-        }
 
-      g_string_append_len (channel->write_buf, buf, copy_bytes);
-      *bytes_written = count;
-    }
-  else
-    {
-      gsize bytes_to_go = count, buf_space, err;
-      gchar *outbuf;
-      gsize oldlen = channel->write_buf->len;
+reconvert:
 
-      if (channel->partial_write_buf[0] != '\0')
-        {
-          gsize partial_chars = strlen (channel->partial_write_buf);
-          gsize partial_buf_size = MIN (6, partial_chars + count);
-          guint chars_in_buf = partial_buf_size;
-          gchar *inbuf;
-
-          g_assert (partial_chars < 6);
+          if (!channel->do_encode)
+            {
+              const gchar *badchar;
 
-          memcpy (channel->partial_write_buf + partial_chars, buf,
-            chars_in_buf - partial_chars);
-          inbuf = channel->partial_write_buf;
-redo:
-          buf_space = MIN (channel->buf_size / 4, channel->write_buf->allocated_len
-                           - channel->write_buf->len);
-         g_string_set_size (channel->write_buf, channel->write_buf->len
-                             + buf_space);
-          outbuf = channel->write_buf->str + channel->write_buf->len - buf_space;
+              /* UTF-8, just validate, emulate g_iconv */
 
-          err = g_iconv (channel->write_cd, &inbuf, &partial_buf_size,
-                         &outbuf, &buf_space);
+              if (!g_utf8_validate (from_buf, from_buf_len, &badchar))
+                {
+                  gunichar try_char;
 
-          g_string_truncate (channel->write_buf, channel->write_buf->len - buf_space);
+                  left_len = from_buf + from_buf_len - badchar;
 
-          if (err == (size_t) -1)
-            {
-              if (errno == E2BIG)
-                goto redo;
+                  try_char = g_utf8_get_char_validated (badchar, left_len);
 
-              if (errno == EINVAL)
-                {
-                  if (chars_in_buf == partial_buf_size)
+                  switch (try_char)
                     {
-                      /* Didn't convert anything */
-
-                      g_assert (chars_in_buf < 6); /* More of the same character */
-                      g_assert (chars_in_buf == count + partial_chars);
-
-                      channel->partial_write_buf[chars_in_buf] = '\0';
-                      *bytes_written = count;
-                      return G_IO_STATUS_NORMAL;
+                      case -2:
+                        g_assert (left_len < 6);
+                        errnum = EINVAL;
+                        break;
+                      case -1:
+                        errnum = EILSEQ;
+                        break;
+                      default:
+                        g_assert_not_reached ();
+                        errnum = 0; /* Don't confunse the compiler */
                     }
-
-                  g_assert (chars_in_buf - partial_buf_size > partial_chars);
-                  /* Converted the character of which a part was sitting in
-                   * the partial character buffer before the write.
-                   */
+                  err = (size_t) -1;
                 }
               else
                 {
-                  switch (errno)
-                   {
-                      case EINVAL:
-                      case E2BIG:
-                        g_assert_not_reached ();
-                      case EILSEQ:
-                        g_set_error (error, G_CONVERT_ERROR,
-                          G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
-                          _("Invalid byte sequence in conversion input"));
-                      default:
-                        g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
-                          _("Error during conversion: %s"), strerror (errno));
-                   }
-
-                  g_string_truncate (channel->write_buf, oldlen);
-                  *bytes_written = 0;
-                  channel->partial_write_buf[partial_chars] = '\0';
-
-                  return G_IO_STATUS_ERROR;
+                  err = (size_t) 0;
+                  errnum = 0;
+                  left_len = 0;
                 }
-            }
 
-          *bytes_written = (chars_in_buf - partial_buf_size) - partial_chars;
-          buf += *bytes_written;
-          bytes_to_go -= *bytes_written;
-          channel->partial_write_buf[0] = '\0';
-        }
-
-      do
-        {
-          buf_space = MIN (channel->write_buf->allocated_len
-                           - channel->write_buf->len, channel->buf_size / 4);
-
-         g_string_set_size (channel->write_buf, channel->write_buf->len + buf_space);
-          outbuf = channel->write_buf->str + channel->write_buf->len - buf_space;
-    
-          err = g_iconv (channel->write_cd, (gchar**) &buf, &bytes_to_go,
-                        &outbuf, &buf_space);
-
-          g_string_truncate (channel->write_buf, channel->write_buf->len - buf_space);
-          *bytes_written = count - bytes_to_go;
+              g_string_append_len (channel->write_buf, from_buf,
+                                   from_buf_len - left_len);
+              from_buf += from_buf_len - left_len;
+            }
+          else
+            {
+               gchar *outbuf;
+
+               left_len = from_buf_len;
+               outbuf = channel->write_buf->str + channel->write_buf->len;
+               g_string_set_size (channel->write_buf, channel->write_buf->len
+                                  + space_in_buf);
+               err = g_iconv (channel->write_cd, (gchar **) &from_buf, &left_len,
+                              &outbuf, &space_in_buf);
+               errnum = errno;
+               g_string_truncate (channel->write_buf, channel->write_buf->len
+                                  - space_in_buf);
+            }
 
           if (err == (size_t) -1)
             {
-              switch (errno)
+              switch (errnum)
                {
                   case EINVAL:
-                    {
-                      gint bytes_left = count - *bytes_written;
+                    g_assert (left_len < 6);
 
-                      g_assert (bytes_left < 6);
+                    if (from_buf_old_len == 0)
+                      {
+                        /* Not from partial_write_buf */
 
-                      memcpy (channel->partial_write_buf, buf, bytes_left);
-                      channel->partial_write_buf[bytes_left] = '\0';
-                    }
-                    *bytes_written = count;
+                        memcpy (channel->partial_write_buf, from_buf, left_len);
+                        channel->partial_write_buf[left_len] = '\0';
+                        return G_IO_STATUS_NORMAL;
+                      }
 
-                    return G_IO_STATUS_NORMAL;
-                  case E2BIG:
-                    {
-                      gsize wrote_bytes;
+                    /* Working in partial_write_buf */
 
-                      status = channel->funcs->io_write (channel,
-                        channel->write_buf->str, channel->write_buf->len,
-                        &wrote_bytes, error);
-                      g_string_erase (channel->write_buf, 0, wrote_bytes);
+                    if (left_len == from_buf_len)
+                      {
+                        /* Didn't convert anything, must still have
+                         * less than a full character
+                         */
 
-                      if (status != G_IO_STATUS_NORMAL)
-                        return status;
-                    }
-                    continue;
+                        g_assert (count == from_buf_len - from_buf_old_len);
+
+                        channel->partial_write_buf[from_buf_len] = '\0';
+
+                        if (bytes_written)
+                          *bytes_written = count;
+
+                        return G_IO_STATUS_NORMAL;
+                      }
+
+                    g_assert (from_buf_len - left_len >= from_buf_old_len);
+
+                    /* We converted all the old data. This is fine */
+
+                    break;
+                  case E2BIG:
+                    if (from_buf_len == left_len)
+                      {
+                        /* Nothing was written, add enough space for
+                         * at least one character.
+                         */
+                        space_in_buf += MAX_CHAR_SIZE;
+                        goto reconvert;
+                      }
+                    break;
                   case EILSEQ:
                     g_set_error (error, G_CONVERT_ERROR,
                       G_CONVERT_ERROR_ILLEGAL_SEQUENCE,
                       _("Invalid byte sequence in conversion input"));
+                    if (from_buf_old_len > 0 && from_buf_len == left_len)
+                      g_warning ("Illegal sequence due to partial character "
+                                 "at the end of a previous write.\n");
+                    else
+                      wrote_bytes += from_buf_len - left_len - from_buf_old_len;
+                    if (bytes_written)
+                      *bytes_written = wrote_bytes;
+                    channel->partial_write_buf[0] = '\0';
                     return G_IO_STATUS_ERROR;
                   default:
                     g_set_error (error, G_CONVERT_ERROR, G_CONVERT_ERROR_FAILED,
                       _("Error during conversion: %s"), strerror (errno));
+                    if (from_buf_len >= left_len + from_buf_old_len)
+                      wrote_bytes += from_buf_len - left_len - from_buf_old_len;
+                    if (bytes_written)
+                      *bytes_written = wrote_bytes;
+                    channel->partial_write_buf[0] = '\0';
                     return G_IO_STATUS_ERROR;
-               }
-
+                }
             }
-        }
-      while (bytes_to_go > 0);
-    }
 
-  if (channel->write_buf->len > channel->buf_size)
-    {
-      gsize wrote_bytes;
+          g_assert (from_buf_len - left_len >= from_buf_old_len);
 
-      status = channel->funcs->io_write (channel, channel->write_buf->str,
-                                         channel->write_buf->len, &wrote_bytes, error);
+          wrote_bytes += from_buf_len - left_len - from_buf_old_len;
 
-      if (wrote_bytes > 0)
-        g_string_erase (channel->write_buf, 0, wrote_bytes);
+          if (from_buf_old_len > 0)
+            {
+              /* We were working in partial_write_buf */
 
-      return status;
+              buf += from_buf_len - left_len - from_buf_old_len;
+              channel->partial_write_buf[0] = '\0';
+            }
+          else
+            buf = from_buf;
+        }
     }
-  else
-    return G_IO_STATUS_NORMAL;
+
+  if (bytes_written)
+    *bytes_written = wrote_bytes;
+
+  return G_IO_STATUS_NORMAL;
 }
 
+
 /**
  * g_io_channel_error_quark:
  *
index 9c13d6c832e2dcec5b13ba4c9f3a22831864b227..5cad1b3d5351e7b8943373345b197c6998a63b18 100644 (file)
@@ -418,6 +418,7 @@ g_io_channel_new_file (const gchar *filename,
     MODE_A = 1 << 2,
     MODE_PLUS = 1 << 3,
   } mode_num;
+  struct stat buffer;
 
   g_return_val_if_fail (filename != NULL, NULL);
   g_return_val_if_fail (mode != NULL, NULL);
@@ -482,8 +483,17 @@ g_io_channel_new_file (const gchar *filename,
 
   create_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
   fid = open (filename, flags, create_mode);
-  if (fid < 0)
+  if (fid == -1)
+    {
+      g_set_error (error, G_FILE_ERROR,
+                   g_file_error_from_errno (errno),
+                   strerror (errno));
+      return (GIOChannel *)NULL;
+    }
+
+  if (fstat (fid, &buffer) == -1) /* In case someone opens a FIFO */
     {
+      close (fid);
       g_set_error (error, G_FILE_ERROR,
                    g_file_error_from_errno (errno),
                    strerror (errno));
@@ -493,7 +503,8 @@ g_io_channel_new_file (const gchar *filename,
   channel = (GIOChannel *) g_new (GIOUnixChannel, 1);
 
   channel->close_on_unref = TRUE;
-  channel->is_seekable = TRUE;
+  channel->is_seekable = S_ISREG (buffer.st_mode) || S_ISCHR (buffer.st_mode)
+                         || S_ISBLK (buffer.st_mode);
 
   switch (mode_num)
     {